esameits24-26backend/src/routes/corsi.ts

259 lines
6.6 KiB
TypeScript
Raw Normal View History

import { Router } from "express";
import type { ResultSetHeader, RowDataPacket } from "mysql2";
import pool from "../db/pool.js";
import { mapDbError } from "../errors/db.js";
import { requireAuth, requireReferenteAcademy } from "../middleware/auth.js";
import { validateBody } from "../validation/middleware.js";
import {
validateCorsoCreate,
validateCorsoUpdate,
type CorsoCreateInput,
type CorsoUpdateInput,
} from "../validation/corso.js";
const router = Router();
interface CorsoRow extends RowDataPacket {
CorsoID: number;
Titolo: string;
Descrizione: string | null;
Categoria: string;
DurataOre: number;
Obbligatorio: number;
Attivo: number;
}
function toCorsoResponse(row: CorsoRow) {
return {
id: row.CorsoID,
titolo: row.Titolo,
descrizione: row.Descrizione,
categoria: row.Categoria,
durataOre: row.DurataOre,
obbligatorio: Boolean(row.Obbligatorio),
attivo: Boolean(row.Attivo),
};
}
function parseIdParam(value: string | string[]): number | null {
const raw = Array.isArray(value) ? value[0] : value;
const id = Number(raw);
if (!Number.isInteger(id) || id <= 0) return null;
return id;
}
function parseAttivoFilter(value: unknown): boolean | null {
if (value === "true" || value === true) return true;
if (value === "false" || value === false) return false;
return null;
}
async function findCorsoById(id: number): Promise<CorsoRow | undefined> {
const [rows] = await pool.execute<CorsoRow[]>(
`SELECT CorsoID, Titolo, Descrizione, Categoria, DurataOre, Obbligatorio, Attivo
FROM corso
WHERE CorsoID = ?`,
[id]
);
return rows[0];
}
router.use(requireAuth);
router.get("/", async (req, res) => {
const conditions: string[] = [];
const values: (string | number)[] = [];
const categoria = req.query.categoria?.toString().trim();
if (categoria) {
conditions.push("Categoria = ?");
values.push(categoria);
}
const attivo = parseAttivoFilter(req.query.attivo);
if (attivo !== null) {
conditions.push("Attivo = ?");
values.push(attivo ? 1 : 0);
}
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
const [rows] = await pool.execute<CorsoRow[]>(
`SELECT CorsoID, Titolo, Descrizione, Categoria, DurataOre, Obbligatorio, Attivo
FROM corso
${whereClause}
ORDER BY Categoria, Titolo`,
values
);
res.json(rows.map(toCorsoResponse));
});
router.get("/:id", async (req, res) => {
const id = parseIdParam(req.params.id);
if (!id) {
res.status(400).json({ error: "ID non valido" });
return;
}
const corso = await findCorsoById(id);
if (!corso) {
res.status(404).json({ error: "Corso non trovato" });
return;
}
res.json(toCorsoResponse(corso));
});
router.post("/", requireReferenteAcademy, validateBody(validateCorsoCreate), async (_req, res) => {
const body = res.locals.validatedBody as CorsoCreateInput;
try {
const [result] = await pool.execute<ResultSetHeader>(
`INSERT INTO corso (Titolo, Descrizione, Categoria, DurataOre, Obbligatorio, Attivo)
VALUES (?, ?, ?, ?, ?, ?)`,
[
body.titolo,
body.descrizione,
body.categoria,
body.durataOre,
body.obbligatorio ? 1 : 0,
body.attivo ? 1 : 0,
]
);
const created = await findCorsoById(result.insertId);
if (!created) {
res.status(500).json({ error: "Errore durante la creazione del corso" });
return;
}
res.status(201).json(toCorsoResponse(created));
} catch (error) {
const mapped = mapDbError(error);
if (mapped) {
res.status(mapped.status).json({
error: mapped.error,
fields: mapped.fields,
});
return;
}
throw error;
}
});
router.put("/:id", requireReferenteAcademy, validateBody(validateCorsoUpdate), async (req, res) => {
const id = parseIdParam(req.params.id);
if (!id) {
res.status(400).json({ error: "ID non valido" });
return;
}
const existing = await findCorsoById(id);
if (!existing) {
res.status(404).json({ error: "Corso non trovato" });
return;
}
const updates = res.locals.validatedBody as CorsoUpdateInput;
const setClauses: string[] = [];
const values: (string | number | null)[] = [];
if (updates.titolo !== undefined) {
setClauses.push("Titolo = ?");
values.push(updates.titolo);
}
if (updates.descrizione !== undefined) {
setClauses.push("Descrizione = ?");
values.push(updates.descrizione);
}
if (updates.categoria !== undefined) {
setClauses.push("Categoria = ?");
values.push(updates.categoria);
}
if (updates.durataOre !== undefined) {
setClauses.push("DurataOre = ?");
values.push(updates.durataOre);
}
if (updates.obbligatorio !== undefined) {
setClauses.push("Obbligatorio = ?");
values.push(updates.obbligatorio ? 1 : 0);
}
if (updates.attivo !== undefined) {
setClauses.push("Attivo = ?");
values.push(updates.attivo ? 1 : 0);
}
try {
await pool.execute<ResultSetHeader>(
`UPDATE corso SET ${setClauses.join(", ")} WHERE CorsoID = ?`,
[...values, id]
);
const updated = await findCorsoById(id);
if (!updated) {
res.status(500).json({ error: "Errore durante l'aggiornamento del corso" });
return;
}
res.json(toCorsoResponse(updated));
} catch (error) {
const mapped = mapDbError(error);
if (mapped) {
res.status(mapped.status).json({
error: mapped.error,
fields: mapped.fields,
});
return;
}
throw error;
}
});
router.put("/:id/disattiva", requireReferenteAcademy, async (req, res) => {
const id = parseIdParam(req.params.id);
if (!id) {
res.status(400).json({ error: "ID non valido" });
return;
}
const existing = await findCorsoById(id);
if (!existing) {
res.status(404).json({ error: "Corso non trovato" });
return;
}
await pool.execute("UPDATE corso SET Attivo = 0 WHERE CorsoID = ?", [id]);
const updated = await findCorsoById(id);
res.json(toCorsoResponse(updated!));
});
router.delete("/:id", requireReferenteAcademy, async (req, res) => {
const id = parseIdParam(req.params.id);
if (!id) {
res.status(400).json({ error: "ID non valido" });
return;
}
const existing = await findCorsoById(id);
if (!existing) {
res.status(404).json({ error: "Corso non trovato" });
return;
}
try {
await pool.execute("DELETE FROM corso WHERE CorsoID = ?", [id]);
res.status(204).send();
} catch (error) {
const mapped = mapDbError(error);
if (mapped) {
res.status(mapped.status).json({ error: mapped.error });
return;
}
throw error;
}
});
export default router;