1 IQV Infra — Teknik Dokümantasyon
muhammedfatihercakir edited this page 2026-09-30 19:58:57 +03:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<html> <html><head></head>

IQV Infra — Teknik Dokümantasyon

Bu doküman IQV Infra projesinin mevcut kaynak kodu üzerinden çıkarılmış teknik referansıdır. Hedef kitle frontend/backend geliştiricileri, DevOps ekipleri, sistem yöneticileri, teknik destek ekipleri ve projeyi devralacak/bakım yapacak mühendislerdir.

Bu dokümandaki teknik bilgiler gerçek kaynak kod üzerinden doğrulanmıştır. Kaynak koddan kesin olarak doğrulanamayan noktalar ayrıca belirtilmiştir. Secret değerler dokümana dahil edilmemiştir.


İçindekiler

  1. Amaç ve Kapsam
  2. Genel Mimari
  3. Repository Yapısı
  4. Frontend Mimarisi
  5. Backend Mimarisi
  6. Kimlik Doğrulama ve Yetkilendirme
  7. Public / Müşteri Erişim Mimarisi
  8. Veri Modelleri
  9. Infra Form Domain Yapısı
  10. Form Bölümleri ve Alan Davranışları
  11. Progress / Tamamlandı Mekanizması
  12. Makine Yönetimi
  13. Proje Tipi / Durum Yönetimi
  14. Ekler ve Dosya Yönetimi
  15. E-posta ve Bildirim Altyapısı
  16. PDF / Export
  17. API Endpointleri
  18. Environment Variables
  19. Docker ve Deployment
  20. Test Altyapısı
  21. CI / Quality Pipeline
  22. Responsive / Mobil / Tablet
  23. Güvenlik
  24. Bakım Kuralları ve Bilinen Kısıtlar

1. Amaç ve Kapsam

IQV Infra, IQVizyon'un müşteri sahalarındaki teknoloji altyapısını (sunucu, ağ, uzaktan erişim, güvenlik, tezgah/makine bilgileri) kayıt altına alan; bu bilgileri token'sız (session cookie tabanlı) bir "public" müşteri formu ile müşteriye doldurtan; IQVizyon tarafında kontrol/onay ("IQVizyon Kontrolü" ve "Kurulum Kararı") sürecini yöneten ve tüm bu süreci e-posta/Telegram bildirimleriyle destekleyen bir kurum-içi web uygulamasıdır.

Uygulamanın iki ana bileşeni vardır:

  • Backend (backend/): Node.js (CommonJS modül sistemi) + Express 4, MongoDB resmi sürücüsü (mongodb npm paketi — ODM/Mongoose kullanılmaz), JWT tabanlı kimlik doğrulama (Bearer token), Swagger/OpenAPI ile kendini otomatik belgeler (/api-docs, /openapi.json).
  • Frontend (dashboard/): React 18 + TypeScript + Vite 4, antd 5 (UI bileşen kütüphanesi) ve @iqvizyonui/react-components (kurumsal tasarım sistemi), Redux Toolkit + redux-persist (oturum kalıcılığı), React Router 6.

Bu dokümanın kapsamı yalnızca iqv-react-dashboard reposudur. Doküman; mimari kararları, veri modellerini, iş kurallarını, güvenlik sınırlarını, deployment/test/CI altyapısını ve bilinen kısıtları/legacy alanları kapsar. Uygulamanın kullanım kılavuzu ayrı bir belgededir (IQV_Infra_Son_Kullanici_Kilavuzu.md) ve bu dokümanın konusu değildir.

Kaynaklar: README.md, backend/package.json, dashboard/package.json, repository dizin ağacının doğrudan gözlemi.


2. Genel Mimari

[Tarayıcı] --(HTTPS)--> [nginx (Docker) / static-server.mjs (native)]
                             |-- statik dosyalar (React SPA build çıktısı)
                             |-- /api/, /organizations, /infra-record,
                             |   /infra-public-*, /infra-logs/, /infra-admin-*,
                             |   /it/public*, /health, /api-docs, /openapi.json
                             |   --(reverse proxy)--> [Express backend :4000]
                                              |-- MongoDB (iqvizyon veritabanı)
                                              |-- SMTP (Office365, opsiyonel)
                                              |-- Telegram Bot API (opsiyonel)
                                              |-- Gemini API (opsiyonel, /api/it/bil)

Docker Compose ortamında üç servis çalışır: mongo, backend, dashboard (bkz. bölüm 19). Docker olmadan (native) kurulumda scripts/common/static-server.mjs, nginx'in yerini alan bağımlılıksız bir Node.js statik dosya + reverse-proxy sunucusudur ve aynı proxy path sözleşmesini uygular — böylece dev/native/Docker/prod ortamları arasında hangi path'in backend'e, hangisinin SPA'ya gittiği tutarlıdır.

Backend ile frontend tek bir kök .env dosyasını paylaşır (bkz. bölüm 18) — bu, iki bağımsız .env dosyasının birbirinden sapmasını (drift) yapısal olarak engeller.

Kimlik doğrulama iki tamamen ayrı mekanizma kullanır:

  • Admin/personel tarafı: JWT (Bearer token), localStorage + redux-persist ile istemcide saklanır (bkz. bölüm 6).
  • Müşteri/public form tarafı: HttpOnly session cookie (iqv_infra_public_session), token URL'de hiçbir zaman taşınmaz (bkz. bölüm 7).

Bu iki mekanizma aynı JWT_SECRET ile imzalanır ama purpose alanı ile ayrıştırılır (bkz. bölüm 6.3 ve 23.2) — mimari olarak bilinçli bir kısıt olarak dokümante edilmiştir.

Kaynaklar: docker-compose.yml, dashboard/nginx.conf, scripts/common/static-server.mjs, backend/src/app.js, README.md.


3. Repository Yapısı

iqv-react-dashboard/
├── backend/
│   ├── src/
│   │   ├── app.js                 # Express router mount haritası (createApp)
│   │   ├── server.js              # Entrypoint — HTTP sunucu başlatma
│   │   ├── config/                # env.js, db.js
│   │   ├── controllers/           # HTTP request/response sözleşmeleri
│   │   ├── docs/                  # OpenAPI/Swagger üretimi + özel gruplama eklentisi
│   │   ├── middleware/            # auth, publicSession, errorHandler
│   │   ├── models/                # ham MongoDB collection sarmalayıcıları
│   │   ├── repositories/          # MongoDB CRUD katmanı
│   │   ├── routes/                # Express Router tanımları
│   │   ├── services/              # iş mantığı
│   │   ├── utils/                 # apiError, asyncHandler, publicSession, regex, infraStatus…
│   │   └── validators/            # request body doğrulama
│   └── tests/                     # unit / integration / auth / security / contract
├── dashboard/
│   ├── src/
│   │   ├── components/it/         # Infra form domain'inin tamamı (form, önizleme, public görünüm)
│   │   ├── components/hooks/      # useBreakpoint vb. paylaşılan hook'lar
│   │   ├── routes/                # api.tsx, web.tsx, browserRouter.tsx, guard'lar
│   │   ├── services/               # axios tabanlı API istemcileri
│   │   ├── store/                  # Redux Toolkit + redux-persist
│   │   ├── interfaces/models/it.ts # domain veri modeli (TypeScript, 710 satır)
│   │   └── utils/
│   ├── nginx.conf, Dockerfile, vite.config.ts
│   └── tests/                      # unit / component (Vitest)
├── tests/e2e/                      # Playwright uçtan uca testler
├── scripts/                        # kurulum/güncelleme/kaldırma (linux/windows × docker/native), CI script'leri
├── docs/                           # MkDocs dokümantasyon sitesi
├── .github/workflows/              # ci.yml, docs.yml
├── docker-compose.yml
├── .env.example
└── README.md

Kaynaklar: repository dizin ağacının device_list_dir ile doğrudan gözlemi, backend/src/app.js, README.md.


4. Frontend Mimarisi

4.1 Teknoloji Yığını

React 18 + TypeScript, Vite 4 (build/dev sunucusu), antd 5 (UI bileşenleri: Table, Modal, Form, Select…), @iqvizyonui/react-components (IQVizyon kurumsal tasarım sistemi — Field/Input/Label/Select gibi bileşenler için), Redux Toolkit + redux-persist (oturum kalıcılığı), React Router 6 (createBrowserRouter), @loadable/component (route bazlı code-splitting), dayjs (tarih işleme), vite-plugin-pwa (Service Worker/PWA desteği), axios (HTTP istemcisi).

Kaynaklar: dashboard/package.json, dashboard/vite.config.ts, dashboard/src/components/it/ITForm.tsx.

4.2 Routing

dashboard/src/routes/web.tsx yol sabitlerini, dashboard/src/routes/browserRouter.tsx gerçek route ağacını (createBrowserRouter) tanımlar. api.tsx ise backend API path sabitlerini tutar (React route'u değildir — bkz. bölüm 5.2 ile karşılaştırma).

Route Bileşen Koruma/Yetki Açıklama
/ Redirect — Giriş durumuna göre /infra veya /login'e yönlendirir.
/login Login AuthLayout (korumasız) Kullanıcı adı/parola girişi, başarılı girişte JWT alınır.
/infra Dashboard RequireAuth + Layout Ana Infra kayıt listesi/özet ekranı.
/users Users RequireAuth + RequirePermission('users.read') Personel/kullanıcı yönetimi.
/personel → /users yönlendirme RequireAuth Geriye dönük uyumluluk alias'ı (eski link).
/it ITPage RequireAuth + Layout Yeni kayıt oluşturma + kayıt seçimi/önizleme akışının admin girişi.
/hatirlatici ReminderPage RequireAuth + Layout Hatırlatıcı planlama ekranı.
/form PublicITView Yok (public, token'sız — HttpOnly session cookie ile) Müşterinin dolduracağı public form; RequireAuth/Layout ağacının bilinçli olarak dışında.
  • | NotFoundPage | — | Eşleşmeyen tüm path'ler. /api-docs, /api-docs/, /openapi.json | — (React route'u DEĞİL) | — | Backend tarafından servis edilir (bkz. bölüm 5.2); nginx/proxy bu path'leri SPA'ya değil doğrudan backend'e yönlendirir.

24.2 Genel Bakım Kuralları (kod içi yorumlardan çıkarılan proje kültürü)

  • Yeni bir alan/uç nokta eklerken var olan isimlendirme sözleşmesine uyulmalı (snake_case alan adları, section_* whitelist deseni, *_requirement metin alan deseni).
  • Public tarafa asla mass-assignment ile yazma açılmamalı — her yeni public alan, submitPublicProgress/setPublicSectionStatus içinde açıkça whitelist'e eklenmelidir.
  • steps[]'e eklenen her yeni adım derin kopya olmalı; var olan adımlar asla güncellenmemeli/silinmemeli (audit trail bütünlüğü).
  • Yeni bir secret/env değişkeni eklenirken .env.example'a şablon (değersiz) olarak eklenmeli, koda gömülmemeli.
  • docs:check CI gate'i, yeni bir endpoint OpenAPI şemasına yansıtılmadan eklenirse pipeline'ı kırar — yeni route eklerken backend/src/docs/ güncellenmelidir.
  • Yeni bir kalem/madde (REQUIREMENT_ITEMS listesine) eklenirken hem frontend constants.ts hem backend infraRecord.service.js'teki bağımsız sabit listeler birlikte güncellenmelidir — bu ikisi kasıtlı olarak birbirinden bağımsız sabit kodlanmıştır (istemciden gelen ham değerlere güvenilmemesi için), bu yüzden tek taraflı güncelleme veri tutarsızlığına yol açar.
  • Var olan alanlar yeniden adlandırılmaz; yeni bir ihtiyaç doğduğunda mevcut alana ek/yeni bir alt-alan eklenir (bkz. it.ts içindeki "MEVCUT alanların HİÇBİRİ yeniden adlandırılmadı" yorumları) — bu, eski kayıtlarla geriye dönük uyumluluğu korumak için proje genelinde tutarlı biçimde izlenen bir kuraldır.
  • Yeni bir kapasite/gereksinim alt-nesnesi eklenirken var olan { device_type, cpu_core, ram_gb/ram_unit, disk_ssd_gb/disk_ssd_unit, operating_system } şekli tekrar kullanılır, ikinci bir benzer şekil icat edilmez (bkz. provided_capacity/requested_capacity'nin birebir aynı şekli paylaşması).

Kaynaklar: yukarıdaki tüm bölümlerde atıfta bulunulan dosyalar; backend/src/repositories/infraRecord.repository.js, backend/src/services/bil.service.js, dashboard/src/routes/browserRouter.tsx, dashboard/src/services/itService.ts, dashboard/src/interfaces/models/it.ts.


Doküman sonu.

</html> </html># IQV Infra — Teknik Dokümantasyon

Bu doküman IQV Infra projesinin mevcut kaynak kodu üzerinden çıkarılmış teknik referansıdır. Hedef kitle frontend/backend geliştiricileri, DevOps ekipleri, sistem yöneticileri, teknik destek ekipleri ve projeyi devralacak/bakım yapacak mühendislerdir.

Bu dokümandaki teknik bilgiler gerçek kaynak kod üzerinden doğrulanmıştır. Kaynak koddan kesin olarak doğrulanamayan noktalar ayrıca belirtilmiştir. Secret değerler dokümana dahil edilmemiştir.


İçindekiler

  1. [Amaç ve Kapsam](#1-amaç-ve-kapsam)
  2. [Genel Mimari](#2-genel-mimari)
  3. [Repository Yapısı](#3-repository-yapısı)
  4. [Frontend Mimarisi](#4-frontend-mimarisi)
  5. [Backend Mimarisi](#5-backend-mimarisi)
  6. [Kimlik Doğrulama ve Yetkilendirme](#6-kimlik-doğrulama-ve-yetkilendirme)
  7. [Public / Müşteri Erişim Mimarisi](#7-public--müşteri-erişim-mimarisi)
  8. [Veri Modelleri](#8-veri-modelleri)
  9. [Infra Form Domain Yapısı](#9-infra-form-domain-yapısı)
  10. [Form Bölümleri ve Alan Davranışları](#10-form-bölümleri-ve-alan-davranışları)
  11. [Progress / Tamamlandı Mekanizması](#11-progress--tamamlandı-mekanizması)
  12. [Makine Yönetimi](#12-makine-yönetimi)
  13. [Proje Tipi / Durum Yönetimi](#13-proje-tipi--durum-yönetimi)
  14. [Ekler ve Dosya Yönetimi](#14-ekler-ve-dosya-yönetimi)
  15. [E-posta ve Bildirim Altyapısı](#15-e-posta-ve-bildirim-altyapısı)
  16. [PDF / Export](#16-pdf--export)
  17. [API Endpointleri](#17-api-endpointleri)
  18. [Environment Variables](#18-environment-variables)
  19. [Docker ve Deployment](#19-docker-ve-deployment)
  20. [Test Altyapısı](#20-test-altyapısı)
  21. [CI / Quality Pipeline](#21-ci--quality-pipeline)
  22. [Responsive / Mobil / Tablet](#22-responsive--mobil--tablet)
  23. [Güvenlik](#23-güvenlik)
  24. [Bakım Kuralları ve Bilinen Kısıtlar](#24-bakım-kuralları-ve-bilinen-kısıtlar)

1. Amaç ve Kapsam

IQV Infra, IQVizyon'un müşteri sahalarındaki teknoloji altyapısını (sunucu, ağ, uzaktan erişim, güvenlik, tezgah/makine bilgileri) kayıt altına alan; bu bilgileri token'sız (session cookie tabanlı) bir "public" müşteri formu ile müşteriye doldurtan; IQVizyon tarafında kontrol/onay ("IQVizyon Kontrolü" ve "Kurulum Kararı") sürecini yöneten ve tüm bu süreci e-posta/Telegram bildirimleriyle destekleyen bir kurum-içi web uygulamasıdır.

Uygulamanın iki ana bileşeni vardır:

  • Backend (backend/): Node.js (CommonJS modül sistemi) + Express 4, MongoDB resmi sürücüsü (mongodb npm paketi — ODM/Mongoose kullanılmaz), JWT tabanlı kimlik doğrulama (Bearer token), Swagger/OpenAPI ile kendini otomatik belgeler (/api-docs, /openapi.json).
  • Frontend (dashboard/): React 18 + TypeScript + Vite 4, antd 5 (UI bileşen kütüphanesi) ve @iqvizyonui/react-components (kurumsal tasarım sistemi), Redux Toolkit + redux-persist (oturum kalıcılığı), React Router 6.

Bu dokümanın kapsamı yalnızca iqv-react-dashboard reposudur. Doküman; mimari kararları, veri modellerini, iş kurallarını, güvenlik sınırlarını, deployment/test/CI altyapısını ve bilinen kısıtları/legacy alanları kapsar. Uygulamanın kullanım kılavuzu ayrı bir belgededir (IQV_Infra_Son_Kullanici_Kilavuzu.md) ve bu dokümanın konusu değildir.

Kaynaklar: README.md, backend/package.json, dashboard/package.json, repository dizin ağacının doğrudan gözlemi.


2. Genel Mimari

[Tarayıcı] --(HTTPS)--> [nginx (Docker) / static-server.mjs (native)]
                             |-- statik dosyalar (React SPA build çıktısı)
                             |-- /api/, /organizations, /infra-record,
                             |   /infra-public-*, /infra-logs/, /infra-admin-*,
                             |   /it/public*, /health, /api-docs, /openapi.json
                             |   --(reverse proxy)--> [Express backend :4000]
                                              |-- MongoDB (iqvizyon veritabanı)
                                              |-- SMTP (Office365, opsiyonel)
                                              |-- Telegram Bot API (opsiyonel)
                                              |-- Gemini API (opsiyonel, /api/it/bil)

Docker Compose ortamında üç servis çalışır: mongo, backend, dashboard (bkz. bölüm 19). Docker olmadan (native) kurulumda scripts/common/static-server.mjs, nginx'in yerini alan bağımlılıksız bir Node.js statik dosya + reverse-proxy sunucusudur ve aynı proxy path sözleşmesini uygular — böylece dev/native/Docker/prod ortamları arasında hangi path'in backend'e, hangisinin SPA'ya gittiği tutarlıdır.

Backend ile frontend tek bir kök .env dosyasını paylaşır (bkz. bölüm 18) — bu, iki bağımsız .env dosyasının birbirinden sapmasını (drift) yapısal olarak engeller.

Kimlik doğrulama iki tamamen ayrı mekanizma kullanır:

  • Admin/personel tarafı: JWT (Bearer token), localStorage + redux-persist ile istemcide saklanır (bkz. bölüm 6).
  • Müşteri/public form tarafı: HttpOnly session cookie (iqv_infra_public_session), token URL'de hiçbir zaman taşınmaz (bkz. bölüm 7).

Bu iki mekanizma aynı JWT_SECRET ile imzalanır ama purpose alanı ile ayrıştırılır (bkz. bölüm 6.3 ve 23.2) — mimari olarak bilinçli bir kısıt olarak dokümante edilmiştir.

Kaynaklar: docker-compose.yml, dashboard/nginx.conf, scripts/common/static-server.mjs, backend/src/app.js, README.md.


3. Repository Yapısı

iqv-react-dashboard/
├── backend/
│   ├── src/
│   │   ├── app.js                 # Express router mount haritası (createApp)
│   │   ├── server.js              # Entrypoint — HTTP sunucu başlatma
│   │   ├── config/                # env.js, db.js
│   │   ├── controllers/           # HTTP request/response sözleşmeleri
│   │   ├── docs/                  # OpenAPI/Swagger üretimi + özel gruplama eklentisi
│   │   ├── middleware/            # auth, publicSession, errorHandler
│   │   ├── models/                # ham MongoDB collection sarmalayıcıları
│   │   ├── repositories/          # MongoDB CRUD katmanı
│   │   ├── routes/                # Express Router tanımları
│   │   ├── services/              # iş mantığı
│   │   ├── utils/                 # apiError, asyncHandler, publicSession, regex, infraStatus…
│   │   └── validators/            # request body doğrulama
│   └── tests/                     # unit / integration / auth / security / contract
├── dashboard/
│   ├── src/
│   │   ├── components/it/         # Infra form domain'inin tamamı (form, önizleme, public görünüm)
│   │   ├── components/hooks/      # useBreakpoint vb. paylaşılan hook'lar
│   │   ├── routes/                # api.tsx, web.tsx, browserRouter.tsx, guard'lar
│   │   ├── services/               # axios tabanlı API istemcileri
│   │   ├── store/                  # Redux Toolkit + redux-persist
│   │   ├── interfaces/models/it.ts # domain veri modeli (TypeScript, 710 satır)
│   │   └── utils/
│   ├── nginx.conf, Dockerfile, vite.config.ts
│   └── tests/                      # unit / component (Vitest)
├── tests/e2e/                      # Playwright uçtan uca testler
├── scripts/                        # kurulum/güncelleme/kaldırma (linux/windows × docker/native), CI script'leri
├── docs/                           # MkDocs dokümantasyon sitesi
├── .github/workflows/              # ci.yml, docs.yml
├── docker-compose.yml
├── .env.example
└── README.md

Kaynaklar: repository dizin ağacının device_list_dir ile doğrudan gözlemi, backend/src/app.js, README.md.


4. Frontend Mimarisi

4.1 Teknoloji Yığını

React 18 + TypeScript, Vite 4 (build/dev sunucusu), antd 5 (UI bileşenleri: Table, Modal, Form, Select…), @iqvizyonui/react-components (IQVizyon kurumsal tasarım sistemi — Field/Input/Label/Select gibi bileşenler için), Redux Toolkit + redux-persist (oturum kalıcılığı), React Router 6 (createBrowserRouter), @loadable/component (route bazlı code-splitting), dayjs (tarih işleme), vite-plugin-pwa (Service Worker/PWA desteği), axios (HTTP istemcisi).

Kaynaklar: dashboard/package.json, dashboard/vite.config.ts, dashboard/src/components/it/ITForm.tsx.

4.2 Routing

dashboard/src/routes/web.tsx yol sabitlerini, dashboard/src/routes/browserRouter.tsx gerçek route ağacını (createBrowserRouter) tanımlar. api.tsx ise backend API path sabitlerini tutar (React route'u değildir — bkz. bölüm 5.2 ile karşılaştırma).

Route Bileşen Koruma/Yetki Açıklama
/ Redirect — Giriş durumuna göre /infra veya /login'e yönlendirir.
/login Login AuthLayout (korumasız) Kullanıcı adı/parola girişi, başarılı girişte JWT alınır.
/infra Dashboard RequireAuth + Layout Ana Infra kayıt listesi/özet ekranı.
/users Users RequireAuth + RequirePermission('users.read') Personel/kullanıcı yönetimi.
/personel → /users yönlendirme RequireAuth Geriye dönük uyumluluk alias'ı (eski link).
/it ITPage RequireAuth + Layout Yeni kayıt oluşturma + kayıt seçimi/önizleme akışının admin girişi.
/hatirlatici ReminderPage RequireAuth + Layout Hatırlatıcı planlama ekranı.
/form PublicITView Yok (public, token'sız — HttpOnly session cookie ile) Müşterinin dolduracağı public form; RequireAuth/Layout ağacının bilinçli olarak dışında.
* NotFoundPage — Eşleşmeyen tüm path'ler.
/api-docs, /api-docs/, /openapi.json — (React route'u DEĞİL) — Backend tarafından servis edilir (bkz. bölüm 5.2); nginx/proxy bu path'leri SPA'ya değil doğrudan backend'e yönlendirir.

/form, paylaşılan RequireAuth/Layout ağacının bilinçli olarak dışında ayrı bir üst-seviye route'tur — Layout yalnızca nested <Outlet/> içeren bir parent bileşen olduğu ve RequireAuth token yoksa koşulsuz /login'e yönlendirdiği için, bu iki paylaşılan bileşene dokunmadan token'sız bir genel-erişim görünümü başka türlü ayrıştırılamaz.

Kaynaklar: dashboard/src/routes/web.tsx, dashboard/src/routes/browserRouter.tsx, dashboard/src/routes/api.tsx.

4.3 Layout ve Navigasyon

Korumalı sayfalar (/infra, /users, /it, /hatirlatici) ortak bir Layout bileşeni altında (kenar menü + üst bar + <Outlet/>) render edilir; AuthLayout, /login gibi korumasız sayfalar için ayrı, sadeleştirilmiş bir kabuk sağlar. /form (public) hiçbir ortak layout'u kullanmaz — kendi bağımsız sayfa kabuğuna sahiptir, böylece müşteri hiçbir admin navigasyon öğesini (menü, kullanıcı adı, çıkış butonu vb.) görmez.

Auth Guard'ları: requireAuth.tsx: JWT'nin yapısal geçerliliğini (3 parça, exp süresi) render öncesi senkron kontrol eder; token yoksa/yapısı bozuksa/süresi dolmuşsa protected içerik hiç render edilmeden /login'e yönlendirir. Ardından GET /api/v1/auth/me ile backend'e karşı gerçek doğrulama yapılır ("protected page flash" düzeltmesi) — 401 dönerse oturum temizlenir, ağ hatası/5xx durumunda önceki geçerli oturuma güvenilmeye devam edilir (kullanıcı sonsuz login döngüsüne düşmesin diye).

requirePermission.tsx: RequireAuth'ın içinde kullanılan, backend'deki PermissionKey sözleşmesiyle birebir aynı frontend izin çözümlemesi (utils/permissions.ts) — yalnızca users.read/create/update/delete izinleri tanımlıdır (Infra'da ayarlar/sözlük modülü yoktur). Bu yalnızca bir UI kolaylığıdır; gerçek, sahtelenemez yetki kontrolü her zaman backend'dedir (people.routes.js → requirePermission middleware'i) — bu ayrım bölüm 6.6'da tekrar vurgulanmıştır.

Kaynaklar: dashboard/src/routes/requireAuth.tsx, dashboard/src/routes/requirePermission.tsx, dashboard/src/utils/permissions.ts, dashboard/src/services/authApi.ts, dashboard/src/routes/browserRouter.tsx.

4.4 State Yönetimi

Tek bir Redux slice (adminSlice) — { token, user } şeklinde Admin state'i, redux-persist ile localStorage'da saklanır (persist anahtarı CONFIG.appName). store/index.tsx'te combineReducers({ admin }) + persistReducer ile store kurulur. Projede ikinci bir domain-seviyesi slice yoktur — Infra formunun kendisi (bölüm 9) global Redux state'i ile değil, bileşen içi useState/useEffect ile yönetilir; bu bilinçli bir tasarım tercihidir (form akışı tek bir sayfa/bileşen ağacında yaşar, çapraz sayfa paylaşımına ihtiyaç yoktur).

Slice Dosya State şekli Amaç
admin store/slices/adminSlice.tsx { token: string | null, user: object | null } JWT ve giriş yapmış kullanıcı bilgisi; redux-persist ile kalıcı.

Kaynaklar: dashboard/src/store/index.tsx, dashboard/src/store/slices/adminSlice.tsx.

4.5 API / Service Katmanı (services/)

Paylaşılan bir axios instance (http) Bearer token interceptor'ı ve 401 → otomatik logout davranışını uygular; bunun üzerine kurulu servis modülleri:

Servis dosyası Kapsadığı uçlar Not
authApi.ts /api/v1/auth/login, /api/v1/auth/me Login sonrası /me ile ikinci doğrulama.
peopleApi.ts /api/v1/users CRUD Personel yönetimi.
reminderService.ts /api/reminders, /api/reminders/assignees Hatırlatıcı listesi/plan kaydetme.
infraLogService.ts /infra-logs/event Yalnızca istemci tarafı olaylar (logout, pdf_downloaded) — diğer tüm loglar zaten backend'de üretilir.
itService.ts Infra formunun tüm admin + public uçları Bkz. 4.6 — projenin en kritik service dosyası.

Kaynaklar: dashboard/src/services/authApi.ts, peopleApi.ts, reminderService.ts, infraLogService.ts, itService.ts.

4.6 Form Mimarisi — itService.ts ve İki Ayrı Kimlik Doğrulama Yolu

itService.ts, Infra formunun tüm backend çağrılarını topluyor ve bilinçli olarak iki farklı HTTP istemcisi kullanıyor:

  • Admin/personel uçları (getOrganizations, listInfraRecords, saveInfraInfo, sendInfraMail, sendReviewResultMail, askBil, closeAdminReview, saveAdminGeneralNote) → paylaşılan http (Bearer token + 401→logout).
  • Public/müşteri uçları (exchangePublicSession, getPublicInfraRecord, submitPublicProgress, setPublicSectionStatus) → düz axios + withCredentials: true (token yok; http'nin 401→logout davranışı burada anlamsız olurdu çünkü public tarafta hiç JWT yok).

Önemli bulgu — kısmen ölü kod: itService.getPublicProgress ve itService.savePublicProgressDraft, backend'de karşılığı olmayan /infra-public-progress (GET/POST) uç noktasını çağırır. Bu iki fonksiyon proje genelinde (dashboard/src) hiçbir bileşen tarafından çağrılmaz — grep ile doğrulanmıştır. Aktif kullanım referansı tespit edilemedi. Gerçek "kısmi taslak kaydetme" işlevi yerine submitPublicProgress (final gönderim) ve setPublicSectionStatus (bölüm bazlı tik) kullanılmaktadır (bkz. bölüm 24.1).

withInfraBase() yardımcı fonksiyonu, VITE_INFRA_API_BASE_URL/VITE_API_BASE_URL env değişkeni boşsa relative path'e düşer (prod'da aynı origin üzerinden nginx proxy ile çözülür).

Kaynaklar: dashboard/src/services/itService.ts, backend/src/routes/infraPublicRecord.routes.js (karşılaştırma için), proje geneli grep taraması.

4.7 Public Form / PublicITView

PublicITView.tsx, müşterinin /form adresinde gördüğü kapsayıcı bileşendir. Mount olduğunda önce exchangePublicSession (bootstrap — yalnızca bir kez, mail linkinden gelen 302 sonrası zaten cookie yazılmış durumdadır ama sayfa doğrudan /form'a atlanırsa da session'ın var olup olmadığı kontrol edilir), ardından getPublicInfraRecord (GET /infra-public-record, cookie otomatik gider) ile whitelist'li kaydı çeker. mode='public' prop'u ile ITPreview.tsx'i render eder — bu modda: admin-only alanlar (IQVizyon Kontrolü paneli, Kurulum Kararı, tam steps[] geçmişi) hiç render edilmez, yalnızca müşterinin doldurması gereken alanlar (Sunucu Odası/Statik IP, Tezgah Bilgileri, Sistem Kapasitesi "Sağlanan" kolonu, Uzaktan Erişim, Genel Not) düzenlenebilir haldedir, "Gönder" butonu görünür.

Kaynaklar: dashboard/src/components/it/PublicITView.tsx, ITPreview.tsx, itService.ts.

4.8 Admin / Edit Mode

Admin tarafı iki alt-moda ayrılır (mode prop'u ITPreview.tsx'e üç değerden biri olarak geçer): 'new' (ilk kayıt oluşturma — ITForm.tsx), 'admin-existing' (var olan bir kaydın önizleme/düzenleme ekranı — ITPreview.tsx, tam yetkiyle). admin-existing modunda: tüm bölümler düzenlenebilir (müşterinin doldurduğu "İstenen" kapasite gönderim sonrası kilitlenir — bkz. bölüm 5.4/11), "IQVizyon Kontrolü" paneli (AdminReviewPanel.tsx) görünür ve 27 kalemlik onay/not girişine izin verir, "Kurulum Kararı" seçimi (ready/conditional_ready/not_ready) yapılabilir, "Süreç Geçmişi" (WorkflowHistory.tsx) tüm steps[] adımlarını zaman çizelgesi olarak gösterir, "Mail Gönder" ve "Kontrol Sonucu Maili" butonları aktiftir.

Kaynaklar: dashboard/src/components/it/index.tsx, ITForm.tsx, ITPreview.tsx, AdminReviewPanel.tsx, WorkflowHistory.tsx.

4.9 Responsive Davranış (Frontend Uygulaması)

Frontend, useBreakpoint(breakPoint = 768) adlı kendi hook'unu (components/hooks/breakpoint.ts) kullanarak window.innerWidth < breakPoint mantığıyla mobil/masaüstü ayrımı yapar; bu hook antd'nin kendi grid md/lg breakpoint sistemiyle birlikte (ITForm.tsx, ServerAndMachineSection.tsx grid kırılımları için) kullanılır. Kesme noktalarının tam değerleri ve gerekçesi bölüm 22'de kapsamlı olarak ele alınmıştır — bu alt bölüm yalnızca hangi frontend mekanizmasının (hook + CSS + antd grid) bu davranışı ürettiğini belgeler, sayısal detaylar için bölüm 22'ye bakınız.

Kaynaklar: dashboard/src/components/hooks/breakpoint.ts, dashboard/src/components/it/ITForm.tsx, ServerAndMachineSection.tsx.

4.10 Tema / UI Component Yapısı

UI bileşenleri iki katmandan gelir: antd 5 (genel amaçlı Table, Modal, Form, Select, DatePicker vb.) ve @iqvizyonui/react-components (IQVizyon kurumsal tasarım sistemi — Field, Input, Label, Select gibi marka standardına uygun sarmalayıcı bileşenler, ör. ITForm.tsx içinde doğrudan import edilerek kullanılır). Domain'e özgü bileşenler (components/it/*) bu iki katmanı birleştirerek üretilmiştir; ayrı bir üçüncü, projeye özel "tema sistemi"/design-token dosyası bu turda tespit edilmemiştir — Kaynak koddan kesin olarak doğrulanamadı (kapsamlı bir tema dosyası taraması bu turda yapılmadı, yalnızca bileşen importlarından çıkarım yapılmıştır).

Kaynaklar: dashboard/src/components/it/ITForm.tsx, dashboard/package.json (antd, @iqvizyonui/react-components bağımlılıkları).

4.11 PDF / Export Frontend Davranışı

PDF butonu hem admin hem public ekranda, sayfa başlığının yanında (headerActionsContainer portalı ile) render edilir; tıklandığında pdfLoading state'i butonun tekrar tıklanmasını engeller (çift üretim/tıklama koruması). Public tarafta PDF indirme, infraLogService.logInfraClientEvent('pdf_downloaded', infraId) ile backend'e loglanır — bu, infra-logs koleksiyonuna yazılan, istemci tarafında üretildiği için backend'in doğrudan gözlemleyemediği iki olaydan biridir (diğeri logout). PDF üretiminin kendisi tamamen istemci tarafında (PDFReport.ts) gerçekleşir; backend'de ayrı bir PDF-render uç noktası yoktur. Detaylı içerik/alan kapsamı için bölüm 16'ya bakınız.

Kaynaklar: dashboard/src/components/it/PDFReport.ts, ITPreview.tsx (PDF buton mantığı grep ile doğrulandı), infraLogService.ts.

4.12 Bildirim / Modal / Popup Yapısı

Kullanıcıya dönük anlık bildirimler antd'nin message/notification API'leri üzerinden verilir (ör. kayıt kaydedildi/güncellendi, mail gönderildi, hata mesajları) — antd bir bağımlılık olduğundan bu API'ler projede kullanılabilir durumdadır; kapsamlı bir kullanım noktası envanteri bu turda çıkarılmamıştır. Modal bileşenleri belirgin iki noktada kullanılır: AuthorizedPersonModal.tsx (yetkili kişi ekle/düzenle) ve MachinePickerModal.tsx (sabit marka listesinden çoklu makine seçimi, "Diğer" serbest metin dahil). Bölüm bazlı durum kontrolü (SectionStatusControl.tsx) ayrı bir modal değil, satır-içi tik/kalem kontrolüdür (bkz. bölüm 11).

Kaynaklar: dashboard/src/components/it/AuthorizedPersonModal.tsx, MachinePickerModal.tsx, SectionStatusControl.tsx.

4.13 Component Ağacı — components/it/

Dosya Sorumluluk
index.tsx Sayfa kapsayıcısı — yeni kayıt / mevcut kayıt seçimi, ITForm + ITPreview orkestrasyonu.
ITForm.tsx İlk kayıt formu: Firma/Müşteri, Yetkili Kişi, Düzenleyen, Düzenleme Tarihi, Makine, Sistem, Durum.
ITPreview.tsx (2616 satır — projenin en büyük dosyası) Kayıt önizleme + tüm form bölümlerinin (Kaynak…Kontrol) render'ı, admin/public mod ayrımı, Gönder/Kaydet/Güncelle/Tamamlandı-Tamamlanmadı akışları, PDF butonu.
AdminReviewPanel.tsx "IQVizyon Kontrolü" (12. bölüm) — 27 kalemlik konsolide inceleme, onay/not, Kurulum Kararı.
ServerAndMachineSection.tsx (1890 satır) Server ve Tezgah/Makine Bilgileri tabloları, Sunucu Odası/Statik IP, Sağlanan Kapasite.
RemoteAccessPanel.tsx Uzaktan erişim (VPN/AnyDesk) seçimi ve alanları.
RequirementSection.tsx Gereksinim grupları için ortak tablo yapısı (Sunucu/Ağ/Uzaktan Erişim gereksinimleri).
SecurityControlTable.tsx 10. Güvenlik Kontrolü tablosu (satır bazlı Evet/Hayır + not).
SectionStatusControl.tsx Bölüm bazlı "Durum ✓/✎" tik/kalem kontrolü.
WorkflowHistory.tsx (923 satır) Süreç geçmişi (steps[]) zaman çizelgesi görünümü.
AuthorizedPersonModal.tsx Yetkili kişi ekle/düzenle modalı.
MachinePickerModal.tsx Sabit marka listesinden çoklu makine seçimi ("Diğer" serbest metin dahil).
PublicITView.tsx Müşteri public form kapsayıcısı (bkz. bölüm 4.7 ve 7).
constants.ts 27 kalemlik REQUIREMENT_ITEMS, sabit metinler, seçenek listeleri (bkz. bölüm 10).
helpers.ts (1005 satır) Ortak saf fonksiyonlar (normalizasyon, hasPendingCustomerSubmission, tarih formatlama…).
PDFReport.ts PDF üretim mantığı (bkz. bölüm 16).

Kaynaklar: dashboard/src/components/it/*.tsx, *.ts (satır sayıları wc -l ile doğrulandı).


5. Backend Mimarisi

5.1 Katman Mimarisi (Genel Bakış)

routes/  -->  controllers/  -->  services/  -->  repositories/  -->  models/ (raw collection) --> MongoDB
                                      |
                                middleware/ (auth, publicSession, errorHandler)
                                      |
                                 utils/ (apiError, asyncHandler, authorizedPeople, frontendUrl,
                                         infraStatus, publicSession, regex)

ODM kullanılmaz — tüm repository'ler MongoDB resmi sürücüsünün ham collection() API'sini kullanır ({ _id, ...rest } → { _id: hex string, ...rest } dönüşümü ortak bir toRecord fonksiyonuyla her repository'de tekrarlanır). Katmanlar arası bağımlılık her zaman tek yönlüdür: bir controller asla bir repository'yi doğrudan çağırmaz, bir servis asla bir route dosyasını import etmez — bağımlılıklar app.js seviyesinde (dependency injection benzeri) monte edilir.

Kaynaklar: backend/src/repositories/*.js, backend/src/models/*.js.

5.2 Entrypoint — server.js

backend/src/server.js, Express uygulamasını (createApp) çalıştırılabilir bir HTTP sunucusuna bağlayan giriş noktasıdır: config/env.js'ten yapılandırmayı okur, config/db.js ile MongoDB'ye bağlanır, bağlantı başarılıysa app.listen(PORT) çağrılır. Uygulamanın MongoDB'siz başlamadığı — yani veritabanı bağlantısı sunucu açılışının bir ön koşulu olduğu — config/db.js'teki bağlantı akışından çıkarılabilir.

Kaynaklar: backend/src/server.js, backend/src/config/db.js, backend/src/config/env.js.

5.3 Express App Factory — createApp(deps)

backend/src/app.js, createApp(deps) adlı bir fabrika fonksiyonu ihraç eder: bağımlılıklar (repository/servis örnekleri, jwtSecret vb.) parametre olarak enjekte edilir, böylece testler gerçek MongoDB bağlantısı kurmadan sahte (mock/in-memory) bağımlılıklarla tam bir Express app örneği oluşturabilir (bkz. bölüm 20 — backend integration testleri bu deseni kullanır). Bu, projenin test edilebilirlik için bilinçli bir mimari tercihidir.

Kaynaklar: backend/src/app.js, backend/tests/integration/*.test.js.

5.4 app.js Router Mount Haritası

app.js şu router'ları mount eder: /api/v1/auth, /api/v1/users (people), /organizations (prefix'siz, kasıtlı istisna), /infra-record (prefix'siz), /health, /api/it (bil), /api/reminders, /infra-logs, kök seviyede (prefix'siz) infraPublicRecord router'ı (/infra-public-session, /infra-public-record, /infra-public-progress-submit, /infra-public-section-status, /it/public-access/:token, geriye dönük /it/public*) ve yine kök seviyede infraAdminReview router'ı (/infra-public-review-close, /infra-admin-general-note). Swagger UI /api-docs ve /openapi.json da backend'den servis edilir (React route'u değildir — bkz. bölüm 4.2).

/organizations ve /infra-record'un /api/v1 prefix'i almaması bilinçli bir istisnadır — bu iki uç, dev'de Vite proxy listesine ve prod'da nginx'e ayrıca eklenmiştir (bkz. bölüm 19.3).

Kaynaklar: backend/src/app.js, backend/src/routes/infraPublicRecord.routes.js, backend/src/routes/infraAdminReview.routes.js, dashboard/src/routes/api.tsx.

5.5 Middleware Pipeline

İstek işleme sırası (genel → özel): (1) CORS (CORS_ORIGIN'e göre yapılandırılmış), (2) JSON body parser, (3) route-özel rate limiter'lar (loginLimiter, publicLimiter — express-rate-limit), (4) route-özel kimlik doğrulama middleware'i — admin uçlarında createAuthMiddleware(jwtSecret) (Bearer JWT doğrular, req.user'ı doldurur), public uçlarında requirePublicSession (HttpOnly cookie doğrular, req.publicSession'ı doldurur), (5) route handler (controller), (6) merkezi errorHandler.middleware.js — zincirin sonunda, tüm route'lardan sonra mount edilen tek bir hata yakalayıcı.

Kaynaklar: backend/src/middleware/auth.middleware.js, publicSession.middleware.js, errorHandler.middleware.js, backend/src/app.js.

5.6 Routes → Controllers → Services → Repositories Katmanlaşması

Her domain (auth, people, organizations, infraRecord, infraPublicRecord, infraAdminReview, it/bil, reminder, infraLog) bu dört katmanı ayrı ayrı dosyalarda uygular: route dosyası yalnızca HTTP method/path/middleware bağlar; controller yalnızca HTTP request/response sözleşmesini (body okuma, status kodu, hata fırlatma) yönetir; service iş mantığını (doğrulama, iş kuralı, whitelist, bildirim tetikleme) içerir; repository yalnızca MongoDB sorgusu/yazması yapar. infraPublicRecord.controller.js ve infraAdminReview aynı InfraRecordService'i kullanır — ikinci bir servis kopyası yoktur, bu da public ve admin akışlarının iş kurallarının (ör. whitelist, steps[] append mantığı) tek bir yerde tutarlı kalmasını garanti eder.

Kaynaklar: backend/src/controllers/*.js, backend/src/services/*.js, backend/src/repositories/*.js.

5.7 Servis Katmanı Envanteri

Servis Sorumluluk
auth.service.js Login (bcrypt + JWT imzalama), me() (JWT doğrulanmış kullanıcı bilgisi).
people.service.js Kişi (kullanıcı) CRUD, kendi hesabında yetki yükseltme engeli.
organizations.service.js Yalnızca list() — ince katman.
infraRecord.service.js (1443 satır) Infra kaydı CRUD, public token/session, mail gönderimi, admin review kapanışı, müşteri gönderim işleme, bölüm durumu — bkz. bölüm 5.8.
infraLog.service.js Merkezi kullanıcı işlem logu (infra-logs koleksiyonu) — asla ana işlemi bozmaz.
infraSubmitNotifier.service.js Müşteri "Gönder" sonrası mail+Telegram bildirimi (birbirinden bağımsız, hatasız asla exception fırlatmaz).
reminder.service.js (483 satır) Hatırlatıcı listesi (mevcut Infra kayıtlarından türetilen görünüm) + plan kaydetme.
reminderNotifier.service.js Zamanı gelmiş hatırlatıcı planları için Telegram bildirimi — periyodik setInterval döngüsü.
mailService.js Nodemailer SMTP transporter (Office365 587/STARTTLS), önbelleğe alınmış tekil transporter.
telegram.service.js Telegram Bot API tek gönderim noktası.
bil.service.js /api/it/bil — Gemini tabanlı sistem kapasitesi önerisi.
health.service.js Liveness/readiness.

Kaynaklar: backend/src/services/*.js.

5.8 InfraRecordService — Ana İş Mantığı

En kritik servistir; öne çıkan noktalar:

  • Public token üretimi: crypto.randomBytes(24).toString('base64url') — 24 byte, tahmin edilemez. Eski kayıtlarda token yoksa ensurePublicToken() ile bir kez üretilip kalıcı hale getirilir.
  • hasPendingCustomerSubmission / hasAnyCustomerSubmission: yeni bir durum alanı icat edilmeden, kaydın steps[] dizisi üzerinden türetilen iş kuralları (son müşteri adımı son iqvizyon_control adımından sonra mı?). Frontend (helpers.ts) ile birebir aynı mantık backend'de de bağımsız olarak uygulanır — güvenlik amaçlı çift kontrol.
  • REQUESTED_CAPACITY_FIELDS kilidi: müşteri formu bir kez gönderildiyse, admin PUT'u "İstenen" kapasite alanlarını (device_type, cpu_core, ram_gb, …) artık değiştiremez — update() bu alanları payload'dan siler.
  • submitPublicProgress: mass-assignment yok — yalnızca 6 bilinen alan (customer_general_note, public_progress, server_room_available, customer_static_ip, machine_details, provided_capacity, remote_access) gövdeden okunur. Yeni bir customer_control adımı derin kopya olarak steps[]'e eklenir; aynı anda admin_review_progress sıfırlanır (yeni kontrol turu), Kurulum Kararı ve önceki karar notu temizlenir, status: 0. Başarılı DB yazımından sonra (asla önce değil) mail+Telegram bildirimi tetiklenir; bildirim katmanı hata verse bile kayıt geri alınmaz. 60 saniyelik dedupe penceresi (SUBMIT_NOTIFY_DEDUPE_MS) çift tıklamada tekrar bildirim gitmesini engeller.
  • closeAdminReview: decision alanına göre status (0/1) ve review_status (open/closed) belirlenir — bu, dashboard'daki "Durum"/"Kurulum" ayrımının tek kaynağıdır (bkz. utils/infraStatus.js). Yeni bir iqvizyon_control adımı steps[]'e eklenir.
  • getPublicRecord: açık bir whitelist (PUBLIC_FIELDS, ~40 alan) ile admin-içi alanlar (ör. reviewed_by_id, user_id, tam admin_review_progress) dışarıda bırakılır. remote_access içindeki parolalar maskRemoteAccess() ile her zaman maskelenir — public yanıtta hiçbir zaman ham parola dönmez.
  • Mail gönderimi: her alıcıya ayrı ayrı sendMail çağrısı yapılır (toplu to: [a,b] kullanılmaz) — her alıcı yalnızca kendi adresini görsün diye.

Kaynaklar: backend/src/services/infraRecord.service.js (~1443 satır).

5.9 Controller/Validator Katmanı

infraRecord.controller.js ve infraRecord.validation.js: request/response sözleşmeleri, ObjectId doğrulama, sunucu tarafında mail alıcısı çözümlemesi (req.user'dan bağımsız güvenilir kaynak), server-assigned/removed alan listeleri. infraPublicRecord.controller.js ve infraAdminReview aynı InfraRecordService'i kullanır.

Kaynaklar: backend/src/controllers/infraRecord.controller.js, backend/src/validators/infraRecord.validation.js.

5.10 MongoDB Bağlantısı (config/db.js)

config/db.js, MONGODB_URI/MONGODB_DB env değişkenlerinden resmi mongodb sürücüsüyle tek bir bağlantı kurar ve db referansını server.js/app.js'e döner; koleksiyon adları da env'den okunur (bkz. bölüm 18) — bu sayede test ortamı farklı bir veritabanı adı/host kullanarak prod verisinden izole çalışabilir.

Kaynaklar: backend/src/config/db.js, .env.example.

5.11 Hata İşleme Sözleşmesi

errorHandler.middleware.js, zincirin sonunda mount edilen tek merkezi hata yakalayıcıdır. utils/apiError.js'teki ApiError sınıfı (statusCode + mesaj) fırlatılan hatalar doğrudan o status kodu ve mesajla; beklenmeyen (fırlatılmamış tip) hatalar ise 500 + genel bir mesajla döner — ham stack trace veya iç sistem detayı istemciye asla sızdırılmaz (bkz. bölüm 23.1, hata sanitizasyonu). utils/asyncHandler.js, her async route handler'ı sarmalayarak reddedilen promise'lerin otomatik olarak bu merkezi handler'a düşmesini sağlar — controller'larda tekrar tekrar try/catch yazılmasını gereksiz kılar.

Kaynaklar: backend/src/middleware/errorHandler.middleware.js, backend/src/utils/apiError.js, backend/src/utils/asyncHandler.js.

5.12 Health Endpoint

GET /health (liveness — sunucu sürecinin ayakta olduğunu doğrular, bağımlılık kontrolü yapmaz) ve GET /health/ready (readiness — gerçek bir MongoDB ping'i içerir; Mongo erişilemezse "unhealthy" döner). Docker backend servisinin HEALTHCHECK'i bu /health/ready ucunu kullanır (bkz. bölüm 19.2).

Kaynaklar: backend/src/services/health.service.js, backend/src/routes/health.routes.js, backend/Dockerfile.

5.13 Loglama

Uygulama içi iş-olayı logu (infra-logs koleksiyonu, infraLog.service.js) kullanıcı bazlı, tek dokümana $push ile eklenen bir audit-trail'dir — bu genel amaçlı bir sistem/HTTP access log çerçevesi (ör. winston, morgan) değildir. backend/package.json bağımlılıklarında ayrı bir genel loglama kütüphanesi bu turda tespit edilmemiştir — Kaynaklar koddan kesin olarak doğrulanamadı (kapsamlı bir package.json taraması bu amaçla yapılmadı); konsola (console.log/console.error) yapılan olası doğrudan loglamalar bu turda satır satır incelenmedi.

Kaynaklar: backend/src/services/infraLog.service.js.

5.14 Graceful Shutdown

Bu turda server.js içinde SIGTERM/SIGINT sinyallerini yakalayıp HTTP sunucusunu ve MongoDB bağlantısını düzenli kapatan açık bir graceful-shutdown bloğu doğrulanmamıştır — Kaynak koddan kesin olarak doğrulanamadı. Docker Compose'da backend servisi bir healthcheck'e sahiptir (bkz. bölüm 19.1) ama bu, süreç seviyesinde bir graceful-shutdown garantisi vermez; konteyner orkestrasyonu (Docker/Compose) varsayılan SIGTERM davranışına güvenir.

Kaynaklar: backend/src/server.js (bu turda graceful-shutdown bloğu için özel olarak taranmadı).

5.15 API Versiyonlama

Kısmi ve tutarsız bir versiyonlama vardır: auth ve people (users) route'ları /api/v1/... prefix'i altında mount edilirken, organizations, infra-record, infra-public-*, infra-logs, infra-admin-*, /api/it, /api/reminders gibi uçların hiçbiri v1 taşımaz (bazıları hiç /api prefix'i bile almaz — bkz. bölüm 5.4). Bu, projenin tutarlı, tek bir API versiyonlama stratejisi uygulamadığının açık bir göstergesidir; yeni bir major sürüm (ör. v2) ihtiyacı doğarsa, hangi uçların nasıl taşınacağına dair önceden tanımlı bir plan kaynak kodda mevcut değildir.

Kaynaklar: backend/src/app.js, tüm backend/src/routes/*.js dosyalarının path önekleri.


6. Kimlik Doğrulama ve Yetkilendirme

6.1 Authentication — Admin/Personel JWT Akışı

POST /api/v1/auth/login → auth.service.js: kullanıcı adı case-insensitive tam eşleşme (user.repository.js — buildExactInsensitiveRegex), status !== 'active' ise reddedilir, bcrypt.compare ile parola doğrulanır, JWT { _id, username, full_name, role, permissions, company_id, organization_id } payload'ıyla JWT_SECRET ile imzalanır (JWT_EXPIRES_IN, varsayılan 12h). GET /api/v1/auth/me, doğrulanmış req.user._id'den kullanıcıyı tekrar okur (spoof edilemez — payload'daki diğer alanlara güvenilmez, yalnızca _id bir DB sorgusu tetikler).

Kaynaklar: backend/src/services/auth.service.js, backend/src/repositories/user.repository.js.

6.2 Authorization — Rol/İzin Modeli

PermissionKey yalnızca users.read/create/update/delete içerir (Infra'da başka bir modül/kaynak için izin tanımı yoktur). Roller: superadmin, companyadmin, organizationadmin, admin → bu dört rol tam yetkiye (tüm users.* izinlerine) sahiptir; user rolü → varsayılan olarak hiçbir izne sahip değildir (none-by-default). Buna ek olarak satır-seviyesi bir kısıtlama vardır: user rolündeki bir kullanıcı yalnızca kendi kaydını görebilir/düzenleyebilir (self-record restriction). isSelfPrivilegeEscalationRestricted — bir kullanıcı kendi rolünü/durumunu/iznini değiştiremez (admin-tier olmayan roller için), yalnızca değer gerçekten değişiyorsa engellenir (no-op istekler serbesttir). Tüm bu kontroller yalnızca server-side'dır — auth.middleware.js ve people.service.js içinde uygulanır.

Kaynaklar: backend/src/middleware/auth.middleware.js, backend/src/services/people.service.js, backend/src/routes/people.routes.js (requirePermission('users.read'|'users.create'|'users.update'|'users.delete')).

6.3 Middleware Katmanı

createAuthMiddleware(jwtSecret) Bearer token'ı doğrular (Authorization: Bearer <jwt> header'ından okur), geçersiz/süresi dolmuş token'da 401 döner, geçerliyse req.user'a JWT payload'ını yerleştirir. requirePermission(key), req.user.permissions (veya rol bazlı tam-yetki kısayolu) üzerinden route-level yetkilendirme sağlayan ikinci bir middleware katmanıdır — createAuthMiddleware'in sonrasında, route-özel olarak zincire eklenir (yalnızca people.routes.js bu ikinci katmanı kullanır; diğer admin route'ları yalnızca kimlik doğrulaması ister, ek bir izin kontrolü yapmaz).

Kaynaklar: backend/src/middleware/auth.middleware.js.

6.4 Public Erişim — Ayrı Bir Mekanizma

Müşteri/public form tarafı, JWT Bearer akışını hiç kullanmaz. Bunun yerine bootstrap sırasında kurulan bir HttpOnly session cookie'sine dayanan tamamen ayrı bir mekanizma çalışır (requirePublicSession middleware'i) — bu mekanizmanın tam akışı, cookie ayarları ve session doğrulaması bölüm 7'de ayrıntılı olarak ele alınmıştır. Burada vurgulanması gereken mimari nokta: admin JWT'si ile public session token'ı aynı JWT_SECRET ile imzalanır ama purpose alanı ('infra_public_session') ile ayrıştırılır — bkz. 6.5.

Kaynaklar: backend/src/middleware/publicSession.middleware.js, backend/src/utils/publicSession.js.

6.5 Ortak Secret Kısıtı

JWT admin oturumu ile müşteri public session'ı aynı JWT_SECRET ile imzalanır — yeni bir secret icat edilmemiştir, ancak purpose: 'infra_public_session' alanı ile ayrıştırılır. verifyPublicSessionToken, purpose uyuşmayan bir token'ı (örn. bir admin JWT'si cookie'ye elle yerleştirilirse) geçersiz sayar; ancak kriptografik anahtar aynı olduğu için, tek bir sızmış JWT_SECRET her iki jeton türünü de sahtelenebilir kılar — bu, bölüm 23.2'de "Bilinen Güvenlik Kısıtları" altında tekrar ele alınmıştır.

Kaynaklar: backend/src/utils/publicSession.js.

6.6 Frontend Guard'ları — UX Kolaylığı, Güvenlik Sınırı Değil

RequireAuth ve RequirePermission (bkz. bölüm 4.3), backend'deki gerçek kontrolleri birebir taklit eden ama sahtelenebilir istemci-taraflı kontrollerdir. Kaynak kod bunu açıkça belirtir: requirePermission.tsx'in izin çözümlemesi backend'deki PermissionKey sözleşmesiyle birebir aynıdır fakat "bu yalnızca bir UI kolaylığıdır" — gerçek, sahtelenemez yetki kontrolü her zaman backend'dedir (people.routes.js → requirePermission middleware'i). Bir kullanıcı tarayıcı geliştirici araçlarıyla frontend guard'ını atlatsa bile, backend auth.middleware.js/requirePermission kontrolleri isteği yine de reddeder.

Kaynaklar: dashboard/src/routes/requireAuth.tsx, dashboard/src/routes/requirePermission.tsx, backend/src/middleware/auth.middleware.js.


7. Public / Müşteri Erişim Mimarisi

7.1 Mail Linki → Session Bootstrap Akışı

Mail linki: {FRONTEND_BASE_URL}/it/public-access/<public_token>
    ↓ GET (backend, React SPA'ya HİÇ uğramaz)
infraPublicRecord.controller.js -> publicAccessRedirect
    ↓ token doğrulanır (PUBLIC_TOKEN_PATTERN + DB lookup)
    ↓ HttpOnly session cookie yazılır: iqv_infra_public_session
    ↓ 302 redirect
/form  (React SPA burada mount edilir — token URL'de HİÇBİR ZAMAN görünmez)
    ↓ PublicITView.tsx -> GET /infra-public-record (cookie otomatik gider)

Session, GET /it/public-access/:publicToken uç noktasında (kök seviyede mount edilen infraPublicRecord router'ı, bkz. bölüm 5.4) oluşturulur — mail linkindeki public_token, bu tek istekte doğrulanır ve hemen ardından cookie'ye dönüştürülür; token bu noktadan sonra hiçbir istekte tekrar taşınmaz. Alternatif olarak POST /infra-public-session uç noktası da aynı bootstrap işlemini gövdede gönderilen bir publicToken ile yapar (SPA zaten /form'daysa ve session henüz kurulmamışsa kullanılan yol).

Eski /it/public ve /it/public/<token> linkleri (route rename öncesi) backend tarafında aynı şekilde /form'a 302 ile yönlendirilir (geriye dönük uyumluluk) — hem dashboard/nginx.conf hem vite.config.ts dev proxy'sinde bu path'ler backend'e proxy'lenir (SPA fallback'e düşmemesi için).

Kaynaklar: backend/src/routes/infraPublicRecord.routes.js, dashboard/src/components/it/PublicITView.tsx, dashboard/src/routes/web.tsx, dashboard/nginx.conf, dashboard/vite.config.ts, tests/e2e/public-form.spec.ts.

utils/publicSession.js, cookie'yi şu bayraklarla yazar: httpOnly: true (JavaScript'ten okunamaz — XSS ile çalınamaz), sameSite: 'lax' (çapraz-site POST'larda gönderilmez, aynı-site navigasyonda gönderilir — mail linkinden gelen GET navigasyonu için doğru denge), secure: NODE_ENV === 'production' (yalnızca prod'da HTTPS zorunluluğu; local/dev'de HTTP üzerinden de çalışabilmesi için NODE_ENV !== 'production' iken secure: false). Cookie içeriği: { infraId, purpose: 'infra_public_session' } payload'lı bir JWT, JWT_SECRET ile imzalı, TTL 24 saat. cookie-parser bağımlılığı eklenmemiş — tek, bilinen cookie adı için minimal, elle yazılmış bir ayrıştırıcı kullanılır.

Kaynaklar: backend/src/utils/publicSession.js.

7.3 Session Doğrulaması — requirePublicSession

publicSession.middleware.js içindeki requirePublicSession, gelen isteğin iqv_infra_public_session cookie'sini okur, verifyPublicSessionToken ile doğrular (imza + exp + purpose === 'infra_public_session'). verifyPublicSessionToken hiçbir koşulda throw etmez — geçersiz/süresi dolmuş/purpose uyuşmayan (örn. bir admin JWT'si cookie'ye elle yerleştirilirse) her durumda null döner ve middleware bu durumda isteği 401 ile reddeder. Doğrulama başarılıysa req.publicSession = { infraId } set edilir; sonraki controller'lar (getRecord, submitProgress, setSectionStatus) bu infraId'yi kullanır.

Kaynaklar: backend/src/middleware/publicSession.middleware.js, backend/src/utils/publicSession.js.

7.4 Müşteri Hangi Kayıt Üzerinde İşlem Yapabiliyor

Müşteri yalnızca kendisine mail ile gönderilen public_token'ın işaret ettiği tek bir Infra kaydı üzerinde işlem yapabilir — session cookie'sindeki infraId, bootstrap anında bu token'dan çözülüp cookie'ye gömülür ve sonraki her istekte (GET /infra-public-record, POST /infra-public-progress-submit, POST /infra-public-section-status) bu sabit infraId kullanılır. İstek gövdesinde/URL'inde farklı bir kayıt kimliği kabul edilmez — gövdeden id/infra_id gibi bir alan okunmaz, tamamen cookie'deki değere güvenilir.

Kaynaklar: backend/src/controllers/infraPublicRecord.controller.js, backend/src/middleware/publicSession.middleware.js.

7.5 Başka Kayda Erişim Nasıl Engelleniyor

Bir müşteri başka bir Infra kaydına erişmeye çalıştığında iki bağımsız engel devreye girer: (1) cookie'deki infraId sabittir ve müşteri tarafından değiştirilemez (HttpOnly — JavaScript ile okunup değiştirilemez; imzalı JWT olduğu için ham metin olarak da manipüle edilemez — imza doğrulaması başarısız olur). (2) Farklı bir kayda erişmek isteyen bir müşteri, o kaydın kendi public_token'ını (kendi mail linkini) bilmedikçe hiçbir zaman o kayıt için bir session kuramaz — public_token 24 byte crypto.randomBytes ile üretildiği için tahmin edilemez (bkz. bölüm 5.8, 23.1).

Kaynaklar: backend/src/utils/publicSession.js, backend/src/services/infraRecord.service.js (public token üretimi).

7.6 Session Expiration

Session cookie'si 24 saatlik sabit bir TTL ile imzalanır (JWT exp claim'i). Süre dolduğunda verifyPublicSessionToken null döner ve requirePublicSession isteği 401 ile reddeder — müşteri tekrar mail linkine tıklayarak (veya POST /infra-public-session ile, eğer henüz elindeki token geçerliyse) yeni bir session bootstrap etmesi gerekir. public_token'ın kendisinin (mail linkindeki asıl anahtar) ayrı bir süre sınırı kaynak kodda tespit edilmemiştir — token kayıt var olduğu sürece geçerli görünmektedir (ensurePublicToken yalnızca eksikse üretir, süre kontrolü yapmaz). Kaynak koddan kesin olarak doğrulanamadı: public_token'ın kendisi için ayrı bir expiry/rotasyon mekanizması bu turda tespit edilememiştir.

Kaynaklar: backend/src/utils/publicSession.js, backend/src/services/infraRecord.service.js.

7.7 Public Uç Nokta Envanteri

Method Path Auth Not
POST /infra-public-session publicToken (gövdede, tek seferlik) Bootstrap — cookie yazar, kayıt döndürmez.
GET /it/public-access/:publicToken publicToken (URL) Mail linkinin gerçek hedefi — 302 → /form.
GET /it/public/:publicToken, GET /it/public publicToken Geriye dönük uyumluluk — 302 → /form.
GET /infra-public-record requirePublicSession (cookie) Müşteri ekranının okuduğu whitelist'li kayıt.
POST /infra-public-progress-submit requirePublicSession Final "Gönder".
POST /infra-public-section-status requirePublicSession Bölüm bazlı Durum tik/kalem.

Rate limit: tüm public uçlar express-rate-limit ile 5 dakikada 60 istek (publicLimiter), auth.routes.js'teki loginLimiter ile aynı desen.

Aktif kullanım doğrulandı — /infra-public-progress (GET/POST) backend'de tanımlı değildir; frontend'deki karşılığı (itService.getPublicProgress/savePublicProgressDraft) da hiçbir yerden çağrılmaz (bkz. bölüm 4.6). Bu iki endpoint projenin gerçek akışının parçası değildir.

7.8 OpenVPN Sabit IP — Müşteriye Kasıtlı Olarak Açık

FIXED_REQUIREMENT_TEXT sabiti (dashboard/src/components/it/constants.ts), müşteriye gösterilecek standart gereksinim metinlerini içerir — bunlardan biri gerçek, kasıtlı olarak müşteriye açık, statik OpenVPN IP adresidir: 135.181.251.237. Bu bir sızıntı değildir; müşterinin VPN kurulumunda kullanması gereken sabit IQVizyon uç noktasıdır ve FIXED_REQUIREMENT_TEXT içinde kasıtlı olarak yer alır — public form üzerinden müşteriye normal iş akışının bir parçası olarak gösterilir.

Kaynaklar: backend/src/routes/infraPublicRecord.routes.js, dashboard/src/components/it/constants.ts.


8. Veri Modelleri

8.1 MongoDB Koleksiyonları (iqvizyon veritabanı)

Koleksiyon (env değişkeni) Varsayılan ad Amaç
MONGODB_USERS_COLLECTION iqvizyon-users Admin/personel kullanıcıları.
MONGODB_ORGANIZATIONS_COLLECTION organizations Firma/Müşteri listesi.
MONGODB_INFRA_RECORD_COLLECTION infra-record Ana Infra kaydı (tüm form + steps[] süreç geçmişi).
MONGODB_INFRA_REMINDER_COLLECTION infra-reminder Hatırlatıcı planları — her tarih/saat ayrı kayıt.
(sabit, env'den değil) infra-logs Kullanıcı başına tek doküman, işlem satırları $push ile eklenir.

Kaynaklar: .env.example, backend/src/config/env.js, backend/src/models/*.js, backend/src/repositories/infraLogs.repository.js, backend/src/repositories/infraReminder.repository.js.

8.2 InfraRecord — Ana Alan Tablosu

dashboard/src/interfaces/models/it.ts (710 satır) domain'in tek gerçek kaynağıdır; backend loosely-typed (MongoDB, çoğu alan opsiyonel) olduğu için bu tip tanımı da neredeyse tüm alanları opsiyonel bırakır. Aşağıdaki tablo InfraRecord arayüzünün gerçek alan adlarını listeler:

Alan Tip Zorunlu Açıklama
_id string Hayır (yalnızca yanıtta) MongoDB ObjectId'nin hex string hali.
public_token string Hayır Public müşteri linkinin anahtarı (bootstrap kaynağı).
organization_id, organization_name string Hayır Firma/organizasyon referansı.
company_name, company string Hayır Görüntülenen firma adı (iki alan aynı bilgiyi taşıyabilir — geriye dönük uyumluluk).
user_id string Hayır Kaydı oluşturan admin kullanıcının id'si.
created_at, updated_at ISO string Hayır (backend her kayıtta atar) Dashboard sıralaması ve "Kurulum" özet kartı için.
authorized_person AuthorizedPersonPayload[] Hayır Yetkili kişi listesi — { name, permission, mail, phone }. Ek geriye dönük alan adları (authority, role, yetkisi) da kabul edilir.
edited_by, edited_at string Hayır Kaydı düzenleyen kişi/tarih (form üstü meta bilgi).
machine_count number | string Hayır Makine sayısı.
machines string[] Hayır Seçilen makine markaları listesi.
machine_list string Hayır Virgülle ayrılmış makine listesi (görüntü amaçlı).
system, system_source string Hayır Sistem tipi/kaynağı (Cloud/Local).
source_status string Hayır Proje tipi — Satış/Demo (bkz. bölüm 13).
device_type, cpu_core, ram_gb/ram_unit, disk_ssd_gb/disk_ssd_unit, operating_system çeşitli Hayır "İstenen" (requested) sistem kapasitesi — REQUESTED_CAPACITY_FIELDS kilidi kapsamındadır (bkz. bölüm 5.8, 11).
provided_capacity nesne (aynı 5 alt-alan: device_type, cpu_core, ram_gb/ram_unit, disk_ssd_gb/disk_ssd_unit, operating_system) Hayır Müşterinin/admin'in girdiği "Sağlanan" kapasite — ayrı, opsiyonel alt-nesne.
administrator_requirement, virtualization_requirement, sleep_shutdown_requirement, automatic_start_requirement, machine_access_requirement, server_ip_requirement, iqvizyon_access_requirement, installation_update_access_requirement, vpn_requirement, anydesk_requirement, outbound_traffic_requirement, inbound_traffic_requirement string Hayır 12 sabit gereksinim metni alanı (Sunucu/Ağ/Uzaktan Erişim/Güvenlik gereksinim grupları).
remote_access RemoteAccess ({ type: 'vpn'|'anydesk', vpn?, anydesk? }) Hayır Uzaktan erişim yöntemi ve detayları; parolalar public yanıtta maskelenir.
status number (0/1) Hayır Kayıt seviyesi Kurulum durumu — tek kaynak closeAdminReview (bkz. bölüm 11).
review_status string (open/closed) Hayır Admin kontrol turunun açık/kapalı olduğu.
reviewed_by, reviewed_by_id, reviewed_at çeşitli Hayır Son kontrolü yapan admin bilgisi.
installation_decision '' | 'ready' | 'conditional_ready' | 'not_ready' Hayır Kurulum Kararı seçimi.
public_progress Record<string, RequirementRowState> Hayır Müşteri tarafı kalem/bölüm bazlı tamamlanma durumu (bkz. bölüm 11).
admin_review_progress Record<string, AdminRequirementReviewItem> Hayır IQVizyon tarafı 27-kalemlik onay/not durumu (bkz. bölüm 12/17-kalem tablosu).
customer_general_note string Hayır Müşterinin public formda yazdığı genel not.
internal_general_note string Hayır IQVizyon'un kendi iç notu — public whitelist'te yer almaz, müşteri ekranına asla ulaşmaz.
general_notes { customer?, admin? } Hayır Not alanlarının yapılandırılmış görünümü (admin notu: { note, updated_at, updated_by }).
server_room_available, customer_static_ip string Hayır Sunucu Odası/Statik IP alt-bölümü.
machine_details MachineDetail[] Hayır Tezgah/Makine Bilgileri tablosu satırları (bkz. bölüm 12).
steps WorkflowStep[] Hayır Süreç geçmişi — append-only audit trail (bkz. 8.3).
workflow_state, locked çeşitli Hayır Bazı yanıt şekillerinde görülen ek durum bayrakları.
iqvizyon_note string Hayır (yalnızca public GET) IQVizyon genel notunun salt-okunur public görünümü (kaynak internal_general_note — public whitelist üzerinden sızdırılmadan, ayrı bir salt-okunur alan olarak sunulur).
iqvizyon_review_notes Record<string,string> Hayır (yalnızca public GET) IQVizyon'un onaylamadığı satırlara yazdığı notlar ({itemKey | machine-<no>: not}).

Kaynaklar: dashboard/src/interfaces/models/it.ts (710 satır, tam okundu), backend/src/services/infraRecord.service.js, backend/src/validators/infraRecord.validation.js.

8.3 Nested Yapılar — Detaylı Şekiller

Yapı Alanlar Not
AuthorizedPerson (form) name, email, phone, permission Ekrandaki serbest metin girişi.
AuthorizedPersonPayload (backend'e giden) name, permission, mail, phone Alan adları backend sözleşmesiyle hizalı (email→mail).
MachineDetail no, machine_name, brand, model_type, control_unit, ip_address, completed, note, notes?: string[] Tezgah Bilgileri tablosu satırı; notes[] açıklama geçmişidir (append-only).
RemoteAccessVpn ip, username, password, has_password? Public GET'te password boş/maskeli döner, has_password bayrağı dolu olup olmadığını belirtir.
RemoteAccessAnyDesk id, password, unattended_access, auto_start, has_password? Aynı maskeleme kuralı geçerlidir.
RequirementRowState (public_progress değeri) completed, note, notes?: string[] 27 kalemlik gereksinim listesinin her satırı için müşteri durumu.
AdminRequirementReviewItem (admin_review_progress değeri) approved, completed, note, notes?: string[], machines?: [{no, approved, completed, note, notes?}] IQVizyon'un onay/tik/not girişi; makine satırları için ayrı bir alt-liste.
WorkflowStep type (iqvizyon_created|customer_control|iqvizyon_control), status, note, date/created_at, + o adımın ürettiği alanların derin kopyası (public_progress, admin_review_progress, provided_capacity, requested_capacity, source, 12 gereksinim metni, decision, installation_decision*, vb.) Bkz. 8.4 — append-only audit trail.

Kaynaklar: dashboard/src/interfaces/models/it.ts.

8.4 steps[] — Süreç Geçmişi (Append-Only Log)

Her kayıt yaşam döngüsü boyunca üç tür adım biriktirir; hiçbir adım güncellenmez/silinmez, yalnızca sona eklenir:

  1. iqvizyon_created — ilk kayıt anında (InfraRecordService.create), IQVizyon'un müşteriye ilk gönderdiği talebin derin kopya snapshot'ı (company_name, organization_id, authorized_person, source_status dahil).
  2. customer_control — müşterinin her "Gönder" işleminde (submitPublicProgress), o andaki public_progress, provided_capacity, requested_capacity, remote_access, source (machine_count/machines/machine_list/system/system_source), 12 gereksinim metninin derin kopyası.
  3. iqvizyon_control — IQVizyon'un her Tamamlandı/Tamamlanmadı kararında (closeAdminReview), decision, review_status, reviewed_by/reviewed_by_id/reviewed_at, installation_decision/installation_decision_label/installation_decision_note ve admin_review_progress'in derin kopyası.

Bu tasarım, sonradan ana kayıt güncellense bile geçmiş adımların değişmemesini garanti eder (audit trail) — bkz. bölüm 23.1.

Kaynaklar: backend/src/services/infraRecord.service.js, dashboard/src/interfaces/models/it.ts (WorkflowStep), dashboard/src/components/it/WorkflowHistory.tsx.

8.5 organizations ve iqvizyon-users Şemaları

organizations: { _id, company_id, company_name, organization_name, location?, city?, department?, manager_id?, status? } — yalnızca status boş/yok/active (case-insensitive) olan kayıtlar dropdown'da listelenir (kök neden düzeltmesi: eski kayıtlarda status alanı hiç yoktu, katı {status:'active'} filtresi listeyi boş gösteriyordu).

iqvizyon-users: { _id, username, password (bcrypt hash), full_name?, email?, role?, permissions?, company_id?, organization_id?, company_name?, status? } — parola hash'i toSafeUser() ile her API yanıtından önce çıkarılır.

Kaynaklar: backend/src/models/organization.model.js, backend/src/models/user.model.js, backend/src/repositories/organizations.repository.js.


9. Infra Form Domain Yapısı

Form iki aşamalı bir akış izler:

  1. Yeni kayıt (ITForm.tsx): Firma/Müşteri, Yetkili Kişi(ler), Düzenleyen, Düzenleme Tarihi, Makine (sabit marka listesinden çoklu seçim + "Diğer" serbest metin), Sistem, Durum (Proje Tipi) — POST /infra-record ile kaydedilir; iqvizyon_created adımı otomatik oluşur ve public_token üretilir.
  2. Önizleme/Detay (ITPreview.tsx): tüm bölümlerin (10-15. bölümler arası, form içeriği) render'ı; admin modunda düzenlenebilir + Kaydet/Güncelle, public modunda müşterinin doldurabileceği alanlar + Gönder.

mode prop'u üç değer alır: 'new', 'admin-existing', 'public' — bu, hangi alanların salt-okunur, hangi butonların görünür olduğunu belirler (bkz. bölüm 4.8).

Müşteri formu (PublicITView.tsx → ITPreview.tsx mode='public') ile admin düzenleme ekranı (mode='admin-existing') aynı bileşen ağacını (ITPreview.tsx) paylaşır — iki ayrı form implementasyonu yoktur; hangi alanların düzenlenebilir olduğu tamamen mode prop'una göre koşullu render ile belirlenir. Bu, iki tarafın alan adlarının/davranışının hiçbir zaman birbirinden sapmamasını sağlayan bilinçli bir tasarım kararıdır.

Kaynaklar: dashboard/src/components/it/index.tsx, ITForm.tsx, ITPreview.tsx, PublicITView.tsx.


10. Form Bölümleri ve Alan Davranışları

constants.ts'teki REQUIREMENT_ITEMS (27 kalem), 8 grup altında toplanır: Kaynak, Sistem Kapasite Bilgileri, Sunucu ve Gereksinimleri, Ağ (Network) Gereksinimleri, Uzaktan Erişim Gereksinimleri, Güvenlik Gereksinimleri, Server, Makine Bilgileri. Buna ek olarak UI'da ayrıca: Sunucu Odası/Statik IP alt-bölümü (ServerAndMachineSection.tsx), Tezgah/Makine Bilgileri tablosu, 10. Güvenlik Kontrolü tablosu (SecurityControlTable.tsx), 12. IQVizyon Kontrolü paneli (AdminReviewPanel.tsx), Süreç Geçmişi (WorkflowHistory.tsx).

Grup İçerik / Örnek Alanlar Kimin Doldurduğu
Kaynak Makine sayısı, makine markaları, sistem (Cloud/Local), proje tipi (Satış/Demo) Admin (kayıt oluşturma anında)
Sistem Kapasite Bilgileri İstenen vs. Sağlanan: Sunucu tipi, CPU çekirdek, RAM, SSD, işletim sistemi İstenen: admin/Bil önerisi; Sağlanan: müşteri/admin
Sunucu ve Gereksinimleri administrator_requirement, virtualization_requirement, sleep_shutdown_requirement, automatic_start_requirement Sabit metin (IQVizyon politikası) + müşteri tik/not
Ağ (Network) Gereksinimleri machine_access_requirement, server_ip_requirement, iqvizyon_access_requirement, installation_update_access_requirement Sabit metin + müşteri tik/not
Uzaktan Erişim Gereksinimleri vpn_method/vpn_requirement, anydesk_method/anydesk_requirement Sabit metin + müşteri seçim/tik
Güvenlik Gereksinimleri outbound_traffic_requirement, inbound_traffic_requirement Sabit metin + müşteri tik/not
Server Sunucu Odası mevcut mu, Statik IP Müşteri
Makine Bilgileri Tezgah tablosu satırları (machine_details[]) Müşteri

FIXED_REQUIREMENT_TEXT sabiti, müşteriye gösterilecek standart gereksinim metinlerini içerir — bunlardan biri gerçek, kasıtlı olarak müşteriye açık, statik OpenVPN IP adresidir: 135.181.251.237 (bkz. bölüm 7.8 — bu bir sızıntı değildir).

Kaynaklar: dashboard/src/components/it/constants.ts (394 satır), ServerAndMachineSection.tsx, SecurityControlTable.tsx.


11. Progress / Tamamlandı Mekanizması

İki bağımsız "tamamlandı" kavramı vardır — birbirine karıştırılmamalıdır:

  1. Bölüm bazlı müşteri "Durum" tiki (public_progress.section_*) — SectionStatusControl.tsx üzerinden, POST /infra-public-section-status ile anında (Gönder'i beklemeden) kaydedilir; sayfa yenilendiğinde kaybolmaması için. 10 sabit section_* anahtarı (SECTION_STATUS_KEYS, hem frontend constants.ts'te hem backend infraRecord.service.js'te bağımsız olarak sabit kodlanmıştır — istemciden gelen ham string asla doğrudan bir MongoDB alan yolu parçası olarak kullanılmaz — bkz. bölüm 23.1).
  2. Kalem bazlı tamamlanma (public_progress.<itemKey>.completed, RequirementRowState) — 27 kalemlik gereksinim listesinin her satırında müşterinin tik attığı, notlarla birlikte tutulan alan bazlı durum.
  3. Admin tarafı 27-kalemlik onay (admin_review_progress.<itemKey>.approved/.completed) — IQVizyon'un "IQVizyon Kontrolü" panelinde her kalemi tek tek onayladığı, ayrı bir onay katmanı (müşterinin completed tiki ile karıştırılmaz — müşteri "tamamladım" der, IQVizyon "onaylıyorum" der).
  4. Kayıt seviyesi Kurulum durumu (status: 0 | 1) — tek kural: status === 1 ⇒ "Kurulum" (tamamlandı), aksi halde "Durum" (tamamlanmadı). Bu değeri yalnızca IQVizyon'un closeAdminReview (Tamamlandı/Tamamlanmadı) kararı değiştirir. source_status (Satış/Demo/Kurulum proje tipi) bu kararda kullanılmaz (bkz. bölüm 13).

Bu dört katman birlikte, "müşteri ne dedi" (public_progress) ile "IQVizyon ne onayladı" (admin_review_progress) ile "kayıt resmi olarak kapandı mı" (status) sorularını birbirinden ayıran bir üç-seviyeli onay zinciri oluşturur.

Kaynaklar: backend/src/utils/infraStatus.js, backend/src/services/infraRecord.service.js (setPublicSectionStatus, closeAdminReview), dashboard/src/components/it/SectionStatusControl.tsx, AdminReviewPanel.tsx, constants.ts.


12. Makine Yönetimi

İki farklı "makine" kavramı vardır:

  • Makine listesi (Kaynak bölümü): MachinePickerModal.tsx ile sabit marka listesinden (MACHINE_BRAND_OPTIONS: FANUC, Siemens, Mitsubishi, Heidenhain, Haas, Mazak, DMG MORI, Okuma, Makino, Brother, Doosan, Hurco, Quaser, Spinner, YCM — + "Diğer" serbest metin) çoklu seçim; virgülle ayrılmış machine_list string'ine yazılır, machines[] dizisi de ayrıca tutulur.
  • Tezgah Bilgileri tablosu (Makine Bilgileri bölümü): ServerAndMachineSection.tsx içinde her satırda No, Tezgah Adı (machine_name), Marka (brand), Model Tipi (model_type), Kontrol Ünitesi (control_unit), IP Adresi (ip_address) alanları — müşterinin doldurduğu, machine_details[] (MachineDetail[]) olarak submitPublicProgress ile kaydedilen ayrı bir veri yapısı. Her satırın kendi completed bayrağı ve note/notes[] (açıklama geçmişi) alanı vardır. Bir satırın tamamlanma koşulu dört ana alanın (machine_name, brand, model_type, control_unit — ServerAndMachineSection.tsx'te tanımlı) tamamının dolu olmasıdır (tek kaynak, ServerAndMachineSection.tsx içinde).

IQVizyon tarafı, AdminRequirementReviewItem.machines[] alt-listesi üzerinden her makine satırını ayrı ayrı onaylayabilir ({no, approved, completed, note, notes?}) — bu, admin_review_progress'in makine bölümüne özgü, satır-bazlı bir alt-yapısıdır.

Kaynaklar: dashboard/src/components/it/MachinePickerModal.tsx, ServerAndMachineSection.tsx, constants.ts (MACHINE_BRAND_OPTIONS), dashboard/src/interfaces/models/it.ts (MachineDetail, AdminRequirementReviewItem.machines).


13. Proje Tipi / Durum Yönetimi

source_status (Satış/Demo), SOURCE_STATUS_OPTIONS sabitinden gelir ve yalnızca bilgilendirme/raporlama amaçlıdır — isInfraRecordCompleted kararına dahil değildir (bölüm 11). Demo modunda (isDemoSourceStatus) /api/it/bil'e hiç istek atılmaz; DEMO_MINIMUM_REQUIREMENTS sabit değerleri kullanılır (Bil önerisi yerine).

Kayıt seviyesi durum (bölüm 11'de anlatılan status: 0|1 ve review_status: open|closed), proje tipinden bağımsız, tek bir state machine izler:

[Kayıt oluşturuldu] --(iqvizyon_created)--> status=0, review_status=open (kayıt yoksa)
        |
        v
[Müşteri "Gönder"] --(customer_control)--> status=0, admin_review_progress SIFIRLANIR,
        |                                   installation_decision temizlenir
        v
[IQVizyon "Tamamlandı" kararı] --(iqvizyon_control, decision='completed')--> status=1, review_status='closed'
        veya
[IQVizyon "Tamamlanmadı" kararı] --(iqvizyon_control, decision='incomplete')--> status=0, review_status='open'

Müşteri her yeni "Gönder" yaptığında (yeni bir customer_control adımı), önceki admin kontrol turu sıfırlanır — bu, IQVizyon'un her yeni müşteri gönderimini baştan değerlendirmesini garanti eder (eski onayların yanlışlıkla yeni veriye uygulanmasını engeller).

Kaynaklar: dashboard/src/components/it/constants.ts (SOURCE_STATUS_OPTIONS, DEMO_MINIMUM_REQUIREMENTS), ITForm.tsx (isDemoSourceStatus importu), backend/src/utils/infraStatus.js, backend/src/services/infraRecord.service.js (submitPublicProgress, closeAdminReview).


14. Ekler ve Dosya Yönetimi

Projede klasik dosya-yükleme (attachment) uç noktası veya alanı tespit edilmemiştir — backend/src/routes ve infraRecord.service.js içinde herhangi bir multipart/upload/multer referansı bulunmamaktadır; InfraRecord/MachineDetail/WorkflowStep tip tanımlarında (dashboard/src/interfaces/models/it.ts) dosya/URL/attachment tipinde herhangi bir alan yoktur. Formda yer alan tüm bilgi türleri (kapasite değerleri, gereksinim metinleri, uzaktan erişim bilgileri, tezgah bilgileri) yapılandırılmış metin/sayı/boolean alanlarıdır — ikili dosya içeriği (belge, fotoğraf, sertifika vb.) hiçbir noktada kabul edilmez veya saklanmaz.

Bu özellik kaynak kodda mevcut değildir. Bir dosya ekleme ihtiyacı doğarsa: backend'e multer (veya benzeri) tabanlı bir upload middleware'i, bir depolama katmanı (yerel disk/S3 uyumlu nesne depolama) ve InfraRecord/MachineDetail tiplerine yeni bir attachments[] alanı eklenmesi gerekecektir — bunların hiçbiri şu an mevcut değildir.

Kaynaklar: backend/src/app.js, backend/src/services/infraRecord.service.js, dashboard/src/interfaces/models/it.ts (alan taraması), backend/package.json (bağımlılık listesinde multer yok).


15. E-posta ve Bildirim Altyapısı

15.1 SMTP (Office365)

mailService.js: Nodemailer, secure: false + requireTLS: true (587/STARTTLS, Office365 için doğru kombinasyon), önbelleğe alınmış tekil transporter. SMTP_HOST/SMTP_USER/SMTP_PASSWORD eksikse getTransporter() açıkça "SMTP yapılandırması eksik: ..." hatası fırlatır — uygulama yine de başlar, yalnızca mail-gönderen istekler 500 döner. Modül ilk yüklendiğinde bir kez verifyTransporter() çağrılır (teşhis amaçlı, isteğe bağlı). Geriye dönük uyumluluk: SMTP_PASSWORD yoksa eski SMTP_PASS, SMTP_TLS yoksa eski SMTP_REQUIRE_TLS okunur — Legacy/uyumluluk amacıyla tutulduğu doğrulandı.

15.2 Mail Tetikleyicileri

Tetikleyici Servis fonksiyonu Alıcı İçerik
Admin "Mail Gönder" sendEditLinkMail authorized_person[].mail (her birine ayrı) Public form linki.
IQVizyon Kontrol kararı sendReviewResultMail Aynı Tamamlandı/Tamamlanmadı sonucu + eksik kalemler (varsa, incomplete_items).
Müşteri "Gönder" infraSubmitNotifier.notifyCustomerSubmit MAIL_TO (env, tek adres — IQVizyon ekibi) Firma/başlık/Infra ID + varsa genel not.

MAIL_TO boşsa müşteri gönderim maili sessizce atlanır (SKIPPED, hata değil) — uygulama akışı bloklanmaz.

Her alıcıya ayrı ayrı sendMail çağrısı yapılır (toplu to: [a,b] kullanılmaz) — her alıcı yalnızca kendi adresini görsün diye (bkz. bölüm 5.8, 23.1).

15.3 Telegram

telegram.service.js: Bot API sendMessage/getMe, 10 sn timeout. İki kullanım noktası:

  • (a) müşteri "Gönder" bildirimi → INFRA_TELEGRAM_CHAT_ID (sabit grup).
  • (b) hatırlatıcı bildirimi → reminderNotifier.service.js, her kişinin kendi telegram_id'sine, periyodik setInterval (varsayılan/min 15000 ms, REMINDER_NOTIFY_INTERVAL_MS) ile — zaten gönderilmiş kişi-plan çiftleri notified_person_ids ile tekrar bildirilmez.

Her iki bildirim kanalı da (mail + Telegram) birbirinden bağımsız denenir, biri başarısız olsa diğeri etkilenmez, hiçbiri DB yazımını geri almaz — infraSubmitNotifier.service.js hiçbir koşulda exception fırlatmaz. Aktif kullanım doğrulandı.

Kaynaklar: backend/src/services/mailService.js, infraSubmitNotifier.service.js, telegram.service.js, reminderNotifier.service.js, .env.example.


16. PDF / Export

components/it/PDFReport.ts (370 satır) PDF üretimini tamamen istemci tarafında yapar; PDF_STANDARD sabiti (constants.ts) documentCode: IQV-IT-SRV-001 gibi standart doküman meta bilgilerini taşır. PDF butonu hem admin hem public ekranda, sayfa başlığının yanında (headerActionsContainer portalı ile) render edilir; tıklandığında pdfLoading state'i butonun tekrar tıklanmasını engeller. Public tarafta PDF indirme, infraLogService.logInfraClientEvent('pdf_downloaded', infraId) ile backend'e loglanır. Backend'de ayrı bir PDF render/export uç noktası yoktur — PDF üretimi bir HTTP isteği değil, tarayıcıda çalışan bir istemci fonksiyonudur.

Kaynak koddan kesin olarak doğrulanamadı: PDF'in tam sayfa/alan içeriği (hangi bölümlerin, hangi sırada, hangi biçimde PDF'e yansıdığı) bu turda satır satır incelenmedi (dosya adı ve constants.ts'teki PDF_STANDARD sabiti üzerinden genel amacı doğrulandı).

Kaynaklar: dashboard/src/components/it/PDFReport.ts (satır sayısı ve import noktası doğrulandı), constants.ts, ITPreview.tsx (PDF buton mantığı grep ile doğrulandı), infraLogService.ts.


17. API Endpointleri

Aşağıdaki tablo, backend/src/routes/*.js altındaki her router dosyasının tanımladığı tüm uç noktaları listeler (route dosyası bazında gruplanmıştır).

17.1 Kimlik Doğrulama — auth.routes.js

Method Endpoint Auth Yetki/Session Açıklama
POST /api/v1/auth/login Yok (rate-limited: loginLimiter) — Kullanıcı adı/parola ile giriş, JWT üretir.
GET /api/v1/auth/me Bearer JWT — Token'ın geçerliliğini ve kullanıcı bilgisini yeniden doğrular.

17.2 Personel/Kullanıcı — people.routes.js

Method Endpoint Auth Yetki/Session Açıklama
GET /api/v1/users Bearer JWT users.read Kullanıcı listesi.
POST /api/v1/users Bearer JWT users.create Yeni kullanıcı oluşturma.
PUT /api/v1/users/:id Bearer JWT users.update Kullanıcı güncelleme.
PATCH /api/v1/users/:id Bearer JWT users.update Kullanıcı kısmi güncelleme.
DELETE /api/v1/users/:id Bearer JWT users.delete Kullanıcı silme.

17.3 Organizasyonlar — organizations.routes.js

Method Endpoint Auth Yetki/Session Açıklama
GET /organizations Bearer JWT — Firma/Müşteri listesi (prefix'siz — bilinçli istisna, bkz. 5.4).

17.4 Infra Kayıtları (Admin) — infraRecord.routes.js

Method Endpoint Auth Yetki/Session Açıklama
GET /infra-record Bearer JWT — Tüm Infra kayıtları listesi.
GET /infra-record/:id Bearer JWT — Tek kayıt detayı.
POST /infra-record Bearer JWT — Yeni kayıt oluşturma (iqvizyon_created adımı otomatik).
PUT /infra-record/:id Bearer JWT — Kayıt güncelleme (REQUESTED_CAPACITY_FIELDS kilidi uygulanır).
POST /infra-record/:id/send-mail Bearer JWT — Public link maili (sendEditLinkMail).
POST /infra-record/:id/review-mail Bearer JWT — Kontrol sonucu maili (sendReviewResultMail).

17.5 IQVizyon Kontrolü (Admin Review) — infraAdminReview.routes.js

Method Endpoint Auth Yetki/Session Açıklama
POST /infra-public-review-close Bearer JWT — Kurulum Kararı (Tamamlandı/Tamamlanmadı) — closeAdminReview.
POST /infra-admin-general-note Bearer JWT — Kurulum Kararı/iç not kaydı (debounce'lu).

17.6 IT / Bil (Gemini) — it.routes.js

Method Endpoint Auth Yetki/Session Açıklama
POST /api/it/bil Bearer JWT — Gemini tabanlı sistem kapasitesi önerisi; GEMINI_API_KEY yoksa kontrollü GEMINI_API_KEY_MISSING hatası.

17.7 Hatırlatıcı — reminder.routes.js

Method Endpoint Auth Yetki/Session Açıklama
GET /api/reminders Bearer JWT — Hatırlatıcı listesi.
GET /api/reminders/assignees Bearer JWT — Atanabilir kişi listesi.
PATCH /api/reminders/:id Bearer JWT — Plan kaydetme.

17.8 İşlem Logu — infraLog.routes.js

Method Endpoint Auth Yetki/Session Açıklama
POST /infra-logs/event Bearer JWT — Yalnızca istemci-taraflı olaylar (logout, pdf_downloaded); kullanıcı bilgisi req.user'dan, firma adı DB'den — gövdeden güvenilmez veri okunmaz.

17.9 Sağlık Kontrolü — health.routes.js

Method Endpoint Auth Yetki/Session Açıklama
GET /health Yok — Liveness.
GET /health/ready Yok — Readiness (gerçek Mongo ping).

17.10 Dokümantasyon — docs.routes.js

Method Endpoint Auth Yetki/Session Açıklama
GET /openapi.json Yok — OpenAPI şeması (JSON).
GET /api-docs (+ /api-docs/) Yok — Swagger UI (özel gruplama eklentisiyle).

17.11 Public/Müşteri — infraPublicRecord.routes.js

Method Endpoint Auth Yetki/Session Açıklama
POST /infra-public-session Yok (rate-limited: publicLimiter) publicToken (gövdede, tek seferlik) Session bootstrap — cookie yazar, kayıt döndürmez.
GET /it/public-access/:publicToken Yok (rate-limited) publicToken (URL) Mail linkinin gerçek hedefi — 302 → /form.
GET /it/public/:publicToken Yok (rate-limited) publicToken (URL) Geriye dönük uyumluluk → 302 → /form.
GET /it/public Yok (rate-limited) — Geriye dönük uyumluluk (token'sız eski landing) → 302 → /form.
GET /infra-public-record Yok (rate-limited) requirePublicSession (cookie) Müşteri ekranının okuduğu whitelist'li kayıt (PUBLIC_FIELDS).
POST /infra-public-progress-submit Yok (rate-limited) requirePublicSession Final "Gönder" — yeni customer_control adımı.
POST /infra-public-section-status Yok (rate-limited) requirePublicSession Bölüm bazlı Durum tik/kalem.

Not: /infra-public-progress (GET/POST) backend'de tanımlı değildir — frontend'in ilgili istemci fonksiyonları (itService.getPublicProgress/savePublicProgressDraft) da kullanılmamaktadır (bkz. bölüm 4.6, 7.7, 24.1).

Kaynaklar: backend/src/app.js, tüm backend/src/routes/*.js dosyaları (grep -nE "router\.(get|post|put|patch|delete)" ile doğrudan doğrulandı).


18. Environment Variables

Tek, paylaşılan kök .env dosyası — hem backend/src/config/env.js (../../../env göreli yolu, çalışma dizininden değil, dosyanın kendi konumundan çözülür) hem dashboard/vite.config.ts (envDir: '..') aynı dosyayı okur. Yalnızca VITE_ önekli değişkenler frontend bundle'ına girer.

Değişken Zorunlu Default Kullanıldığı Yer Açıklama
PORT Hayır 4000 backend/src/server.js Backend HTTP portu.
NODE_ENV Hayır — backend/src/utils/publicSession.js, config/env.js production iken public session cookie secure: true.
MONGODB_URI Evet (pratikte) mongodb://127.0.0.1:27017 config/db.js MongoDB bağlantı adresi.
MONGODB_DB Hayır iqvizyon config/db.js Veritabanı adı.
MONGODB_USERS_COLLECTION Hayır iqvizyon-users repositories/user.repository.js Kullanıcı koleksiyonu adı.
MONGODB_ORGANIZATIONS_COLLECTION Hayır organizations repositories/organizations.repository.js Firma koleksiyonu adı.
MONGODB_INFRA_RECORD_COLLECTION Hayır infra-record repositories/infraRecord.repository.js Ana Infra koleksiyonu adı.
MONGODB_INFRA_REMINDER_COLLECTION Hayır infra-reminder repositories/infraReminder.repository.js Hatırlatıcı koleksiyonu adı.
JWT_SECRET Evet Yok (fallback yok) auth.service.js, utils/publicSession.js JWT imzalama — hem admin hem public session için (tek paylaşılan secret, bkz. 6.5/23.2).
JWT_EXPIRES_IN Hayır 12h auth.service.js Admin JWT ömrü.
CORS_ORIGIN Hayır http://localhost:5173 app.js Virgülle ayrılmış izinli origin listesi.
FRONTEND_BASE_URL Hayır boş utils/frontendUrl.js Mail linki origin önceliği.
PUBLIC_API_BASE_URL Hayır boş docs/ (OpenAPI üretimi) OpenAPI "Production" sunucu girdisi.
SMTP_HOST Hayır boş mailService.js SMTP sunucu adresi.
SMTP_PORT Hayır boş mailService.js SMTP portu (587 önerilir).
SMTP_SECURE Hayır boş mailService.js TLS/SSL modu.
SMTP_TLS (alias: SMTP_REQUIRE_TLS) Hayır boş mailService.js STARTTLS zorunluluğu.
SMTP_USER Hayır boş mailService.js SMTP kullanıcı adı.
SMTP_PASSWORD (alias: SMTP_PASS) Hayır boş mailService.js SMTP parolası — secret, dokümana yazılmaz.
SMTP_FROM Hayır boş mailService.js Giden mail "from" adresi.
MAIL_TO Hayır boş infraSubmitNotifier.service.js Müşteri "Gönder" bildirim maili alıcısı; boşsa sessizce atlanır.
TELEGRAM_BOT_TOKEN Hayır boş telegram.service.js Telegram bot token'ı — secret, dokümana yazılmaz; boşsa bildirim sessizce atlanır.
INFRA_TELEGRAM_CHAT_ID Hayır boş infraSubmitNotifier.service.js Müşteri gönderim bildirim grubu id'si.
REMINDER_NOTIFY_INTERVAL_MS Hayır (min 15000 uygulanır) reminderNotifier.service.js Hatırlatıcı döngü aralığı.
GEMINI_API_KEY Hayır boş bil.service.js /api/it/bil için Gemini API anahtarı — secret, dokümana yazılmaz; boşsa GEMINI_API_KEY_MISSING kontrollü hatası.
GEMINI_MODEL Hayır gemini-3.1-flash-lite (README) bil.service.js Kullanılacak Gemini model adı.
VITE_API_BASE_URL Hayır boş (same-origin) dashboard/src/services/* Frontend API taban adresi.
VITE_DEMO_MODE Hayır — frontend Demo modu anahtarı.
VITE_REQRES_API_KEY Hayır — — Eski demo entegrasyonu — aktif kullanım referansı tespit edilemedi (bkz. bölüm 24.1).
VITE_DEV_API_PROXY_TARGET Hayır http://localhost:4000 dashboard/vite.config.ts Dev proxy hedefi.
IQV_INFRA_HTTP_PORT Hayır 8080 docker-compose.yml Docker'da dışa açılan port.
E2E_* Hayır — tests/e2e/* Yalnızca Playwright test çalıştırıcıları için.
K6_* Hayır — scripts/ (k6 testleri) Yalnızca k6 yük testi çalıştırıcıları için.

Hiçbir gerçek secret değeri bu dokümana yazılmamıştır. OpenVPN sabit IP'si (135.181.251.237, bkz. bölüm 7.8) bir environment variable değildir — constants.ts içinde sabit kodlanmış, kasıtlı olarak müşteriye açık bir metindir; bu yüzden bu tabloda yer almaz.

Kaynaklar: .env.example, backend/src/config/env.js, README.md (Environment Variables tablosu ile çapraz doğrulandı).


19. Docker ve Deployment

19.1 docker-compose.yml

3 servis: mongo (image mongo:7, dışa port açılmaz, iqv-infra-net iç ağı, healthcheck), backend (kendi Dockerfile'ından build, env_file: .env, MONGODB_URI compose içinde mongodb://mongo:27017 olarak override edilir, yalnızca expose: 4000 — dışa port yok), dashboard (nginx runtime, depends_on: backend healthy, dışa açılan port ${IQV_INFRA_HTTP_PORT:-8080}:80). Named volume iqv-infra-mongo-data MongoDB verisinin kalıcılığını sağlar. backend'in dışa hiç port açmaması, tüm dış trafiğin yalnızca dashboard (nginx) üzerinden reverse-proxy ile backend'e ulaşabildiği anlamına gelir — backend doğrudan internete açık değildir.

19.2 Dockerfile'lar

backend/Dockerfile: çok aşamalı (deps → runtime), node:22-alpine, npm ci --omit=dev, root olmayan node kullanıcısı, .env imaja gömülmez (compose volume ile bağlanır), HEALTHCHECK gerçek bir /health/ready isteği atar (Mongo ping başarısızsa unhealthy).

dashboard/Dockerfile: node:22-alpine build aşaması (npm run build, VITE_API_BASE_URL build-arg, prod'da boş bırakılır — API aynı origin'den nginx proxy ile çağrılır) → nginx:1.27-alpine runtime (yalnızca statik dosyalar + nginx, Node/kaynak kod yok). HEALTHCHECK kök / adresine wget.

19.3 nginx.conf

Statik varlıklar için 30 günlük cache; index.html no-cache; güvenlik başlıkları (X-Content-Type-Options, X-Frame-Options: SAMEORIGIN, Referrer-Policy); backend uçları (/api/, /health, /openapi.json, /api-docs — ^~ ile regex önceliği bilinçli olarak aşılır, aksi halde statik dosya regex'i /api-docs/assets/*.css gibi istekleri yakalayıp 404 dönerdi —, /organizations, /infra-record, tüm /infra-public-*, /it/public-access, /it/public, /infra-logs/) iqv_infra_backend upstream'ine proxy'lenir; geri kalan her şey SPA fallback (try_files ... /index.html).

19.4 Native Kurulum (Docker'sız)

Docker olmadan PM2 ile iki süreç (iqv-infra-backend, iqv-infra-frontend); frontend scripts/common/static-server.mjs ile servis edilir (nginx'in bağımlılıksız native karşılığı), aynı proxy path listesini uygular (bölüm 19.3'teki path'lerle tutarlı — böylece hangi ortamda çalışılırsa çalışılsın SPA-fallback/backend-proxy davranışı sapmaz). Varsayılan portlar: frontend 5173, backend 4000. scripts/ altında hem Linux hem Windows, hem Docker hem native varyantları için ayrı kurulum/güncelleme/kaldırma script'leri bulunur.

19.5 Production vs. Development Farkları

Yön Development Production (Docker)
Statik dosya sunumu Vite dev server (HMR) nginx (build çıktısı)
API proxy Vite server.proxy (vite.config.ts) nginx location blokları
Public session cookie secure false (NODE_ENV !== 'production') true (NODE_ENV === 'production')
Backend portu Doğrudan 4000 erişilebilir Yalnızca iç ağda expose, dışa kapalı
.env konumu Repo kökü, geliştirici tarafından elle yönetilir env_file: .env, container'a mount edilir (imaja gömülmez)

Kaynaklar: docker-compose.yml, backend/Dockerfile, dashboard/Dockerfile, dashboard/nginx.conf, dashboard/vite.config.ts, README.md (Installation bölümü, scripts/ listesi doğrulandı).


20. Test Altyapısı

Test Katmanı Framework Konum Kapsam
Backend unit Node.js yerleşik node --test backend/tests/unit/*.test.js (10 dosya) Servis/utility fonksiyonları izole test.
Backend integration node --test backend/tests/integration/*.test.js (3 dosya) createApp(deps) fabrikası üzerinden uçtan uca HTTP-seviyesi testler (mock bağımlılıklarla).
Backend auth node --test backend/tests/auth/*.test.js (1 dosya) Login/JWT akışı.
Backend security node --test backend/tests/security/*.test.js (1 dosya) Güvenlik odaklı senaryolar (whitelist, mass-assignment koruması vb.).
Backend contract (OpenAPI) node --test backend/tests/contract/*.test.js (1 dosya) OpenAPI şeması ↔ gerçek router tutarlılığı (docs:check ile aynı amaç, test seviyesinde).
Frontend unit + component Vitest dashboard/tests/unit/, dashboard/tests/component/ (toplam 8 *.test.ts* dosyası) Saf fonksiyonlar (helpers.ts vb.) ve bileşen davranışları.
E2E Playwright tests/e2e/*.spec.ts (6 spec dosyası) Tarayıcı seviyesinde uçtan uca senaryolar.
Yük testi k6 scripts/ (K6_* env değişkenleri ile) Performans/yük testi — kapsamı bu turda ayrıntılı incelenmedi.

E2E spec dosyaları: api-docs, infra-form, infra-list, login, public-form, theme.

Backend test reporter'ları hem spec (stdout) hem junit/json/özel html formatında backend/tests/reports/ altına yazar. Kök package.json, npm run test:all (scripts/run-tests.js) ile tüm katmanları tek komutta çalıştırır; test:full bayraklı sürümü de vardır.

public-form.spec.ts (97 satır, tam okundu) gerçek e2e senaryoları doğrular: session yokken /form doğrudan açılır ama kayıt göstermez; mail linkinin 302 ile /form'a yönlendirdiği ve token'ın adreste kalmadığı; refresh sonrası session'ın (HttpOnly cookie) korunduğu; eski /it/public linklerinin /form'a yönlendirildiği; public token'ın hiçbir sayfa HTML'inde render edilmediği.

Test sayılarının (kaç adet it()/test() bloğu içerdiği) kesin toplamı bu turda gerçek bir test çalıştırması ile doğrulanmamıştır — Kaynak koddan/gerçek çalıştırmadan kesin olarak doğrulanamadı; yukarıdaki dosya sayıları find/dizin listelemesi ile sayılmıştır, blok içi test adedi değildir.

Kaynaklar: backend/tests/**, dashboard/tests/**, tests/e2e/*.spec.ts, package.json (root), backend/package.json, dashboard/package.json.


21. CI / Quality Pipeline

.github/workflows/ci.yml ("IQV Infra CI") main dalına push/PR ve workflow_dispatch ile tetiklenir. İş adımları:

  1. Security Precheck (scripts/ci/security-precheck.mjs) — commit edilmiş .env, private key, token, kimlikli Mongo URI taraması; secret sızıntısı bulursa pipeline en baştan kırılır.
  2. Workflow Lint — actionlint + shellcheck (workflow YAML ve script kalitesi).
  3. Backend testleri — unit/integration/auth/security/contract (bkz. bölüm 20).
  4. Frontend testleri — unit/component (Vitest).
  5. E2E — Playwright.
  6. Lint — kod stili/statik analiz.
  7. docs:check — OpenAPI şeması ↔ canlı router farkı; fark varsa CI kırılır (yeni bir endpoint OpenAPI'ye yansıtılmadan eklenirse bu adım pipeline'ı durdurur).
  8. quality-report.mjs — iqv-infra-quality-report artifact'i üretir (REPORT.md/REPORT.json/QUALITY.svg), actions/upload-artifact@v4 ile if: always() (test başarısız olsa bile rapor yüklenir).
  9. runtime-smoke.mjs — gerçek build/deploy sonrası /, /form, /health, /api-docs, /api-docs/, /api-docs/assets/swagger-ui.css, /openapi.json adreslerine gerçek HTTP isteği atarak canlı sağlık kontrolü yapar.

.github/workflows/docs.yml ayrı bir iş akışıdır: MkDocs build --strict → GitHub Pages deploy, yalnızca main'e docs/mkdocs.yml değişikliği içeren push'ta çalışır; PR'larda yalnızca build yapılır (deploy yok). Bu ayrım, dokümantasyon sitesinin uygulama CI'sından bağımsız, kendi tetikleyicisiyle yayınlanmasını sağlar.

Kaynaklar: .github/workflows/ci.yml, .github/workflows/docs.yml, README.md (CI/CD bölümü ile çapraz doğrulandı), scripts/ci/*.mjs (dosya listesi doğrulandı).


22. Responsive / Mobil / Tablet

Frontend'in kendi useBreakpoint(breakPoint = 768) hook'u (components/hooks/breakpoint.ts), window.innerWidth < breakPoint mantığıyla mobil/masaüstü ayrımı yapar (varsayılan eşik 768px). components/it/it.css içinde doğrudan tespit edilen @media kesme noktaları:

Kesme noktası Anlamı
767px Mobil görünüm üst sınırı.
768px Mobil eşiği — useBreakpoint hook'u ile tutarlı.
1199px Tablet/masaüstü ayrımı — ServerAndMachineSection.tsx'in grid kırılımı için kullanılır.

ServerAndMachineSection.tsx yorum satırında açıkça belirtilen grid davranışı: "768–991px (md): Makine tam satır, Sistem+Durum yarı yarıya; ≥992px (lg): Makine|Sistem|Durum 3 eşit kolon" — bu, antd grid sisteminin kendi md/lg breakpoint'lerini (antd varsayılanları: md=768, lg=992) kullanır, it.css'teki özel @media sorgularından ayrı bir mekanizmadır.

Bu nedenle projede iki paralel responsive mekanizma vardır: (1) useBreakpoint hook'u + it.css özel @media sorguları (768/767/1199px), (2) antd'nin kendi grid md/lg sistemi (768/992px). İkisi birbirine yakın ama özdeş olmayan eşik değerleri kullanır — bu bir çelişki değil, iki farklı katmanın (özel CSS vs. antd grid) kendi varsayılanlarıyla birlikte kullanılmasıdır.

Mobil, tablet-portrait, tablet-landscape ve masaüstü için birbirinden bağımsız, dört ayrı, açıkça adlandırılmış eşik değeri (ör. ayrı bir tailwind.config.mjs breakpoint tablosu) bu turda tespit edilmemiştir — Kaynak koddan kesin olarak doğrulanamadı: tailwind.config.mjs (varsa) bu turda kapsamlı olarak incelenmedi; projede Tailwind kullanılıp kullanılmadığı da bu turda kesin olarak doğrulanamadı.

Kaynaklar: dashboard/src/components/hooks/breakpoint.ts, dashboard/src/components/it/it.css (@media satırları grep ile doğrulandı), dashboard/src/components/it/ServerAndMachineSection.tsx (yorum satırı), ITForm.tsx.


23. Güvenlik

23.1 Uygulanan Güvenlik Kontrolleri

  • JWT (admin): Bearer token, JWT_SECRET ile imzalı, JWT_EXPIRES_IN (varsayılan 12h) ile sınırlı ömür; GET /api/v1/auth/me her sayfa yüklemesinde token'ı backend'e karşı yeniden doğrular (bkz. bölüm 6.1). Kaynak: backend/src/services/auth.service.js.
  • HttpOnly session cookie (public): httpOnly, sameSite: lax, prod'da secure: true, 24 saat TTL, purpose alanıyla admin JWT'den ayrıştırılmış (bkz. bölüm 7.2). Kaynak: backend/src/utils/publicSession.js.
  • CORS: CORS_ORIGIN env değişkeni ile virgülle ayrılmış izinli origin listesi (bkz. bölüm 18/23.2). Kaynak: backend/src/app.js, config/env.js.
  • Validation: infraRecord.validation.js ile request body doğrulaması, ObjectId format kontrolü. Kaynak: backend/src/validators/infraRecord.validation.js.
  • Mass-assignment/whitelist koruması: getPublicRecord (PUBLIC_FIELDS whitelist'i), submitPublicProgress (yalnızca 6 sabit alan gövdeden okunur), setPublicSectionStatus (SECTION_STATUS_KEYS sabit anahtar listesi) — istemciden gelen hiçbir alan adı doğrudan bir MongoDB yol parçası olarak kullanılmaz. Kaynak: backend/src/services/infraRecord.service.js.
  • Rate limiting: login (loginLimiter) ve tüm public uçlar (publicLimiter) express-rate-limit ile 5 dakikada 60 istek sınırına tabidir. Kaynak: backend/src/routes/auth.routes.js, infraPublicRecord.routes.js.
  • Hata sanitizasyonu: errorHandler.middleware.js, ham stack trace/iç sistem detayını istemciye asla döndürmez — yalnızca ApiError'ın statusCode+mesajı ya da genel 500 mesajı döner (bkz. bölüm 5.11). Kaynak: backend/src/middleware/errorHandler.middleware.js.
  • Yetki kontrolü: requirePermission route-level middleware'i, PermissionKey sözleşmesiyle users.* uçlarını korur; kendi hesabında yetki yükseltme engeli (isSelfPrivilegeEscalationRestricted) (bkz. bölüm 6.2). Kaynak: backend/src/middleware/auth.middleware.js, people.service.js.
  • Audit trail (steps[]): her kayıt için append-only, değiştirilemez süreç geçmişi — kim ne zaman ne yaptı sorgusunu garanti altına alır (bkz. bölüm 8.4). Kaynak: backend/src/services/infraRecord.service.js.
  • Secret yönetimi: tüm secret'lar (JWT_SECRET, SMTP_PASSWORD, TELEGRAM_BOT_TOKEN, GEMINI_API_KEY) yalnızca env üzerinden okunur, koda gömülmez; .env Docker imajına dahil edilmez (bkz. bölüm 19.2). Kaynak: backend/src/config/env.js, backend/Dockerfile.
  • Parola/credential loglama koruması: mailService.js, infraLog.service.js (SENSITIVE_KEY_PATTERN ile hassas alan adlarının değerleri hiç yazılmaz), telegram.service.js (token URL'de değil header'da/path'te, hata mesajlarında yer almaz). Kaynak: yukarıdaki dosyalar.
  • Remote access parolaları: public yanıtta her zaman maskRemoteAccess() ile maskelenir; boş gelen parola, önceki kayıtlı parolayı korur (public GET parolayı asla döndürmediği için). Kaynak: backend/src/services/infraRecord.service.js.
  • REQUESTED_CAPACITY_FIELDS kilidi: müşteri gönderdikten sonra admin bile "İstenen" değerleri PUT ile değiştiremez. Kaynak: backend/src/services/infraRecord.service.js.
  • Regex injection/ReDoS koruması: utils/regex.js kullanıcı girdisini escape eder, Türkçe karakter sınıflarını genişletir. Kaynak: backend/src/utils/regex.js.

23.2 Bilinen Güvenlik Kısıtları

  • Tek paylaşılan JWT_SECRET: hem admin oturum JWT'si hem müşteri public session JWT'si aynı secret ile imzalanır (purpose alanı ile ayrıştırılır, ama kriptografik anahtar aynıdır). Secret sızarsa her iki jeton türü de sahtelenebilir. Kaynak: backend/src/utils/publicSession.js.
  • CORS_ORIGIN varsayılanı: .env.example'da varsayılan http://localhost:5173 — prod ortamında bu değerin gerçek domain(ler)e ayarlanması operasyonel bir zorunluluktur; yanlış yapılandırılırsa (* verilirse) CORS koruması etkisiz kalır. Kaynak: .env.example, backend/src/config/env.js.
  • SMTP kimlik doğrulama diagnostiği: verifyTransporter() her sendEditLinkMail/sendReviewResultMail çağrısında SMTP'ye gerçek bir doğrulama isteği atar — bu, yanlış yapılandırılmış SMTP durumunda gecikme/ek yük yaratabilir (fonksiyonel bir açık değil, operasyonel bir not). Kaynak: backend/src/services/infraRecord.service.js.
  • Dosya yükleme/ek mekanizması yoktur — bu bir güvenlik açığı değil ama beklenen bir özellik ise eksiktir (bkz. bölüm 14).
  • /infra-public-progress uç noktası backend'de yok — frontend'de tanımlı ama çağrılmayan ölü kod, güvenlik riski oluşturmaz ancak bakım karmaşası yaratabilir (bkz. bölüm 7.7, 24.1).
  • public_token için ayrı bir expiry/rotasyon mekanizması tespit edilmemiştir (bkz. bölüm 7.6) — session cookie'sinin 24 saatlik TTL'i vardır ama mail linkinin kendisinin (asıl token) ne zaman geçersiz sayılacağı kaynak kodda açık değildir. Kaynak koddan kesin olarak doğrulanamadı.
  • API versiyonlama tutarsızlığı: yalnızca auth/people /api/v1 altındadır, diğer uçların çoğu versiyonsuzdur (bkz. bölüm 5.15) — bu doğrudan bir güvenlik açığı değildir ama gelecekteki breaking-change yönetimini zorlaştırır. Kaynak: backend/src/app.js.

Kaynaklar: yukarıda madde madde belirtilen dosyalar; .env.example.


24. Bakım Kuralları ve Bilinen Kısıtlar

24.1 Bilinen Kısıtlar

Öğe Sınıflandırma Gerekçe
vpn_priority / anydesk_priority alanları Legacy/uyumluluk amacıyla tutulduğu doğrulandı. UI sıralaması artık hardcoded; infraRecord.repository.js update() her PUT'ta bu alanları fırsatçı biçimde $unset eder.
remote_vpn / remote_anydesk eski progress anahtarları Legacy/uyumluluk amacıyla tutulduğu doğrulandı. Tek remote_access anahtarı ile değiştirildi.
customer_machine_details progress-flag anahtarı Legacy/uyumluluk amacıyla tutulduğu doğrulandı. Satır bazlı makine tamamlanma kontrolü ile değiştirildi.
Eski device_type değerleri "Server"/"Desktop" Legacy/uyumluluk amacıyla tutulduğu doğrulandı. Yalnızca eski kayıtların görüntülenmesi için; yeni kayıtlarda yalnızca "Sanal Sunucu"/"Fiziksel Sunucu" üretilir.
SMTP_PASS, SMTP_REQUIRE_TLS env adları Legacy/uyumluluk amacıyla tutulduğu doğrulandı. env.js'te sırasıyla SMTP_PASSWORD/SMTP_TLS için fallback.
/it/public, /it/public/:token route'ları Legacy/uyumluluk amacıyla tutulduğu doğrulandı. /form'a 302 yönlendirme; eski mail linkleri için.
/personel route'u Legacy/uyumluluk amacıyla tutulduğu doğrulandı. Canonical route /users'tır; /personel yalnızca eski linkler için /users'a yönlendiren bir alias'tır (browserRouter.tsx).
itService.getPublicProgress / savePublicProgressDraft Aktif kullanım referansı tespit edilemedi. Backend'de karşılık gelen route yok, frontend'de hiçbir bileşenden çağrılmıyor (grep ile doğrulandı).
VITE_REQRES_API_KEY env değişkeni Aktif kullanım referansı tespit edilemedi. Kaynak koddan kesin olarak doğrulanamadı (bu turda kaynak kod içinde kullanım noktası kapsamlı aranmadı, yalnızca .env.example'da tanımlı olduğu görüldü). Yorum satırlarından (itService.ts, apiRoutes) "eski ReqRes demo entegrasyonunun artık kullanılmadığı, gerçek backend'e taşındığı" anlaşılıyor.
Dosya ekleme (attachment) özelliği Kaynak kodda mevcut değil. Bkz. bölüm 14 — herhangi bir upload middleware'i/alanı tespit edilmedi.
API versiyonlama tutarsızlığı Aktif/bilinçli tasarım tercihi olarak doğrulanamadı — muhtemelen zaman içinde organik biçimde oluştu. Kaynak koddan kesin olarak doğrulanamadı. Bkz. bölüm 5.15.
public_token expiry/rotasyon mekanizması Kaynak koddan kesin olarak doğrulanamadı. Bkz. bölüm 7.6.

24.2 Genel Bakım Kuralları (kod içi yorumlardan çıkarılan proje kültürü)

  • Yeni bir alan/uç nokta eklerken var olan isimlendirme sözleşmesine uyulmalı (snake_case alan adları, section_* whitelist deseni, *_requirement metin alan deseni).
  • Public tarafa asla mass-assignment ile yazma açılmamalı — her yeni public alan, submitPublicProgress/setPublicSectionStatus içinde açıkça whitelist'e eklenmelidir.
  • steps[]'e eklenen her yeni adım derin kopya olmalı; var olan adımlar asla güncellenmemeli/silinmemeli (audit trail bütünlüğü).
  • Yeni bir secret/env değişkeni eklenirken .env.example'a şablon (değersiz) olarak eklenmeli, koda gömülmemeli.
  • docs:check CI gate'i, yeni bir endpoint OpenAPI şemasına yansıtılmadan eklenirse pipeline'ı kırar — yeni route eklerken backend/src/docs/ güncellenmelidir.
  • Yeni bir kalem/madde (REQUIREMENT_ITEMS listesine) eklenirken hem frontend constants.ts hem backend infraRecord.service.js'teki bağımsız sabit listeler birlikte güncellenmelidir — bu ikisi kasıtlı olarak birbirinden bağımsız sabit kodlanmıştır (istemciden gelen ham değerlere güvenilmemesi için), bu yüzden tek taraflı güncelleme veri tutarsızlığına yol açar.
  • Var olan alanlar yeniden adlandırılmaz; yeni bir ihtiyaç doğduğunda mevcut alana ek/yeni bir alt-alan eklenir (bkz. it.ts içindeki "MEVCUT alanların HİÇBİRİ yeniden adlandırılmadı" yorumları) — bu, eski kayıtlarla geriye dönük uyumluluğu korumak için proje genelinde tutarlı biçimde izlenen bir kuraldır.
  • Yeni bir kapasite/gereksinim alt-nesnesi eklenirken var olan { device_type, cpu_core, ram_gb/ram_unit, disk_ssd_gb/disk_ssd_unit, operating_system } şekli tekrar kullanılır, ikinci bir benzer şekil icat edilmez (bkz. provided_capacity/requested_capacity'nin birebir aynı şekli paylaşması).

Kaynaklar: yukarıdaki tüm bölümlerde atıfta bulunulan dosyalar; backend/src/repositories/infraRecord.repository.js, backend/src/services/bil.service.js, dashboard/src/routes/browserRouter.tsx, dashboard/src/services/itService.ts, dashboard/src/interfaces/models/it.ts.


Doküman sonu.