Barcha loyihalarIshlamoqda

PG Insight

Ochiq multi-tenant monitoring xizmati: har kim ro'yxatdan o'tib, o'z PostgreSQL bazasini qo'shadi va faqat o'zi qo'shgan bazalarni ko'radi

Bu loyiha nimani isbotlaydi

  • Tenant izolyatsiyasi: global guard har bir :targetId route'ida egani tekshiradi, begona target'ga 403 emas, 404 qaytaradi; himoyasiz mutating route qo'shilsa test yiqiladi
  • SSRF himoyasi: DNS javobidagi barcha IP tekshiriladi, ulanish tekshirilgan IP'ga qotiriladi (TLS SNI saqlanadi); production'da platform-db, 169.254.169.254, 127.0.0.1 rad etildi
  • Ixtiyoriy SQL'ni cheklash: EXPLAIN 6 qatlamda himoyalangan, READ ONLY va 5 s statement_timeout bilan; pg_sleep 0.05 s da, generate_series(1, 10000000000) 5.4 s da 400 bilan to'xtatildi
  • Kuzatiladigan bazaga yukni o'lchadim: 10 daqiqada 1 yadroning 0.06% i, konteyner CPU'si +0.3%
  • Operatsiya: SHA-tagli deploy, health tekshiruvidan keyingina yozib qo'yish, ikki yo'nalishli rollback mashqi
Rol
Yakka: dizayn, backend, frontend, xavfsizlik, testlar, deploy va operatsiya (81 commit)
Muddat
2026.08 – 2026.10
Texnologiyalar
  • NestJS
  • TypeScript
  • Prisma
  • PostgreSQL
  • TimescaleDB
  • Socket.IO
  • React
  • Vite
  • Tailwind CSS
  • Recharts
  • Jest
  • Vitest
  • Docker
  • Caddy

Asosiy raqamlar

Ko'rsatkichQiymatTuriDalil
10 daqiqada target'ga yuborilgan so'rovlar2 430O'lchanganDalilni ko'rish
Target'ga yuk (1 yadroga nisbatan)0,06 %O'lchanganDalilni ko'rish
So'rovning o'rtacha ijro vaqti0,15 msO'lchanganDalilni ko'rish
Yig'ilgan ma'lumot kechikishi1,4 sO'lchanganDalilni ko'rish
EXPLAIN ichidagi pg_sleep'ni to'xtatish vaqti0,05 sO'lchanganDalilni ko'rish
Avtomatik testlar (188 backend + 49 frontend)237O'lchanganDalilni ko'rish

Hal qilinadigan muammo

Ishlab turgan PostgreSQL'dagi sekin so'rovlar, lock kutishlari, bloat va replikatsiya kechikishi turli vosita va SQL orqali alohida tekshiriladi. Ularni bitta xizmatga yig'dim; bu begona bazalarga ulanadigan ochiq xizmat bo'lgani uchun tenant izolyatsiyasi, SSRF himoyasi va ixtiyoriy SQL'ni cheklash boshidanoq kerak edi.

Arxitektura

Collector'lar har bir bazadan 3 soniya–5 daqiqa oralig'ida metrika yig'ib, TimescaleDB'ga (10 ta hypertable) yozadi; jonli sahifalar esa bazaga to'g'ridan-to'g'ri so'rov yuboradi. Foydalanuvchi, target, alert va audit Prisma 7 boshqaradigan platforma DB'da (PostgreSQL 16). NestJS 11 global guard'lari (Throttler → JWT → TargetAccess) har bir :targetId route'ida egalikni tekshiradi, target so'rovlari esa target boshiga pool'da (maks. 5) xom SQL bilan bajariladi. Rollar ikkita: admin (birinchi hisob) va user (o'z target'lari, 3 tagacha, faqat ommaviy manzillar); UI koreys, ingliz va o'zbek tillarida.

Ma'lumotlar modeli

Asosiy cheklovlar va indekslar

  • users.role DEFAULT 'user', bcrypt 12
  • refresh_tokens.tokenHash UNIQUE: SHA-256 해시만 저장, 사용할 때마다 교체
  • targets.passwordEncrypted: AES-256-GCM, createdByUserId = 테넌트 소유자, sslMode DEFAULT 'require'
  • hypertable 10개, 1일 chunk; connection_metrics, query_metrics는 7일 뒤 압축, retention 7–30일
  • audit_logs: 로그인, 가입, 대상 변경, 모든 EXPLAIN 기록

Asosiy loyihaviy qarorlar

Demo emas, ochiq multi-tenant xizmat

Vaziyat
Har kim o'z bazasini kuzata olsin.
Qaror
Ro'yxatdan o'tish ochiq, har bir foydalanuvchi faqat o'zi qo'shgan target'larni ko'radi.
Evaziga
Tenant izolyatsiyasi, SSRF, kvota va rate limit majburiy bo'ldi.

Egalik tekshiruvi controller'da emas, global guard'da

Vaziyat
Yangi route qo'shilganda tekshiruvni unutib bo'lmasin.
Qaror
Throttler → JWT → TargetAccess tartibidagi global guard'lar har bir :targetId route'ini tekshiradi.
Evaziga
Boshqa ID'li route'lar uchun @TargetAccess dekoratori kerak.

Begona target'ga 403 emas, 404

Vaziyat
Target mavjudligini ham oshkor qilmaslik kerak.
Qaror
Ruxsat bo'lmasa, mavjud bo'lmagan target bilan bir xil 404 qaytadi.
Evaziga
Admin uchun ham mavjudlik alohida tekshiriladi.

Host'ni resolve qilib, IP'ga qotirish

Vaziyat
Tekshiruv va ulanish paytidagi DNS javobi farq qilsa (DNS rebinding), ichki tarmoqqa kirish mumkin.
Qaror
DNS javobidagi barcha IP net.BlockList bilan tekshiriladi va ulanish tekshirilgan IP'ga to'g'ridan-to'g'ri qilinadi.
Evaziga
TLS uchun SNI qo'lda beriladi; o'z-o'zini hosting uchun TARGET_ALLOW_PRIVATE_HOSTS.

EXPLAIN qoladi, lekin READ ONLY + 5 s + funksiya denylist

Vaziyat
Egasi o'z bazasida reja ko'ra olishi kerak, lekin EXPLAIN ANALYZE so'rovni haqiqatan bajaradi.
Qaror
6 qatlamli tekshiruvdan keyin READ ONLY tranzaksiyada statement_timeout va lock_timeout bilan bajariladi.
Evaziga
Vaqt va funksiya cheklovi sabab ba'zi to'g'ri so'rovlar ham rad etilishi mumkin.

Yangi target uchun standart sslmode=require

Vaziyat
pg driver'da libpq'dagi prefer (TLS, bo'lmasa oddiy ulanish) yo'q, shuning uchun standart prefer amalda shifrlanmagan ulanish edi.
Qaror
Yangi target'lar va DB default'i require'ga o'zgartirildi (47287b4).
Evaziga
TLS'siz bazada foydalanuvchi disable'ni o'zi tanlaydi.

Migratsiyalar orqaga qaytarilmaydi

Vaziyat
Rollback faqat image'ni almashtirsa, tez va xavfsiz.
Qaror
Har bir migratsiya qo'shimcha shaklda (avval qo'shish, keyin tozalash).
Evaziga
Har migratsiya oldingi kod bilan mos bo'lishi kerak.

Oldin va keyin

Oldinrestart → 0 active targets, no data 14:23–17:25 UTC
Keyinrestart → 2 active targets, 2,430 queries / 10 minDalilni ko'rish
Nima o'zgartirildi
Orchestrator onModuleInit'da pool'lar ulanib bo'lishidan oldin ishga tushardi; endi onApplicationBootstrap'da ishga tushadi va restart tartibini tekshiradigan lifecycle testi qo'shildi (6246ce2). Shu bosqichda lock so'rovidagi RENAME xatosi, metrika vaqti, pauza holati, TLS default'i va /health 500 → 503 ham tuzatildi.

Nosozlik tarixi

Belgi
API restartdan keyin hech bir target kuzatilmadi; overhead o'lchovida PG Insight'dan 0 ta so'rov chiqdi.
Sabab
Log Starting collection for 0 active target(s) ni ko'rsatdi: orchestrator onModuleInit'da pool'lar ulanishidan oldin ishga tushgan, 14:23–17:25 UTC oralig'idagi ma'lumot yo'qoldi.
Yechim
Ishga tushish onApplicationBootstrap'ga ko'chirildi (6246ce2); restartdan keyin ikkala target ham darhol yig'iladi.
Takrorlanmasligi uchun
Xatoni qayta chiqaradigan lifecycle testi qo'shildi, u pauzadagi target'lar o'tkazib yuborilishini ham tekshiradi; yangi testlar eski kodda yiqilishi tasdiqlangan.

Test va sifat

Testlar soni
237
Qamrov
—
CI
GitHub Actions (워크플로 준비, billing 문제로 현재 중단)
Test vositalari
Jest, Supertest, Vitest, 실제 PostgreSQL 16

Deploy va ekspluatatsiya

Deploy muhiti
iwinv VPS, Docker Compose: api (NestJS, root bo'lmagan), web (Caddy + React statik fayllari, /api/* va /socket.io/* proxy), platform-db (PostgreSQL 16), metrics-db (TimescaleDB). DB portlari tashqariga ochilmagan. deploy.sh: conflict belgisi tekshiruvi → git merge --ff-only → api va web SHA tegi bilan build → health 45 × 2 s kutiladi → faqat muvaffaqiyatda APP_TAG va .deploy-history yangilanadi. API konteyneri ishga tushganda prisma migrate deploy bajaradi.
Backup
Server umumiy cron backup'i (har kuni 03:00, 7 kun) platform-db va metrics-db'ni qamraydi. Foydalanuvchi bazalarining pg_dump backup'ini faqat admin yarata oladi.
Rollback
rollback.sh build'siz mavjud image bilan api va web'ni qayta yaratadi, avval ikkala image borligini tekshiradi. Production'da ikki yo'nalishda (71dceaf ↔ d587ce6) sinaldi, ikkalasida ham health 200.
Monitoring
Real foydalanish: LedgerCore va FlashDrop production bazalari target sifatida qo'shilib kuzatiladi (o'sha serverdagi ichki Docker tarmog'i). /api/v1/health (DB ishlamasa 503), audit loglar (login, sign-up, target o'zgarishi, har bir EXPLAIN), sign-up IP boshiga soatiga 5 ta, login daqiqasiga 10 ta. GitHub private vulnerability reporting (SECURITY.md)

Skrinshotlar

Cheklovlar va xulosalar

Hozirgi cheklovlar

  • Hisob boshqaruvi yo'q: email tasdiqlash, parolni tiklash va hisobni o'chirish hali qilinmagan
  • Diagnostika, xavfsizlik natijalari, tavsiyalar va xato xabarlari backend'dan faqat inglizcha keladi (UI uch tilda)
  • connection_by_app hypertable'iga retention qo'yilmagan va cheksiz o'sadi; metrika DB'ning migratsiya mexanizmi yo'q (init-platform.sql faqat volume birinchi yaratilganda ishlaydi)
  • Real-time socket JWT va egalik bilan himoyalangan, lekin UI hali subscribeToTarget'ni chaqirmaydi va REST polling bilan yangilanadi
  • Monitoring so'rovlari optimallashtirilmagan: pg_settings har 10 s qayta o'qiladi, target boshiga pool 5 ta; kesh va 2–3 ulanishli pool keyingi qadam
  • query_metrics hajmi (target boshiga soatiga ~4 MB) 22 daqiqalik oynadan taxmin; 24 soatlik o'lchov hali qilinmagan
  • Mavjud ikki target (LedgerCore, FlashDrop) ichki Docker tarmog'ida prefer bilan, ya'ni shifrlanmagan ulanishda (trafik serverdan chiqmaydi)
  • CI workflow tayyor, lekin GitHub Actions billing sababli to'xtagan, testlar lokalda yuritiladi. Kalit almashtirish skripti (rotate-encryption-key.ts) Prisma 7 adapter'isiz yozilgan, production'da sinalmagan; Playwright E2E yo'q

Asosiy kod

Hozirgi holat va keyingi qadamlar

Yig'ish kuzatilayotgan bazaga 10 daqiqada 2 430 so'rov va jami 353.4 ms ijro vaqti berdi, ya'ni 1 yadroning ~0.06% i. Haqiqiy production bazalarda (LedgerCore, FlashDrop) 5 ta, kod ko'rigi va testlarda yana 3 ta xato topildi; har biri avval test bilan qayta chiqarilib, keyin tuzatildi. Boshqa holatlar: real-time broadcast xatosi collector'larni to'xtatgani (b9825e4), lock so'rovidagi RENAME sintaksis xatosi (b9825e4), connection metrikasiga sessiya boshlangan vaqt yozilgani (d587ce6, 891 noto'g'ri qator o'chirildi), pauzadagi target restartdan keyin o'zi yoqilgani (41569c7), parol o'zgarganda SSL yo'qolgani va prefer amalda shifrlamagani (41569c7, 47287b4), birinchi rollback mashqida eski image yo'qligi sababli skript xavfsiz to'xtagani.