import { Router } from "@oak/oak"; import { db } from "../config/database.ts"; import { authenticateToken, authorize, getCurrentUser } from "../middleware/auth.ts"; import { sanitizeInput } from "../middleware/security.ts"; import type { Department, SubDepartment } from "../types/index.ts"; const router = new Router(); // Get all departments router.get("/", authenticateToken, async (ctx) => { try { const departments = await db.query( "SELECT * FROM departments ORDER BY name" ); ctx.response.body = departments; } catch (error) { console.error("Get departments error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Get department by ID router.get("/:id", authenticateToken, async (ctx) => { try { const deptId = ctx.params.id; const departments = await db.query( "SELECT * FROM departments WHERE id = ?", [deptId] ); if (departments.length === 0) { ctx.response.status = 404; ctx.response.body = { error: "Department not found" }; return; } ctx.response.body = departments[0]; } catch (error) { console.error("Get department error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Get sub-departments by department ID router.get("/:id/sub-departments", authenticateToken, async (ctx) => { try { const deptId = ctx.params.id; const subDepartments = await db.query( "SELECT * FROM sub_departments WHERE department_id = ? ORDER BY name", [deptId] ); ctx.response.body = subDepartments; } catch (error) { console.error("Get sub-departments error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Create department (SuperAdmin only) router.post("/", authenticateToken, authorize("SuperAdmin"), async (ctx) => { try { const body = await ctx.request.body.json() as { name: string }; const { name } = body; if (!name) { ctx.response.status = 400; ctx.response.body = { error: "Department name required" }; return; } const sanitizedName = sanitizeInput(name); const result = await db.execute( "INSERT INTO departments (name) VALUES (?)", [sanitizedName] ); const newDepartment = await db.query( "SELECT * FROM departments WHERE id = ?", [result.insertId] ); ctx.response.status = 201; ctx.response.body = newDepartment[0]; } catch (error) { const err = error as { code?: string }; if (err.code === "ER_DUP_ENTRY") { ctx.response.status = 400; ctx.response.body = { error: "Department already exists" }; return; } console.error("Create department error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Create sub-department (SuperAdmin or Supervisor for their own department) router.post("/sub-departments", authenticateToken, async (ctx) => { try { const user = getCurrentUser(ctx); const body = await ctx.request.body.json() as { department_id: number; name: string }; const { department_id, name } = body; if (!name || !department_id) { ctx.response.status = 400; ctx.response.body = { error: "Department ID and name are required" }; return; } // Check authorization if (user.role === 'Supervisor' && user.departmentId !== department_id) { ctx.response.status = 403; ctx.response.body = { error: "You can only create sub-departments for your own department" }; return; } if (user.role !== 'SuperAdmin' && user.role !== 'Supervisor') { ctx.response.status = 403; ctx.response.body = { error: "Unauthorized" }; return; } const sanitizedName = sanitizeInput(name); const result = await db.execute( "INSERT INTO sub_departments (department_id, name) VALUES (?, ?)", [department_id, sanitizedName] ); const newSubDepartment = await db.query( "SELECT * FROM sub_departments WHERE id = ?", [result.lastInsertId] ); ctx.response.status = 201; ctx.response.body = newSubDepartment[0]; } catch (error) { const err = error as { code?: string }; if (err.code === "ER_DUP_ENTRY") { ctx.response.status = 400; ctx.response.body = { error: "Sub-department already exists in this department" }; return; } console.error("Create sub-department error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Delete sub-department (SuperAdmin or Supervisor for their own department) router.delete("/sub-departments/:id", authenticateToken, async (ctx) => { try { const user = getCurrentUser(ctx); const subDeptId = ctx.params.id; // Get the sub-department to check department ownership const subDepts = await db.query( "SELECT * FROM sub_departments WHERE id = ?", [subDeptId] ); if (subDepts.length === 0) { ctx.response.status = 404; ctx.response.body = { error: "Sub-department not found" }; return; } const subDept = subDepts[0]; // Check authorization if (user.role === 'Supervisor' && user.departmentId !== subDept.department_id) { ctx.response.status = 403; ctx.response.body = { error: "You can only delete sub-departments from your own department" }; return; } if (user.role !== 'SuperAdmin' && user.role !== 'Supervisor') { ctx.response.status = 403; ctx.response.body = { error: "Unauthorized" }; return; } // Delete associated activities first (cascade should handle this, but being explicit) await db.execute("DELETE FROM activities WHERE sub_department_id = ?", [subDeptId]); // Delete the sub-department await db.execute("DELETE FROM sub_departments WHERE id = ?", [subDeptId]); ctx.response.body = { message: "Sub-department deleted successfully" }; } catch (error) { console.error("Delete sub-department error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Legacy route for creating sub-department under specific department (SuperAdmin only) router.post("/:id/sub-departments", authenticateToken, authorize("SuperAdmin"), async (ctx) => { try { const deptId = ctx.params.id; const body = await ctx.request.body.json() as { name: string }; const { name } = body; if (!name) { ctx.response.status = 400; ctx.response.body = { error: "Name is required" }; return; } const sanitizedName = sanitizeInput(name); const result = await db.execute( "INSERT INTO sub_departments (department_id, name) VALUES (?, ?)", [deptId, sanitizedName] ); const newSubDepartment = await db.query( "SELECT * FROM sub_departments WHERE id = ?", [result.lastInsertId] ); ctx.response.status = 201; ctx.response.body = newSubDepartment[0]; } catch (error) { const err = error as { code?: string }; if (err.code === "ER_DUP_ENTRY") { ctx.response.status = 400; ctx.response.body = { error: "Sub-department already exists in this department" }; return; } console.error("Create sub-department error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); export default router;