Improve error handling and ordering flows
This commit is contained in:
@@ -1,105 +1,136 @@
|
||||
const pool = require("../db");
|
||||
|
||||
const clean = (value) => value === "" ? null : value;
|
||||
|
||||
const decimal = (value) => {
|
||||
if(value === undefined || value === null || value === "") return null;
|
||||
|
||||
return String(value).replace(",", ".");
|
||||
};
|
||||
|
||||
const isMissingPositionColumn = (error) =>
|
||||
error && error.code === "ER_BAD_FIELD_ERROR" && String(error.sqlMessage || "").includes("position");
|
||||
|
||||
const attachIps = async (resources) => {
|
||||
if(!resources.length) return resources;
|
||||
|
||||
const ids = resources.map((resource) => resource.id);
|
||||
const placeholders = ids.map(() => "?").join(",");
|
||||
|
||||
const [ipRows] = await pool.query(
|
||||
`SELECT id, resource_id, ip, type, comment
|
||||
FROM resource_ips
|
||||
WHERE resource_id IN (${placeholders})
|
||||
ORDER BY id ASC`,
|
||||
ids
|
||||
);
|
||||
|
||||
const ipMap = new Map();
|
||||
|
||||
ipRows.forEach((ipRow) => {
|
||||
if(!ipMap.has(ipRow.resource_id)){
|
||||
ipMap.set(ipRow.resource_id, []);
|
||||
}
|
||||
|
||||
ipMap.get(ipRow.resource_id).push({
|
||||
id: ipRow.id,
|
||||
ip: ipRow.ip,
|
||||
type: ipRow.type,
|
||||
comment: ipRow.comment
|
||||
});
|
||||
});
|
||||
|
||||
return resources.map((resource) => ({
|
||||
...resource,
|
||||
ips: ipMap.get(resource.id) || []
|
||||
}));
|
||||
};
|
||||
|
||||
/* ACTIVE */
|
||||
exports.getActive = async (req,res)=>{
|
||||
try{
|
||||
let rows;
|
||||
|
||||
const [rows] = await pool.query(`
|
||||
SELECT
|
||||
r.*,
|
||||
JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'id', ip.id,
|
||||
'ip', ip.ip,
|
||||
'type', ip.type,
|
||||
'comment', ip.comment
|
||||
)
|
||||
) AS ips
|
||||
try{
|
||||
[rows] = await pool.query(`
|
||||
SELECT r.*
|
||||
FROM resources r
|
||||
LEFT JOIN resource_ips ip ON r.id = ip.resource_id
|
||||
WHERE r.status != 'gekündigt'
|
||||
GROUP BY r.id
|
||||
ORDER BY COALESCE(position, id) ASC
|
||||
`);
|
||||
}catch(e){
|
||||
|
||||
if(!isMissingPositionColumn(e)) throw e;
|
||||
|
||||
[rows] = await pool.query(`
|
||||
SELECT r.*
|
||||
FROM resources r
|
||||
WHERE r.status != 'gekündigt'
|
||||
ORDER BY r.id ASC
|
||||
`);
|
||||
|
||||
rows.forEach(r=>{
|
||||
|
||||
/* JSON string → array */
|
||||
if(typeof r.ips === "string"){
|
||||
try{
|
||||
r.ips = JSON.parse(r.ips);
|
||||
}catch{
|
||||
r.ips=[];
|
||||
}
|
||||
}
|
||||
|
||||
/* remove null entries */
|
||||
if(r.ips && r.ips[0] && r.ips[0].id === null){
|
||||
r.ips=[];
|
||||
res.json(await attachIps(rows));
|
||||
|
||||
}catch(e){
|
||||
|
||||
console.error("GET active resources error:",e);
|
||||
res.status(500).json({error:"Ressourcen konnten nicht geladen werden"});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
res.json(rows);
|
||||
|
||||
};
|
||||
|
||||
/* CANCELLED */
|
||||
|
||||
exports.getCancelled = async (req,res)=>{
|
||||
try{
|
||||
|
||||
const [rows] = await pool.query(`
|
||||
SELECT
|
||||
r.*,
|
||||
JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'id', ip.id,
|
||||
'ip', ip.ip,
|
||||
'type', ip.type,
|
||||
'comment', ip.comment
|
||||
)
|
||||
) AS ips
|
||||
let rows;
|
||||
|
||||
try{
|
||||
[rows] = await pool.query(`
|
||||
SELECT r.*
|
||||
FROM resources r
|
||||
LEFT JOIN resource_ips ip ON r.id = ip.resource_id
|
||||
WHERE r.status = 'gekündigt'
|
||||
GROUP BY r.id
|
||||
ORDER BY COALESCE(position, id) ASC
|
||||
`);
|
||||
}catch(e){
|
||||
|
||||
if(!isMissingPositionColumn(e)) throw e;
|
||||
|
||||
[rows] = await pool.query(`
|
||||
SELECT r.*
|
||||
FROM resources r
|
||||
WHERE r.status = 'gekündigt'
|
||||
ORDER BY r.id ASC
|
||||
`);
|
||||
|
||||
rows.forEach(r=>{
|
||||
|
||||
if(typeof r.ips === "string"){
|
||||
try{
|
||||
r.ips = JSON.parse(r.ips);
|
||||
}catch{
|
||||
r.ips=[];
|
||||
}
|
||||
}
|
||||
|
||||
if(r.ips && r.ips[0] && r.ips[0].id === null){
|
||||
r.ips=[];
|
||||
res.json(await attachIps(rows));
|
||||
|
||||
}catch(e){
|
||||
|
||||
console.error("GET cancelled resources error:",e);
|
||||
res.status(500).json({error:"Gekuendigte Ressourcen konnten nicht geladen werden"});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
res.json(rows);
|
||||
|
||||
};
|
||||
|
||||
/* CREATE */
|
||||
|
||||
exports.create = async (req,res)=>{
|
||||
try{
|
||||
|
||||
const clean = v => v === "" ? null : v;
|
||||
|
||||
function decimal(val){
|
||||
|
||||
if(!val) return null
|
||||
|
||||
return String(val).replace(",", ".")
|
||||
|
||||
if(!req.body.name || !String(req.body.name).trim()){
|
||||
return res.status(400).json({error:"Name darf nicht leer sein"});
|
||||
}
|
||||
|
||||
|
||||
const data = {
|
||||
name: req.body.name,
|
||||
name: String(req.body.name).trim(),
|
||||
produkt: clean(req.body.produkt),
|
||||
provider: clean(req.body.provider),
|
||||
art: clean(req.body.art),
|
||||
@@ -121,14 +152,41 @@ kuendbar_ab: clean(req.body.kuendbar_ab),
|
||||
|
||||
status: clean(req.body.status),
|
||||
kuendigungsdatum: clean(req.body.kuendigungsdatum),
|
||||
|
||||
bemerkung: clean(req.body.bemerkung)
|
||||
};
|
||||
|
||||
try{
|
||||
const [[positionRow]] = await pool.query(
|
||||
"SELECT COALESCE(MAX(position), 0) + 1 AS nextPosition FROM resources"
|
||||
);
|
||||
|
||||
data.position = positionRow.nextPosition;
|
||||
}catch(e){
|
||||
|
||||
if(!isMissingPositionColumn(e)) throw e;
|
||||
|
||||
}
|
||||
|
||||
await pool.query("INSERT INTO resources SET ?",data);
|
||||
|
||||
res.json({message:"created"});
|
||||
|
||||
}catch(e){
|
||||
|
||||
console.error("CREATE resource error:",e);
|
||||
|
||||
let message = "Ressource konnte nicht gespeichert werden";
|
||||
let status = 500;
|
||||
|
||||
if(e.code === "WARN_DATA_TRUNCATED"){
|
||||
message = "Ungueltiges Zahlenformat";
|
||||
status = 400;
|
||||
}
|
||||
|
||||
res.status(status).json({error:message});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -136,10 +194,23 @@ res.json({message:"created"});
|
||||
/* UPDATE */
|
||||
|
||||
exports.update = async (req,res)=>{
|
||||
try{
|
||||
|
||||
const clean = v => v === "" ? null : v;
|
||||
if(req.body.name !== undefined && !String(req.body.name).trim()){
|
||||
return res.status(400).json({error:"Name darf nicht leer sein"});
|
||||
}
|
||||
|
||||
Object.keys(req.body).forEach(k=>{
|
||||
if(k === "kosten_monat" || k === "kosten_jahr"){
|
||||
req.body[k] = decimal(req.body[k]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(k === "name" && req.body[k] !== undefined && req.body[k] !== null){
|
||||
req.body[k] = String(req.body[k]).trim();
|
||||
return;
|
||||
}
|
||||
|
||||
req.body[k]=clean(req.body[k]);
|
||||
});
|
||||
|
||||
@@ -150,12 +221,29 @@ await pool.query(
|
||||
|
||||
res.json({message:"updated"});
|
||||
|
||||
}catch(e){
|
||||
|
||||
console.error("UPDATE resource error:",e);
|
||||
|
||||
let message = "Ressource konnte nicht gespeichert werden";
|
||||
let status = 500;
|
||||
|
||||
if(e.code === "WARN_DATA_TRUNCATED"){
|
||||
message = "Ungueltiges Zahlenformat";
|
||||
status = 400;
|
||||
}
|
||||
|
||||
res.status(status).json({error:message});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* DELETE */
|
||||
|
||||
exports.remove = async (req,res)=>{
|
||||
try{
|
||||
|
||||
await pool.query(
|
||||
"DELETE FROM resources WHERE id=?",
|
||||
@@ -164,4 +252,11 @@ await pool.query(
|
||||
|
||||
res.json({message:"deleted"});
|
||||
|
||||
}catch(e){
|
||||
|
||||
console.error("DELETE resource error:",e);
|
||||
res.status(500).json({error:"Ressource konnte nicht geloescht werden"});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user