1
This commit is contained in:
@@ -22,10 +22,13 @@ export function requireJwtAuth(config: AppConfig, userRepository: UserRepository
|
||||
}
|
||||
|
||||
const claims = await verifyAccessToken(token, config);
|
||||
const user = userRepository.findById(claims.userId);
|
||||
const user = await userRepository.findById(claims.userId);
|
||||
if (!user) {
|
||||
throw unauthorized('用户不存在');
|
||||
}
|
||||
if (user.accountStatus === 'disabled') {
|
||||
throw unauthorized('账号已被禁用');
|
||||
}
|
||||
if (user.tokenVersion !== claims.tokenVersion) {
|
||||
throw unauthorized('登录状态已失效,请重新登录');
|
||||
}
|
||||
|
||||
@@ -1,27 +1,54 @@
|
||||
import type { ErrorRequestHandler } from 'express';
|
||||
|
||||
import { HttpError } from '../errors.js';
|
||||
import { toHttpError } from '../errors.js';
|
||||
import {
|
||||
applyApiResponseHeaders,
|
||||
buildApiLogContext,
|
||||
wantsApiEnvelope,
|
||||
} from '../http.js';
|
||||
|
||||
export const errorHandler: ErrorRequestHandler = (error, request, response, _next) => {
|
||||
const statusCode =
|
||||
error instanceof HttpError ? error.statusCode : 500;
|
||||
const message =
|
||||
error instanceof HttpError
|
||||
? error.message
|
||||
: '服务器内部错误';
|
||||
export const errorHandler: ErrorRequestHandler = (
|
||||
error,
|
||||
request,
|
||||
response,
|
||||
_next,
|
||||
) => {
|
||||
const normalizedError = toHttpError(error);
|
||||
const meta = applyApiResponseHeaders(request, response);
|
||||
|
||||
request.log?.error(
|
||||
{
|
||||
err: error,
|
||||
request_id: request.requestId,
|
||||
...buildApiLogContext(request, response),
|
||||
user_id: request.userId ?? null,
|
||||
status: normalizedError.statusCode,
|
||||
error_code: normalizedError.code,
|
||||
},
|
||||
'request failed',
|
||||
);
|
||||
|
||||
response.status(statusCode).json({
|
||||
error: {
|
||||
message,
|
||||
},
|
||||
response.status(normalizedError.statusCode);
|
||||
|
||||
const errorPayload = {
|
||||
code: normalizedError.code,
|
||||
message: normalizedError.message,
|
||||
...(normalizedError.expose && normalizedError.details !== undefined
|
||||
? { details: normalizedError.details }
|
||||
: {}),
|
||||
};
|
||||
|
||||
if (wantsApiEnvelope(request)) {
|
||||
response.json({
|
||||
ok: false,
|
||||
data: null,
|
||||
error: errorPayload,
|
||||
meta,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
response.json({
|
||||
error: errorPayload,
|
||||
meta,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,7 +2,15 @@ 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();
|
||||
export const requestIdMiddleware: RequestHandler = (
|
||||
request,
|
||||
response,
|
||||
next,
|
||||
) => {
|
||||
const requestId =
|
||||
request.header('x-request-id')?.trim() || crypto.randomUUID();
|
||||
request.requestId = requestId;
|
||||
request.requestStartedAt = Date.now();
|
||||
response.setHeader('x-request-id', requestId);
|
||||
next();
|
||||
};
|
||||
|
||||
49
server-node/src/middleware/responseEnvelope.ts
Normal file
49
server-node/src/middleware/responseEnvelope.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { RequestHandler, Response } from 'express';
|
||||
|
||||
import {
|
||||
applyApiResponseHeaders,
|
||||
isStandardApiErrorResponse,
|
||||
isStandardApiSuccessEnvelope,
|
||||
toApiErrorBody,
|
||||
toApiSuccessBody,
|
||||
} from '../http.js';
|
||||
|
||||
function isLegacyApiErrorBody(body: unknown) {
|
||||
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
('error' in body || 'message' in body || 'code' in body) &&
|
||||
!('meta' in body && 'ok' in body)
|
||||
);
|
||||
}
|
||||
|
||||
function patchJsonResponse(response: Response) {
|
||||
const originalJson = response.json.bind(response);
|
||||
|
||||
response.json = ((body: unknown) => {
|
||||
if (
|
||||
isStandardApiSuccessEnvelope(body) ||
|
||||
isStandardApiErrorResponse(body)
|
||||
) {
|
||||
applyApiResponseHeaders(response.req, response);
|
||||
return originalJson(body);
|
||||
}
|
||||
|
||||
if (response.statusCode >= 400 || isLegacyApiErrorBody(body)) {
|
||||
return originalJson(toApiErrorBody(response.req, response, body));
|
||||
}
|
||||
|
||||
return originalJson(toApiSuccessBody(response.req, response, body));
|
||||
}) as Response['json'];
|
||||
}
|
||||
|
||||
export const responseEnvelopeMiddleware: RequestHandler = (
|
||||
_request,
|
||||
response,
|
||||
next,
|
||||
) => {
|
||||
patchJsonResponse(response);
|
||||
next();
|
||||
};
|
||||
10
server-node/src/middleware/routeMeta.ts
Normal file
10
server-node/src/middleware/routeMeta.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { RequestHandler } from 'express';
|
||||
|
||||
import { setRouteMeta, type ApiRouteMeta } from '../http.js';
|
||||
|
||||
export function routeMeta(meta: ApiRouteMeta): RequestHandler {
|
||||
return (_request, response, next) => {
|
||||
setRouteMeta(response, meta);
|
||||
next();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user