1. Niyə sizə ümumiyyətlə ChatGPT App-də audit & lifecycle lazımdır
Prototip yazdığınız müddətdə istifadəçi — özünüzsünüz, DB — lokal SQLite, “insidentlər” isə git reset --hard ilə “müalicə olunur”, hər şey şirin və ev ab-havasında görünür.
Amma sizin GiftGenius (və ya başqa ChatGPT tətbiqi (App)) real istifadəçilər, xüsusilə ödənişlər və PII ilə işləməyə başlayan kimi qəfil aşağıdakılar peyda olur:
- müştərinin təhlükəsizlik mütəxəssisləri belə bir sualla: “sifarişlərimizi kim görə bilər və kim onları dəyişib?”;
- hüquqşünaslar belə bir sualla: “məlumatları nə qədər saxlayırsınız və “məni sil” sorğusunu necə yerinə yetirirsiniz?”;
- istehsal reallığı belə bir sualla: “developer prodda cədvəli drop etsə, nə olacaq?”.
Bu mühazirədə dörd dayağı nəzərdən keçirəcəyik:
- Audit‑jurnallar — təhlükəsizlik və audit üçün ayrıca loglama qatı.
- Data retention — müxtəlif məlumat tiplərinin ömrü və bunu necə reallaşdırmaq.
- İstifadəçi sorğusu ilə silmə — texniki baxımdan “unutulma hüququ”.
- Business continuity & backups — üzünüzü və məlumatları itirmədən nasazlıqlardan necə çıxmaq.
Mümkün olduqca nümunələri tədris App‑imizə bağlayacağıq (şərti GiftGenius — Next.js + Apps SDK + MCP).
2. Audit‑jurnallar: kim, nə, nə vaxt və nə ilə bitdi
Audit‑jurnallar adi jurnallardan nə ilə fərqlənir
Adi application‑jurnallar — developer üçün mehriban mesajlardır. Onlarda stack trace’lər, debug məlumatı, dəyişənlərin qəribə dəyərləri, sazlama console.log("burada mütləq null olmamalıdır") yaşayır. Onların ömrü qısadır və mühəndislər oxuyurlar.
Audit‑jurnallar — başqa bir aləmdir. Onların əsas auditoriyası — təhlükəsizlikçilər, auditorlar, bəzən hüquqşünaslardır. Onlara “55-ci sətirdə NullPointer” sətri lazım deyil, onlara bu cür yazı lazımdır: “istifadəçi X Y təşkilatının ödəniş ayarlarını filan vaxt dəyişdi, nəticə — uğurlu”. Audit yazıları adətən xeyli daha uzun (illərlə) yaşayır və araşdırmalar zamanı sübut hesab olunur.
Əsas fərqlər:
| Xüsusiyyət | Application Logs | Audit Logs |
|---|---|---|
| Məqsəd | Sazlama, diaqnostika | Təhlükəsizlik, uyğunluq, araşdırmalar |
| Auditoriya | Tərtibatçılar, SRE | Təhlükəsizlik mütəxəssisləri, hüquqşünaslar, bəzən tənzimləyicilər |
| Məlumat tərkibi | Tex. detallar, stack trace | Kim/nə/nə vaxt/hansı resursa/hansı nəticə ilə |
| Saxlama müddəti | Həftələr–ay | İllər (çox vaxt ≥ 1 il) |
| Jurnallar üzərində əməliyyatlar | Silmək/yenidən yazmaq olar | Yaxşı olardı append‑only, UPDATE/DELETE olmadan |
OWASP və bənzər təlimatlar ayrıca vurğulayır: audit‑jurnalları ayrıca saxlama yerində və ya cədvəldə saxlamaq daha yaxşıdır, tətbiqin adi jurnalları ilə qarışdırmamaq lazımdır.
ChatGPT tətbiqəsi kontekstində nəyi loglamaq lazımdır
Kommersiya ilə işləyən ChatGPT tətbiqi üçün auditin məntiqli minimumu belədir:
- autentifikasiya hadisələri: login, logout, giriş cəhdləri;
- kritik məlumatlarla əməliyyatlar: profillərin, sifarişlərin, ödəniş ayarlarının yaradılması/yenilənməsi/silinməsi;
- administrativ hərəkətlər: rolların dəyişdirilməsi, tenant ayarlarının dəyişdirilməsi;
- həssas MCP/Agents alətlərinin çağırışları: create_order, charge_customer, cancel_subscription və s.
Yaxşı intuisiya: insident zamanı “bunu kim etdi və nədən keçdi?” deyə soruşmaq istəyəcəyiniz hər şey auditə getməlidir.
Audit hadisəsinin strukturu
Yararlı mental model: hər bir yazı “kim / hansı əməl / nəyə qarşı / hansı konteksdə / hansı nəticə ilə”dir. Çox vaxt bunu belə struktur kimi ifadə edirlər: who, action, resource, context, outcome.
GiftGenius üçün TypeScript‑də interfeysi təsvir edək:
// lib/audit.ts
export type AuditAction =
| "auth.login"
| "auth.logout"
| "order.create"
| "order.cancel"
| "account.delete"
| "giftidea.generate";
export interface AuditEvent {
eventId: string; // uuid
timestamp: string; // ISO
actor: {
userId: string | null; // loginədək null ola bilər
tenantId?: string | null;
ip?: string | null;
client: "chatgpt-app" | "admin-panel" | string;
};
action: AuditAction;
resource?: {
type: string; // "order", "user", ...
id?: string;
};
context?: {
mcpTool?: string;
requestId?: string;
};
outcome: {
status: "success" | "failure";
reason?: string | null;
};
}
Nəzərə alın ki, hadisəyə tam e‑mail, kart nömrələri və əvvəlki mühazirələrdə maskalamağı və gərəksiz yerə loglamamağı öyrəndiyimiz digər PII daxil edilmir.
Audit‑jurnalları harada və necə saxlamaq
Saxlama üçün minimal tələblər:
- adi jurnallardan ayrıca cədvəl və ya hətta ayrıca DB — təsadüfən nəyisə silmək daha çətin olsun deyə;
- imkan daxilində append‑only rejim: texniki olaraq bu, “bu cədvələ heç vaxt UPDATE/DELETE etmirik” siyasəti ola bilər, üstəlik yalnız INSERT və SELECT icazələri olan DB rolu;
- məhdud giriş: bütün mühəndis heyətin tam audit oxumağa icazəsi olmamalıdır.
Əgər PostgreSQL‑i Prisma/Drizzle vasitəsilə istifadə edirsinizsə, model belə görünə bilər (sadələşdirilmiş nümunə):
CREATE TABLE audit_events (
event_id uuid PRIMARY KEY,
created_at timestamptz NOT NULL DEFAULT now(),
actor_user_id text,
actor_tenant_id text,
actor_ip inet,
action text NOT NULL,
resource_type text,
resource_id text,
context_mcp_tool text,
context_request_id text,
outcome_status text NOT NULL,
outcome_reason text
);
Sxem ehtiyaclarınıza uyğunlaşdırılır, lakin əsas məsələ — strukturlaşdırmadır. Bir sətirlik JSON zibili ilə sonra özünüz əziyyət çəkəcəksiniz.
Auditin App‑imizdə reallaşdırılması
Next.js tətbiqində kiçik bir helper yazaq (Node mühiti, məsələn MCP serverində və ya API route‑da):
// lib/audit.ts
import { randomUUID } from "crypto";
import { db } from "./db"; // DB üçün müştəri
export async function logAudit(event: Omit<AuditEvent, "eventId" | "timestamp">) {
const full: AuditEvent = {
...event,
eventId: randomUUID(),
timestamp: new Date().toISOString(),
};
// Real həyatda — növbə/fon vasitəsilə, burada sadəcə insert
await db.insertInto("audit_events").values({
event_id: full.eventId,
created_at: full.timestamp,
actor_user_id: full.actor.userId,
action: full.action,
outcome_status: full.outcome.status,
outcome_reason: full.outcome.reason ?? null,
// ...digər sahələr
});
}
İndi sifarişi yaradan handler‑ə çağırışı əlavə edək (tutaq ki, bu MCP aləti və ya server endpoint‑dir):
// app/api/orders/route.ts
export async function POST(req: Request) {
const user = await requireUser(req); // autentifikasiya modulundan
const body = await req.json();
const order = await createOrderInDb(user, body);
await logAudit({
actor: { userId: user.id, client: "chatgpt-app" },
action: "order.create",
resource: { type: "order", id: order.id },
context: { mcpTool: "create_order_tool" },
outcome: { status: "success" },
});
return Response.json(order);
}
Eyni şeyi təhlükəli əməliyyatların ətrafında da etmək olar — sifarişin ləğvi, ödəniş rekvizitlərinin dəyişdirilməsi, hesabın silinməsi.
Ayrı, strukturlaşdırılmış audit qatı əldə etdik — əla. Növbəti təbii sual: nə qədər bu hadisələr (və digər istifadəçi məlumatları) yaşamalıdır və müddət bitəndə onlarla nə etməli?
3. Data retention: məlumatlarınız nə qədər yaşayır
Niyə hər şeyi əbədi saxlamaq olmaz
“Bəlkə gərək olar” mühəndis instinkti istifadəçi məlumatları kontekstində çox təhlükəlidir.
Birincisi, nə qədər çox və daha uzun müddət məlumat toplayırsınızsa, sızıntı halında nəticələr bir o qədər ağır olur: benzinlə dolu çən nə qədər qalındırsa, yanğın bir o qədər dəhşətlidir. Bir çox məlumat mühafizəsi rəhbərlikləri məlumatları “zəhərli aktiv” adlandırır: faydalı saxlamaq lazımdır, amma həcmi və müddəti minimallaşdırmaq şərti ilə.
İkincisi, GDPR/CCPA səviyyəsində qanunvericilik “emal məqsədi üçün lazım olandan artıq saxlamamaq” prinsipini müəyyənləşdirir. Yəni şəxsi məlumatları “birdən lazım olar” deyə sonsuz saxlayıb olmaz. Hər bir məlumat tipi üçün aydın saxlanma müddətləri və silmə və ya anonimləşdirmə prosedurları olmalıdır.
Üçüncüsü, bulud saxlanması pula başa gəlir. Böyük log cədvəlləri və çat tarixçələri sürətlə böyüyür və bir ildən sonra birdən-birə provayder hesabının yarısı “dünənki zibil” olur.
Fərqli məlumatlar — fərqli müddətlər
Şirkət təcrübəsi və açıq təlimatlar təxminən belə mənzərə verir:
| Məlumat tipi | Tipik saxlanma müddətləri |
|---|---|
| Debug‑jurnallar, texniki metrikalar | 1–12 ay |
| Audit‑jurnallar | ≥ 12 ay, bəzən 2–5 il |
| Sifarişlər, ödənişlər, hesab-fakturalar | 3–7 il (mühasibat/vergilər tələblərinə görə) |
| Session‑lar, müvəqqəti token‑lər | saatlar–günlər |
| Xam çatlar / sorğular | bir neçə həftədən bir neçə aya qədər, yaxud ümumiyyətlə saxlamırıq |
| Anonim aqreqatlar (analitika) | uzun müddət, çünki PII artıq yoxdur |
Vacib: bu hüquqi məsləhət deyil, mühəndis orientiridir. Real məhsul üçün müddətləri hüquqşünaslarla razılaşdıracaqsınız, amma texniki cəhətdən artıq fərqli TTL‑ləri reallaşdırmağa hazır olmalısınız.
Kodda retention necə reallaşdırılır
Ən çox rast gəlinən pattern: cədvəldə created_at və ya expires_at var və sizdə köhnə yazıları silən və ya anonimləşdirən periodik proses olur.
Nümunə: 90 gündən köhnə adi jurnalların təmizlənməsi.
// scripts/cleanup-logs.ts
import { db } from "../lib/db";
async function cleanup() {
await db
.deleteFrom("app_logs")
.where("created_at", "<", new Date(Date.now() - 90 * 24 * 60 * 60 * 1000));
console.log("Old logs removed");
}
cleanup().catch(console.error);
Bu skripti cron ilə, cədvəl üzrə GitHub Actions ilə və ya buludun planlaşdırıcısı ilə işə sala bilərsiniz.
PII üçün silmənin əvəzinə tez-tez anonimləşdirmə edilir. Məsələn, N ildən köhnə sifarişlər konkret istifadəçi ilə əlaqəni itirir:
UPDATE orders
SET user_id = NULL
WHERE created_at < now() - interval '3 years';
Bu halda məbləğlər, mallar və digər “mühasibatlıq” qalır, amma konkret şəxslə əlaqə itir.
Unutmayın ki, ehtiyat nüsxələrin də öz saxlanma müddəti olmalıdır. Backup‑ların dövriliyi və saxlanma müddətini ayrıca backup bölməsində müzakirə edəcəyik, amma ideya eynidir: hətta arxivləri də sonsuz saxlamaq olmaz, əks halda “unutulma hüququ” formaliliyə çevrilir.
4. İstifadəçi sorğusu ilə silmə: “unutulma hüququ” kodda
Tələb haradan yaranır
Avropa GDPR (və oxşar qanunlar) “unutulma hüququ” deyilən anlayışı təqdim edir: istifadəçi öz şəxsi məlumatlarının silinməsini tələb edə bilər və şirkət bunu əsassız gecikdirmədən etməlidir.
Belə bir tətbiqin developer baxımından bu o deməkdir ki, gec-tez sizə “barəmdəki bütün məlumatları silin” sorğusu gələcək (və ya siz “Delete my data” düyməsini əlavə edəcəksiniz) və təkcə users cədvəlindəki yazını silmək deyil, həm də bütün izi üzrə keçmək lazım olacaq: sifarişlər, session‑lar, token‑lər, fəaliyyət jurnalları, CRM, ödənişlər və s.
Amma sizi müəyyən məlumatları saxlamağa məcbur edən qanunlar da var: məsələn, maliyyə tranzaksiyaları. Burada hüquqi mürəkkəblik texniki mürəkkəblikdən çoxdur.
Nələri təmizləmək lazımdır
GiftGenius üçün minimal dəst:
- istifadəçi profili (ad, e‑mail, ayarlar);
- session‑lar, refresh token‑lər, OAuth provayderləri ilə əlaqələr;
- şəxsləndirilmiş formada lazım olmayan sifarişlər (və ya onları anonimləşdirmək olar);
- PII olan jurnallar və audit yazıları (məsələn, xam e‑mail).
Bununla yanaşı, hesabat üçün vacib olan, amma artıq şəxsiyyətsizləşdirilmiş məlumatlar qalır — sifariş məbləğləri, tranzaksiya sayı, ölkələr üzrə aqreqatlar və s.
Silinmə alqoritmi nümunəsi
Ssenari sxemi:
- İstifadəçi (autentifikasiya olunmuş) “Hesabımı sil” düyməsinə basır.
- Serverə onun userId ilə sorğu gedir.
- Server:
- asılı yazıları silir/anonimləşdirir (sifarişlər, session‑lar, inteqrasiyalar);
- profilindəki PII‑ni təmizləyir;
- audit‑jurnalda “məlumatların silinməsi sorğusu yerinə yetirildi” yazısını yazır.
Sadəlik üçün bunu bir neçə cədvəl üzərində minimal variantda göstərəcəyik. Real məhsulda eyni nüvə ətrafında əlavə entitilər (inteqrasiyalar, üçüncü tərəf servisləri və s.) əlavə edəcəksiniz.
Next.js‑də servisləşdirmə kodu (sadələşdirilmiş nümunə):
// app/api/delete-me/route.ts
import { db } from "@/lib/db";
import { logAudit } from "@/lib/audit";
export async function POST(req: Request) {
const user = await requireUser(req);
await db.transaction(async (tx) => {
await tx.deleteFrom("sessions").where("user_id", "=", user.id);
await tx.deleteFrom("orders").where("user_id", "=", user.id);
await tx.updateTable("users")
.set({
is_deleted: true,
name: null,
email: null,
})
.where("id", "=", user.id);
await logAudit({
actor: { userId: user.id, client: "chatgpt-app" },
action: "account.delete",
outcome: { status: "success" },
});
});
return new Response(null, { status: 204 });
}
Real dünyada bura xarici API çağırışları (məsələn, Stripe — customer əlaqəsini kəsmək üçün) əlavə edəcəksiniz, həm də tranzaksiyanı daha ciddi edəcəksiniz. Amma prinsip artıq var: hər şey bir yerdə, audit yazısı ilə.
Backup‑larla qarşılıqlı əlaqə
“Bəs backup‑lar” kimi çətin hissə ilə bir çox maraqlı suallar bağlıdır. Siz istifadəçini iş DB‑dən silsəniz belə, onun məlumatları gecə snapshot‑larında qala bilər. Bunun “əslində heç kimi silmirik”ə çevrilməməsi üçün iki yanaşma var:
- Backup‑ların özlərinin ömrü məhduddur (məsələn, 30–90 gün) və bu müddət bitəndə məlumatlarla birlikdə yox olurlar. Retention müddəti bitdikdən sonra nə əsas DB, nə də arxivlərdə həmin istifadəçi qalır.
- Əgər siz sistemi backup‑dan qaldırırsınızsa, silinmiş ID‑lərin reyestri var və bərpadan sonra silmə/anonimləşdirmə skriptlərini yenidən işlədirsiniz.
Böyük şirkətlər bəzən crypto‑shredding tətbiq edirlər: istifadəçinin PII‑si ayrıca açarla şifrələnir və silmə tələbi gələndə məhz açar məhv edilir. Hətta haradasa şifrəli nüsxələr (jurnallarda, backup‑larda) qalsa belə, açarsız bu faydasız tullantıdır. Bu əladır, amma startap üçün bir az “raket elmi”dir.
Vacib UX məqamı
Unutmayın ki, silmə — təkcə SQL deyil. İstifadəçi gözləyir:
- sorğu göndərməyin anlaşılan yolunu (düymə, forma, e‑mail);
- məqbul icra müddətini (praktikada 30 günədək);
- uğur haqqında bildiriş və ya əsaslandırılmış imtina (məsələn, bəzi məlumatları qanuna görə saxlamaq lazım olduqda).
Texniki tərəfdən sizdə artıq hər şey hazırdır: təmizləməyi bacarırsınız, əməliyyatı jurnallaşdırırsınız və backup‑larda artıqı saxlamırsınız.
5. Business continuity & ehtiyat nüsxələmə
İndi təsəvvür edin ki, yuxarıdakılar ideal işləyir… ta ki, fəlakətli DROP TABLE orders, buludda nasazlıq və ya regionun çöküşü baş verənədək. Bizə xidməti məqbul vaxtda bərpa etmək və kritik məlumatları itirməmək üçün mexanizmlər lazımdır.
RTO və RPO — ağrınızı müəyyən edən iki hərf
Disaster Recovery üçün iki baza parametr:
- RTO (Recovery Time Objective) — nə qədər vaxt əlçatan olmaya bilərsiniz. Məsələn, əgər RTO = 1 saatdırsa, ciddi nasazlıqdan sonra sistemi maksimum bir saata qaldırmalısınız.
- RPO (Recovery Point Objective) — zaman baxımından nə qədər məlumat itkisinə hazırsınız. Əgər RPO = 10 dəqiqədirsə, bərpa zamanı son 10 dəqiqənin tarixçəsini itirmək olar, amma daha çox yox.
Məhsul nə qədər kritikdirsə (bankçılıq, ticarət sistemləri), hər iki parametr sıfra bir o qədər yaxındır. Tədris GiftGenius üçün RTO ~ bir neçə saat və RPO ~ 15–60 dəqiqə ilə yaşamaq olar, amma bunu da hansısa şəkildə reallaşdırmaq lazımdır.
Stekdə nə səhv gedə bilər
Vercel + bulud DB + xarici API‑lər üzərində ChatGPT tətbiqi kontekstində tipik bəlalar belədir:
- OpenAI API əlçatan deyil: App‑iniz tool‑call‑lara xəta ilə cavab verir.
- Vercel (və ya başqası) nasazdır: vidcet backend‑inizə çata bilmir.
- Məlumat bazası zədələnib və ya nəsə təsadüfən silinib (məsələn, DROP TABLE).
- İnfrastruktur idarə edən hesab sındırılıb və ya pozulub.
Bunların hamısına backup‑lar, replikasiyalar və tətbiqin nasazlıq zamanı ağıllı davranışı ilə cavab verirsiniz.
Ehtiyat nüsxələmə strategiyaları
Müasir bulud Postgres/digər DB‑lər adətən minimum üç seçim verir:
- Tam backup‑lar + inkrementallar.
Gündə bir dəfə DB‑nin tam snapshot‑unu etmək və aralarında inkremental dəyişiklikləri saxlamaq. Bərpa — konkret snapshot‑a qayıdış və dəyişiklik jurnalının prokrutkasıdır. - Point‑in‑Time Recovery (PITR).
Baza tranzaksiya jurnalını (WAL) yazır və istənilən zamana bərpə imkanı verir (məsələn, “cədvəli drop etməzdən əvvəl 14:03:00 vəziyyəti”). - Başqa regiona replikasiya.
DB‑nin passiv və ya aktiv replikasını başqa regionda/buludda saxlamaq. Əsas region itirildikdə tətbiqi replikaya keçirib yalnız hələ çatmayan məlumatları itirmək olar.
Bizim miqyas üçün adətən DB provayderində PITR‑i aktivləşdirmək və periodik off‑site backup‑lar kifayətdir.
Sadə nümunə: lokal/dev DB üçün gündəlik dump
Production üçün idarə olunan DB‑yə güvənsəniz belə, staging/dev üçün bəzən sadə skript istəyirsiniz:
# scripts/backup.sh
#!/usr/bin/env bash
set -e
DATE=$(date +%F)
pg_dump "$DATABASE_URL" > "backups/backup-$DATE.sql"
echo "Backup created: backups/backup-$DATE.sql"
Bunu cron və ya GitHub Actions vasitəsilə işə salmaq olar. Əsas — backup‑ları da saxlanma müddətinə görə silməyi unutmayın.
Xarici servislər sıradan çıxanda App‑in davranışı
Backup və PITR “tam hər şey çökdüsə və ya məlumatlar korlanıbsa nə etməli” problemini həll edir. Amma biznes reallığında daha çox qismən nasazlıqlar baş verir — xarici API düşdü, şəbəkə kəsildi, ödəniş sistemi ilişdi.
OpenAI API və ya ödəniş sistemi yatanda ən pis strategiya — cavabda çılpaq 500 və mənasız stack trace ilə yıxılmaqdır. İdeal halda:
- backend { error: "upstream_unavailable" } kimi strukturlaşdırılmış xəta qaytarır;
- vidcet insan üçün anlaşılan mesaj göstərir: “Xidmət müvəqqəti olaraq əlçatan deyil, bir az sonra yenidən cəhd edin”;
- sistem yatan API‑ni sonsuz retry‑larla bombalamır (Circuit Breaker və s. pattern‑ləri dayanıqlılıq modulunda ayrıca baxacağıq).
Xarici xətanı nəzərə alan MCP aləti handler‑i nümunəsi:
// mcp/tools/createGiftIdea.ts
export async function createGiftIdea(args: Input): Promise<Output> {
try {
return await callOpenAiModel(args);
} catch (err) {
await logAudit({
actor: { userId: args.userId ?? null, client: "chatgpt-app" },
action: "giftidea.generate",
outcome: { status: "failure", reason: "openai_unavailable" },
});
throw new Error("UPSTREAM_UNAVAILABLE");
}
}
Sonra MCP ilə vidcet arasında olan aralıq qat artıq bu xətanı UI‑da necə səliqəli göstərməyi bilir.
Bərpanın yoxlanması: restore olmadan backup — sadəcə fayldır
Klassik anti‑pattern: backup gündəlik edilir, hamı razıdır… ta ki, onların bərpa olunmasının mümkünsüz olduğu üzə çıxana qədər (format dəyişib, açar itib, yer çatmayıb).
Minimal plan:
- periodik (məsələn, ayda bir dəfə) backup‑dan staging mühitini qaldırmaq;
- baza ssenarilərdən keçmək: login, sifariş yaradılması, App‑in işi;
- bərpa vaxtı və məlumat itkisi sizin RTO/RPO‑nuza uyğun olduğuna əmin olmaq.
“DevOps dini” kursu əvəzinə bu mühazirə çərçivəsində anlamaq kifayətdir: ehtiyat nüsxələmə prosesləri App arxitekturasının bir hissəsidir, “buludda kimsə edər” deyil.
6. Vizualizasiya: məlumatların və hadisələrin həyat dövrü
Bunun təkcə söz olaraq qalmaması üçün iki sadə sxem çəkək.
İstifadəçi məlumatlarının həyat dövrü
flowchart TD
A["Məlumatların yaradılması<br/>(qeydiyyat, sifariş)"] --> B["Saxlama və istifadə<br/>(prod DB)"]
B --> C["Arxiv/aqreqasiya<br/>(anonim metrikalar)"]
B --> D[Silmə sorğusu]
D --> E[Silmə/anonimləşdirmə<br/>prod DB-də]
E --> F["Backup‑ların saxlanma müddətinin bitməsi<br/>(retention)"]
Baş ideya: məlumatların həyat dövrü prod DB‑də bitmir — backup‑larda da davam edir.
Riskli əməl üçün audit axını
sequenceDiagram
participant User as İstifadəçi
participant ChatGPT as ChatGPT
participant App as Sizin backend/MCP
participant DB as Baza
participant Audit as Audit storage
User->>ChatGPT: "Sifariş #123-ü ləğv et"
ChatGPT->>App: callTool cancel_order
App->>DB: UPDATE orders SET status='canceled'
App->>Audit: INSERT audit_event {actor, action, resource, outcome}
App-->>ChatGPT: Əməliyyatın nəticəsi
ChatGPT-->>User: Nəticə haqqında mesaj
7. Audit & lifecycle üzrə tipik səhvlər
Səhv №1: Audit‑jurnalları ilə adi jurnalların qarışdırılması.
Bütün mesajlar bir ümumi logs indeksinə düşəndə, yarım ildən sonra “istifadəçi admin rolunu dəyişdi” ilə “yenə null reference var”ı heç kim ayırd edə bilməyəcək. Auditdə biznes səviyyəsində strukturlaşdırılmış hadisələr (bax: audit hadisəsinin strukturu bölməsi) və məhdud girişli ayrıca saxlama olmalıdır.
Səhv №2: Audit və debug jurnallarında PII loglamaq.
Tam e‑mail, telefon, çatdırılma ünvanı, kartın son dörd rəqəmi — bütün bunlar çox vaxt təsadüfən jurnallarda görünür. Bu, sızıntı riskini artırır və məxfilik tövsiyələrinə ziddir. Bunun əvəzinə identifikatorları və maskalanmış dəyərləri loglayın.
Səhv №3: Retention siyasətinin olmaması — “hər şeyi həmişə saxlayırıq”.
MVP mərhələsində bu “olar” kimi görünür, amma bir ildən sonra cədvəlləriniz nəhəng ölçülərə çatır və analitikadakı istənilən sorğu DB‑nin özünə qarşı DDoS‑a çevrilir. Üstəlik, müasir məlumat qanunlarında qoyulmuş minimallaşdırma prinsipini pozursunuz. Məlumat tipləri üzrə minimal TTL‑lər düşünülməli, təmizləmə avtomatlaşdırılmalıdır.
Səhv №4: “Sorğu ilə silmə” == DELETE FROM users.
Sadəcə istifadəçinin sətrini sildinizsə, amma onun PII‑si sifarişlərdə, session‑larda və jurnallarda qalıbsa, əslində heç kimi silməmisiniz. Düzgün yanaşma — bütün əlaqəli entitilər üzrə tranzaksiyalı keçmək, silmək mümkün olmayan yerdə anonimləşdirməkdir. Və silmə faktını audit hadisəsi kimi jurnallaşdırmağı unutmayın.
Səhv №5: Məlumatları silərkən backup‑ları nəzərə almamaq.
İstifadəçini prodda sildiniz — yaxşı, amma onun məlumatları hələ bir il köhnə snapshot‑larda yaşayır. Onlardan bərpa edəndə hər şey “dirilir” və siz öz vədlərinizi və Privacy Policy‑ni yenidən pozursunuz. Ya backup‑ların saxlanma müddətini məhdudlaşdırmaq, ya da bərpadan sonra silmələri təkrar tətbiq etmək lazımdır.
Səhv №6: “Bizdə backup‑lar aktivdir, deməli hər şey yaxşıdır”, amma heç kim bərpanı sınamayıb.
Heç vaxt restore edilməmiş backup — sadəcə bahalı fayldır. Periodik bərpa yoxlaması olmadan nə faktiki RTO/RPO‑nu bilirsiniz, nə də ümumiyyətlə DR planınızın işlədiyini. Minimum — checklist üzrə backup‑dan staging‑in müntəzəm test qaldırılmasıdır.
Səhv №7: Sənədləşmə ilə reallıq arasında uyğunsuzluq.
Privacy Policy‑də jurnalları 30 gün saxladığınızı və məlumatları sorğu ilə sildiyinizi yazırsınız, amma kodda hər şey həmişəlik qalır. ChatGPT mağazası, enterprise müştərilər və auditorlar bunu asanlıqla belə suallarla üzə çıxaracaq: “retention cədvəlinizi göstərin” və “konkret istifadəçinin silinməsini nümayiş etdirin”. Yaxşısı budur ki, əvvəlcə edin, sonra yazın.
GO TO FULL VERSION