This commit is contained in:
2026-03-19 16:07:35 +07:00
commit 39b0358b08
63 changed files with 3128 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
import type { NextFunction, Request, Response } from "express";
import { RbacService } from "../services/rbac.service.js";
import { SYSTEM_ROLE_CODES } from "../constants/permissions.js";
const rbacService = new RbacService();
export async function requireAdmin(request: Request, _response: Response, next: NextFunction) {
try {
await rbacService.assertAnyRole(request.session.user!.id, [
SYSTEM_ROLE_CODES.ROOT,
SYSTEM_ROLE_CODES.GROUP_ADMIN
]);
next();
} catch (error) {
next(error);
}
}

View File

@@ -0,0 +1,10 @@
import type { NextFunction, Request, Response } from "express";
import { UnauthorizedError } from "../utils/errors.js";
export function requireAuth(request: Request, _response: Response, next: NextFunction) {
if (!request.session.user) {
return next(new UnauthorizedError());
}
return next();
}

View File

@@ -0,0 +1,10 @@
import type { NextFunction, Request, Response } from "express";
import { AppError } from "../utils/errors.js";
export function errorHandler(error: Error, _request: Request, response: Response, _next: NextFunction) {
if (error instanceof AppError) {
return response.status(error.statusCode).json({ error: error.message });
}
return response.status(500).json({ error: "Internal server error" });
}

View File

@@ -0,0 +1,6 @@
import type { NextFunction, Request, Response } from "express";
export function attachRequestContext(request: Request, _response: Response, next: NextFunction) {
request.app.locals.requestStartedAt = new Date();
next();
}

View File

@@ -0,0 +1,18 @@
import type { NextFunction, Request, Response } from "express";
import type { ZodSchema } from "zod";
import { AppError } from "../utils/errors.js";
export function validateBody(schema: ZodSchema) {
return (request: Request, _response: Response, next: NextFunction) => {
const result = schema.safeParse(request.body);
if (!result.success) {
return next(
new AppError(result.error.issues.map((issue: { message: string }) => issue.message).join(", "), 400)
);
}
request.body = result.data;
return next();
};
}