feat: migrate backend foundation to NestJS

This commit is contained in:
2026-07-17 17:41:41 +08:00
parent 39f7332f33
commit f219eb0bf8
37 changed files with 2691 additions and 402 deletions

View File

@@ -5,6 +5,8 @@ const read = file => fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
const httpSource = read('apps/api/src/core/http.ts');
const serverSource = read('apps/api/src/server.ts');
const envelopeInterceptorSource = read('apps/api/src/nest/api.interceptor.ts');
const exceptionFilterSource = read('apps/api/src/nest/api-exception.filter.ts');
const apiSource = read('apps/taro/src/services/api.ts');
const typesSource = read('apps/taro/src/types.ts');
const tenantLocatorSource = read('apps/api/src/features/tenant/locator.ts');
@@ -13,8 +15,10 @@ const studentRouteSource = read('apps/api/src/features/tenant-admin/classes.ts')
const studentCursorSource = read('apps/api/src/features/tenant-admin/student-cursor.ts');
assert.match(httpSource, /meta:\s*\{ \.\.\.existingMeta, requestId \}/, 'API responses must expose requestId without discarding endpoint metadata');
assert.match(serverSource, /sendJson\(res, 200, withResponseMeta\(result, requestId\)\)/, 'Successful API responses must carry requestId metadata');
assert.match(serverSource, /withResponseMeta\(\{ \.\.\.body, requestId \}, requestId\)/, 'Error API responses must carry the same requestId in the legacy field and metadata envelope');
assert.match(serverSource, /useGlobalInterceptors\(new ApiEnvelopeInterceptor\(\)\)/, 'Successful responses must use the global API envelope interceptor');
assert.match(envelopeInterceptorSource, /withResponseMeta\(body, String\(reply\.getHeader\('x-request-id'\)/, 'Successful API responses must carry requestId metadata');
assert.match(serverSource, /useGlobalFilters\(new ApiExceptionFilter\(\)\)/, 'Errors must use the global API exception filter');
assert.match(exceptionFilterSource, /withResponseMeta\(\{ error: message, code, requestId \}, requestId\)/, 'Error API responses must carry the same requestId in the legacy field and metadata envelope');
assert.match(apiSource, /responseHeaderValue\(response\.header, 'x-request-id'\)/, 'The Taro API client must fall back to the response header requestId');
assert.match(apiSource, /this\.requestId = payload\.requestId/, 'ApiError must retain requestId for support and observability');
assert.doesNotMatch(typesSource, /\[key:\s*string\]:\s*unknown/, 'The shared API envelope must not silently accept arbitrary response fields');