fix: harden auth and investment synchronization
Backend security / security (push) Successful in 8s
Backend verify / verify (push) Successful in 11m9s
Conventional commits / commits (pull_request) Successful in 28s
Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 10m38s

Resolve A01-A10 and C01-C07 from the backend audit.
Preserve auth failures, forced candle reconciliation and forecast freshness.
Validate MOEX responses and nested HTTP errors; remove unused legacy paths.

Verified: 209 unit tests, 262 SQLite e2e tests on Linux, OpenAPI and durability.
Record the remaining macOS native SQLite abort in the audit.
This commit is contained in:
2026-09-27 14:09:05 +03:00
parent 5d3835e796
commit 4796d9d3f0
55 changed files with 1954 additions and 569 deletions
@@ -3,7 +3,7 @@ import { randomUUID } from 'node:crypto';
import { createHash } from 'node:crypto';
import type { Prisma } from '../../../../../generated/prisma/client';
import { PrismaService } from '../../../../prisma/prisma.service';
import type { TbankSyncAttempt } from '../../../../integrations/tbank/tbank-access.service';
import type { TbankSyncAttempt } from '../../../../integrations/tbank/tbank.api';
import {
confirmedCandleCoverage,
type CandleRange,
@@ -78,8 +78,37 @@ export class CandleRepository {
previous?.state === 'running' &&
previous.processRunId === runId &&
active.has(previous.attemptId)
)
return { work: previous, launch: false };
) {
const previousIntent = previous.intent as Prisma.JsonObject;
const previousNow =
typeof previousIntent.now === 'string' ? previousIntent.now : '';
const nextPriority = Math.min(previous.priority, priority);
if (
nextPriority === previous.priority &&
(!intent.force ||
(previousIntent.force === true &&
(!intent.now || intent.now <= previousNow)))
)
return { work: previous, launch: false };
const work = await tx.tbankSyncWork.update({
where: { id: previous.id },
data: {
priority: nextPriority,
intent: {
...previousIntent,
...(intent.force
? {
force: true,
...(intent.now && intent.now > previousNow
? { now: intent.now }
: {}),
}
: {}),
},
},
});
return { work, launch: false };
}
if (previous)
await tx.tbankSyncStage.deleteMany({ where: { workId: previous.id } });
const data = {
@@ -3,10 +3,10 @@ import { randomUUID } from 'node:crypto';
import { isDeepStrictEqual } from 'node:util';
import type { Prisma } from '../../../../../generated/prisma/client';
import { PrismaService } from '../../../../prisma/prisma.service';
import type { TbankSyncAttempt } from '../../../../integrations/tbank/tbank-access.service';
import type {
TbankBond,
TbankShare,
TbankSyncAttempt,
} from '../../../../integrations/tbank/tbank.api';
export type CatalogQuery = {