Update frontend login page.jsx และ backend

This commit is contained in:
admin
2025-09-29 13:25:09 +07:00
parent aca3667a9d
commit 7dd5ce8015
52 changed files with 2903 additions and 1289 deletions

View File

@@ -1,15 +1,23 @@
import { Router } from 'express';
import { requireAuth } from '../middleware/auth.js';
import { enrichPermissions } from '../middleware/permissions.js';
import { requireRole } from '../middleware/rbac.js';
import { requirePerm } from '../middleware/permGuard.js';
import { sequelize } from '../db/sequelize.js';
import RfaModel from '../db/models/RFA.js';
import DrawingModel from '../db/models/Drawing.js';
import RfaDrawMapModel from '../db/models/RfaDrawingMap.js';
import CorrModel from '../db/models/Correspondence.js';
import DocModel from '../db/models/Document.js';
import CorrDocMapModel from '../db/models/CorrDocumentMap.js';
// FILE: src/routes/maps.js
// Maps routes
// - Manage relationships between RFAs and Drawings, Correspondences and Documents
// - Requires appropriate permissions via requirePerm middleware
// - Uses project scope for RFA-Drawing maps and Correspondence-Document maps
// - rfa:update for RFA-Drawing maps
// - correspondence:update for Correspondence-Document maps
import { Router } from "express";
import { requireAuth } from "../middleware/auth.js";
import { enrichPermissions } from "../middleware/permissions.js";
import { requireRole } from "../middleware/rbac.js";
import { requirePerm } from "../middleware/permGuard.js";
import { sequelize } from "../db/sequelize.js";
import RfaModel from "../db/models/RFA.js";
import DrawingModel from "../db/models/Drawing.js";
import RfaDrawMapModel from "../db/models/RfaDrawingMap.js";
import CorrModel from "../db/models/Correspondence.js";
import DocModel from "../db/models/Document.js";
import CorrDocMapModel from "../db/models/CorrDocumentMap.js";
const r = Router();
const RFA = RfaModel(sequelize);
@@ -22,62 +30,121 @@ const CorrDoc = CorrDocMapModel(sequelize);
async function ensureRfaMembership(req, res) {
const rfaId = Number(req.params.rfa_id);
const row = await RFA.findByPk(rfaId);
if (!row) { res.status(404).json({ error:'RFA not found' }); return false; }
const roles = req.user?.roles || []; const isAdmin = roles.includes('Admin');
if (!row) {
res.status(404).json({ error: "RFA not found" });
return false;
}
const roles = req.user?.roles || [];
const isAdmin = roles.includes("Admin");
if (isAdmin) return true;
const { getUserProjectIds } = await import('../middleware/abac.js');
const { getUserProjectIds } = await import("../middleware/abac.js");
const memberProjects = await getUserProjectIds(req.user?.user_id);
if (!memberProjects.includes(Number(row.project_id))) { res.status(403).json({ error:'Forbidden: not a project member' }); return false; }
if (!memberProjects.includes(Number(row.project_id))) {
res.status(403).json({ error: "Forbidden: not a project member" });
return false;
}
return true;
}
async function ensureCorrMembership(req, res) {
const corrId = Number(req.params.corr_id);
const row = await Corr.findByPk(corrId);
if (!row) { res.status(404).json({ error:'Correspondence not found' }); return false; }
const roles = req.user?.roles || []; const isAdmin = roles.includes('Admin');
if (!row) {
res.status(404).json({ error: "Correspondence not found" });
return false;
}
const roles = req.user?.roles || [];
const isAdmin = roles.includes("Admin");
if (isAdmin) return true;
const { getUserProjectIds } = await import('../middleware/abac.js');
const { getUserProjectIds } = await import("../middleware/abac.js");
const memberProjects = await getUserProjectIds(req.user?.user_id);
if (!memberProjects.includes(Number(row.project_id))) { res.status(403).json({ error:'Forbidden: not a project member' }); return false; }
if (!memberProjects.includes(Number(row.project_id))) {
res.status(403).json({ error: "Forbidden: not a project member" });
return false;
}
return true;
}
// RFA <-> Drawing
r.get('/maps/rfa/:rfa_id/drawings', requireAuth, async (req, res) => {
const rows = await RfaDraw.findAll({ where: { rfa_id: Number(req.params.rfa_id) } });
r.get("/maps/rfa/:rfa_id/drawings", requireAuth, async (req, res) => {
const rows = await RfaDraw.findAll({
where: { rfa_id: Number(req.params.rfa_id) },
});
res.json(rows);
});
r.post('/maps/rfa/:rfa_id/drawings/:drawing_id', requireAuth, enrichPermissions(), requirePerm('rfa:update'), async (req, res) => {
if (!(await ensureRfaMembership(req, res))) return;
const { rfa_id, drawing_id } = { rfa_id: Number(req.params.rfa_id), drawing_id: Number(req.params.drawing_id) };
await RfaDraw.create({ rfa_id, drawing_id });
res.status(201).json({ ok: true });
});
r.delete('/maps/rfa/:rfa_id/drawings/:drawing_id', requireAuth, enrichPermissions(), requirePerm('rfa:update'), async (req, res) => {
if (!(await ensureRfaMembership(req, res))) return;
const { rfa_id, drawing_id } = { rfa_id: Number(req.params.rfa_id), drawing_id: Number(req.params.drawing_id) };
const count = await RfaDraw.destroy({ where: { rfa_id, drawing_id } });
res.json({ ok: count > 0 });
});
r.post(
"/maps/rfa/:rfa_id/drawings/:drawing_id",
requireAuth,
enrichPermissions(),
requirePerm("rfa:update"),
async (req, res) => {
if (!(await ensureRfaMembership(req, res))) return;
const { rfa_id, drawing_id } = {
rfa_id: Number(req.params.rfa_id),
drawing_id: Number(req.params.drawing_id),
};
await RfaDraw.create({ rfa_id, drawing_id });
res.status(201).json({ ok: true });
}
);
r.delete(
"/maps/rfa/:rfa_id/drawings/:drawing_id",
requireAuth,
enrichPermissions(),
requirePerm("rfa:update"),
async (req, res) => {
if (!(await ensureRfaMembership(req, res))) return;
const { rfa_id, drawing_id } = {
rfa_id: Number(req.params.rfa_id),
drawing_id: Number(req.params.drawing_id),
};
const count = await RfaDraw.destroy({ where: { rfa_id, drawing_id } });
res.json({ ok: count > 0 });
}
);
// Correspondence <-> Document
r.get('/maps/correspondence/:corr_id/documents', requireAuth, async (req, res) => {
const rows = await CorrDoc.findAll({ where: { correspondence_id: Number(req.params.corr_id) } });
res.json(rows);
});
r.post('/maps/correspondence/:corr_id/documents/:doc_id', requireAuth, enrichPermissions(), requirePerm('correspondence:update'), async (req, res) => {
if (!(await ensureCorrMembership(req, res))) return;
const { corr_id, doc_id } = { corr_id: Number(req.params.corr_id), doc_id: Number(req.params.doc_id) };
await CorrDoc.create({ correspondence_id: corr_id, document_id: doc_id });
res.status(201).json({ ok: true });
});
r.delete('/maps/correspondence/:corr_id/documents/:doc_id', requireAuth, enrichPermissions(), requirePerm('correspondence:update'), async (req, res) => {
if (!(await ensureCorrMembership(req, res))) return;
const { corr_id, doc_id } = { corr_id: Number(req.params.corr_id), doc_id: Number(req.params.doc_id) };
const count = await CorrDoc.destroy({ where: { correspondence_id: corr_id, document_id: doc_id } });
res.json({ ok: count > 0 });
});
r.get(
"/maps/correspondence/:corr_id/documents",
requireAuth,
async (req, res) => {
const rows = await CorrDoc.findAll({
where: { correspondence_id: Number(req.params.corr_id) },
});
res.json(rows);
}
);
r.post(
"/maps/correspondence/:corr_id/documents/:doc_id",
requireAuth,
enrichPermissions(),
requirePerm("correspondence:update"),
async (req, res) => {
if (!(await ensureCorrMembership(req, res))) return;
const { corr_id, doc_id } = {
corr_id: Number(req.params.corr_id),
doc_id: Number(req.params.doc_id),
};
await CorrDoc.create({ correspondence_id: corr_id, document_id: doc_id });
res.status(201).json({ ok: true });
}
);
r.delete(
"/maps/correspondence/:corr_id/documents/:doc_id",
requireAuth,
enrichPermissions(),
requirePerm("correspondence:update"),
async (req, res) => {
if (!(await ensureCorrMembership(req, res))) return;
const { corr_id, doc_id } = {
corr_id: Number(req.params.corr_id),
doc_id: Number(req.params.doc_id),
};
const count = await CorrDoc.destroy({
where: { correspondence_id: corr_id, document_id: doc_id },
});
res.json({ ok: count > 0 });
}
);
export default r;