livrare lot 2
This commit is contained in:
commit
8ecc78e729
763 changed files with 164593 additions and 0 deletions
625
backend/REFACTOR_CONTEXT.md
Normal file
625
backend/REFACTOR_CONTEXT.md
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue