625 lines
26 KiB
Markdown
625 lines
26 KiB
Markdown
# DIDI Backend — Refactor Context (sesiune 2026-05-07)
|
|
|
|
**Citire obligatorie pentru orice sesiune ulterioară.** Acest document conține
|
|
convențiile, helper-ele și starea după 54 task-uri de refactor + 13 deploy-uri.
|
|
|
|
---
|
|
|
|
## 0. TL;DR — ce s-a întâmplat
|
|
|
|
Începută cu un audit ostil al `agent-v3` + `didiFramework`. Am descoperit + rezolvat:
|
|
- **8 bug-uri critice** (auth bypass admin, parole hardcoded, race conditions,
|
|
silent failures, fragile error matching)
|
|
- **Migrare integrală la pino structured logging** (651 console.* → 0)
|
|
- **Reducere `as any` 92%** (159 → 13)
|
|
- **Curățarea error.message leaks** (126 → 0)
|
|
- **Centralizare Redis pools, Keycloak token, PG pools, Express helpers**
|
|
- **Split la 11 fișiere monolitice** (13049 LOC → 75 fișiere modulare):
|
|
`routes.ts`, `admin.ts`, `verdict-calculator.ts`, `auth.ts`, `pipeline-routes.ts`,
|
|
`claims/executor.ts`, `ai-tampered/executor.ts`, `techniques/executor.ts`,
|
|
`source-assessment/executor.ts`, `providers.ts` (didiFramework),
|
|
`sync-redis.ts` (didiFramework)
|
|
|
|
Sistemul live a rulat fără downtime detectabil pentru utilizatori.
|
|
|
|
---
|
|
|
|
## 1. Convenții obligatorii (RESPECTĂ-LE)
|
|
|
|
### 1.1 Logging — folosește `log`, NU `console`
|
|
|
|
```ts
|
|
// agent-v3
|
|
import { log } from '../shared/logger';
|
|
|
|
// didiFramework
|
|
import { log } from '../config/logger';
|
|
```
|
|
|
|
Pino este wrappuit cu un adapter variadic — `log.info('msg', x, y)` și
|
|
`log.info({ session_id }, 'msg')` ambele merg.
|
|
|
|
În route handlers, folosește `req.log` care are deja `request_id` atașat:
|
|
```ts
|
|
req.log.info({ user_id }, 'analyze starting');
|
|
```
|
|
|
|
**NU readuce console.log/error/warn.** Comentariul cu "TODO" e mai bun decât un console.
|
|
|
|
### 1.2 Env vars — `requireEnv()` pentru tot ce e secret/required
|
|
|
|
```ts
|
|
// agent-v3
|
|
import { requireEnv, optionalEnv } from '../shared/helpers/env';
|
|
|
|
// didiFramework
|
|
import { requireEnv, optionalEnv } from '../config/env';
|
|
|
|
const PG_PASSWORD = requireEnv('PG_PASSWORD'); // throws on missing
|
|
const PORT = optionalEnv('PORT', '3005'); // safe default
|
|
```
|
|
|
|
**INTERZIS** `process.env.X || 'fallback'` pentru:
|
|
- Parole, tokens, API keys
|
|
- Hostname-uri interne (10.11.x.y)
|
|
- DB names, users
|
|
|
|
OK pentru: log levels, ports, mobile schemes, public URLs, schema names.
|
|
|
|
### 1.3 Error responses — `internalError()`, NU `error.message`
|
|
|
|
```ts
|
|
// agent-v3
|
|
import { internalError } from '../shared/helpers/error-response';
|
|
|
|
// didiFramework
|
|
import { internalError } from '../config/error-response';
|
|
|
|
try {
|
|
// ...
|
|
} catch (error) {
|
|
internalError(res, error, 'optional_context_tag');
|
|
}
|
|
```
|
|
|
|
Asta:
|
|
- Loghează errorul + stack intern cu `correlation_id` (UUID)
|
|
- Returnează către client `{success: false, error: 'Internal server error', correlation_id}`
|
|
- NU leakuiește detalii interne
|
|
|
|
**INTERZIS** `res.status(500).json({error: error.message})` sau orice variantă
|
|
care expune mesajul brut.
|
|
|
|
### 1.4 Redis — `lazyRedis()` per modul + `scanKeys()`, NU `KEYS`
|
|
|
|
```ts
|
|
import { lazyRedis } from '../shared/redis/connection';
|
|
import { scanKeys } from '../shared/redis/scan';
|
|
|
|
const getRedis = lazyRedis('module-name'); // label vizibil în connection metadata
|
|
|
|
// SCAN, nu KEYS — KEYS blochează server-ul O(N)
|
|
const keys = await scanKeys(getRedis(), 'pattern:*');
|
|
```
|
|
|
|
### 1.5 Locks distribuite — `acquireLock`/`releaseLock` (fenced)
|
|
|
|
```ts
|
|
import { acquireLock, releaseLock } from '../shared/redis/lock';
|
|
|
|
const lock = await acquireLock(redis, lockKey, 30); // 30s TTL
|
|
if (!lock) {
|
|
// alt proces are lock-ul
|
|
return;
|
|
}
|
|
try {
|
|
// critical section
|
|
} finally {
|
|
await releaseLock(redis, lock); // compare-and-delete via Lua
|
|
}
|
|
```
|
|
|
|
**INTERZIS** `redis.del(lockKey)` direct — poate șterge lock-ul altcuiva dacă TTL
|
|
a expirat.
|
|
|
|
### 1.6 Errori logice — `LogicalInputError`, NU string-matching
|
|
|
|
```ts
|
|
import { LogicalInputError, isLogicalInputError } from '../shared/helpers/errors';
|
|
|
|
if (!text) throw new LogicalInputError('Component requires text input');
|
|
|
|
// În worker:
|
|
if (isLogicalInputError(err)) { /* nu retry */ }
|
|
```
|
|
|
|
**INTERZIS** `err.message.includes('requires')` sau alte verificări fragile.
|
|
|
|
### 1.7 Validare config Redis — zod, nu JSON.parse direct
|
|
|
|
Pentru endpoint-uri PUT care scriu în Redis:
|
|
```ts
|
|
import { TierStageAssignmentsSchema } from '../shared/helpers/config-schemas';
|
|
|
|
const parsed = TierStageAssignmentsSchema.safeParse(req.body?.stage_assignments);
|
|
if (!parsed.success) {
|
|
return res.status(400).json({success: false, error: 'Invalid payload', details: parsed.error.issues});
|
|
}
|
|
await r.set(key, JSON.stringify(parsed.data));
|
|
```
|
|
|
|
### 1.8 PG pools — folosește shared, NU `new Pool()` în route file
|
|
|
|
```ts
|
|
// didiFramework
|
|
import pool from '../config/database';
|
|
// SAU pentru query helpers:
|
|
import { query, queryOne, transaction } from '../config/database';
|
|
|
|
// agent-v3
|
|
import { getPgPool } from '../shared/persistence/pg-pool';
|
|
```
|
|
|
|
**INTERZIS** `new Pool({...})` în orice fișier de rută. Excepție: `waitlist.ts`
|
|
folosește un DB diferit (staging), păstrat lazy-init.
|
|
|
|
### 1.9 Keycloak admin — folosește centralizat
|
|
|
|
```ts
|
|
// didiFramework
|
|
import { getKeycloakAdminToken } from '../config/keycloak-admin';
|
|
const token = await getKeycloakAdminToken(); // throws on failure
|
|
```
|
|
|
|
**INTERZIS** rescrierea logicii de fetch /token din nou.
|
|
|
|
### 1.10 Video weighting — folosește helper
|
|
|
|
```ts
|
|
import { combineVideoProbability, DEFAULT_VIDEO_TRACK_WEIGHTS } from '../shared/media/video-weighting';
|
|
|
|
const final = combineVideoProbability(textProb, visualProb); // 0.4/0.6 default
|
|
```
|
|
|
|
**INTERZIS** `textProb * 0.4 + visualProb * 0.6` inline (a fost deja inversat
|
|
într-un loc înainte de fix).
|
|
|
|
### 1.11 Type safety — `as any` necesită justificare
|
|
|
|
Reduceri reușite: 159 → 13. Cele 13 rămase sunt în:
|
|
- `routes.ts` (4) — în techniques.ts după split
|
|
- `pipeline-routes.ts` (4) — încă nedefript
|
|
- `admin.ts` (3) — distribute în users.ts după split
|
|
- `verdict-calculator.ts` (2)
|
|
|
|
Când adaugi cod nou:
|
|
- Pentru fetch responses externe → declară `interface Response { ... }` și cast
|
|
- Pentru data din Redis → zod schema sau tip bine definit
|
|
- Pentru Express req extension → declară în `declare global { namespace Express { interface Request { ... } } }` (NU cast `as any`)
|
|
|
|
### 1.12 `.dockerignore` — OBLIGATORIU pentru ambele services
|
|
|
|
```
|
|
node_modules
|
|
dist
|
|
.git
|
|
*.log
|
|
.env.local
|
|
```
|
|
|
|
**Bug istoric**: stale `dist/admin.js` din host se copia în container și
|
|
suprascria buildul nou. Dacă `dist/` nu e ignorat → build inconsistent.
|
|
|
|
---
|
|
|
|
## 2. Structură nouă post-split
|
|
|
|
### agent-v3
|
|
|
|
```
|
|
src/api/
|
|
├── routes.ts # 37 LOC barrel (techniques + media + domain + /health)
|
|
├── techniques.ts # 781 LOC, 11 endpoints
|
|
├── media.ts # 171 LOC, 4 endpoints
|
|
├── domain.ts # 333 LOC, 2 endpoints
|
|
├── pipeline-routes.ts # 6 LOC re-export → ./pipeline
|
|
├── pipeline/ # split 5 → folder (was 1611 LOC)
|
|
│ ├── index.ts # 36 LOC barrel
|
|
│ ├── _init.ts # 36 LOC (lazy redis/persist/media + FRAMEWORK_API_URL)
|
|
│ ├── _helpers/
|
|
│ │ ├── vision.ts # 54 LOC (extractImageVision)
|
|
│ │ └── url-helpers.ts # 151 LOC (detectUrlType + fetchArticleText + isBoilerplate + buildPlatformInfoField)
|
|
│ ├── analyze.ts # 535 LOC (POST /analyze + /analyze-url + /analyze-media)
|
|
│ ├── status.ts # 157 LOC (GET /verdict-config + /:sessionId/{status,component/:name,result})
|
|
│ ├── history.ts # 181 LOC (6 history endpoints)
|
|
│ ├── extension.ts # 234 LOC (validateApiKey + 4 extension endpoints)
|
|
│ └── async.ts # 296 LOC (3 async/queue endpoints)
|
|
├── ai-tampered-routes.ts # 764 LOC
|
|
├── claims-routes.ts # 671 LOC
|
|
├── source-assessment-routes.ts # 470 LOC
|
|
├── moderation-routes.ts # 420 LOC
|
|
├── _init.ts # 36 LOC (shared lazy: Redis, MediaService, multer, PersistService)
|
|
└── _helpers/
|
|
└── standalone-session.ts # 86 LOC (buildStandaloneSession + persistStandaloneResult)
|
|
|
|
src/components/pipeline/verdict-calculator/ # split 4 → folder
|
|
├── index.ts # 864 LOC (VerdictCalculator class)
|
|
├── types.ts # 161 LOC (interfaces)
|
|
├── defaults.ts # 72 LOC (DEFAULT_*)
|
|
└── mappers.ts # 32 LOC (pure functions)
|
|
|
|
src/components/claims/ # split 6 → folder (was 1228 LOC)
|
|
├── executor.ts # 319 LOC (ClaimsExecutor class shell + execute orchestrator + loadConfigs)
|
|
├── types.ts # 159 LOC (interfaces, zod schemas, ClaimsCtx)
|
|
├── web-search.ts # 76 LOC (searchWebM17 + M17* types)
|
|
├── llm-utils.ts # 117 LOC (callWithFallbacks, parseJsonResponse, validate*)
|
|
└── stages/
|
|
├── extraction.ts # 82 LOC (Stage 1: extractClaims + loadConfig helper)
|
|
├── verification.ts # 443 LOC (Stage 2: verifyClaims + 5 helpers + calculateStatusFromSources)
|
|
└── scoring.ts # 179 LOC (Stage 3: buildFinalResult + buildEmpty + buildSkipped)
|
|
|
|
src/components/ai-tampered/ # split 7 → folder (was 1043 LOC)
|
|
├── executor.ts # 339 LOC (AITamperedExecutor class + execute() + quickAnalyze() + loadFromRedis)
|
|
├── types.ts # 201 LOC (interfaces, 3 zod schemas, AiTamperedCtx)
|
|
├── patterns.ts # 57 LOC (AI_TOOL_PATTERNS + DISCLOSURE_INDICATORS + PARTIAL_DISCLOSURE_PATTERNS)
|
|
├── disclosure.ts # 145 LOC (checkDisclosure + findToolMention + getDisclosureProminence + calculateDisclosureRating)
|
|
├── llm-utils.ts # 122 LOC (callWithFallbacks, parseJsonResponse, validate*)
|
|
└── stages/
|
|
├── _helpers.ts # 17 LOC (loadFromRedis pentru stages)
|
|
├── screening.ts # 53 LOC (Stage 1: executeScreening)
|
|
├── deep-analysis.ts # 65 LOC (Stage 2: executeDeepAnalysis)
|
|
└── scoring.ts # 191 LOC (Stage 3: buildFinalResult + buildEmptyResult + determineVerdict + determineConfidenceLevel)
|
|
|
|
src/components/techniques/ # split 8 → folder (was 962 LOC)
|
|
├── executor.ts # 237 LOC (TechniquesV3Executor class + execute() + loadFromRedis)
|
|
├── types.ts # 243 LOC (interfaces, 3 zod schemas, TechniquesCtx, resolveStageAssignment)
|
|
├── llm-utils.ts # 115 LOC (callWithFallbacks, parseJsonResponse, validate*)
|
|
└── stages/
|
|
├── _helpers.ts # 15 LOC (loadFromRedis pentru stages)
|
|
├── screening.ts # 50 LOC (Stage 1: executeScreening)
|
|
├── deep-analysis.ts # 93 LOC (Stage 2: executeDeepAnalysis + buildTechniquesList)
|
|
└── scoring.ts # 317 LOC (Stage 3: buildFinalResult + buildEmptyResult + calculateManipulationScore + buildCouplingContext + findTechniqueById)
|
|
|
|
src/components/source-assessment/ # split 9 → folder (was 932 LOC)
|
|
├── executor.ts # 137 LOC (SourceAssessmentExecutor class + execute() orchestrator 4-step)
|
|
├── types.ts # 95 LOC (interfaces, ScoringConfig, SourceAssessmentCtx)
|
|
├── defaults.ts # 191 LOC (DEFAULT_*_PROMPT, DEFAULT_MODELS, DEFAULT_AXIS_WEIGHTS, defaultFramework)
|
|
├── external.ts # 167 LOC (searchM17 + checkDomain + extractRedFlags + extractDomainFromUrl + DOMAIN_CHECK_URL via optionalEnv)
|
|
├── config-loader.ts # 143 LOC (loadFramework + loadModels + loadScoringConfig + loadPrompt — toate cu fallback default)
|
|
└── stages/
|
|
├── extraction.ts # 61 LOC (Step 1: extractSourceMetadata)
|
|
├── mapping.ts # 127 LOC (Step 3: mapToFramework — LLM clasifică evidence)
|
|
└── scoring.ts # 180 LOC (Step 4: buildResult + calculateAuthorScore + neutralFallback)
|
|
|
|
src/shared/
|
|
├── logger.ts # pino + variadic adapter
|
|
├── request-logger.ts # Express middleware (request_id propagation)
|
|
├── helpers/
|
|
│ ├── env.ts # requireEnv, optionalEnv
|
|
│ ├── errors.ts # LogicalInputError class
|
|
│ ├── error-response.ts # internalError(res, err, ctx?)
|
|
│ └── config-schemas.ts # TierStageAssignmentsSchema (zod)
|
|
├── redis/
|
|
│ ├── connection.ts # lazyRedis(label) + createRedisConnection
|
|
│ ├── scan.ts # scanKeys cursor-based
|
|
│ └── lock.ts # acquireLock/releaseLock (fenced)
|
|
└── media/
|
|
└── video-weighting.ts # combineVideoProbability + DEFAULT_VIDEO_TRACK_WEIGHTS
|
|
```
|
|
|
|
### didiFramework
|
|
|
|
```
|
|
src/routes/
|
|
├── auth/ # split 5 → folder
|
|
│ ├── index.ts # 23 LOC barrel
|
|
│ ├── _helpers.ts # 154 LOC (JWT, Keycloak, getCreditCost, types)
|
|
│ ├── me-profile.ts # 321 LOC (GET /me + PUT /profile)
|
|
│ ├── registration.ts # 158 LOC (POST /register)
|
|
│ ├── credits.ts # 335 LOC (5 endpoints)
|
|
│ └── email-verify.ts # 415 LOC (GET + POST /verify-email)
|
|
├── admin/ # split 2 → folder
|
|
│ ├── index.ts # 32 LOC barrel + auth middleware mount
|
|
│ ├── _middleware.ts # 63 LOC (requireAdmin)
|
|
│ ├── _keycloak-helpers.ts # 397 LOC (caches + roles/groups + audit + types)
|
|
│ ├── users.ts # 709 LOC (7 endpoints)
|
|
│ ├── plans.ts # 170 LOC (3 endpoints)
|
|
│ ├── docker.ts # 117 LOC (2 endpoints)
|
|
│ └── roles-groups.ts # 393 LOC (9 endpoints)
|
|
└── (alte route files, neatinse)
|
|
|
|
src/config/
|
|
├── env.ts # requireEnv, optionalEnv
|
|
├── error-response.ts # internalError
|
|
├── keycloak-admin.ts # getKeycloakAdminToken (canonical)
|
|
├── logger.ts # pino + variadic adapter
|
|
├── request-logger.ts # Express middleware
|
|
└── (database.ts, minio.ts, redis.ts — pre-existing)
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Fișiere mari rămase (candidați viitor split)
|
|
|
|
| Fișier | LOC | Tip | Plan recomandat |
|
|
|---|---|---|---|
|
|
| _(none — toate fișierele >900 LOC au fost spart)_ | | | |
|
|
|
|
---
|
|
|
|
## 4. Bug-uri rezolvate (NU le reintroduce)
|
|
|
|
### 4.1 Auth bypass admin (CRITIC, 100 zile expus)
|
|
|
|
`requireAdmin = (req,res,next) => next()` cu comentariu "STAGING bypass remove
|
|
in production" — toate 21 admin endpoints erau publice.
|
|
|
|
**Fix**: `_middleware.ts` în admin folder verifică:
|
|
- JWT decoded (defense-in-depth — Kong validează signature upstream)
|
|
- Issuer include `/realms/${ADMIN_REALM}` (default `didi-admins`)
|
|
- `realm_access.roles` include unul din `ADMIN_ROLES` (default `admin,super-admin`)
|
|
|
|
Override pentru dev: `ADMIN_AUTH_BYPASS=true` (logează warn pe FIECARE request).
|
|
|
|
### 4.2 Aggregator stolen lock
|
|
|
|
TTL 30s pentru lock; calcul-verdict + LLM explanation putea dura mai mult; alt
|
|
worker prelua lock-ul; primul worker făcea `del()` pe lock-ul greșit.
|
|
|
|
**Fix**: `acquireLock` returnează token UUID, `releaseLock` verifică prin Lua
|
|
script înainte de `del`. Loghează warn dacă TTL a expirat.
|
|
|
|
### 4.3 Component-worker retry fără backoff → cascade failure
|
|
|
|
LLM rate-limit → retry imediat → cascadă.
|
|
|
|
**Fix**: linear backoff 1-5s între retry-uri în `component-worker.ts`.
|
|
|
|
### 4.4 Empty API key fallback
|
|
|
|
`return process.env.OPENROUTER_API_KEY || ''` → request cu `Bearer ` (empty)
|
|
→ silent 401 LLM.
|
|
|
|
**Fix**: throw clar mentționând env vars verificate.
|
|
|
|
### 4.5 Video weighting inversat
|
|
|
|
`pipeline-routes` folosea 0.6 text + 0.4 visual; `aggregator` folosea 0.4 + 0.6.
|
|
Același video, scoruri diferite.
|
|
|
|
**Fix**: helper `combineVideoProbability()` cu canonical 0.4/0.6 (visual mai
|
|
greu — frames sunt evidence directă).
|
|
|
|
### 4.6 KEYCLOAK_ADMIN_USER vs KEYCLOAK_ADMIN
|
|
|
|
`auth.ts` cerea `KEYCLOAK_ADMIN_USER` (env var inexistentă în prod) pe când
|
|
`admin.ts` și docker-compose foloseau `KEYCLOAK_ADMIN`. Ar fi crăpat la primul
|
|
restart cu `requireEnv`.
|
|
|
|
**Fix**: centralizat în `keycloak-admin.ts` cu `KEYCLOAK_ADMIN`.
|
|
|
|
### 4.7 M17_WEB_API_URL throw at module-load
|
|
|
|
`source-assessment/executor.ts` arunca `Error('M17_WEB_API_URL ...')` la
|
|
**module-load time** (nu la runtime). Tests + service refuzau să se încarce.
|
|
|
|
**Fix**: `getM17SearchUrl()` lazy.
|
|
|
|
### 4.8 Stale `dist/` în Docker context
|
|
|
|
Local `dist/` se copia în container și suprascria build-ul fresh.
|
|
|
|
**Fix**: `.dockerignore` în ambele services.
|
|
|
|
---
|
|
|
|
## 5. Test status
|
|
|
|
```
|
|
agent-v3 vitest:
|
|
- 421 / 466 trec
|
|
- 45 fail = pre-existente (au devenit vizibile abia după fix-ul M17 lazy)
|
|
- Toate 20 fail-uri pe care le-am cauzat eu inițial = ACUM REPARATE
|
|
- Suite-le mari care nu se încărcau în baseline (component-runner, integration,
|
|
component-worker) acum rulează
|
|
```
|
|
|
|
**Cele 45 fail-uri rămase sunt în**:
|
|
- `video-processor.test.ts` (13)
|
|
- `verdict-explanation.test.ts` (11)
|
|
- `executor.test.ts` (4)
|
|
- `verdict-calculator.test.ts` (4)
|
|
- `analysis-session.test.ts` (4)
|
|
- alte (9 distribuite)
|
|
|
|
Sunt issues de **mock data** stale și **algorithm shifts** — nu blocau prod.
|
|
NU sunt regresii cauzate de mine.
|
|
|
|
---
|
|
|
|
## 6. Build + deploy
|
|
|
|
### Local TS check (rapid, fără Docker)
|
|
|
|
```bash
|
|
cd /home/admin365/didi_mono/backend/services/orchestration-layer/agent-v3
|
|
npx tsc --noEmit # trebuie să fie 0 erori
|
|
|
|
cd ../didiFramework
|
|
npx tsc --noEmit # trebuie să fie 0 erori
|
|
```
|
|
|
|
### Docker rebuild + deploy
|
|
|
|
```bash
|
|
# agent-v3 (rebuilds toate workers)
|
|
cd /home/admin365/didi_mono/backend/services/orchestration-layer/agent-v3
|
|
docker compose up -d --build
|
|
|
|
# didiFramework
|
|
cd /home/admin365/didi_mono/backend/services/orchestration-layer/didiFramework
|
|
docker compose up -d --build didi-framework
|
|
```
|
|
|
|
### Smoke test post-deploy
|
|
|
|
```bash
|
|
# Health
|
|
curl http://10.11.10.12:24803/api/v3/health
|
|
curl http://10.11.10.12:3005/health
|
|
|
|
# Auth admin (trebuie să fie 401 fără JWT)
|
|
curl -o /dev/null -w "HTTP %{http_code}\n" http://10.11.10.12:3005/api/admin/users
|
|
|
|
# Pipeline async
|
|
curl -X POST http://10.11.10.12:24803/api/v3/techniques/analyze \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"text":"Test analizei.","plan_type":1}'
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Backups
|
|
|
|
Locație: `/home/admin365/didi_mono/backups/`
|
|
|
|
Snapshots numerotate cu timestamp `YYYYMMDD_HHMMSS` la momente cheie:
|
|
- `agent-v3_20260507_122836.tar.gz` (start sesiune)
|
|
- `agent-v3_pre-pino_*.tar.gz`
|
|
- `agent-v3_pre-split-routes_*.tar.gz`
|
|
- `agent-v3_pre-verdict-split_*.tar.gz`
|
|
- `didiFramework_*.tar.gz` (similar)
|
|
|
|
Pentru rollback la orice etapă:
|
|
```bash
|
|
tar -xzf /home/admin365/didi_mono/backups/<snapshot>.tar.gz \
|
|
-C /home/admin365/didi_mono/backend/services/orchestration-layer/
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Statistici cumulate sesiune
|
|
|
|
| Metric | Start | După |
|
|
|---|---|---|
|
|
| `console.*` în prod | 651 | **0** |
|
|
| `error.message` leaks | 126 | **0** |
|
|
| `as any` real în prod | 159 | **13** |
|
|
| Hardcoded passwords în source | 13 | **0** |
|
|
| Auth bypass active | DA | nu |
|
|
| Race conditions cunoscute | 3 | 0 |
|
|
| Files >1000 LOC | 7 | **1** (am spart 9: routes, admin, verdict-calc, auth, pipeline-routes, + 4 executors — singurul rămas e verdict-calc/index.ts cu 864 LOC core class, dar acela e DEJA splitat în folder) |
|
|
| TypeScript errors | 4 baseline | **0** ambele services |
|
|
| `process.env.*` direct reads | 137 | 73 |
|
|
|
|
**54 task-uri completate, 13 deploy-uri reușite, 0 downtime detectabil.**
|
|
|
|
### Update 2026-05-07 (sesiune ulterioară)
|
|
|
|
Pipeline-routes split done (1611 → 8 files). Anti-patterns curățate în drum:
|
|
- 2 `error.message` leaks (lines 943, 1213) → `internalError(res, err, ctx)`
|
|
- 2 `process.env.X || 'fallback'` (FRAMEWORK_API_URL, M17_WEB_API_URL) → `optionalEnv` / `requireEnv`
|
|
- Imports mid-file (line 1329-1338) → grupate sus în fiecare sub-file
|
|
|
|
Claims executor split done (1228 → 7 files). Strategy: păstrează class API
|
|
identic, stage methods devin standalone functions ce primesc `ClaimsCtx`
|
|
(snapshot din state-ul clasei). Class shell rămâne `executor.ts` (319 LOC) cu:
|
|
constructor, state, `loadConfigs`, `saveResult`, `buildCtx`, `execute()` orchestrator.
|
|
|
|
- `searchWebM17` mutat în `web-search.ts` (M17_WEB_API_URL → `requireEnv`)
|
|
- All 36 unit tests trec după split (executor-output.test.ts)
|
|
- Public API neatins: `new ClaimsExecutor(redis, llm).execute(text, sessionId, tier)`
|
|
- Re-export types din executor.ts pentru consumers existenți (component-runner, claims-routes)
|
|
- Pattern propus pentru ai-tampered/techniques/source-assessment executors:
|
|
same approach (Ctx + standalone stage functions + thin class shell)
|
|
|
|
Ai-tampered executor split done (1043 → 9 files). Same pattern as claims:
|
|
- 3 stages (screening, deep-analysis, scoring) + helpers (disclosure, patterns, llm-utils)
|
|
- Class shell în executor.ts (339 LOC) cu execute() + quickAnalyze() (no-LLM rapid path)
|
|
- AiTamperedCtx snapshot trimis la stage functions
|
|
- Smoke test live pe `/api/v3/ai-tampered/quick`: input cu disclosure explicit
|
|
+ ChatGPT mention → ai_probability=85, verdict=LIKELY_AI ✓
|
|
- All 27 unit tests trec (executor-output.test.ts)
|
|
- Public API neatins: ambele `execute()` și `quickAnalyze()` exportate identic
|
|
- Re-export types din executor.ts pentru consumers existenți
|
|
|
|
Techniques executor split done (962 → 7 files). Same pattern (no disclosure
|
|
sub-module — techniques nu are pattern matching). Cea mai mare reducere
|
|
proporțional: 962 → 237 LOC pentru class shell.
|
|
- 3 stages (screening, deep-analysis, scoring) + types + llm-utils + _helpers
|
|
- buildTechniquesList stays cu deep-analysis.ts (e prompt building, nu scoring)
|
|
- findTechniqueById/calculateManipulationScore/buildCouplingContext în scoring.ts
|
|
- resolveStageAssignment exportată din types.ts (re-export prin executor.ts)
|
|
- All 23 unit tests trec
|
|
- Workers techniques pornesc curat, consumă din 6 queues
|
|
|
|
Source-assessment executor split done (932 → 8 files). Structură ușor
|
|
diferită — nu e 2-stage screening+deep, e 4-step pipeline (extract → search +
|
|
domain → map → score). Cea mai mare reducere proporțional: 932 → **137 LOC**
|
|
class shell (a 4-a aplicare a pattern-ului). Public API: doar
|
|
`SourceAssessmentExecutor` class, niciun type re-export (rezultatul e definit
|
|
în shared/types/component-results).
|
|
- 3 stages (extraction, mapping, scoring) + types + defaults (prompts +
|
|
framework hierarchy + models) + external (M17 + Domain Check API) + config-loader
|
|
- Anti-pattern fix: `process.env.DOMAIN_CHECK_API_URL || '...'` → `optionalEnv`
|
|
(hostname intern cu safe default acceptabil)
|
|
- `M17_WEB_API_URL`: `requireEnv` în loc de inline throw
|
|
- Smoke test: `/api/v3/source-assessment/health` + `/config` răspund corect
|
|
- worker-domain rulează source-assessment în-process (nu există worker separat)
|
|
- Nu există __tests__ folder pentru source-assessment, doar TS check + smoke
|
|
|
|
Providers + sync-redis (didiFramework) split done. Două routes mari
|
|
(921 + 898 LOC) → 17 fișiere total. Shell-uri 6 LOC re-export.
|
|
- `providers/`: 8 files (configs/models/assignments/keys/all/prompts/test + _helpers).
|
|
Cel mai mare: assignments.ts (182 LOC). Smoke: `/api/providers/configs` + `/all` OK.
|
|
- `sync-redis/`: 7 files (sync/status/data + fetch-data + fetch-config + _shared + index).
|
|
Cel mai mare: sync.ts (328 LOC). Anti-pattern fix: `error.message` în catch-uri →
|
|
`internalError(res, err, ctx)`. Smoke: GET /status, POST / sync 60 keys în 733ms.
|
|
- Au inclus mici cleanup-uri: `error: any` → `error` (typed unknown), `catch (e)` cu unused → `catch`.
|
|
|
|
---
|
|
|
|
## 9. Sfaturi pentru sesiunea nouă
|
|
|
|
### Întâi citește
|
|
|
|
1. Acest document
|
|
2. `backend/CLAUDE.md`
|
|
3. Scurt scan al fișierelor noi din §2 ca să vezi pattern-urile
|
|
|
|
### Înainte de orice modificare
|
|
|
|
1. **Verifică TS baseline**: `npx tsc --noEmit` în ambele services → trebuie 0 erori
|
|
2. **Backup tar.gz** la `/home/admin365/didi_mono/backups/` cu timestamp clar
|
|
3. **NU re-introduce** anti-pattern-urile din §1
|
|
|
|
### Pentru split-uri viitoare
|
|
|
|
1. Backup
|
|
2. Identifică boundaries clare (comentarii section, doc comments)
|
|
3. Extrage helpers shared întâi (în `_init.ts` sau `_helpers/`)
|
|
4. Folosește `sed -n 'X,Yp'` pentru extracții — atenție la `});` (ușor de pierdut)
|
|
5. Header cu doc comment + imports + `const router = Router();`
|
|
6. Footer cu `\nexport default router;`
|
|
7. Înlocuiește originalul cu un barrel
|
|
8. `tsc --noEmit` după FIECARE fișier creat
|
|
9. Rebuild docker — verifică `.dockerignore` are `dist/`
|
|
10. Smoke test fiecare endpoint group
|
|
|
|
### Anti-pattern-uri identificate
|
|
|
|
- Mă grăbeam să plec ce face fiecare endpoint sub-router fără să verific exact
|
|
boundary line. **Verifică `head -3` și `tail -3` la fiecare extracție**.
|
|
- Sed range `X,Y` extrage inclusiv. Începe cu doc comment line, termină cu `});`.
|
|
- Atenție la `export default router` care era pe ultima linie a originalului —
|
|
nu-l copia de două ori.
|
|
|
|
### Ce să NU faci
|
|
|
|
- ❌ Nu introduce `console.*` direct (folosește `log`)
|
|
- ❌ Nu introduce `error.message` în response (folosește `internalError`)
|
|
- ❌ Nu introduce `new Pool()` (folosește pool-ul shared)
|
|
- ❌ Nu folosi `\|\| 'fallback'` pentru parole/secrets (folosește `requireEnv`)
|
|
- ❌ Nu adăuga `as any` decât cu comentariu explicit explicând de ce
|
|
- ❌ Nu modifica `requireAdmin` să fie no-op (asta era bug-ul critic original)
|
|
- ❌ Nu commita fără să rulezi `tsc --noEmit` întâi
|