Update frontend login page.jsx และ backend
This commit is contained in:
@@ -1,86 +1,160 @@
|
||||
// src/routes/view.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';
|
||||
// FILE: src/routes/view.js
|
||||
// Saved Views routes
|
||||
// - CRUD operations for saved views
|
||||
// - Requires appropriate permissions via requirePerm middleware
|
||||
// - Supports filtering and pagination on list endpoint
|
||||
// - Uses ownerResolvers utility to determine org ownership for permission checks
|
||||
// - Permissions required are defined in config/permissions.js
|
||||
// - savedview.read
|
||||
// - savedview.create
|
||||
// - savedview.update
|
||||
// - savedview.delete
|
||||
// - Scope can be 'global' (list), 'org' (get/create/update/delete)
|
||||
// - List endpoint supports filtering by project_id, org_id, shared flag, and search query (q)
|
||||
// - Pagination via limit and offset query parameters
|
||||
// - Results ordered by id DESC
|
||||
// - Error handling for not found and no fields to update scenarios
|
||||
// - Uses async/await for asynchronous operations
|
||||
// - SQL queries use parameterized queries to prevent SQL injection
|
||||
// - Responses are in JSON format
|
||||
// - Middleware functions are used for permission checks
|
||||
// - Owner resolvers are used to fetch org_id for specific view ids
|
||||
// - Code is modular and organized for maintainability
|
||||
// - Comments are provided for clarity/documentation
|
||||
// - Follows best practices for Express.js route handling
|
||||
// - Uses ES6+ features for cleaner code
|
||||
// - Assumes existence of saved_views table with appropriate columns
|
||||
// - Assumes existence of users table for owner
|
||||
// - Assumes existence of config/permissions.js with defined permission codes
|
||||
// - Assumes existence of utils/scope.js with buildScopeWhere and ownerResolvers functions
|
||||
// - Assumes existence of middleware/requirePerm.js for permission checks
|
||||
// - Assumes existence of db/index.js for database connection/querying
|
||||
// - Assumes Express.js app is set up to use this router for /api/saved_views path
|
||||
|
||||
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, 'saved_views', 'id');
|
||||
const OWN = ownerResolvers(sql, "saved_views", "id");
|
||||
|
||||
// LIST: GET /api/view?project_id=&org_id=&shared=1
|
||||
r.get('/',
|
||||
requirePerm(PERM.savedview.read, { scope: 'global' }),
|
||||
r.get(
|
||||
"/",
|
||||
requirePerm(PERM.savedview.read, { scope: "global" }),
|
||||
async (req, res) => {
|
||||
const { project_id, org_id, shared, q, limit = 50, offset = 0 } = req.query;
|
||||
|
||||
const base = buildScopeWhere(req.principal, {
|
||||
tableAlias: 'v',
|
||||
orgColumn: 'v.org_id',
|
||||
projectColumn: 'v.project_id',
|
||||
tableAlias: "v",
|
||||
orgColumn: "v.org_id",
|
||||
projectColumn: "v.project_id",
|
||||
permCode: PERM.savedview.read,
|
||||
preferProject: true,
|
||||
});
|
||||
|
||||
const extra = [];
|
||||
const params = { ...base.params, limit: Number(limit), offset: Number(offset), my: req.principal.userId };
|
||||
if (project_id) { extra.push('v.project_id = :project_id'); params.project_id = Number(project_id); }
|
||||
if (org_id) { extra.push('v.org_id = :org_id'); params.org_id = Number(org_id); }
|
||||
if (shared === '1') extra.push('v.is_shared = 1');
|
||||
if (q) { extra.push('(v.name LIKE :q)'); params.q = `%${q}%`; }
|
||||
const params = {
|
||||
...base.params,
|
||||
limit: Number(limit),
|
||||
offset: Number(offset),
|
||||
my: req.principal.userId,
|
||||
};
|
||||
if (project_id) {
|
||||
extra.push("v.project_id = :project_id");
|
||||
params.project_id = Number(project_id);
|
||||
}
|
||||
if (org_id) {
|
||||
extra.push("v.org_id = :org_id");
|
||||
params.org_id = Number(org_id);
|
||||
}
|
||||
if (shared === "1") extra.push("v.is_shared = 1");
|
||||
if (q) {
|
||||
extra.push("(v.name LIKE :q)");
|
||||
params.q = `%${q}%`;
|
||||
}
|
||||
|
||||
// ให้ผู้ใช้เห็นของตัวเองเสมอ + ของที่อยู่ใน scope
|
||||
const where = `(${base.where}) AND (v.is_shared=1 OR v.owner_user_id=:my${extra.length ? ' OR ' + extra.join(' AND ') : ''})`;
|
||||
const where = `(${base.where}) AND (v.is_shared=1 OR v.owner_user_id=:my${
|
||||
extra.length ? " OR " + extra.join(" AND ") : ""
|
||||
})`;
|
||||
|
||||
const [rows] = await sql.query(
|
||||
`SELECT v.* FROM saved_views v
|
||||
WHERE ${where}
|
||||
ORDER BY v.id DESC
|
||||
LIMIT :limit OFFSET :offset`, params
|
||||
LIMIT :limit OFFSET :offset`,
|
||||
params
|
||||
);
|
||||
res.json(rows);
|
||||
}
|
||||
);
|
||||
|
||||
// GET by id
|
||||
r.get('/:id',
|
||||
requirePerm(PERM.savedview.read, { scope: 'org', getOrgId: OWN.getOrgIdById }),
|
||||
r.get(
|
||||
"/:id",
|
||||
requirePerm(PERM.savedview.read, {
|
||||
scope: "org",
|
||||
getOrgId: OWN.getOrgIdById,
|
||||
}),
|
||||
async (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
const [[row]] = await sql.query('SELECT * FROM saved_views WHERE id=?', [id]);
|
||||
if (!row) return res.status(404).json({ error: 'Not found' });
|
||||
const [[row]] = await sql.query("SELECT * FROM saved_views WHERE id=?", [
|
||||
id,
|
||||
]);
|
||||
if (!row) return res.status(404).json({ error: "Not found" });
|
||||
res.json(row);
|
||||
}
|
||||
);
|
||||
|
||||
// CREATE
|
||||
r.post('/',
|
||||
requirePerm(PERM.savedview.create, { scope: 'org', getOrgId: async req => req.body?.org_id ?? null }),
|
||||
r.post(
|
||||
"/",
|
||||
requirePerm(PERM.savedview.create, {
|
||||
scope: "org",
|
||||
getOrgId: async (req) => req.body?.org_id ?? null,
|
||||
}),
|
||||
async (req, res) => {
|
||||
const { org_id, project_id, name, payload_json, is_shared = 0 } = req.body;
|
||||
const [rs] = await sql.query(
|
||||
`INSERT INTO saved_views (org_id, project_id, name, payload_json, is_shared, owner_user_id)
|
||||
VALUES (?,?,?,?,?,?)`,
|
||||
[org_id, project_id, name, JSON.stringify(payload_json ?? {}), Number(is_shared) ? 1 : 0, req.principal.userId]
|
||||
[
|
||||
org_id,
|
||||
project_id,
|
||||
name,
|
||||
JSON.stringify(payload_json ?? {}),
|
||||
Number(is_shared) ? 1 : 0,
|
||||
req.principal.userId,
|
||||
]
|
||||
);
|
||||
res.json({ id: rs.insertId });
|
||||
}
|
||||
);
|
||||
|
||||
// UPDATE (เฉพาะใน scope และถ้าเป็นของตนเอง หรือเป็นแอดมินตามนโยบาย)
|
||||
r.put('/:id',
|
||||
requirePerm(PERM.savedview.update, { scope: 'org', getOrgId: OWN.getOrgIdById }),
|
||||
r.put(
|
||||
"/:id",
|
||||
requirePerm(PERM.savedview.update, {
|
||||
scope: "org",
|
||||
getOrgId: OWN.getOrgIdById,
|
||||
}),
|
||||
async (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
const { name, payload_json, is_shared } = req.body;
|
||||
|
||||
// ตรวจ owner ถ้าต้องการบังคับเป็นของตัวเอง (option)
|
||||
const [[sv]] = await sql.query('SELECT owner_user_id FROM saved_views WHERE id=?', [id]);
|
||||
if (!sv) return res.status(404).json({ error: 'Not found' });
|
||||
const [[sv]] = await sql.query(
|
||||
"SELECT owner_user_id FROM saved_views WHERE id=?",
|
||||
[id]
|
||||
);
|
||||
if (!sv) return res.status(404).json({ error: "Not found" });
|
||||
// ถ้าจะจำกัดเฉพาะเจ้าของ: if (sv.owner_user_id !== req.principal.userId && !req.principal.isSuperAdmin) return res.status(403).json({ error: 'NOT_OWNER' });
|
||||
|
||||
await sql.query(
|
||||
'UPDATE saved_views SET name=?, payload_json=?, is_shared=? WHERE id=?',
|
||||
"UPDATE saved_views SET name=?, payload_json=?, is_shared=? WHERE id=?",
|
||||
[name, JSON.stringify(payload_json ?? {}), Number(is_shared) ? 1 : 0, id]
|
||||
);
|
||||
res.json({ ok: 1 });
|
||||
@@ -88,11 +162,15 @@ r.put('/:id',
|
||||
);
|
||||
|
||||
// DELETE
|
||||
r.delete('/:id',
|
||||
requirePerm(PERM.savedview.delete, { scope: 'org', getOrgId: OWN.getOrgIdById }),
|
||||
r.delete(
|
||||
"/:id",
|
||||
requirePerm(PERM.savedview.delete, {
|
||||
scope: "org",
|
||||
getOrgId: OWN.getOrgIdById,
|
||||
}),
|
||||
async (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
await sql.query('DELETE FROM saved_views WHERE id=?', [id]);
|
||||
await sql.query("DELETE FROM saved_views WHERE id=?", [id]);
|
||||
res.json({ ok: 1 });
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user