Egy multi-tenant ERP-ben az audit-log nem opcionalis. A NIS2 + a GDPR + a sok forintos tranzakcio mind annak feltetelezi, hogy minden allamvaltozas leirhato: ki tette, mikor, mit valtoztatott. A Netorigo Admin audit-log modulja egy ev fejlesztes utan kerult elo standard formaba 2026 elejen.
Mit rogzitunk
Minden state-mutating action egy audit_events rekordot ir. A sema:
id- ULIDtenant_id- tenant-scoped, indexelveactor_id- a kerest indito user vagy szolgaltatas (user:12345,system:webhook,ai_assistant:67890)actor_type- enum (user,system,ai_assistant,api_key)entity_type- mit valtoztattunk (product,order,invoice,user,role, ...)entity_id- melyik konkret rekordotaction- enum (create,update,delete,state_change,permission_grant, ...)before- JSON, az allapot a valtoztatas elott (csak az erintett mezok)after- JSON, az allapot a valtoztatas utanrequest_id- ULID, ami a HTTP request-en at vegig azonos, igy egy request altal okozott osszes esemenyt egybe lehet fuzniip_address,user_agent- contextcreated_at- timestamptz
A before/after csak a kulonbsegre szukitett, nem az egesz rekord. Ezt egy NestJS interceptor szuri ki: a service-method elott a relevant fields-et lefoglaja, utana a kulonbseget irja le.
Mit NEM rogzitunk
Ket csoportot szandekosan kihagyunk:
- Read-only health checks - a
/health,/api/health,/livez,/readyzendpointok 1 masodpercenkent futnak, es nem allamvaltozasok. Ezeket az interceptor early-return-nel kihagyja. - Pagination cursorok - a
GET /products?cursor=abc123&limit=50szinten nem allamvaltozas, csak egy olvasas. Ha minden listing-keresert egy audit-rekord keletkezne, a tabla naponta 100k+ sort hizna minden tenanten, es a hasznos audit-jelek eltunnenek a zajban.
A szabaly: csak POST, PUT, PATCH, DELETE HTTP methods + a specifikus state_change szolgaltatas-hivasok (peldaul Order.markAsPaid()) iratnak audit-eventet. A GET-eket nem.
Read-side: szuro UI
Az /admin/audit oldalon egy szuro-baros lista van:
- Datum-tartomany (alap: utolso 7 nap)
- Actor (autocomplete user-listabol, vagy
system/ai_assistant) - Entity type + opcionalis
entity_id(peldaulproduct:12345minden valtoztatasa) - Action (multi-select)
- Search - free-text a
before/afterJSON-okban (Postgrestsvectorindexszel)
A lista 50 soronkent paginalva, kibovithetok a before -> after diff-tel egy expandalhato panel-ben. JSON-diff syntax-highlight-tal (red = removed, green = added, yellow = changed).
A CSV export egy BullMQ job: a felhasznalo elinditja, a worker generalja, email-ben kuldi el a download-linket. Egy 30-napos export egy 100k-soros log-ra kb. 4-5 perc.
Retention
Alapertelmezett retention: 365 nap. Ez tenanten allithat be (legminimum 30 nap, max 7 ev). Az ennel idosebb rekordok egy heti cleanup-job-bal tolodnek S3 cold storage-ba (gzipped JSONL), es 7 ev utan torlodnek. Ha valaha kell, a cold-storage adatokat egy admin-eszkozzel be lehet importalni vissza.
A duplikalas-policy: a webhook-fertozes
2026 februarjaban egy ugyfel beallitott egy webhookot, ami minden order.updated esemenyre egy PUT /orders/:id kerest tett vissza ugyanazokkal az adatokkal. Ezzel egy ciklust hozott letre: 14,000 spurious audit-rekord 11 perc alatt, mind ugyanazzal a before == after allapottal.
A fix: az interceptor most ellenorzi, hogy a before es after byte-szinten egyezik-e. Ha igen, NEM ir audit-eventet (csak inkrementalja egy audit_dedupe_counter metrikat, ami a Prometheus dashboard-on latszik). Ez nem hibanak szamit a kliens fele - a webhook visszater 200-zal, csak nem keletkezik fals audit.
A dedupe metric Norbinak is hasznos: ha egy tenanton hirtelen megugrik, valoszinuleg egy konfiguracios problema van valamelyik webhookjukban.