livrare lot 2

This commit is contained in:
EVOTECH IT SRL 2026-07-10 03:39:53 -07:00
commit 8ecc78e729
763 changed files with 164593 additions and 0 deletions

625
backend/REFACTOR_CONTEXT.md Normal file
View file

@ -0,0 +1,625 @@
# 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