Skip to main content
Projede iki bağımsız React SPA vardır. İkisi de aynı stack üzerine kuruludur ama ayrı Node.js projeleri, ayrı Dockerfile’lar ve ayrı Keycloak realm’leridir.
Frontend React’tır, Vue değil. Her iki SPA da React 19 + Vite 6 + TypeScript 5.8 kullanır.

Teknoloji yığını

  • React 19 + react-dom 19, Vite 6, TypeScript 5.8
  • react-router-dom 7 — routing (Admin’de basename import.meta.env.BASE_URL’den okunur)
  • zustand 5 — global state (store/)
  • @tanstack/react-query 5 — server state / cache
  • axios 1.8 — HTTP client (lib/axios.ts)
  • keycloak-js 26 — SSO / OAuth2 + PKCE
  • Radix UI primitive’leri + Tailwind CSS 3 + class-variance-authority + tailwind-merge
  • lucide-react (ikon), sonner (toast), recharts (grafik, Admin), @tanstack/react-table (tablo, Admin)

Klasör yapısı

lib/axios.ts — merkezi HTTP client

Tek bir Axios instance (baseURL = import.meta.env.VITE_API_BASE_URL, timeout: 30s) iki interceptor ile gelir. Request interceptor — her isteğe X-App-Id header’ı (VITE_APP_ID) ve Keycloak Bearer token’ı ekler. Token 30 saniyeden kısa sürede expire olacaksa otomatik yeniler:
Refresh başarısız olursa safeKeycloakLogin() çağrılır — bu, 10 saniyelik throttle ile redirect-loop’u önler (yanlış audience/issuer durumunda login → SSO → 401 → login döngüsüne girmeyi engeller). Response interceptor — RFC 7807 (application/problem+json) hatalarını yakalar, status’a göre toast gösterir ve ProblemDetailsError fırlatır:
  • 401 — token reddedildi. Otomatik redirect YOK (loop riski); kullanıcıya “Giriş Yap” action butonlu toast.
  • 403 — ProblemDetails’taki reason alanı not_invited veya unknown_realm ise (kullanıcı Keycloak’a girdi ama bu app’te davetli/tanımlı değil) “Çıkış Yap” önerili özel mesaj gösterilir. Diğer 403’ler “yetkiniz yok” toast’ı.
  • 422errors alanı varsa toast atılmaz (component inline gösterir).
  • 400 / 404 / 409 / 429 / 5xx — duruma özel toast’lar.
Tüm hatalar bir diagnostic buffer’a (supportContext) kaydedilir — destek modalı snapshot’ında traceId ile görünür.

store/auth.store.ts — Keycloak oturum yönetimi

zustand store, uygulama açılırken bir kez initialize() çalıştırır:
Authenticated ise:
  1. provisionKeycloakSession(keycloak.token) — backend’e token gönderilir; kullanıcı local DB’ye eşlenir (davetli ise Draft/Pending → Active). Bu çağrı olmadan korumalı her endpoint 403 döner (ActiveAccountAuthorizationHandler).
  2. useMeStore.getState().bootstrap()/me çekilir, tüm sayfalar (avatar, profil, role-bazlı menü) buradan okur.
Provision 403 dönerse accessDeniedReason ayarlanır: not_invited / account_blocked / unknown_realm / provision_error / unknown. Roller, hem realm_access.roles hem resource_access[clientId].roles’ten birleştirilir (SuperAdmin/Admin/Staff client-level gelir).

VITE_* ortam değişkenleri

Vite, VITE_ öneki ile başlayan env’leri build sırasında bundle’a gömer:

Dockerfile (iki aşamalı)

node:22-alpine ile build, nginx:1.27-alpine ile serve:
Admin SPA prod’da /admin/ altında serve edilir. VITE_BASE_PATH=/admin/ ile build edilir; vite.config.ts bunu base olarak okur ve nginx BASE_PATH env’iyle template’i render eder. Tüm Keycloak redirect URI’ları origin + base path kullanır — aksi halde logout sonrası kullanıcı website’a düşer ve silent-check-sso.html 404 olur.

npm script’leri

Sonraki adımlar

OpenAPI / client üretimi

npm run generate-api detayları.

Controller'lar

SPA’ların çağırdığı endpoint aileleri.

Keycloak ortamları

Realm ve client yapılandırması.

Dev Docker

SPA’ları container’da çalıştırma.