refactor!: simplify runtime coordination #7
Backend verify / commits (pull_request) Successful in 29s
Backend verify / security (pull_request) Successful in 8s
Backend verify / verify (pull_request) Failing after 9m36s

Keep financial ownership, atomic publication and refresh CAS.
Failed refresh leaves cookies untouched; logout still clears them.

Remove full-history candle prefetch and special metadata retries.
Use native Pino request context instead of parallel middleware.

BREAKING CHANGE: remove investment stateVersion, queued sync state,
and tbank-sync status progress/resources. Consumers must observe
resources through sync=observe with the updated frontend contract.
This commit is contained in:
2026-10-03 11:38:08 +03:00
parent 0f27da9fd1
commit d698ed9425
48 changed files with 185 additions and 925 deletions
+59 -9
View File
@@ -1,16 +1,66 @@
import { Global, Module } from '@nestjs/common';
import type { Request, Response } from 'express';
import type { Options } from 'pino-http';
import { RequestContextService } from './request-context.service';
import { RequestLoggingMiddleware } from './request-logging.middleware';
import { Global, Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { LoggerModule } from 'nestjs-pino';
import { randomUUID } from 'node:crypto';
import { constants as httpStatus } from 'node:http2';
import { Environment } from '../../config/env.validation';
import { StructuredLogger } from './structured-logger.service';
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function completion(request: Request, response: Response, value: { responseTime: number }) {
const route: unknown = request.route;
const path = typeof route === 'object' && route !== null && 'path' in route &&
typeof route.path === 'string'
? `${request.baseUrl}${route.path}`
: '<unmatched>';
return {
method: request.method,
route: path,
status: response.statusCode,
durationMs: Math.round(value.responseTime),
...(typeof response.locals.publicErrorCode === 'string' ? { code: response.locals.publicErrorCode } : {}),
};
}
export function loggingOptions(config: ConfigService<Environment, true>): Options<Request, Response> {
return {
level: config.getOrThrow('LOG_LEVEL'),
messageKey: 'message',
genReqId: (_request, response) => {
const id = randomUUID();
response.setHeader('X-Request-ID', id);
return id;
},
customProps: (request) => {
const incoming = request.header('x-request-id');
return { requestId: request.id, ...(incoming && uuid.test(incoming) ? { clientRequestId: incoming } : {}) };
},
customLogLevel: (_request, response) => {
if (response.statusCode >= httpStatus.HTTP_STATUS_INTERNAL_SERVER_ERROR) return 'error';
if ([httpStatus.HTTP_STATUS_FORBIDDEN, httpStatus.HTTP_STATUS_TOO_MANY_REQUESTS, httpStatus.HTTP_STATUS_UNAUTHORIZED].includes(response.statusCode)) return 'warn';
return 'info';
},
customSuccessMessage: () => 'http.request.completed',
customErrorMessage: () => 'http.request.completed',
customSuccessObject: completion,
customErrorObject: (request, response, _error, value: { responseTime: number }) => completion(request, response, value),
redact: ['req.headers.authorization', 'req.headers.cookie', 'req.body'],
serializers: { req: () => undefined },
};
}
@Global()
@Module({
providers: [
RequestContextService,
RequestLoggingMiddleware,
StructuredLogger,
],
exports: [RequestContextService, RequestLoggingMiddleware, StructuredLogger],
imports: [LoggerModule.forRootAsync<Request, Response>({
inject: [ConfigService],
useFactory: (config: ConfigService<Environment, true>) => ({ pinoHttp: loggingOptions(config), assignResponse: true }),
})],
providers: [StructuredLogger],
exports: [StructuredLogger],
})
export class LoggingModule {}