SASE v2 — Dify.ai Knowledge Base
Platform: sase.tr — Türkiye otomotiv pazarı için VIN/şasi numarası sorgulama + oto parça katalog platformu.
URL: https://sase.tr | Repo: /home/s/ss | Tarih: 2026-02-24
1. Proje Özeti
SASE v2, araç sahiplerinin VIN numarasını girerek aracın markasını, modelini ve parça kataloglarını görüntüleyebildiği abonelik tabanlı bir SaaS platformdur. Kullanıcılar aboneliklerine göre belirli markaların parça kataloglarına erişebilir; ayrıca VIN girmeden doğrudan marka → model → parça kategorisi şeklinde katalog gezinebilir (VIN-less catalog browser). Platform Türkçe araçlara ve Türkiye pazarına odaklanmıştır.
Temel Özellikler:
- VIN decode: 17 karakterlik VIN numarasından araç bilgisi ve parça kategorileri
- İnteraktif şema görüntüleyici: hotspot'lı parça diyagramları
- OEM kod arama ve kopyalama
- VIN-less katalog tarayıcısı (marka → model → kategori)
- Abonelik yönetimi: marka bazlı erişim kontrolü
- Stripe kart + EFT ödeme yöntemleri
- Referral sistemi
2. Tech Stack
Backend
| Katman |
Teknoloji |
Versiyon |
| Framework |
NestJS |
10.4 |
| Dil |
TypeScript |
5.7 |
| Runtime |
Node.js |
22 |
| Veritabanı |
PostgreSQL |
17 |
| ORM |
Drizzle ORM |
0.41 |
| Cache |
Redis |
7.4 |
| Redis İstemci |
ioredis |
5.4 |
| Job Queue |
BullMQ |
5.30 |
| Auth |
Better Auth |
1.2 |
| Dosya Depolama |
MinIO (S3) |
— |
| E-posta |
Postal |
— |
| Ödeme |
Stripe |
— |
| Browser Otomasyon |
Playwright |
1.50 |
| HTTP İstemci |
undici |
7.22 |
| Rate Limiting |
@nestjs/throttler |
6.3 |
| Güvenlik Header |
Helmet |
8.1 |
| Observability |
OpenTelemetry |
0.212 |
Frontend
| Katman |
Teknoloji |
Versiyon |
| Build Tool |
Vite |
6.3 |
| UI Kütüphanesi |
React |
19 |
| Router |
TanStack Router |
1.120 |
| Server State |
TanStack Query |
5 |
| Client State |
Zustand |
5 |
| Stil |
Tailwind CSS |
4 |
| Bileşen Kütüphanesi |
shadcn/ui (Radix) |
— |
| İkonlar |
lucide-react |
0.468 |
| Toast |
sileo |
0.0.7 |
| Video |
Remotion |
4.x |
| Analytics |
PostHog |
latest |
| RUM |
Grafana Faro |
2.2 |
Genel
| Araç |
Teknoloji |
| Monorepo |
pnpm 10.29 + Turborepo 2 |
| Linting/Format |
Biome (2-space, double quotes, semicolons, trailing commas, 100-char) |
| Test |
Vitest 3, Playwright 1.50 |
| CI/CD |
GitHub Actions → SSH deploy → PM2 |
| Doğrulama |
Zod 3.24 |
3. Proje Yapısı
4. Veritabanı Şeması
ORM: Drizzle ORM 0.41 + PostgreSQL 17
Kural: Tablo/kolon isimleri snake_case, TypeScript'te camelCase.
4.1 Core Tablolar (apps/api/src/database/schema/core.ts)
users
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK, default random |
| name |
varchar(255) |
NOT NULL |
| email |
varchar(255) |
NOT NULL, UNIQUE |
| emailVerified |
boolean |
default false |
| image |
text |
nullable |
| role |
varchar(20) |
default "user" → "user" | "admin" |
| referralCode |
varchar(20) |
UNIQUE, nullable |
| referredBy |
uuid |
FK → users.id, nullable |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
Index'ler: users_email_idx (unique), users_referral_code_idx (unique)
sessions (Better Auth)
| Kolon |
Tip |
Kısıt |
| id |
text |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| token |
text |
NOT NULL, UNIQUE |
| expiresAt |
timestamptz |
NOT NULL |
| ipAddress |
varchar(45) |
nullable |
| userAgent |
text |
nullable |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
Index'ler: sessions_token_idx (unique)
accounts (Better Auth OAuth)
| Kolon |
Tip |
Kısıt |
| id |
text |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| accountId |
text |
NOT NULL |
| providerId |
varchar(50) |
NOT NULL → "google", "email", vb. |
| accessToken |
text |
nullable |
| refreshToken |
text |
nullable |
| accessTokenExpiresAt |
timestamptz |
nullable |
| refreshTokenExpiresAt |
timestamptz |
nullable |
| scope |
text |
nullable |
| idToken |
text |
nullable |
| password |
text |
nullable |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
Index'ler: accounts_user_id_idx
verifications (Better Auth e-posta doğrulama)
| Kolon |
Tip |
Kısıt |
| id |
text |
PK |
| identifier |
text |
NOT NULL |
| value |
text |
NOT NULL |
| expiresAt |
timestamptz |
NOT NULL |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
brands
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| name |
varchar(100) |
NOT NULL |
| slug |
varchar(100) |
NOT NULL, UNIQUE |
| logoUrl |
text |
nullable |
| isActive |
boolean |
default true |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
plans
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| name |
varchar(100) |
NOT NULL → "1 Marka", "2 Marka", "3 Marka", "Full Paket" |
| brandCount |
integer |
NOT NULL → 0 = sınırsız |
| priceMonthly |
integer |
NOT NULL (kuruş cinsinden: 20000 = 200 TRY) |
| priceYearly |
integer |
NOT NULL (kuruş cinsinden) |
| isActive |
boolean |
default true |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
userSubscriptions
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| planId |
uuid |
NOT NULL, FK → plans.id |
| status |
varchar(20) |
default "pending" → "pending" | "active" | "cancelled" | "expired" |
| billingPeriod |
varchar(10) |
default "monthly" → "monthly" | "yearly" |
| startDate |
timestamptz |
nullable |
| endDate |
timestamptz |
nullable |
| cancelledAt |
timestamptz |
nullable |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
Index'ler: user_subscriptions_user_id_idx, user_subscriptions_status_idx
userBrands (Abonelik ↔ Marka junction tablosu)
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| subscriptionId |
uuid |
NOT NULL, FK → userSubscriptions.id CASCADE DELETE |
| brandId |
uuid |
NOT NULL, FK → brands.id |
| createdAt |
timestamptz |
default now() |
Index'ler: user_brands_user_id_idx, user_brands_unique_idx (userId, subscriptionId, brandId — UNIQUE)
payments
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| subscriptionId |
uuid |
NOT NULL, FK → userSubscriptions.id |
| amount |
integer |
NOT NULL (kuruş) |
| currency |
varchar(3) |
default "TRY" |
| method |
varchar(20) |
NOT NULL → "stripe" | "eft" |
| status |
varchar(20) |
default "pending" → "pending" | "completed" | "failed" | "refunded" |
| stripePaymentIntentId |
text |
nullable |
| eftReceiptUrl |
text |
nullable |
| adminNote |
text |
nullable |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
Index'ler: payments_user_id_idx, payments_status_idx
vehicles (VIN tabanlı araç konfigürasyonu)
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| vin |
varchar(17) |
NOT NULL, UNIQUE |
| brandId |
uuid |
FK → brands.id, nullable |
| brandName |
varchar(100) |
nullable |
| model |
varchar(255) |
nullable |
| year |
integer |
nullable |
| engine |
varchar(255) |
nullable |
| transmission |
varchar(100) |
nullable |
| bodyType |
varchar(100) |
nullable |
| market |
varchar(100) |
nullable |
| rawData |
jsonb |
nullable |
| source |
varchar(20) |
default "pl24" → "pl24" | "emex" | "corgi" | "vin-api" |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
Index'ler: vehicles_vin_unique_idx (unique)
userVehicles (Kullanıcı ↔ Araç junction)
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| vehicleId |
uuid |
NOT NULL, FK → vehicles.id CASCADE DELETE |
| createdAt |
timestamptz |
default now() |
| lastAccessedAt |
timestamptz |
default now() |
Index'ler: user_vehicles_user_vehicle_idx (unique), user_vehicles_user_id_idx
catalogVehicles (VIN-less katalog tarama — PL24 araç kaydı)
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| source |
varchar(20) |
default "pl24" |
| serviceName |
varchar(100) |
NOT NULL (pl24 servis adı, ör. "vw_parts") |
| brandName |
varchar(100) |
NOT NULL |
| brandId |
uuid |
FK → brands.id, nullable |
| model |
varchar(255) |
NOT NULL |
| year |
varchar(50) |
nullable |
| engine |
varchar(255) |
nullable |
| bodyType |
varchar(100) |
nullable |
| transmission |
varchar(100) |
nullable |
| market |
varchar(100) |
nullable |
| serviceVehicleId |
varchar(255) |
NOT NULL (PL24'teki araç ID'si) |
| catalogPath |
text |
nullable (PL24 catalog URL yolu) |
| architecture |
varchar(30) |
nullable → "P5_MODERN" | "P4_LEGACY" |
| metadata |
jsonb |
nullable |
| categoriesFetched |
boolean |
default false |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
Index'ler: catalog_vehicles_source_vid_idx (serviceName, serviceVehicleId — UNIQUE), catalog_vehicles_brand_name_idx, catalog_vehicles_service_name_idx
categories
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| vehicleId |
uuid |
FK → vehicles.id CASCADE DELETE, nullable (VIN tabanlı) |
| catalogVehicleId |
uuid |
FK → catalogVehicles.id CASCADE DELETE, nullable (VIN-less) |
| name |
varchar(500) |
NOT NULL |
| nameOriginal |
varchar(500) |
nullable |
| parentId |
uuid |
nullable (self-referencing hiyerarşi için) |
| externalId |
varchar(500) |
nullable (PL24/EMEX'teki ID) |
| linkPath |
text |
nullable (PL24 API yolu) |
| linkWid |
varchar(100) |
nullable |
| unavailable |
boolean |
default false |
| source |
varchar(20) |
default "pl24" → "pl24" | "emex" | "parts-catalogs" |
| createdAt |
timestamptz |
default now() |
Index'ler: categories_vehicle_id_idx, categories_catalog_vehicle_id_idx, categories_parent_id_idx, categories_vehicle_name_source_idx (vehicleId, catalogVehicleId, name, source — UNIQUE)
parts
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| vehicleId |
uuid |
FK → vehicles.id CASCADE DELETE, nullable |
| catalogVehicleId |
uuid |
FK → catalogVehicles.id CASCADE DELETE, nullable |
| categoryId |
uuid |
NOT NULL, FK → categories.id CASCADE DELETE |
| oemCode |
varchar(100) |
NOT NULL |
| name |
varchar(500) |
NOT NULL |
| nameOriginal |
varchar(500) |
nullable |
| description |
text |
nullable |
| quantity |
integer |
nullable |
| position |
varchar(100) |
nullable |
| hotspotIndex |
integer |
nullable |
| unavailable |
boolean |
default false |
| remark |
text |
nullable |
| modelCodes |
varchar(500) |
nullable |
| presel |
boolean |
default false |
| source |
varchar(20) |
default "pl24" → "pl24" | "emex" |
| createdAt |
timestamptz |
default now() |
Index'ler: parts_vehicle_id_idx, parts_catalog_vehicle_id_idx, parts_category_id_idx, parts_oem_code_idx
schemaPics
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| categoryId |
uuid |
NOT NULL, FK → categories.id CASCADE DELETE |
| imageUrl |
text |
NOT NULL |
| hotspots |
jsonb |
default "[]" |
| source |
varchar(20) |
default "pl24" |
| createdAt |
timestamptz |
default now() |
Index'ler: schema_pics_category_id_idx
queryLogs
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| vin |
varchar(17) |
NOT NULL |
| brandId |
uuid |
FK → brands.id, nullable |
| source |
varchar(20) |
nullable |
| success |
boolean |
default true |
| errorMessage |
text |
nullable |
| responseTimeMs |
integer |
nullable |
| createdAt |
timestamptz |
default now() |
Index'ler: query_logs_user_id_created_at_idx, query_logs_vin_idx
oemCodeCopies
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| oemCode |
varchar(100) |
NOT NULL |
| partId |
uuid |
nullable |
| vehicleId |
uuid |
nullable |
| categoryId |
uuid |
nullable |
| createdAt |
timestamptz |
default now() |
Index'ler: oem_code_copies_user_id_created_at_idx, oem_code_copies_oem_code_idx, oem_code_copies_created_at_idx
passwordResetTokens
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| userId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| token |
text |
NOT NULL, UNIQUE |
| expiresAt |
timestamptz |
NOT NULL |
| usedAt |
timestamptz |
nullable |
| createdAt |
timestamptz |
default now() |
referrals
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| referrerId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE |
| referredId |
uuid |
NOT NULL, FK → users.id CASCADE DELETE, UNIQUE |
| rewardApplied |
boolean |
default false |
| createdAt |
timestamptz |
default now() |
Index'ler: referrals_referrer_id_idx, referrals_referred_id_idx (unique)
emexCategoryTranslations
| Kolon |
Tip |
Kısıt |
| id |
uuid |
PK |
| originalName |
varchar(500) |
NOT NULL, UNIQUE |
| translatedName |
varchar(500) |
NOT NULL |
| isManual |
boolean |
default false |
| createdAt |
timestamptz |
default now() |
| updatedAt |
timestamptz |
default now() |
4.2 PL24 Tablolar (apps/api/src/database/schema/pl24.ts)
PL24 katalog verileri için önbellek tabloları.
| Tablo |
Amaç |
Önemli Kolonlar |
pl24Catalogs |
PL24 katalog kaydı |
catalogId (unique), brandName |
pl24Vehicles |
Araç konfigürasyonu |
vehicleId (unique), catalogId, modelCode, engine, yearFrom, yearTo |
pl24VehicleVins |
VIN ↔ pl24Vehicle eşleştirme |
pl24VehicleId, vin |
pl24PartGroups |
Parça grupları |
pl24VehicleId, groupId, name, parentGroupId, sortOrder |
pl24Parts |
Parçalar |
pl24VehicleId, groupId, partId, name, quantity, hotspotIndex |
pl24PartNumbers |
OEM kodları |
pl24PartId, oemCode, isMain |
pl24VehicleGroups |
Araç grupları |
catalogId, groupId, name, parentGroupId |
pl24VehicleParts |
Araç-parça junction |
pl24VehicleId, pl24PartId, fitmentInfo |
pl24SchemaPics |
Şema görselleri |
groupId, imageUrl, hotspots (jsonb), width, height |
4.3 EMEX Tablolar (apps/api/src/database/schema/emex.ts)
EMEX scraper verileri için önbellek tabloları.
| Tablo |
Amaç |
Önemli Kolonlar |
emexCatalogs |
EMEX katalog kaydı |
catalogId (unique), brandName |
emexVehicles |
Araç konfigürasyonu |
vehicleId (unique), catalogId, engine, yearFrom, yearTo |
emexVehicleVins |
VIN ↔ emexVehicle |
emexVehicleId, vin |
emexPartGroups |
Parça grupları |
emexVehicleId, groupId, name, nameOriginal, parentGroupId |
emexParts |
Parçalar |
emexVehicleId, groupId, name, nameOriginal, quantity, hotspotIndex |
emexPartNumbers |
OEM kodları |
emexPartId, oemCode, isMain |
emexVehicleGroups |
Araç grupları |
catalogId, groupId, name, parentGroupId |
emexVehicleParts |
Araç-parça junction |
emexVehicleId, emexPartId, fitmentInfo |
emexSchemaPics |
Şema görselleri |
groupId, imageUrl, hotspots (jsonb), width, height |
emexPartImages |
Parça görselleri |
emexPartId, imageUrl |
emexScrapeSessions |
Scrape oturumları |
vin, status (pending|completed|failed), jobId, result, errorMessage |
4.4 Drizzle İlişkiler (relations.ts)
5. API Endpoint Listesi
Global prefix: /api | Auth: Better Auth cookie session | Rate limit: 100 req/15dk varsayılan
5.1 Auth (/api/auth/**)
| Method |
Path |
Auth |
Açıklama |
* |
/api/auth/** |
Public |
Better Auth catch-all (sign-in, sign-up, sign-out, OAuth, session, forgot-password, reset-password) |
Better Auth dahili route'ları: POST /api/auth/sign-in/email, POST /api/auth/sign-up/email, POST /api/auth/sign-out, GET /api/auth/session, GET /api/auth/sign-in/google, POST /api/auth/reset-password, POST /api/auth/forget-password
5.2 Users (/api/users)
| Method |
Path |
Auth |
Açıklama |
Body / Query |
POST |
/users/check-email |
Public |
E-posta var mı kontrol |
{ email } |
GET |
/users/me |
Protected |
Profil bilgisi |
— |
PATCH |
/users/me |
Protected |
Profil güncelle |
{ name?, image? } |
GET |
/users/me/connections |
Protected |
OAuth bağlantıları |
— |
DELETE |
/users/me/connections/:provider |
Protected |
OAuth bağlantısı kaldır |
— |
POST |
/users/me/change-password |
Protected |
Şifre değiştir |
{ currentPassword, newPassword } |
DELETE |
/users/me |
Protected |
Hesap sil |
— |
GET |
/users |
Admin |
Kullanıcı listesi |
?page, ?limit |
GET |
/users/:id |
Admin |
Kullanıcı detayı |
— |
5.3 Brands (/api/brands)
| Method |
Path |
Auth |
Açıklama |
Body |
GET |
/brands |
Public |
Marka listesi (30dk cache) |
— |
POST |
/brands |
Admin |
Marka oluştur |
{ name, slug, logoUrl? } |
PATCH |
/brands/:id |
Admin |
Marka güncelle |
{ name?, slug?, logoUrl?, isActive? } |
5.4 Plans (/api/plans)
| Method |
Path |
Auth |
Açıklama |
Body |
GET |
/plans |
Public |
Plan listesi (cache'li) |
— |
POST |
/plans |
Admin |
Plan oluştur |
{ name, brandCount, priceMonthly, priceYearly } |
PATCH |
/plans/:id |
Admin |
Plan güncelle |
{ name?, brandCount?, priceMonthly?, priceYearly?, isActive? } |
5.5 Subscriptions (/api/subscriptions)
| Method |
Path |
Auth |
Açıklama |
Body / Query |
POST |
/subscriptions |
Protected |
Abonelik oluştur |
{ planId, brandIds[], billingPeriod } |
GET |
/subscriptions/me |
Protected |
Aktif abonelik |
— |
POST |
/subscriptions/trial |
Protected |
Trial başlat |
— |
PATCH |
/subscriptions/cancel |
Protected |
Abonelik iptal |
— |
PATCH |
/subscriptions/resume |
Protected |
Abonelik yeniden başlat |
— |
GET |
/subscriptions |
Admin |
Tüm abonelikler |
?page, ?limit |
5.6 Payments (/api/payments)
| Method |
Path |
Auth |
Açıklama |
Body |
POST |
/payments/stripe/checkout |
Protected |
Stripe ödeme başlat |
{ planKey, billingPeriod, brandIds[] } |
POST |
/payments/stripe/webhook |
Public |
Stripe webhook |
{ type, data } |
POST |
/payments/eft |
Protected |
EFT ödeme oluştur |
{ planKey, billingPeriod, brandIds[] } |
POST |
/payments/eft/:id/receipt |
Protected |
EFT makbuz yükle (multipart, PNG/JPG/PDF max 5MB) |
file |
PATCH |
/payments/eft/:id/approve |
Admin |
EFT ödeme onayla |
{ adminNote? } |
PATCH |
/payments/eft/:id/reject |
Admin |
EFT ödeme reddet |
{ adminNote? } |
GET |
/payments/me |
Protected |
Ödeme geçmişi |
— |
GET |
/payments/pending |
Admin |
Bekleyen EFT ödemeleri |
— |
5.7 Referrals (/api/referrals)
| Method |
Path |
Auth |
Açıklama |
Body |
GET |
/referrals/stats |
Protected |
Referral istatistikleri |
— |
GET |
/referrals/me |
Protected |
Referral kodumu + yönlendirdiklerim |
— |
POST |
/referrals/apply |
Protected |
Referral kodu uygula |
{ code } |
5.8 Vehicles (/api/vehicles)
| Method |
Path |
Auth |
Açıklama |
Body / Query |
GET |
/vehicles/preview/:vin |
Public |
VIN önizleme (hızlı) |
— |
POST |
/vehicles/decode |
Protected |
Tam VIN decode |
{ vin, pcatCarId? } |
GET |
/vehicles/history |
Protected |
VIN arama geçmişi |
?page, ?limit |
POST |
/vehicles/report-vin |
Protected |
Bilinmeyen VIN bildir |
{ vin } |
GET |
/vehicles/:vehicleId/prefetch-status |
Protected |
Prefetch job durumu |
— |
GET |
/vehicles/:vehicleId/categories/:categoryId |
Protected |
Araç kategorisi parçaları |
— |
GET |
/vehicles/:id |
Protected |
Araç detayı |
— |
DELETE |
/vehicles/:id |
Protected |
Araç sil |
— |
5.9 Categories (/api/categories)
| Method |
Path |
Auth |
Açıklama |
GET |
/categories/tree/:vehicleId |
Protected |
Araç için kategori ağacı (cache'li) |
GET |
/categories/:id/children |
Protected |
Alt kategoriler |
GET |
/categories/:id |
Protected |
Kategori detayı |
5.10 Parts (/api/parts)
| Method |
Path |
Auth |
Açıklama |
Query |
GET |
/parts/category/:categoryId |
Protected |
Kategoriye göre parçalar |
— |
GET |
/parts/search |
Protected |
OEM koduna göre ara |
?oem |
GET |
/parts/:id |
Protected |
Parça detayı |
— |
5.11 Catalog — VIN-less Tarayıcı (/api/catalog)
| Method |
Path |
Auth |
Açıklama |
GET |
/catalog/brands |
Protected |
Katalog marka listesi (hasAccess flag'li) |
GET |
/catalog/brands/:brandName/models |
Protected |
Marka model listesi (DB önbellekten, yoksa PL24'ten) |
GET |
/catalog/vehicles/:id |
Protected |
Katalog araç detayı |
GET |
/catalog/vehicles/:id/categories |
Protected |
Katalog araç kategori ağacı (Redis 2h cache) |
GET |
/catalog/vehicles/:id/categories/:categoryId |
Protected |
Alt kategori veya parça+şema (lazy PL24 fetch) |
POST |
/catalog/explore/:serviceName |
Admin |
PL24 servis yapısı keşfi |
5.12 Translations (/api/translations)
| Method |
Path |
Auth |
Açıklama |
Body / Query |
GET |
/translations/:key |
Public |
Tek çeviri |
— |
POST |
/translations/batch |
Protected |
Toplu çeviri |
{ items: [{ key, sourceText }] } |
PUT |
/translations/:key |
Admin |
Çeviri ayarla/güncelle |
{ sourceText, translatedText } |
GET |
/translations/search |
Admin |
Çeviri ara |
?q |
5.13 Analytics (/api/analytics)
| Method |
Path |
Auth |
Açıklama |
Body |
POST |
/analytics/oem-copy |
Protected |
OEM kod kopyalama takibi |
{ oemCode, partId?, vehicleId?, categoryId? } |
5.14 Admin (/api/admin) — Tüm endpoint'ler Admin gerektirir
| Method |
Path |
Açıklama |
Query |
POST |
/admin/users |
Kullanıcı oluştur |
Body: { name, email, password, role? } |
GET |
/admin/dashboard |
Dashboard istatistikleri |
— |
GET |
/admin/users |
Kullanıcı listesi + arama |
?search, ?page, ?limit |
GET |
/admin/users/:id |
Kullanıcı detayı (abonelik + ödeme dahil) |
— |
GET |
/admin/payments/pending |
Bekleyen EFT ödemeleri |
— |
GET |
/admin/query-logs |
Query aktivite logları |
?page, ?limit, ?userId |
GET |
/admin/referrals |
Referral listesi |
?search, ?page, ?limit |
GET |
/admin/stats/daily |
Günlük istatistikler |
— |
GET |
/admin/copy-logs |
OEM kopyalama logları |
?page, ?limit, ?userId |
GET |
/admin/copy-logs/top |
En çok kopyalanan OEM kodları |
?days (def 30), ?limit (def 20) |
5.15 Health (/api/health)
| Method |
Path |
Auth |
Açıklama |
GET |
/health |
Public |
Sistem sağlık kontrolü: DB + Redis + telemetry |
6. Auth & Güvenlik Mimarisi
Better Auth Akışı
Guard Sırası (Global)
ThrottlerGuard → AuthGuard → RolesGuard
Decorator'lar
| Decorator |
Kullanım |
Etki |
@Public() |
Endpoint üstüne |
AuthGuard'ı atlar, anonim erişime izin verir |
@CurrentUser(field?) |
Parametre üstüne |
Kimliği doğrulanmış kullanıcıyı (veya alanını) enjekte eder |
@Roles(...roles) |
Endpoint üstüne |
RolesGuard tetikler, belirtilen rolü gerektirir |
@ThrottleAuth() |
Endpoint üstüne |
5 istek/dakika (giriş işlemleri için) |
@ThrottleVinDecode() |
Endpoint üstüne |
20 istek/dakika (VIN decode) |
@ThrottleGeneral() |
Endpoint üstüne |
100 istek/dakika (varsayılan) |
Interceptor'lar (Global)
| Interceptor |
Etki |
TransformInterceptor |
Tüm başarılı yanıtları sarar: { success: true, data: T, meta?: PaginationMeta } |
LoggingInterceptor |
İstek/yanıt loglar (method, path, status, süre) |
TimeoutInterceptor |
Tüm isteklerde 30 saniye zaman aşımı |
Filtreler (Global)
| Filtre |
Etki |
HttpExceptionFilter |
{ success: false, error: { code, message } } döner |
DrizzleExceptionFilter |
Unique constraint ihlalini → 409 Conflict'e dönüştürür |
Response Format
Başarılı:
Hata:
Hata Kodları
| Kod |
Prefix |
Açıklama |
| AUTH_001 |
Auth |
Geçersiz kimlik bilgileri |
| AUTH_002 |
Auth |
E-posta zaten mevcut |
| AUTH_003 |
Auth |
Oturum süresi dolmuş |
| AUTH_004 |
Auth |
Yetkisiz |
| AUTH_005 |
Auth |
Yasak |
| VIN_001 |
VIN |
Geçersiz VIN |
| VIN_002 |
VIN |
VIN decode başarısız |
| VIN_003 |
VIN |
Marka desteklenmiyor |
| SUB_001 |
Abonelik |
Aktif abonelik yok |
| SUB_002 |
Abonelik |
Marka erişimi reddedildi |
| SUB_003 |
Abonelik |
Geçersiz marka sayısı |
| SUB_004 |
Abonelik |
Abonelik zaten aktif |
| PAY_001 |
Ödeme |
Ödeme başarısız |
| PAY_002 |
Ödeme |
Stripe hatası |
| PAY_003 |
Ödeme |
EFT makbuzu gerekli |
| PAY_004 |
Ödeme |
Ödeme zaten işlenmiş |
| GEN_001 |
Genel |
Bulunamadı |
| GEN_002 |
Genel |
Doğrulama hatası |
| GEN_003 |
Genel |
Dahili hata |
| GEN_004 |
Genel |
Hız sınırı aşıldı |
| GEN_005 |
Genel |
Çakışma |
7. İş Mantığı & Abonelik Modeli
Planlar
| Plan |
Marka Sayısı |
Aylık |
Yıllık |
| 1 Marka |
1 |
200 TRY |
2.000 TRY |
| 2 Marka |
2 |
350 TRY |
3.500 TRY |
| 3 Marka |
3 |
500 TRY |
5.000 TRY |
| Full Paket |
Sınırsız (brandCount=0) |
999 TRY |
9.990 TRY |
Not: Fiyatlar veritabanında kuruş cinsinden saklanır (200 TRY = 20000 kuruş).
Abonelik Durum Akışı
Marka Erişim Kontrolü
brandCount = 0 → Full Paket, tüm markalara erişim
brandCount > 0 → userBrands tablosunda kayıtlı markalara erişim
BrandAccessGuard: VIN decode sırasında kullanıcının o markaya erişimi olup olmadığını kontrol eder
Referral Sistemi
| Tier |
Gerekli Referral |
Ödül |
| Tier 1 |
3 referral |
7 gün abonelik uzatması |
| Tier 2 |
5 referral |
30 gün abonelik uzatması |
Ödeme Akışı
Stripe (Kart):
POST /payments/stripe/checkout → Stripe Checkout Session oluşturulur
- Kullanıcı Stripe ödeme sayfasına yönlendirilir
POST /payments/stripe/webhook webhook'u aboneliği aktifleştirir
EFT (Havale):
POST /payments/eft → EFT kaydı oluşturulur, banka bilgileri gösterilir
- Kullanıcı makbuz yükler:
POST /payments/eft/:id/receipt
- Admin onaylar:
PATCH /payments/eft/:id/approve → abonelik aktifleşir
8. VIN Decode Zinciri
Fallback Zinciri
VIN Yapısı
WMI → Servis Eşleşmesi (Örnekler)
| WMI Prefix |
Marka |
PL24 Servis |
| WVW, 3VW, 9BW, 1VW |
Volkswagen |
vw_parts (P5_MODERN) |
| WAU, TRU |
Audi |
audi_parts (P5_MODERN) |
| WBA, WBS |
BMW |
bmw_parts (P5_MODERN) |
| WDB, WDC |
Mercedes-Benz |
mercedes_parts (P5_MODERN) |
| WF0 |
Ford (Avrupa) |
fordp_parts (P4_LEGACY) |
| NM0 |
Ford (Ticari) |
fordt_parts (P4_LEGACY) |
| VF7 |
Citroën |
citroen_parts (P4_LEGACY) |
| VF3 |
Peugeot |
peugeot_parts (P4_LEGACY) |
| KMH |
Hyundai |
hyundai_parts (P4_LEGACY) |
| KNA, U5Y |
Kia |
kia_parts (P4_LEGACY) |
| W0L |
Opel |
opel_parts (P4_LEGACY) |
| YV1, YV4 |
Volvo |
volvo_parts (P4_LEGACY) |
| JN1, JN6, JN8, VNK |
Nissan |
nissan_parts (P4_LEGACY) |
9. Katalog Entegrasyonları
9.1 Corgi (Offline WMI Decoder)
- Dosya:
apps/api/src/integrations/corgi/
- Tip: Statik lookup tablosu — harici API çağrısı yok
- Amaç: VIN'in ilk 3 karakterinden (WMI) marka ve üretim yılı tespiti
- 60+ üretici: BMW, Mercedes, VW, Toyota, Ford, Nissan, Hyundai, vb.
- Metotlar:
decodeVin(vin), getBrandFromWmi(wmi), getModelYear(vin)
9.2 PL24 (Partslink24)
- Dosya:
apps/api/src/integrations/pl24/
- Amaç: Ana katalog API kaynağı
- Hesabımız: tr-903645 — yalnızca VAG grubu (VW, Audi, Skoda, SEAT, Porsche, Bentley, Cupra)
P5 Modern (JSON API):
- VAG grubu, BMW, Mercedes, Renault, Toyota, JLR, MAN, Mitsubishi, Suzuki
- Endpoint:
/{service}/extern/vehicle/modelfamilies?lang=tr&serviceName={service}
- Auth: JWT token via
PL24AuthService
- Cache: Redis 24s araç decode, 1s parçalar
P4 Legacy (HTML/JSP scraping):
- Ford, Citroën, Peugeot, Hyundai, Kia, Nissan, Infiniti, Opel, Volvo, Polestar
- REST API yok → HTML sayfaları regex ile parse edilir
PL24FordLegacyService.decodeVinForService(vin, serviceName)
- VIN decode URL:
{basePath}/{service}/vin-group.action?vin={vin}&lang=tr
Servis Sınıfları:
PL24Service — P5 Modern handler
PL24FordLegacyService — P4 Legacy HTML scraper
PL24AuthService — JWT yönetimi + oturum çerezleri
9.3 PartsCatalogs
- Dosya:
apps/api/src/integrations/parts-catalogs/
- Amaç: Evrensel kapsama (tüm markalar)
- Tip: REST API v1
- Auth: DataImpulse proxy üzerinden JWT (IP bound)
- Proxy: undici ProxyAgent (PCAT_PROXY_HOST=gw.dataimpulse.com)
- Metotlar:
decodeVin(vin) (tek veya çoklu eşleşme), fetchGroups(catalogId, carId, groupId?), fetchParts(catalogId, carId, groupId)
9.4 EMEX
- Dosya:
apps/api/src/integrations/emex/
- Amaç: Orta Doğu odaklı (emexdwc.ae), Playwright tabanlı web scraper
- Tip: Browser otomasyon (BullMQ kuyruğu üzerinden asenkron)
- Browser pool: Tekil browser örneği, sayfa havuzu — her istekte yeni browser başlatılmaz
- Cache: Redis 5dk TTL (activity touch)
- Timeout: 60 saniye
- Sınıflar:
EmexService, EmexBrowserService, EmexMapper
9.5 NHTSA VIN API
- Dosya:
apps/api/src/integrations/vin-api/
- Amaç: Son çare — ABD VIN veritabanı
- API:
https://vpic.nhtsa.dot.gov/api/vehicles/decodevinvalues/{vin}
- Zaman aşımı: 10 saniye
- Dönen: make, model, modelYear, bodyClass, engineModel, transmissionStyle, plantCountry
10. Job Queues (BullMQ + Redis)
Kuyruk Listesi
| Kuyruk Adı |
Tetikleyici |
Zamanlama |
İşlem |
EMEX_SCRAPE |
On-demand (manuel/admin) |
— |
EMEX araç verisi scrape et |
CATALOG_PREFETCH |
VIN decode sonrası otomatik |
— |
Tüm kaynaklardan kategori+parça prefetch (derinlik sınırlı, cooldown korumalı) |
SUBSCRIPTION_EXPIRY |
Cron |
Günlük 03:00 |
Süresi dolmuş abonelikleri expire et, marka erişimini kaldır |
QUERY_CLEANUP |
Cron |
Haftalık Pazar 04:00 |
Eski query log kayıtlarını sil (90 gün saklama) |
İşleyiciler
CATALOG_PREFETCH:
PrefetchWorkerService tarafından işlenir
- İş verisi:
{ vehicleId, serviceName?, depth? }
- Deneme: 3, geri çekilme: 30s eksponansiyel
- Aktivite penceresinde (5dk) EMEX veya PartsCatalogs kullanımdaysa erteliyor
SUBSCRIPTION_EXPIRY:
status: "active" + endDate < now() olan abonelikleri bulur
status: "expired" olarak günceller
- İlişkili
userBrands kayıtlarını siler
QUERY_CLEANUP:
queryLogs tablosundan 90 günden eski kayıtları siler
EMEX_SCRAPE:
- İş verisi:
{ vin, userId, type, emexVehicleId?, groupId? }
emexScrapeSessions tablosunu günceller
11. Frontend Mimarisi
Router
- Tip: TanStack Router 1.120 (file-based)
routeTree.gen.ts otomatik üretilir → ELLE DÜZENLEME YOK
- Manuel yeniden üretme:
pnpm --filter web exec tsr generate
- Layout grupları:
_auth.tsx (public), dashboard.tsx (protected)
Tüm Route'lar (37 dosya)
Public Route'lar
| Path |
Dosya |
Açıklama |
/ |
routes/index.tsx |
Ana sayfa: hero, özellikler, video demo (Remotion), fiyatlandırma CTA, marka carousel |
/pricing |
routes/pricing.tsx |
Plan karşılaştırma: aylık/yıllık toggle |
/about |
routes/about.tsx |
Hakkımızda sayfası |
/contact |
routes/contact.tsx |
İletişim sayfası |
/kvkk |
routes/kvkk.tsx |
KVKK sayfası |
/terms |
routes/terms.tsx |
Kullanım şartları |
/privacy |
routes/privacy.tsx |
Gizlilik politikası |
/blog |
routes/blog.tsx |
Blog listesi |
/blog/$slug |
routes/blog_/$slug.tsx |
Blog yazısı |
/demo |
routes/demo.tsx |
Demo sayfası |
Auth Route'ları (Layout: _auth.tsx)
| Path |
Dosya |
Açıklama |
/login |
routes/_auth/login.tsx |
E-posta/şifre + Google OAuth |
/register |
routes/_auth/register.tsx |
Hesap oluşturma |
/forgot-password |
routes/_auth/forgot-password.tsx |
Şifre sıfırlama isteği |
/reset-password |
routes/_auth/reset-password.tsx |
Token tabanlı şifre sıfırlama |
Dashboard Route'ları (Protected, Layout: dashboard.tsx)
| Path |
Dosya |
Açıklama |
/dashboard |
routes/dashboard/index.tsx |
Dashboard ana sayfası |
/dashboard/search |
routes/dashboard/search.tsx |
VIN arama sayfası |
/dashboard/history |
routes/dashboard/history.tsx |
VIN arama geçmişi |
/dashboard/subscription |
routes/dashboard/subscription/index.tsx |
Plan + marka seçimi |
/dashboard/subscription/pay |
routes/dashboard/subscription/pay.tsx |
Ödeme (kart/EFT) |
/dashboard/billing |
routes/dashboard/billing.tsx |
Ödeme geçmişi |
/dashboard/settings |
routes/dashboard/settings.tsx |
Kullanıcı ayarları (5 sekme) |
/dashboard/vehicles/$id |
routes/dashboard/vehicles_/$id/index.tsx |
Araç detayı |
/dashboard/vehicles/$id/categories/$categoryId |
routes/dashboard/vehicles_/$id/categories_/$categoryId.tsx |
İnteraktif şema + parça tablosu |
Katalog Route'ları (VIN-less, Protected)
| Path |
Dosya |
Açıklama |
/dashboard/catalog |
routes/dashboard/catalog/index.tsx |
Marka grid'i (hasAccess flag'li) |
/dashboard/catalog/$brandName |
routes/dashboard/catalog_/$brandName/index.tsx |
Marka model listesi |
/dashboard/catalog/$brandName/$modelId |
routes/dashboard/catalog_/$brandName_/$modelId/index.tsx |
Araç detayı + kategori ağacı |
/dashboard/catalog/$brandName/$modelId/categories/$categoryId |
routes/dashboard/catalog_/$brandName_/$modelId/categories_/$categoryId.tsx |
Kategori + parça/şema |
Admin Route'ları (role: "admin")
| Path |
Dosya |
Açıklama |
/dashboard/admin |
routes/dashboard/admin/index.tsx |
Admin genel bakış |
/dashboard/admin/users |
routes/dashboard/admin/users.tsx |
Kullanıcı yönetimi |
/dashboard/admin/payments |
routes/dashboard/admin/payments.tsx |
EFT onay iş akışı |
/dashboard/admin/analytics |
routes/dashboard/admin/analytics.tsx |
Günlük sorgu istatistikleri |
/dashboard/admin/copy-logs |
routes/dashboard/admin/copy-logs.tsx |
OEM kod kopyalama takibi |
/dashboard/admin/referrals |
routes/dashboard/admin/referrals.tsx |
Referral programı takibi |
Bileşenler (13 dosya)
| Bileşen |
Dosya |
Açıklama |
SchemaViewer |
components/schema/schema-viewer.tsx |
İnteraktif parça diyagramı: zoom/pan/fullscreen, hotspot overlay, parça listesi (60/40 split) |
HotspotOverlay |
components/schema/hotspot-overlay.tsx |
Şema görüntüsü üzerinde SVG tıklanabilir bölgeler |
PartsPanel |
components/schema/parts-panel.tsx |
OEM kodlu parça listesi, şema ile senkron |
SchemaToolbar |
components/schema/schema-toolbar.tsx |
Zoom sıfırlama, fullscreen kontrolleri |
BrandSelector |
components/subscription/brand-selector.tsx |
Plan limitli marka çoklu seçimi |
VehicleCard |
components/vehicles/vehicle-card.tsx |
VIN, marka, model, yıl bilgi kartı |
VehicleSelectModal |
components/vehicles/vehicle-select-modal.tsx |
PartsCatalogs çoklu eşleşme için araç seçim modalı |
VinInput |
components/vehicles/vin-input.tsx |
VIN girişi + doğrulama |
CategoryTree |
components/categories/category-tree.tsx |
Ağaç görünümü (genişletilebilir kategori hiyerarşisi) |
CategoryGrid |
components/categories/category-grid.tsx |
Grid görünümü (ikon + şema önizlemeli) |
DailyChart |
components/admin/daily-chart.tsx |
Günlük VIN decode istatistik grafiği |
PaymentContent |
components/payment/payment-content.tsx |
Ödeme formu (kart/EFT), makbuz yükleme, durum takibi |
SettingsContent |
components/settings/settings-content.tsx |
Ayarlar paneli (5 sekme) |
Hooks (4 dosya)
| Hook |
Dosya |
Açıklama |
useAuth() |
hooks/use-auth.ts |
{ user, isLoading, isAuthenticated, isAdmin, signIn, signUp, signOut, session } |
useCategoryParts(vehicleId, categoryId) |
hooks/use-parts.ts |
TanStack Query: şema + parçalar + hotspot'lar |
useSchemaInteraction() |
hooks/use-schema-interaction.ts |
Şema görüntüleyici için zoom/pan/pinch event handler'ları (0.5x–5x) |
usePageMeta(opts) |
hooks/use-page-meta.ts |
<title>, meta description, canonical, OG/Twitter tag'lerini set eder |
Zustand Store'ları (2 dosya)
useAuthStore (stores/auth.store.ts)
useSchemaStore (stores/schema.store.ts)
Lib Yardımcıları (8 dosya)
| Dosya |
Açıklama |
lib/api-client.ts |
ApiClient sınıfı: get/post/patch/delete/upload → /api base URL, cookie credentials, TanStack Query uyumlu |
lib/auth-client.ts |
Better Auth istemcisi: signIn, signUp, signOut, useSession |
lib/i18n.ts |
Zustand tabanlı i18n: t(key), useTranslation(), localStorage kalıcılığı |
lib/user-settings.ts |
localStorage util: getUserSettings(), setUserSetting(key, value) — categoryViewMode, sidebarCollapsed, theme |
lib/posthog.ts |
PostHog: initPostHog(), identifyUser(), resetUser(), capture(event, props) |
lib/faro.ts |
Grafana Faro RUM başlatma ve hata raporlama |
lib/toast.ts |
Sileo toast wrapper: toast.success/error/info/warning(title, opts) |
lib/category-icons.ts |
Kategori adına göre ikon eşleşmesi |
API İstemcisi Kullanımı
Response format: { success: true, data: T } veya hata durumunda ApiError fırlatır.
i18n Namespace Yapısı
| Namespace |
Açıklama |
Örnekler |
common |
Genel UI |
save, cancel, loading, copy, search, monthly, yearly |
nav |
Navigasyon |
search, history, catalog, subscription, billing, settings, admin, logout |
catalog |
Katalog tarayıcı |
title, brands, models, locked, upgradeCta, loadingModels, categories |
auth |
Auth sayfaları |
login, register, email, password, forgotPassword, loginWithGoogle |
subscription |
Abonelik |
choosePlan, subscribe, currentPlan, cancelSubscription, selectBrands, features, plans |
payment |
Ödeme |
creditCard, eftTransfer, bankDetails, uploadReceipt, waitingApproval |
billing |
Fatura |
payments, date, plan, amount, method, status, downloadReceipt |
settings |
Ayarlar |
tabs: profile, security, connections, referral, account |
errors |
Hata mesajları |
generic, network, unauthorized, forbidden, notFound |
language |
Dil seçimi |
tr, en, switchLanguage |
12. Shared Packages
@sase/shared (packages/shared/src/)
TypeScript Tipleri
// Kullanıcı
User { id, name, email, emailVerified, image, role, referralCode, referredBy, createdAt, updatedAt }
UserProfile { id, name, email, image, role, referralCode, subscription: UserSubscriptionSummary | null }
UserSubscriptionSummary { planName, status, brands: string[], expiresAt }
// Araç
Vehicle { id, userId, vin, brandId, brandName, model, year, engine, transmission, bodyType, market, rawData, source, createdAt }
VehicleSource = "pl24" | "emex" | "corgi" | "vin-api"
VinDecodeResult { vehicle: Vehicle, categories: CategoryNode[] }
CategoryNode { id, name, nameOriginal, parentId, children: CategoryNode[], hasSchema }
// Katalog
Category { id, vehicleId, name, nameOriginal, parentId, externalId, source, createdAt }
CategoryWithSchema extends Category { schemaPics: SchemaPic[] }
SchemaPic { id, categoryId, imageUrl, hotspots: Hotspot[], source, createdAt }
Hotspot { index, x, y, width, height, partId, shape, points? }
Part { id, vehicleId, categoryId, oemCode, name, nameOriginal, description, quantity, position, hotspotIndex, source, createdAt }
PartSource = "pl24" | "emex"
PartSearchResult { part: Part, vehicleInfo: { vin, brandName, model, year } }
// Abonelik & Ödeme
Plan { id, name, brandCount, priceMonthly, priceYearly, isActive, createdAt }
Subscription { id, userId, planId, status, startDate, endDate, cancelledAt, createdAt }
SubscriptionStatus = "pending" | "active" | "cancelled" | "expired"
UserBrand { id, userId, subscriptionId, brandId, createdAt }
Payment { id, userId, subscriptionId, amount, currency, method, status, stripePaymentIntentId, eftReceiptUrl, adminNote, createdAt }
PaymentMethod = "stripe" | "eft"
PaymentStatus = "pending" | "completed" | "failed" | "refunded"
CreateSubscriptionInput { planId, brandIds: string[], billingPeriod }
// API Yanıtları
ApiResponse<T> { success: boolean, data: T, meta?: PaginationMeta }
ApiError { success: false, error: { code, message, details? } }
PaginationMeta { page, limit, total, totalPages }
PaginatedResult<T> { items: T[], meta: PaginationMeta }
Zod Şemaları
Sabitler
Util Fonksiyonları
@sase/config (packages/config/src/index.ts)
Zod tabanlı env doğrulama. Uygulama geçersiz env ile başlamaz.
Export'lar: envSchema, Env tipi, validateEnv()
@sase/ui (packages/ui/src/)
shadcn/Radix tabanlı bileşen kütüphanesi.
| Bileşen |
Açıklama |
Button |
CVA varyantları: default, destructive, outline, secondary, ghost, link |
Input |
Temel input alanı |
Card, CardHeader, CardTitle, CardContent, CardDescription, CardFooter |
Kart compound bileşeni |
Badge |
CVA varyantları: default, secondary, destructive, outline |
Label |
Form etiketi |
Skeleton |
Yükleme placeholder |
Separator |
Yatay/dikey ayırıcı |
Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, DialogPortal, DialogOverlay, DialogTrigger |
Modal compound bileşeni |
Tabs, TabsList, TabsTrigger, TabsContent |
Sekme compound bileşeni |
cn(...classes) |
clsx + tailwind-merge utility |
13. Ortam Değişkenleri
Zorunlu
| Değişken |
Açıklama |
DATABASE_URL |
PostgreSQL bağlantı string'i |
REDIS_PASSWORD |
Redis kimlik doğrulama |
BETTER_AUTH_SECRET |
Auth şifreleme anahtarı (min 32 karakter) |
BETTER_AUTH_URL |
Auth servis URL'i (ör. http://localhost:4000) |
MINIO_ENDPOINT |
MinIO/S3 endpoint |
MINIO_ACCESS_KEY |
MinIO erişim anahtarı |
MINIO_SECRET_KEY |
MinIO gizli anahtar |
MINIO_PUBLIC_URL |
Depolanan dosyalar için public URL |
İsteğe Bağlı (Varsayılan Değerleriyle)
| Değişken |
Varsayılan |
Açıklama |
NODE_ENV |
development |
development | production | test |
PORT |
4000 |
API sunucu portu |
REDIS_HOST |
127.0.0.1 |
Redis host |
REDIS_PORT |
6379 |
Redis port |
MINIO_BUCKET_NAME |
sase-schemas |
Storage bucket adı |
MINIO_USE_SSL |
false |
MinIO için HTTPS |
CORS_ORIGIN |
http://localhost:3000 |
İzin verilen origin'ler |
POSTAL_FROM_ADDRESS |
noreply@sase.tr |
Gönderici e-posta |
POSTAL_FROM_NAME |
Sase.tr |
Gönderici adı |
OTEL_ENABLED |
false |
OpenTelemetry etkinleştir |
OTEL_SERVICE_NAME |
sase-api |
OTel servis adı |
OTEL_TRACE_SAMPLE_RATE |
1.0 |
Trace örnekleme oranı (0–1) |
ML_PREDICTION_ENABLED |
false |
ML tahminleri etkinleştir |
PCAT_USE_PROXY |
true |
PartsCatalogs proxy kullan |
PCAT_PROXY_HOST |
gw.dataimpulse.com |
Proxy host |
Koşullu / Servis Bağımlı
| Değişken |
Grup |
Açıklama |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
OAuth |
Google OAuth |
STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY |
Ödeme |
Stripe entegrasyonu |
PL24_BASE_URL, PL24_COMPANY_CODE, PL24_USERNAME, PL24_PASSWORD |
PL24 |
PL24 katalog API |
EMEX_USERNAME, EMEX_PASSWORD |
EMEX |
EMEX scraper |
PCAT_PROXY_USER, PCAT_PROXY_PASS |
PartsCatalogs |
Playwright proxy |
POSTAL_API_URL, POSTAL_API_KEY |
E-posta |
Postal email servisi |
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS |
Observability |
OpenTelemetry exporter |
14. Test & Geliştirme
Test Kimlik Bilgileri
Test Pattern (Backend)
Vitest Konfigürasyonu
- API:
apps/api/vitest.config.ts — src/**/*.spec.ts, v8 coverage
- Web:
apps/web/vitest.config.ts — src/**/*.{test,spec}.{ts,tsx}, jsdom, @/ alias
Test Dosyaları
| Dosya |
Test Edilen |
Satır |
admin.service.spec.ts |
AdminService |
~350 |
brands.service.spec.ts |
BrandsService |
~200 |
categories.service.spec.ts |
CategoriesService |
~250 |
parts.service.spec.ts |
PartsService |
~200 |
payments.service.spec.ts |
PaymentsService |
~350 |
plans.service.spec.ts |
PlansService |
~150 |
referrals.service.spec.ts |
ReferralsService |
~200 |
subscriptions.service.spec.ts |
SubscriptionsService |
~400 |
translations.service.spec.ts |
TranslationsService |
~200 |
users.service.spec.ts |
UsersService |
~250 |
vehicles.service.spec.ts |
VehiclesService |
~250 |
auth.guard.spec.ts |
AuthGuard |
~150 |
roles.guard.spec.ts |
RolesGuard |
~120 |
brand-access.guard.spec.ts |
BrandAccessGuard |
~110 |
vin-validation.pipe.spec.ts |
VinValidationPipe |
~77 |
corgi.service.spec.ts |
CorgiService |
~121 |
telemetry.spec.ts |
OpenTelemetry |
~97 |
15. Deployment & Altyapı
Docker Compose (Geliştirme)
| Servis |
Image |
Port |
Kalıcı Depolama |
| PostgreSQL 17 |
postgres:17-alpine |
127.0.0.1:5432 |
pg_data |
| Redis 7.4 |
redis:7.4-alpine |
127.0.0.1:6379 |
redis_data |
| MinIO |
minio/minio |
9000 (API), 9001 (Console) |
minio_data |
Nginx (docker/nginx/sites/)
| Dosya |
Amaç |
sase.tr.conf |
Frontend SPA proxy + /api yönlendirme + /collect/ Faro CORS proxy + gzip (seviye 6) + 1 yıl asset cache + güvenlik header'ları |
api.sase.tr.conf |
NestJS proxy (60s zaman aşımı VIN decode için) + SSL + .git/.env/node_modules engelleme |
PM2 Süreçleri (ecosystem.config.js)
| Süreç |
Komut |
Port |
Max Bellek |
sase-api |
dist/main.js |
4000 |
512MB |
sase-web |
serve dist |
3000 |
512MB |
sase-worker |
dist/worker.js |
— |
256MB |
CI/CD (GitHub Actions)
ci.yml — Tüm branch'ler + main PR'ları (15 dk limit):
- Biome lint
- TypeScript typecheck
- Vitest unit testler
- Build
deploy.yml — main branch'e push (10 dk limit):
- SSH ile production'a bağlan
git pull origin main
pnpm install
pnpm build
pnpm db:push (migration)
pm2 reload all
16. Temel Komutlar
Özet: Sayılarla SASE v2
| Metrik |
Değer |
| Core DB tabloları |
20 |
| PL24 cache tabloları |
9 |
| EMEX cache tabloları |
11 |
| Toplam DB tabloları |
~40 |
| API endpoint'leri |
70+ |
| Frontend route'ları |
37 |
| React bileşenleri |
13 |
| Custom hook'lar |
4 |
| Zustand store'ları |
2 |
| Shared TypeScript tipleri |
25+ |
| Error kodları |
30+ |
| Desteklenen WMI/marka |
60+ |
| PL24 servis sayısı |
50+ |
| BullMQ kuyrukları |
4 |
| Dil desteği |
2 (tr, en) |
| Fiyat planları |
4 |