feat(dashboard): เพมสวนจดการ user
This commit is contained in:
@@ -10,6 +10,9 @@ import { authJwt } from "./middleware/authJwt.js";
|
||||
import { loadPrincipalMw } from "./middleware/loadPrincipal.js";
|
||||
|
||||
// ROUTES
|
||||
import usersRoutes from "./routes/users.js";
|
||||
import rbacAdminRoutes from "./routes/rbac_admin.js";
|
||||
import dashboardRoutes from "./routes/dashboard.js";
|
||||
import authRoutes from "./routes/auth.js";
|
||||
import lookupRoutes from "./routes/lookup.js";
|
||||
import organizationsRoutes from "./routes/organizations.js";
|
||||
@@ -148,6 +151,8 @@ app.use("/api/volumes", volumesRoutes);
|
||||
app.use("/api/uploads", uploadsRoutes);
|
||||
app.use("/api/users", usersRoutes);
|
||||
app.use("/api/permissions", permissionsRoutes);
|
||||
app.use("/api/rbac", rbacAdminRoutes);
|
||||
app.use("/api/dashboard", dashboardRoutes);
|
||||
|
||||
// 404 / error
|
||||
app.use((req, res) =>
|
||||
|
||||
56
backend/src/routes/dashboard copy.js
Normal file
56
backend/src/routes/dashboard copy.js
Normal file
@@ -0,0 +1,56 @@
|
||||
// backend/src/routes/dashboard.js
|
||||
import { Router } from "express";
|
||||
import { Op } from "sequelize";
|
||||
import { Correspondence, Document, RFA, User } from "../db/index.js"; // import models
|
||||
import { authJwt } from "../middleware/index.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Middleware: ตรวจสอบสิทธิ์สำหรับทุก route ในไฟล์นี้
|
||||
router.use(authJwt.verifyToken);
|
||||
|
||||
// === API สำหรับ User Management Widget ===
|
||||
router.get("/users/summary", async (req, res, next) => {
|
||||
try {
|
||||
const totalUsers = await User.count();
|
||||
const activeUsers = await User.count({ where: { is_active: true } });
|
||||
// ดึง user ที่สร้างล่าสุด 5 คน
|
||||
const recentUsers = await User.findAll({
|
||||
limit: 5,
|
||||
order: [["createdAt", "DESC"]],
|
||||
attributes: ["id", "username", "email", "createdAt"],
|
||||
});
|
||||
|
||||
res.json({
|
||||
total: totalUsers,
|
||||
active: activeUsers,
|
||||
inactive: totalUsers - activeUsers,
|
||||
recent: recentUsers,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// === API อื่นๆ สำหรับ Dashboard ที่เราคุยกันไว้ก่อนหน้า ===
|
||||
router.get("/stats", async (req, res, next) => {
|
||||
try {
|
||||
const sevenDaysAgo = new Date(new Date().setDate(new Date().getDate() - 7));
|
||||
|
||||
const totalDocuments = await Document.count();
|
||||
const newThisWeek = await Document.count({
|
||||
where: { createdAt: { [Op.gte]: sevenDaysAgo } },
|
||||
});
|
||||
const pendingRfas = await RFA.count({ where: { status: "pending" } }); // สมมติตาม status
|
||||
|
||||
res.json({
|
||||
totalDocuments,
|
||||
newThisWeek,
|
||||
pendingRfas,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
23
backend/src/routes/dashboard.js
Normal file
23
backend/src/routes/dashboard.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Router } from "express";
|
||||
import { User } from "../db/index.js";
|
||||
import { authJwt } from "../middleware/index.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use(authJwt.verifyToken);
|
||||
|
||||
router.get("/users/summary", async (req, res, next) => {
|
||||
try {
|
||||
const totalUsers = await User.count();
|
||||
const activeUsers = await User.count({ where: { is_active: true } });
|
||||
res.json({
|
||||
total: totalUsers,
|
||||
active: activeUsers,
|
||||
inactive: totalUsers - activeUsers,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
126
backend/src/routes/rbac_admin copy.js
Normal file
126
backend/src/routes/rbac_admin copy.js
Normal file
@@ -0,0 +1,126 @@
|
||||
// FILE: backend/src/routes/rbac_admin.js
|
||||
// RBAC admin — ใช้ settings.manage ทั้งหมด
|
||||
import { Router } from "express";
|
||||
import sql from "../db/index.js";
|
||||
import { requirePerm } from "../middleware/requirePerm.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
// ROLES
|
||||
r.get("/roles", requirePerm("settings.manage"), async (_req, res) => {
|
||||
const [rows] = await sql.query(
|
||||
"SELECT role_id, role_code, role_name, description FROM roles ORDER BY role_code"
|
||||
);
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
// PERMISSIONS
|
||||
r.get("/permissions", requirePerm("settings.manage"), async (_req, res) => {
|
||||
const [rows] = await sql.query(
|
||||
"SELECT permission_id, perm_code AS permission_code, scope_level, description FROM permissions ORDER BY perm_code"
|
||||
);
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
// role -> permissions
|
||||
r.get(
|
||||
"/roles/:role_id/permissions",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const role_id = Number(req.params.role_id);
|
||||
const [rows] = await sql.query(
|
||||
`SELECT p.permission_id, p.perm_code AS permission_code, p.description
|
||||
FROM role_permissions rp
|
||||
JOIN permissions p ON p.permission_id = rp.permission_id
|
||||
WHERE rp.role_id=? ORDER BY p.perm_code`,
|
||||
[role_id]
|
||||
);
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
|
||||
r.post(
|
||||
"/roles/:role_id/permissions",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const role_id = Number(req.params.role_id);
|
||||
const { permission_id } = req.body || {};
|
||||
await sql.query(
|
||||
"INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?,?)",
|
||||
[role_id, Number(permission_id)]
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
}
|
||||
);
|
||||
|
||||
r.delete(
|
||||
"/roles/:role_id/permissions/:permission_id",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const role_id = Number(req.params.role_id);
|
||||
const permission_id = Number(req.params.permission_id);
|
||||
await sql.query(
|
||||
"DELETE FROM role_permissions WHERE role_id=? AND permission_id=?",
|
||||
[role_id, permission_id]
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
}
|
||||
);
|
||||
|
||||
// user -> roles (global/org/project scope columns มีในตาราง user_roles ตามสคีมา)
|
||||
r.get(
|
||||
"/users/:user_id/roles",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const user_id = Number(req.params.user_id);
|
||||
const [rows] = await sql.query(
|
||||
`SELECT ur.user_id, ur.role_id, r.role_code, r.role_name, ur.org_id, ur.project_id
|
||||
FROM user_roles ur JOIN roles r ON r.role_id = ur.role_id
|
||||
WHERE ur.user_id=? ORDER BY r.role_code`,
|
||||
[user_id]
|
||||
);
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
|
||||
r.post(
|
||||
"/users/:user_id/roles",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const user_id = Number(req.params.user_id);
|
||||
const { role_id, org_id = null, project_id = null } = req.body || {};
|
||||
await sql.query(
|
||||
"INSERT INTO user_roles (user_id, role_id, org_id, project_id) VALUES (?,?,?,?)",
|
||||
[
|
||||
user_id,
|
||||
Number(role_id),
|
||||
org_id ? Number(org_id) : null,
|
||||
project_id ? Number(project_id) : null,
|
||||
]
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
}
|
||||
);
|
||||
|
||||
r.delete(
|
||||
"/users/:user_id/roles",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const user_id = Number(req.params.user_id);
|
||||
const { role_id, org_id = null, project_id = null } = req.body || {};
|
||||
// สร้างเงื่อนไขแบบ dynamic สำหรับ NULL-safe compare
|
||||
const whereOrg = org_id === null ? "ur.org_id IS NULL" : "ur.org_id = ?";
|
||||
const wherePrj =
|
||||
project_id === null ? "ur.project_id IS NULL" : "ur.project_id = ?";
|
||||
const params = [user_id, Number(role_id)];
|
||||
if (org_id !== null) params.push(Number(org_id));
|
||||
if (project_id !== null) params.push(Number(project_id));
|
||||
await sql.query(
|
||||
`DELETE FROM user_roles ur WHERE ur.user_id=? AND ur.role_id=? AND ${whereOrg} AND ${wherePrj}`,
|
||||
params
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
}
|
||||
);
|
||||
|
||||
export default r;
|
||||
@@ -1,126 +1,144 @@
|
||||
// FILE: backend/src/routes/rbac_admin.js
|
||||
// RBAC admin — ใช้ settings.manage ทั้งหมด
|
||||
import { Router } from "express";
|
||||
import sql from "../db/index.js";
|
||||
import { requirePerm } from "../middleware/requirePerm.js";
|
||||
import { Role, Permission, UserProjectRole, Project } from "../db/index.js";
|
||||
import { authJwt, permGuard } from "../middleware/index.js";
|
||||
|
||||
const r = Router();
|
||||
const router = Router();
|
||||
|
||||
// ROLES
|
||||
r.get("/roles", requirePerm("settings.manage"), async (_req, res) => {
|
||||
const [rows] = await sql.query(
|
||||
"SELECT role_id, role_code, role_name, description FROM roles ORDER BY role_code"
|
||||
);
|
||||
res.json(rows);
|
||||
// กำหนดให้ทุก route ในไฟล์นี้ต้องมีสิทธิ์ 'manage_rbac'
|
||||
router.use(authJwt.verifyToken, permGuard("manage_rbac"));
|
||||
|
||||
// == ROLES Management ==
|
||||
router.get("/roles", async (req, res, next) => {
|
||||
try {
|
||||
const roles = await Role.findAll({
|
||||
include: [
|
||||
{
|
||||
model: Permission,
|
||||
attributes: ["id", "name"],
|
||||
through: { attributes: [] },
|
||||
},
|
||||
],
|
||||
order: [["name", "ASC"]],
|
||||
});
|
||||
res.json(roles);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// PERMISSIONS
|
||||
r.get("/permissions", requirePerm("settings.manage"), async (_req, res) => {
|
||||
const [rows] = await sql.query(
|
||||
"SELECT permission_id, perm_code AS permission_code, scope_level, description FROM permissions ORDER BY perm_code"
|
||||
);
|
||||
res.json(rows);
|
||||
router.post("/roles", async (req, res, next) => {
|
||||
try {
|
||||
const { name, description } = req.body;
|
||||
if (!name)
|
||||
return res.status(400).json({ message: "Role name is required." });
|
||||
const newRole = await Role.create({ name, description });
|
||||
res.status(201).json(newRole);
|
||||
} catch (error) {
|
||||
if (error.name === "SequelizeUniqueConstraintError") {
|
||||
return res
|
||||
.status(409)
|
||||
.json({ message: `Role '${name}' already exists.` });
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// role -> permissions
|
||||
r.get(
|
||||
"/roles/:role_id/permissions",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const role_id = Number(req.params.role_id);
|
||||
const [rows] = await sql.query(
|
||||
`SELECT p.permission_id, p.perm_code AS permission_code, p.description
|
||||
FROM role_permissions rp
|
||||
JOIN permissions p ON p.permission_id = rp.permission_id
|
||||
WHERE rp.role_id=? ORDER BY p.perm_code`,
|
||||
[role_id]
|
||||
);
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
router.put("/roles/:id/permissions", async (req, res, next) => {
|
||||
try {
|
||||
const { permissionIds } = req.body;
|
||||
if (!Array.isArray(permissionIds))
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "permissionIds must be an array." });
|
||||
|
||||
r.post(
|
||||
"/roles/:role_id/permissions",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const role_id = Number(req.params.role_id);
|
||||
const { permission_id } = req.body || {};
|
||||
await sql.query(
|
||||
"INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?,?)",
|
||||
[role_id, Number(permission_id)]
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
}
|
||||
);
|
||||
const role = await Role.findByPk(req.params.id);
|
||||
if (!role) return res.status(404).json({ message: "Role not found." });
|
||||
|
||||
r.delete(
|
||||
"/roles/:role_id/permissions/:permission_id",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const role_id = Number(req.params.role_id);
|
||||
const permission_id = Number(req.params.permission_id);
|
||||
await sql.query(
|
||||
"DELETE FROM role_permissions WHERE role_id=? AND permission_id=?",
|
||||
[role_id, permission_id]
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
await role.setPermissions(permissionIds);
|
||||
const updatedRole = await Role.findByPk(req.params.id, {
|
||||
include: [
|
||||
{
|
||||
model: Permission,
|
||||
attributes: ["id", "name"],
|
||||
through: { attributes: [] },
|
||||
},
|
||||
],
|
||||
});
|
||||
res.json(updatedRole);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// user -> roles (global/org/project scope columns มีในตาราง user_roles ตามสคีมา)
|
||||
r.get(
|
||||
"/users/:user_id/roles",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const user_id = Number(req.params.user_id);
|
||||
const [rows] = await sql.query(
|
||||
`SELECT ur.user_id, ur.role_id, r.role_code, r.role_name, ur.org_id, ur.project_id
|
||||
FROM user_roles ur JOIN roles r ON r.role_id = ur.role_id
|
||||
WHERE ur.user_id=? ORDER BY r.role_code`,
|
||||
[user_id]
|
||||
);
|
||||
res.json(rows);
|
||||
// == PERMISSIONS Management ==
|
||||
router.get("/permissions", async (req, res, next) => {
|
||||
try {
|
||||
const permissions = await Permission.findAll({ order: [["name", "ASC"]] });
|
||||
res.json(permissions);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
r.post(
|
||||
"/users/:user_id/roles",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const user_id = Number(req.params.user_id);
|
||||
const { role_id, org_id = null, project_id = null } = req.body || {};
|
||||
await sql.query(
|
||||
"INSERT INTO user_roles (user_id, role_id, org_id, project_id) VALUES (?,?,?,?)",
|
||||
[
|
||||
user_id,
|
||||
Number(role_id),
|
||||
org_id ? Number(org_id) : null,
|
||||
project_id ? Number(project_id) : null,
|
||||
]
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
// == USER-PROJECT-ROLES Management ==
|
||||
router.get("/user-project-roles", async (req, res, next) => {
|
||||
const { userId } = req.query;
|
||||
if (!userId)
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "userId query parameter is required." });
|
||||
try {
|
||||
const assignments = await UserProjectRole.findAll({
|
||||
where: { user_id: userId },
|
||||
include: [
|
||||
{ model: Project, attributes: ["id", "name"] },
|
||||
{ model: Role, attributes: ["id", "name"] },
|
||||
],
|
||||
});
|
||||
res.json(assignments);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
r.delete(
|
||||
"/users/:user_id/roles",
|
||||
requirePerm("settings.manage"),
|
||||
async (req, res) => {
|
||||
const user_id = Number(req.params.user_id);
|
||||
const { role_id, org_id = null, project_id = null } = req.body || {};
|
||||
// สร้างเงื่อนไขแบบ dynamic สำหรับ NULL-safe compare
|
||||
const whereOrg = org_id === null ? "ur.org_id IS NULL" : "ur.org_id = ?";
|
||||
const wherePrj =
|
||||
project_id === null ? "ur.project_id IS NULL" : "ur.project_id = ?";
|
||||
const params = [user_id, Number(role_id)];
|
||||
if (org_id !== null) params.push(Number(org_id));
|
||||
if (project_id !== null) params.push(Number(project_id));
|
||||
await sql.query(
|
||||
`DELETE FROM user_roles ur WHERE ur.user_id=? AND ur.role_id=? AND ${whereOrg} AND ${wherePrj}`,
|
||||
params
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
router.post("/user-project-roles", async (req, res, next) => {
|
||||
const { userId, projectId, roleId } = req.body;
|
||||
if (!userId || !projectId || !roleId)
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "userId, projectId, and roleId are required." });
|
||||
try {
|
||||
const [assignment, created] = await UserProjectRole.findOrCreate({
|
||||
where: { user_id: userId, project_id: projectId, role_id: roleId },
|
||||
defaults: { user_id: userId, project_id: projectId, role_id: roleId },
|
||||
});
|
||||
if (!created)
|
||||
return res
|
||||
.status(409)
|
||||
.json({ message: "This assignment already exists." });
|
||||
res.status(201).json(assignment);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export default r;
|
||||
router.delete("/user-project-roles", async (req, res, next) => {
|
||||
const { userId, projectId, roleId } = req.body;
|
||||
if (!userId || !projectId || !roleId)
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "userId, projectId, and roleId are required." });
|
||||
try {
|
||||
const deletedCount = await UserProjectRole.destroy({
|
||||
where: { user_id: userId, project_id: projectId, role_id: roleId },
|
||||
});
|
||||
if (deletedCount === 0)
|
||||
return res.status(404).json({ message: "Assignment not found." });
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
55
backend/src/routes/users copy.js
Normal file
55
backend/src/routes/users copy.js
Normal file
@@ -0,0 +1,55 @@
|
||||
// FILE: backend/src/routes/users.js
|
||||
import { Router } from "express";
|
||||
import sql from "../db/index.js";
|
||||
import { requirePerm } from "../middleware/requirePerm.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
// ME (ทุกคน)
|
||||
r.get("/me", async (req, res) => {
|
||||
const p = req.principal;
|
||||
const [[u]] = await sql.query(
|
||||
`SELECT user_id, username, email, first_name, last_name, org_id FROM users WHERE user_id=?`,
|
||||
[p.user_id]
|
||||
);
|
||||
if (!u) return res.status(404).json({ error: "User not found" });
|
||||
const [roles] = await sql.query(
|
||||
`SELECT r.role_code, r.role_name, ur.org_id, ur.project_id
|
||||
FROM user_roles ur JOIN roles r ON r.role_id = ur.role_id
|
||||
WHERE ur.user_id=?`,
|
||||
[p.user_id]
|
||||
);
|
||||
res.json({
|
||||
...u,
|
||||
roles,
|
||||
role_codes: roles.map((r) => r.role_code),
|
||||
permissions: [...(p.permissions || [])],
|
||||
project_ids: p.project_ids,
|
||||
org_ids: p.org_ids,
|
||||
is_superadmin: p.is_superadmin,
|
||||
});
|
||||
});
|
||||
|
||||
// USERS LIST (ORG scope) — admin.access
|
||||
r.get(
|
||||
"/",
|
||||
requirePerm("admin.access", { orgParam: "org_id" }),
|
||||
async (req, res) => {
|
||||
const P = req.principal;
|
||||
let rows = [];
|
||||
if (P.is_superadmin) {
|
||||
[rows] = await sql.query(
|
||||
"SELECT user_id, username, email, org_id FROM users ORDER BY user_id DESC LIMIT 500"
|
||||
);
|
||||
} else if (P.org_ids?.length) {
|
||||
const inSql = P.org_ids.map(() => "?").join(",");
|
||||
[rows] = await sql.query(
|
||||
`SELECT user_id, username, email, org_id FROM users WHERE org_id IN (${inSql}) ORDER BY user_id DESC LIMIT 500`,
|
||||
P.org_ids
|
||||
);
|
||||
}
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
|
||||
export default r;
|
||||
@@ -1,55 +1,136 @@
|
||||
// FILE: backend/src/routes/users.js
|
||||
// File: backend/src/routes/users.js
|
||||
import { Router } from "express";
|
||||
import sql from "../db/index.js";
|
||||
import { requirePerm } from "../middleware/requirePerm.js";
|
||||
import { User, Role } from "../db/index.js";
|
||||
import { authJwt, permGuard } from "../middleware/index.js";
|
||||
import { hashPassword } from "../utils/passwords.js";
|
||||
|
||||
const r = Router();
|
||||
const router = Router();
|
||||
|
||||
// ME (ทุกคน)
|
||||
r.get("/me", async (req, res) => {
|
||||
const p = req.principal;
|
||||
const [[u]] = await sql.query(
|
||||
`SELECT user_id, username, email, first_name, last_name, org_id FROM users WHERE user_id=?`,
|
||||
[p.user_id]
|
||||
);
|
||||
if (!u) return res.status(404).json({ error: "User not found" });
|
||||
const [roles] = await sql.query(
|
||||
`SELECT r.role_code, r.role_name, ur.org_id, ur.project_id
|
||||
FROM user_roles ur JOIN roles r ON r.role_id = ur.role_id
|
||||
WHERE ur.user_id=?`,
|
||||
[p.user_id]
|
||||
);
|
||||
res.json({
|
||||
...u,
|
||||
roles,
|
||||
role_codes: roles.map((r) => r.role_code),
|
||||
permissions: [...(p.permissions || [])],
|
||||
project_ids: p.project_ids,
|
||||
org_ids: p.org_ids,
|
||||
is_superadmin: p.is_superadmin,
|
||||
});
|
||||
});
|
||||
// Middleware สำหรับทุก route ในไฟล์นี้
|
||||
router.use(authJwt.verifyToken);
|
||||
|
||||
// USERS LIST (ORG scope) — admin.access
|
||||
r.get(
|
||||
// GET /api/users - ดึงรายชื่อผู้ใช้ทั้งหมด
|
||||
router.get(
|
||||
"/",
|
||||
requirePerm("admin.access", { orgParam: "org_id" }),
|
||||
async (req, res) => {
|
||||
const P = req.principal;
|
||||
let rows = [];
|
||||
if (P.is_superadmin) {
|
||||
[rows] = await sql.query(
|
||||
"SELECT user_id, username, email, org_id FROM users ORDER BY user_id DESC LIMIT 500"
|
||||
);
|
||||
} else if (P.org_ids?.length) {
|
||||
const inSql = P.org_ids.map(() => "?").join(",");
|
||||
[rows] = await sql.query(
|
||||
`SELECT user_id, username, email, org_id FROM users WHERE org_id IN (${inSql}) ORDER BY user_id DESC LIMIT 500`,
|
||||
P.org_ids
|
||||
);
|
||||
permGuard("manage_users"), // ตรวจสอบสิทธิ์
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const users = await User.findAll({
|
||||
attributes: { exclude: ["password_hash"] }, // **สำคัญมาก: ห้ามส่ง password hash ออกไป**
|
||||
include: [
|
||||
{
|
||||
model: Role,
|
||||
attributes: ["id", "name"],
|
||||
through: { attributes: [] }, // ไม่ต้องเอาข้อมูลจากตาราง join (UserRoles) มา
|
||||
},
|
||||
],
|
||||
order: [["username", "ASC"]],
|
||||
});
|
||||
res.json(users);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
|
||||
export default r;
|
||||
// POST /api/users - สร้างผู้ใช้ใหม่
|
||||
router.post("/", permGuard("manage_users"), async (req, res, next) => {
|
||||
const { username, email, password, first_name, last_name, is_active, roles } =
|
||||
req.body;
|
||||
|
||||
if (!username || !email || !password) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Username, email, and password are required" });
|
||||
}
|
||||
|
||||
try {
|
||||
const password_hash = await hashPassword(password);
|
||||
const newUser = await User.create({
|
||||
username,
|
||||
email,
|
||||
password_hash,
|
||||
first_name,
|
||||
last_name,
|
||||
is_active: is_active !== false,
|
||||
});
|
||||
|
||||
if (roles && roles.length > 0) {
|
||||
await newUser.setRoles(roles);
|
||||
}
|
||||
|
||||
const userWithRoles = await User.findByPk(newUser.id, {
|
||||
attributes: { exclude: ["password_hash"] },
|
||||
include: [
|
||||
{
|
||||
model: Role,
|
||||
attributes: ["id", "name"],
|
||||
through: { attributes: [] },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
res.status(201).json(userWithRoles);
|
||||
} catch (error) {
|
||||
if (error.name === "SequelizeUniqueConstraintError") {
|
||||
return res
|
||||
.status(409)
|
||||
.json({ message: "Username or email already exists." });
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/users/:id - อัปเดตข้อมูลผู้ใช้
|
||||
router.put("/:id", permGuard("manage_users"), async (req, res, next) => {
|
||||
const { id } = req.params;
|
||||
const { email, first_name, last_name, is_active, roles } = req.body;
|
||||
|
||||
try {
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" });
|
||||
}
|
||||
|
||||
user.email = email ?? user.email;
|
||||
user.first_name = first_name ?? user.first_name;
|
||||
user.last_name = last_name ?? user.last_name;
|
||||
user.is_active = is_active ?? user.is_active;
|
||||
await user.save();
|
||||
|
||||
if (roles) {
|
||||
await user.setRoles(roles);
|
||||
}
|
||||
|
||||
const updatedUser = await User.findByPk(id, {
|
||||
attributes: { exclude: ["password_hash"] },
|
||||
include: [
|
||||
{
|
||||
model: Role,
|
||||
attributes: ["id", "name"],
|
||||
through: { attributes: [] },
|
||||
},
|
||||
],
|
||||
});
|
||||
res.json(updatedUser);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/users/:id - ลบผู้ใช้ (Soft Delete)
|
||||
router.delete("/:id", permGuard("manage_users"), async (req, res, next) => {
|
||||
try {
|
||||
const user = await User.findByPk(req.params.id);
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" });
|
||||
}
|
||||
user.is_active = false; // Soft Delete
|
||||
await user.save();
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user