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,124 +1,120 @@
// FILE: src/routes/drawings.js
// 03.2 9) เพิ่ม routes/drawings.js (ใหม่)
// - ใช้ร่วมกับ requirePerm() และ buildScopeWhere()
// - สำหรับจัดการ drawings (ดู/เพิ่ม/แก้ไข/ลบ) ตามสิทธิ์ของผู้ใช้
// Drawings routes
// - CRUD operations for drawings
// - Requires appropriate permissions via requirePerm middleware
// - Uses org scope for all permissions
// - drawing:read, drawing:create, drawing:update, drawing:delete
// - Drawing fields: id (PK), org_id, project_id, dwg_no, dwg_code, title, created_by
// - Basic validation: org_id, dwg_no required for create
// FILE: backend/src/routes/drawings.js
import { Router } from "express";
import sql from "../db/index.js";
import { requirePerm } from "../middleware/requirePerm.js";
import { buildScopeWhere, ownerResolvers } from "../utils/scope.js";
import PERM from "../config/permissions.js";
const r = Router();
const OWN = ownerResolvers(sql, "drawings", "id");
// LIST
r.get(
"/",
requirePerm("drawing.read", { scope: "global" }),
requirePerm("drawings.view", { projectParam: "project_id" }),
async (req, res) => {
const { project_id, org_id, code, q, limit = 50, offset = 0 } = req.query;
const p = req.principal;
const params = [];
const cond = [];
const base = buildScopeWhere(req.principal, {
tableAlias: "d",
orgColumn: "d.org_id",
projectColumn: "d.project_id",
permCode: "drawing.read",
preferProject: true,
});
const extra = [];
const params = {
...base.params,
limit: Number(limit),
offset: Number(offset),
};
if (project_id) {
extra.push("d.project_id = :project_id");
params.project_id = Number(project_id);
if (!p.is_superadmin) {
if (project_id) {
if (!p.inProject(Number(project_id)))
return res.status(403).json({ error: "FORBIDDEN_PROJECT" });
cond.push("d.project_id=?");
params.push(Number(project_id));
} else if (p.project_ids?.length) {
cond.push(
`d.project_id IN (${p.project_ids.map(() => "?").join(",")})`
);
params.push(...p.project_ids);
}
} else if (project_id) {
cond.push("d.project_id=?");
params.push(Number(project_id));
}
if (org_id) {
extra.push("d.org_id = :org_id");
params.org_id = Number(org_id);
cond.push("d.org_id=?");
params.push(Number(org_id));
}
if (code) {
extra.push("d.dwg_code = :code");
params.code = code;
cond.push("d.dwg_code=?");
params.push(code);
}
if (q) {
extra.push("(d.dwg_no LIKE :q OR d.title LIKE :q)");
params.q = `%${q}%`;
cond.push("(d.dwg_no LIKE ? OR d.title LIKE ?)");
params.push(`%${q}%`, `%${q}%`);
}
const where = [base.where, ...extra].filter(Boolean).join(" AND ");
const where = cond.length ? `WHERE ${cond.join(" AND ")}` : "";
const [rows] = await sql.query(
`SELECT d.* FROM drawings d WHERE ${where}
ORDER BY d.id DESC LIMIT :limit OFFSET :offset`,
params
`SELECT d.* FROM drawings d ${where} ORDER BY d.id DESC LIMIT ? OFFSET ?`,
[...params, Number(limit), Number(offset)]
);
res.json(rows);
}
);
// GET
r.get(
"/:id",
requirePerm("drawing.read", { scope: "org", getOrgId: OWN.getOrgIdById }),
async (req, res) => {
const id = Number(req.params.id);
const [[row]] = await sql.query("SELECT * FROM drawings WHERE id=?", [id]);
if (!row) return res.status(404).json({ error: "Not found" });
res.json(row);
}
);
r.get("/:id", requirePerm("drawings.view"), async (req, res) => {
const id = Number(req.params.id);
const [[row]] = await sql.query("SELECT * FROM drawings WHERE id=?", [id]);
if (!row) return res.status(404).json({ error: "Not found" });
const p = req.principal;
if (!p.is_superadmin && !p.inProject(row.project_id))
return res.status(403).json({ error: "FORBIDDEN_PROJECT" });
res.json(row);
});
// CREATE
r.post(
"/",
requirePerm("drawing.create", {
scope: "org",
getOrgId: async (req) => req.body?.org_id ?? null,
}),
requirePerm("drawings.upload", { projectParam: "project_id" }),
async (req, res) => {
const { org_id, project_id, dwg_no, dwg_code, title } = req.body;
const { org_id, project_id, dwg_no, dwg_code, title } = req.body || {};
if (!project_id || !dwg_no)
return res.status(400).json({ error: "project_id and dwg_no required" });
const [rs] = await sql.query(
`INSERT INTO drawings (org_id, project_id, dwg_no, dwg_code, title, created_by)
VALUES (?,?,?,?,?,?)`,
[org_id, project_id, dwg_no, dwg_code, title, req.principal.userId]
VALUES (?,?,?,?,?,?)`,
[
org_id || null,
project_id,
dwg_no,
dwg_code || null,
title || null,
req.principal.user_id,
]
);
res.json({ id: rs.insertId });
}
);
// UPDATE
r.put(
"/:id",
requirePerm("drawing.update", { scope: "org", getOrgId: OWN.getOrgIdById }),
async (req, res) => {
const id = Number(req.params.id);
const { title } = req.body;
await sql.query("UPDATE drawings SET title=? WHERE id=?", [title, id]);
res.json({ ok: 1 });
}
);
// UPDATE (ใช้สิทธิ์ drawings.upload)
r.put("/:id", requirePerm("drawings.upload"), async (req, res) => {
const id = Number(req.params.id);
const [[row]] = await sql.query("SELECT * FROM drawings WHERE id=?", [id]);
if (!row) return res.status(404).json({ error: "Not found" });
const p = req.principal;
if (!p.is_superadmin && !p.inProject(row.project_id))
return res.status(403).json({ error: "FORBIDDEN_PROJECT" });
const { title } = req.body || {};
await sql.query("UPDATE drawings SET title=? WHERE id=?", [
title ?? row.title,
id,
]);
res.json({ ok: 1 });
});
// DELETE
r.delete(
"/:id",
requirePerm("drawing.delete", { scope: "org", getOrgId: OWN.getOrgIdById }),
async (req, res) => {
const id = Number(req.params.id);
await sql.query("DELETE FROM drawings WHERE id=?", [id]);
res.json({ ok: 1 });
}
);
r.delete("/:id", requirePerm("drawings.delete"), async (req, res) => {
const id = Number(req.params.id);
const [[row]] = await sql.query("SELECT * FROM drawings WHERE id=?", [id]);
if (!row) return res.status(404).json({ error: "Not found" });
const p = req.principal;
if (!p.is_superadmin && !p.inProject(row.project_id))
return res.status(403).json({ error: "FORBIDDEN_PROJECT" });
await sql.query("DELETE FROM drawings WHERE id=?", [id]);
res.json({ ok: 1 });
});
export default r;