05.1 ปรบปรง backend ทงหมด และ frontend/login
This commit is contained in:
@@ -1,72 +1,6 @@
|
||||
// FILE: src/index.js (ESM)
|
||||
// Main entry point for the backend API server
|
||||
// - Sets up Express app with middleware, routes, error handling
|
||||
// - Connects to database
|
||||
// - Starts server and handles graceful shutdown
|
||||
// ==========================
|
||||
// Context:
|
||||
// - Node.js >= 18 (ESM)
|
||||
// - Express.js 4/5
|
||||
// - MySQL database (using mysql2/promise)
|
||||
// ==========================
|
||||
// Features:
|
||||
// - CORS with dynamic origin checking
|
||||
// - Cookie parsing
|
||||
// - JSON and URL-encoded body parsing
|
||||
// - Access logging
|
||||
// - Health, livez, readyz, info endpoints
|
||||
// - JWT authentication middleware
|
||||
// - Principal loading middleware
|
||||
// - Modular route handlers for various resources
|
||||
// - 404 and error handling middleware
|
||||
// - Graceful shutdown on SIGTERM/SIGINT
|
||||
// ==========================
|
||||
// Assumptions:
|
||||
// - Environment variables for configuration (e.g., PORT, DB connection, FRONTEND_ORIGIN)
|
||||
// - Database connection module at ./db/index.js
|
||||
// - Middleware modules for auth, permissions, principal loading
|
||||
// - Route modules for different API resources
|
||||
// - Logs directory exists or can be created
|
||||
// - Code is written in JavaScript (ESM) and runs in Node.js environment
|
||||
// - Uses ES6+ features for cleaner and more maintainable code
|
||||
// ==========================
|
||||
// Notes:
|
||||
// - Adjust CORS origins as needed for your frontend applications
|
||||
// - Ensure proper error handling and logging as per your requirements
|
||||
// - Customize middleware and routes as per your application's needs
|
||||
// ==========================
|
||||
// Best Practices Followed:
|
||||
// - Assumes existence of necessary database tables and columns
|
||||
// - Assumes existence of necessary middleware and utility functions
|
||||
// - Assumes Express.js app is set up to use this router for /api path
|
||||
// - Assumes existence of necessary environment variables
|
||||
// - Assumes existence of necessary directories and permissions for file storage
|
||||
// - Assumes multer is installed and configured
|
||||
// - Assumes fs and path modules are available for file system operations
|
||||
// - Assumes sql module is set up for database interactions
|
||||
// - Assumes middleware modules are correctly implemented and exported
|
||||
// - Assumes route modules are correctly implemented and exported
|
||||
// - Uses environment variables for configuration
|
||||
// - Uses middleware for modular functionality
|
||||
// - Uses async/await for asynchronous operations
|
||||
// - Uses try/catch for error handling in async functions (if needed)
|
||||
// - Uses parameterized queries to prevent SQL injection
|
||||
// - Uses HTTP status codes for responses (e.g., 404 for not found, 400 for bad request)
|
||||
// - Uses JSON responses for API endpoints
|
||||
// - Uses destructuring and default parameters for cleaner function signatures
|
||||
// - Uses best practices for Express.js route handling
|
||||
// - Uses modular code structure for maintainability
|
||||
// - Uses comments for documentation and clarity
|
||||
// - Uses ES6+ features for cleaner and more maintainable code
|
||||
// - Uses template literals for SQL query construction
|
||||
// - Uses array methods for filtering and joining conditions
|
||||
// - Uses utility functions for common tasks (e.g., building SQL WHERE clauses)
|
||||
// ==========================
|
||||
|
||||
// FILE: backend/src/index.js (ESM) ไฟล์ฉบับ “Bearer-only”
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import express from "express";
|
||||
import cookieParser from "cookie-parser";
|
||||
import cors from "cors";
|
||||
|
||||
import sql from "./db/index.js";
|
||||
@@ -91,93 +25,87 @@ import uploadsRoutes from "./routes/uploads.js";
|
||||
import usersRoutes from "./routes/users.js";
|
||||
import permissionsRoutes from "./routes/permissions.js";
|
||||
|
||||
/* ==========================
|
||||
* CONFIG
|
||||
* ========================== */
|
||||
const PORT = Number(process.env.PORT || 3001);
|
||||
const NODE_ENV = process.env.NODE_ENV || "production";
|
||||
const NODE_ENV = process.env.NODE_ENV || "development";
|
||||
|
||||
// Origin ของ Frontend (ตั้งผ่าน ENV ต่อ environment; dev ใช้ localhost)
|
||||
const FRONTEND_ORIGIN =
|
||||
process.env.FRONTEND_ORIGIN || "https://lcbp3.np-dms.work";
|
||||
|
||||
const ALLOW_ORIGINS = [
|
||||
"http://localhost:3000",
|
||||
"http://127.0.0.1:3000",
|
||||
FRONTEND_ORIGIN,
|
||||
...(process.env.CORS_ALLOWLIST
|
||||
? process.env.CORS_ALLOWLIST.split(",")
|
||||
.map((x) => x.trim())
|
||||
.filter(Boolean)
|
||||
: []),
|
||||
].filter(Boolean);
|
||||
|
||||
// ที่เก็บ log ภายใน container ถูก bind ไปที่ /share/Container/dms/logs/backend
|
||||
const LOG_DIR = process.env.BACKEND_LOG_DIR || "/app/logs";
|
||||
|
||||
// สร้างโฟลเดอร์ log ถ้ายังไม่มี
|
||||
try {
|
||||
if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
|
||||
} catch (e) {
|
||||
console.warn("[WARN] Cannot ensure LOG_DIR:", LOG_DIR, e?.message);
|
||||
}
|
||||
|
||||
/* ==========================
|
||||
* APP INIT
|
||||
* ========================== */
|
||||
const app = express();
|
||||
|
||||
// ✅ อยู่หลัง NPM/Reverse proxy → ให้ trust proxy เพื่อให้ cookie secure / proto ทำงานถูก
|
||||
app.set("trust proxy", 1);
|
||||
|
||||
// CORS แบบกำหนด origin ตามรายการที่อนุญาต + อนุญาต credentials (จำเป็นสำหรับ cookie)
|
||||
// CORS: allow list
|
||||
app.use(
|
||||
cors({
|
||||
origin(origin, cb) {
|
||||
if (!origin) return cb(null, true); // server-to-server / curl
|
||||
return cb(null, ALLOW_ORIGINS.includes(origin));
|
||||
cb(null, ALLOW_ORIGINS.includes(origin));
|
||||
},
|
||||
credentials: true,
|
||||
credentials: false, // Bearer-only
|
||||
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
||||
allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
|
||||
allowedHeaders: [
|
||||
"Content-Type",
|
||||
"Authorization",
|
||||
"X-Requested-With",
|
||||
"Accept",
|
||||
"Origin",
|
||||
"Referer",
|
||||
"User-Agent",
|
||||
"Cache-Control",
|
||||
"Pragma",
|
||||
],
|
||||
exposedHeaders: ["Content-Disposition", "Content-Length"],
|
||||
})
|
||||
);
|
||||
// preflight
|
||||
app.options(
|
||||
"*",
|
||||
cors({
|
||||
origin(origin, cb) {
|
||||
if (!origin) return cb(null, true);
|
||||
return cb(null, ALLOW_ORIGINS.includes(origin));
|
||||
cb(null, ALLOW_ORIGINS.includes(origin));
|
||||
},
|
||||
credentials: true,
|
||||
credentials: false,
|
||||
})
|
||||
);
|
||||
|
||||
app.use(cookieParser());
|
||||
|
||||
// Payload limits
|
||||
app.use(express.json({ limit: "10mb" }));
|
||||
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
|
||||
|
||||
// Access log (ขั้นต่ำ)
|
||||
// minimal access log
|
||||
app.use((req, _res, next) => {
|
||||
console.log(`[REQ] ${req.method} ${req.originalUrl}`);
|
||||
next();
|
||||
});
|
||||
|
||||
/* ==========================
|
||||
* HEALTH / READY / INFO
|
||||
* ========================== */
|
||||
app.get("/health", async (req, res) => {
|
||||
// health/info (เปิดทั้ง /health, /livez, /readyz, /info)
|
||||
app.get("/health", async (_req, res) => {
|
||||
try {
|
||||
const [[{ now }]] = await sql.query("SELECT NOW() AS now");
|
||||
return res.json({ status: "ok", db: "ok", now });
|
||||
res.json({ status: "ok", db: "ok", now });
|
||||
} catch (e) {
|
||||
return res
|
||||
.status(500)
|
||||
.json({ status: "degraded", db: "fail", error: e?.message });
|
||||
res.status(500).json({ status: "degraded", db: "fail", error: e?.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Kubernetes-style endpoints (ถ้าใช้)
|
||||
app.get("/livez", (req, res) => res.send("ok"));
|
||||
app.get("/readyz", async (req, res) => {
|
||||
app.get("/livez", (_req, res) => res.send("ok"));
|
||||
app.get("/readyz", async (_req, res) => {
|
||||
try {
|
||||
await sql.query("SELECT 1");
|
||||
res.send("ready");
|
||||
@@ -185,26 +113,20 @@ app.get("/readyz", async (req, res) => {
|
||||
res.status(500).send("not-ready");
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/info", (req, res) => {
|
||||
app.get("/info", (_req, res) =>
|
||||
res.json({
|
||||
name: "dms-backend",
|
||||
env: NODE_ENV,
|
||||
version: process.env.APP_VERSION || "0.5.0",
|
||||
commit: process.env.GIT_COMMIT || undefined,
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
/* ==========================
|
||||
* ROUTES
|
||||
* ========================== */
|
||||
// /api/health (ถอดจาก healthRouter)
|
||||
// ---------- Public (no auth) ----------
|
||||
app.use("/api", healthRouter);
|
||||
|
||||
// ✅ auth กลุ่มนี้ "ไม่ต้อง" ผ่าน authJwt
|
||||
app.use("/api/auth", authRoutes);
|
||||
|
||||
// จากนี้ไป ทุก /api/* ต้องผ่าน JWT + principal
|
||||
// ---------- Protected (Bearer + Principal) ----------
|
||||
app.use("/api", authJwt(), loadPrincipalMw());
|
||||
|
||||
app.use("/api/lookup", lookupRoutes);
|
||||
@@ -222,33 +144,22 @@ app.use("/api/uploads", uploadsRoutes);
|
||||
app.use("/api/users", usersRoutes);
|
||||
app.use("/api/permissions", permissionsRoutes);
|
||||
|
||||
/* ==========================
|
||||
* NOT FOUND & ERROR HANDLERS
|
||||
* ========================== */
|
||||
app.use((req, res) => {
|
||||
res.status(404).json({ error: "NOT_FOUND", path: req.originalUrl });
|
||||
});
|
||||
|
||||
// 404 / error
|
||||
app.use((req, res) =>
|
||||
res.status(404).json({ error: "NOT_FOUND", path: req.originalUrl })
|
||||
);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
app.use((err, req, res, _next) => {
|
||||
app.use((err, _req, res, _next) => {
|
||||
console.error("[UNHANDLED ERROR]", err);
|
||||
const status = err?.status || 500;
|
||||
res.status(status).json({
|
||||
error: "SERVER_ERROR",
|
||||
message: NODE_ENV === "production" ? undefined : err?.message,
|
||||
});
|
||||
res.status(err?.status || 500).json({ error: "SERVER_ERROR" });
|
||||
});
|
||||
|
||||
/* ==========================
|
||||
* START SERVER
|
||||
* ========================== */
|
||||
// START
|
||||
const server = app.listen(PORT, () => {
|
||||
console.log(`Backend API listening on ${PORT} (env=${NODE_ENV})`);
|
||||
});
|
||||
|
||||
/* ==========================
|
||||
* GRACEFUL SHUTDOWN
|
||||
* ========================== */
|
||||
// Shutdown
|
||||
async function shutdown(signal) {
|
||||
try {
|
||||
console.log(`[SHUTDOWN] ${signal} received`);
|
||||
|
||||
Reference in New Issue
Block a user