05.1 ปรบปรง backend ทงหมด และ frontend/login

This commit is contained in:
admin
2025-10-01 11:14:11 +07:00
parent 5be0f5407b
commit 905afb56f5
43 changed files with 2285 additions and 2834 deletions

View File

@@ -1,50 +1,49 @@
// FILE: src/routes/projects.js
// 03.2 6) เพิ่ม routes/projects.js (ใหม่)
// - ใช้ร่วมกับ requirePerm() และ buildScopeWhere()
// - สำหรับจัดการ projects (ดู/เพิ่ม/แก้ไข/ลบ) ตามสิทธิ์ของผู้ใช้
// Projects routes
// - CRUD operations for projects
// - Requires appropriate permissions via requirePerm middleware
// - Uses org/project scope for all permissions
// - project:read, project:create, project:update, project:delete
// - Project fields: project_id (PK), org_id (FK), project_code, project_name
// - project_code is unique
// - Basic validation: org_id, project_code, project_name required for create
// FILE: backend/src/routes/projects.js
import { Router } from "express";
import sql from "../db/index.js";
import { requirePerm } from "../middleware/requirePerm.js";
import { buildScopeWhere } from "../utils/scope.js";
const r = Router();
// LIST จำกัดตาม org/project scope ของผู้ใช้
// LIST — ORG scope
r.get(
"/",
requirePerm("project.read", { scope: "global" }),
requirePerm("projects.view", { orgParam: "org_id" }),
async (req, res) => {
const { where, params } = buildScopeWhere(req.principal, {
tableAlias: "p",
orgColumn: "p.org_id",
projectColumn: "p.project_id",
permCode: "project.read",
preferProject: true,
});
const p = req.principal;
const { org_id } = req.query;
const params = [];
const cond = [];
if (!p.is_superadmin) {
if (org_id) {
if (!p.inOrg(Number(org_id)))
return res.status(403).json({ error: "FORBIDDEN_ORG" });
cond.push("p.org_id=?");
params.push(Number(org_id));
} else if (p.org_ids?.length) {
cond.push(`p.org_id IN (${p.org_ids.map(() => "?").join(",")})`);
params.push(...p.org_ids);
}
} else if (org_id) {
cond.push("p.org_id=?");
params.push(Number(org_id));
}
const where = cond.length ? `WHERE ${cond.join(" AND ")}` : "";
const [rows] = await sql.query(
`SELECT p.* FROM projects p WHERE ${where}`,
`SELECT p.* FROM projects p ${where} ORDER BY p.project_name`,
params
);
res.json(rows);
}
);
// GET
// GET — PROJECT scope
r.get(
"/:id",
requirePerm("project.read", {
scope: "project",
getProjectId: async (req) => Number(req.params.id),
}),
requirePerm("projects.view", { orgParam: "org_id" }),
async (req, res) => {
const id = Number(req.params.id);
const [[row]] = await sql.query(
@@ -52,54 +51,71 @@ r.get(
[id]
);
if (!row) return res.status(404).json({ error: "Not found" });
const p = req.principal;
if (!p.is_superadmin && !p.inOrg(row.org_id))
return res.status(403).json({ error: "FORBIDDEN_ORG" });
res.json(row);
}
);
// CREATE
// CREATE — ORG scope
r.post(
"/",
requirePerm("project.create", {
scope: "org",
getOrgId: async (req) => req.body?.org_id ?? null,
}),
requirePerm("projects.manage", { orgParam: "org_id" }),
async (req, res) => {
const { org_id, project_code, project_name } = req.body;
const { org_id, project_code, project_name } = req.body || {};
if (!org_id || !project_code || !project_name) {
return res
.status(400)
.json({ error: "org_id, project_code, project_name required" });
}
const [rs] = await sql.query(
"INSERT INTO projects (org_id, project_code, project_name) VALUES (?,?,?)",
[org_id, project_code, project_name]
"INSERT INTO projects (org_id, project_code, project_name, created_by) VALUES (?,?,?,?)",
[Number(org_id), project_code, project_name, req.principal.user_id]
);
res.json({ project_id: rs.insertId });
res.status(201).json({ project_id: rs.insertId });
}
);
// UPDATE
// UPDATE — ORG scope
r.put(
"/:id",
requirePerm("project.update", {
scope: "project",
getProjectId: async (req) => Number(req.params.id),
}),
requirePerm("projects.manage", { orgParam: "org_id" }),
async (req, res) => {
const { project_name } = req.body;
const id = Number(req.params.id);
await sql.query("UPDATE projects SET project_name=? WHERE project_id=?", [
project_name,
id,
]);
const [[row]] = await sql.query(
"SELECT * FROM projects WHERE project_id=?",
[id]
);
if (!row) return res.status(404).json({ error: "Not found" });
const p = req.principal;
if (!p.is_superadmin && !p.inOrg(row.org_id))
return res.status(403).json({ error: "FORBIDDEN_ORG" });
const { project_name } = req.body || {};
await sql.query(
"UPDATE projects SET project_name=?, updated_by=? WHERE project_id=?",
[project_name ?? row.project_name, req.principal.user_id, id]
);
res.json({ ok: 1 });
}
);
// DELETE
// DELETE — ORG scope
r.delete(
"/:id",
requirePerm("project.delete", {
scope: "project",
getProjectId: async (req) => Number(req.params.id),
}),
requirePerm("projects.manage", { orgParam: "org_id" }),
async (req, res) => {
const id = Number(req.params.id);
const [[row]] = await sql.query(
"SELECT * FROM projects WHERE project_id=?",
[id]
);
if (!row) return res.status(404).json({ error: "Not found" });
const p = req.principal;
if (!p.is_superadmin && !p.inOrg(row.org_id))
return res.status(403).json({ error: "FORBIDDEN_ORG" });
await sql.query("DELETE FROM projects WHERE project_id=?", [id]);
res.json({ ok: 1 });
}