feat: migrate runtime backend to node server

This commit is contained in:
victo
2026-04-08 16:41:29 +08:00
parent 9d2fc9e4b8
commit a83841ff2d
70 changed files with 8239 additions and 1561 deletions

View File

@@ -0,0 +1,40 @@
import type { NextFunction, Request, Response } from 'express';
import { verifyAccessToken } from '../auth/token.js';
import type { AppConfig } from '../config.js';
import { unauthorized } from '../errors.js';
import { type UserRepository } from '../repositories/userRepository.js';
function readBearerToken(request: Request) {
const authorization = request.header('authorization')?.trim() || '';
if (!authorization.startsWith('Bearer ')) {
return '';
}
return authorization.slice('Bearer '.length).trim();
}
export function requireJwtAuth(config: AppConfig, userRepository: UserRepository) {
return async (request: Request, _response: Response, next: NextFunction) => {
try {
const token = readBearerToken(request);
if (!token) {
throw unauthorized('缺少 Authorization Bearer Token');
}
const claims = await verifyAccessToken(token, config);
const user = userRepository.findById(claims.userId);
if (!user) {
throw unauthorized('用户不存在');
}
if (user.tokenVersion !== claims.tokenVersion) {
throw unauthorized('登录状态已失效,请重新登录');
}
request.auth = claims;
request.userId = claims.userId;
next();
} catch (error) {
next(error);
}
};
}

View File

@@ -0,0 +1,27 @@
import type { ErrorRequestHandler } from 'express';
import { HttpError } from '../errors.js';
export const errorHandler: ErrorRequestHandler = (error, request, response, _next) => {
const statusCode =
error instanceof HttpError ? error.statusCode : 500;
const message =
error instanceof HttpError
? error.message
: '服务器内部错误';
request.log?.error(
{
err: error,
request_id: request.requestId,
user_id: request.userId ?? null,
},
'request failed',
);
response.status(statusCode).json({
error: {
message,
},
});
};

View File

@@ -0,0 +1,8 @@
import crypto from 'node:crypto';
import type { RequestHandler } from 'express';
export const requestIdMiddleware: RequestHandler = (request, _response, next) => {
request.requestId = request.header('x-request-id')?.trim() || crypto.randomUUID();
next();
};