Files
sase.tr/knowledge.md
Fusion 561b02b3d2 feat(FN-343): remove lingering iyzico references after Stripe migration (+1 more)
Commits merged:
- chore(FN-343): remove lingering iyzico references from docs, config, and scripts
- feat(FN-343): remove lingering iyzico references after Stripe migration

Files changed:
CLAUDE.md                                    | 10 +++++-----
 README.md                                    |  2 +-
 apps/api/src/database/schema/core.ts         |  1 +
 apps/web/src/messages/en.json                |  1 -
 apps/web/src/messages/tr.json                |  1 -
 apps/web/src/routes/dashboard/billing.tsx    |  6 +++---
 docker-compose.coolify.yml                   |  5 ++---
 docs/INDEX.md                                | 27 ++++++++++++-------------
 knowledge.md                                 | 30 ++++++++++++++--------------
 packages/shared/src/constants/error-codes.ts |  1 -
 packages/shared/src/index.ts                 |  1 -
 packages/shared/src/types/payment.ts         | 12 +----------
 scripts/fn342-pw-verify.mjs                  |  2 +-
 scripts/validate-env.sh                      |  3 +--
 14 files changed, 43 insertions(+), 59 deletions(-)

Fusion-Task-Id: FN-343
2026-05-14 02:31:42 +00:00

58 KiB
Raw Blame History

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ı

ss/ (repo root)
├── apps/
│   ├── api/                      # NestJS backend (port 4000, prefix /api)
│   │   └── src/
│   │       ├── main.ts           # Bootstrap: Helmet, CORS, rate limiting, prefix /api
│   │       ├── app.module.ts     # Root modül: global guards/interceptors/filters
│   │       ├── worker.ts         # BullMQ standalone worker process
│   │       ├── health.controller.ts
│   │       ├── auth/             # Better Auth entegrasyonu
│   │       ├── users/            # Kullanıcı hesap yönetimi
│   │       ├── brands/           # Marka CRUD
│   │       ├── plans/            # Fiyat planları CRUD
│   │       ├── subscriptions/    # Abonelik yaşam döngüsü
│   │       ├── payments/         # Stripe + EFT ödeme
│   │       ├── referrals/        # Referral programı
│   │       ├── vehicles/         # VIN decode + araç geçmişi
│   │       ├── categories/       # Parça kategorisi ağacı
│   │       ├── parts/            # Oto parça kataloğu
│   │       ├── catalog/          # VIN-less katalog tarayıcı (PL24 model aileleri)
│   │       ├── translations/     # Otomotiv terim çevirisi
│   │       ├── admin/            # Admin dashboard endpoint'leri
│   │       ├── analytics/        # Kullanım analitik takibi
│   │       ├── common/           # Guard, pipe, interceptor, filter, decorator
│   │       ├── config/           # Runtime konfigürasyon
│   │       ├── database/         # Drizzle ORM kurulum + şemalar
│   │       │   └── schema/       # core.ts, emex.ts, pl24.ts, relations.ts
│   │       ├── redis/            # Redis modül + servis
│   │       ├── storage/          # MinIO/S3 servisi
│   │       ├── email/            # Postal e-posta servisi
│   │       ├── jobs/             # BullMQ kuyrukleri + işleyiciler
│   │       ├── telemetry/        # OpenTelemetry SDK
│   │       └── integrations/
│   │           ├── corgi/        # Offline VIN WMI decoder
│   │           ├── pl24/         # PL24 katalog API + parser'lar
│   │           ├── parts-catalogs/ # PartsCatalogs API
│   │           ├── emex/         # EMEX scraper (Playwright)
│   │           └── vin-api/      # NHTSA VIN API fallback
│   │
│   └── web/                      # Vite + React frontend (port 3000)
│       └── src/
│           ├── main.tsx          # Giriş: RouterProvider, QueryClient, Faro, PostHog
│           ├── routeTree.gen.ts  # Otomatik üretilen route ağacı (ELLE DÜZENLEME)
│           ├── routes/           # TanStack Router dosya tabanlı route'lar
│           ├── components/       # React bileşenleri
│           ├── hooks/            # Custom hook'lar
│           ├── stores/           # Zustand store'ları
│           ├── lib/              # Yardımcı kütüphaneler
│           └── messages/         # i18n: tr.json, en.json
│
├── packages/
│   ├── shared/   (@sase/shared)  # Tipler, Zod şemaları, sabitler, util'ler
│   ├── config/   (@sase/config)  # Zod env doğrulama şeması
│   └── ui/       (@sase/ui)      # shadcn tabanlı React bileşen kütüphanesi
│
├── docker/                       # Docker Compose (PostgreSQL, Redis, MinIO, nginx)
├── scripts/                      # Deploy, debug, keşif script'leri
├── .github/workflows/            # CI (lint+typecheck+test+build) + Deploy
├── ecosystem.config.js           # PM2 process konfigürasyonu
├── turbo.json                    # Turborepo pipeline
├── biome.json                    # Linter/formatter konfigürasyonu
└── pnpm-workspace.yaml           # Workspace kök

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)

users → sessions (1:N)
users → accounts (1:N)
users → userSubscriptions (1:N)
users → userBrands (1:N)
users → payments (1:N)
users → queryLogs (1:N)
users → userVehicles (1:N)
users → referrals [referrer] (1:N)
users → referrals [referred] (1:1)

plans → userSubscriptions (1:N)
userSubscriptions → plans (N:1)
userSubscriptions → users (N:1)
userSubscriptions → userBrands (1:N)
userSubscriptions → payments (1:N)
userBrands → users, userSubscriptions, brands (N:1)
brands → userBrands (1:N)
brands → catalogVehicles (1:N)

catalogVehicles → brands (N:1)
catalogVehicles → categories (1:N)
catalogVehicles → parts (1:N)

vehicles → brands (N:1)
vehicles → userVehicles (1:N)
vehicles → categories (1:N)
vehicles → parts (1:N)

categories → vehicles (N:1)
categories → catalogVehicles (N:1)
categories → categories [parentId] (N:1, hiyerarşi)
categories → parts (1:N)
categories → schemaPics (1:N)

parts → vehicles, catalogVehicles, categories (N:1)
schemaPics → categories (N:1)
referrals → users [referrer, referred] (N:1)

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 ı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 ı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 ı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 ı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 ı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 ı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 ı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 ı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 ı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 ı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 ı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 ı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 ı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 ı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 ıklama
GET /health Public Sistem sağlık kontrolü: DB + Redis + telemetry

6. Auth & Güvenlik Mimarisi

Better Auth Akışı

1. İstemci → POST /api/auth/sign-in/email { email, password }
2. Better Auth doğrular, DB'de session oluşturur
3. Cookie döner: better-auth.session_token (HTTPS'de: __Secure-better-auth.session_token)
4. Sonraki istekler cookie'yi otomatik gönderir
5. AuthGuard → auth.api.getSession() ile doğrular
6. @CurrentUser() decorator'ı kullanıcıyı request'e enjekte eder

Guard Sırası (Global)

ThrottlerGuardAuthGuardRolesGuard

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ı:

{ "success": true, "data": { ... } }
{ "success": true, "data": [...], "meta": { "page": 1, "limit": 20, "total": 100, "totalPages": 5 } }

Hata:

{ "success": false, "error": { "code": "AUTH_001", "message": "Geçersiz kimlik bilgileri" } }

Hata Kodları

Kod Prefix ı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ıı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ışı

pending → (ödeme onayı) → active → (iptal) → cancelled
active → (süre dolumu) → expired
cancelled → (yeniden başlatma, süre içinde) → active

Marka Erişim Kontrolü

  • brandCount = 0 → Full Paket, tüm markalara erişim
  • brandCount > 0userBrands 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):

  1. POST /payments/stripe/checkout → Stripe Checkout Session oluşturulur
  2. Kullanıcı Stripe ödeme sayfasına yönlendirilir
  3. POST /payments/stripe/webhook webhook'u aboneliği aktifleştirir

EFT (Havale):

  1. POST /payments/eft → EFT kaydı oluşturulur, banka bilgileri gösterilir
  2. Kullanıcı makbuz yükler: POST /payments/eft/:id/receipt
  3. Admin onaylar: PATCH /payments/eft/:id/approve → abonelik aktifleşir

8. VIN Decode Zinciri

Fallback Zinciri

VIN Girişi (17 karakter)
  ↓
1. Corgi (Offline WMI DB) → Marka + yıl tanımla
  ↓
2. PL24 API → Tam araç bilgisi + kategori ağacı
  ↓ (başarısız veya marka desteklenmiyor)
3. PartsCatalogs API → Araç bilgisi + kategoriler
  ↓ (başarısız)
4. EMEX Scraper (Playwright, BullMQ) → Orta Doğu odaklı
  ↓ (başarısız)
5. NHTSA VIN API → ABD VIN veritabanı (son çare)

VIN Yapısı

Pozisyon 1-3: WMI (World Manufacturer Identifier) → Marka tanımı
Pozisyon 4-8: VDS (Vehicle Descriptor Section) → Model, tip
Pozisyon 9: Check digit
Pozisyon 10: Model yılı (A=2010, L=2020, R=2024, S=2025, T=2026)
Pozisyon 11-17: VIS (Vehicle Identifier Section) → Seri no

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 ı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 ı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 ı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 ı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 ı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 ı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 ı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.5x5x)
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)

{
  user: { id, name, email, image, role, referralCode } | null,
  isLoading: boolean,
  setUser(user), setLoading(loading)
}

useSchemaStore (stores/schema.store.ts)

{
  highlightedGroup: number | null,  // hover edilmiş hotspot grubu
  selectedGroup: number | null,     // tıklanan hotspot grubu
  zoom: number,                      // 0.55 arasında
  panX: number, panY: number,       // kaydırma offset
  isFullscreen: boolean,
  setHighlightedGroup, setSelectedGroup, setZoom, setPan, resetView, toggleFullscreen
}

Lib Yardımcıları (8 dosya)

Dosya ı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ı

import { api } from "@/lib/api-client";

// GET isteği
const brands = await api.get<Brand[]>("/brands");

// POST isteği
const result = await api.post<Vehicle>("/vehicles/decode", { vin });

// Dosya yükleme
await api.upload("/payments/eft/:id/receipt", formData);

Response format: { success: true, data: T } veya hata durumunda ApiError fırlatır.


i18n Namespace Yapısı

Namespace ı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ı

loginSchema          // email + password (min 8)
registerSchema       // name (min 2) + email + password (uppercase+lowercase+number)
forgotPasswordSchema // email
resetPasswordSchema  // token + password
vinSchema            // 17 karakter, /^[A-HJ-NPR-Z0-9]{17}$/ (I,O,Q yasak), uppercase transform
paginationSchema     // page (default 1) + limit (default 20, max 100)

Sabitler

PLANS = {
  SINGLE: { name: "1 Marka", brandCount: 1, priceMonthly: 20000, priceYearly: 200000 },
  DOUBLE: { name: "2 Marka", brandCount: 2, priceMonthly: 35000, priceYearly: 350000 },
  TRIPLE: { name: "3 Marka", brandCount: 3, priceMonthly: 50000, priceYearly: 500000 },
  FULL:   { name: "Full Paket", brandCount: 0, priceMonthly: 99900, priceYearly: 999000 }
}

REFERRAL_REWARDS = {
  TIER_1: { count: 3, extensionDays: 7 },
  TIER_2: { count: 5, extensionDays: 30 }
}

VIN_REGEX      = /^[A-HJ-NPR-Z0-9]{17}$/
EMAIL_REGEX    = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
OEM_CODE_REGEX = /^[A-Z0-9\-.\s]{3,30}$/i
CURRENCY       = "TRY"

Util Fonksiyonları

isValidVin(vin: string): boolean              // VIN format + check digit
extractWmi(vin: string): string               // İlk 3 karakter
decodeModelYear(vin: string): number | null   // Pozisyon 10 → yıl
formatTRY(kurus: number): string              // 20000 → "200,00 ₺"
kurusToLira(kurus: number): number            // 20000 → 200
liraToKurus(lira: number): number             // 200 → 20000
formatVin(vin: string): string                // Büyük harf
toTurkishSlug(str: string): string            // URL slug
generateReferralCode(): string                // 8 karakter

@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 ı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 ı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 ı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ı (01)
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 ı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

Admin e-posta: admin@sase.tr
Admin şifre:   Sase2026
Test VIN (VW): WVWZZZ1JZ3W597935
API URL:       http://localhost:4000/api
Web URL:       http://localhost:3000

Test Pattern (Backend)

// Drizzle mock → zincirleme sorgu builder simüle edilir
// vi.mock() harici bağımlılıklar için (Redis, PL24, EMEX)
// Test dosyaları: apps/api/src/**/*.spec.ts

Vitest Konfigürasyonu

  • API: apps/api/vitest.config.tssrc/**/*.spec.ts, v8 coverage
  • Web: apps/web/vitest.config.tssrc/**/*.{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):

  1. Biome lint
  2. TypeScript typecheck
  3. Vitest unit testler
  4. Build

deploy.ymlmain branch'e push (10 dk limit):

  1. SSH ile production'a bağlan
  2. git pull origin main
  3. pnpm install
  4. pnpm build
  5. pnpm db:push (migration)
  6. pm2 reload all

16. Temel Komutlar

# Geliştirme
pnpm dev                              # Tüm uygulamaları başlat (Turbo)
pnpm dev --filter=api                 # Yalnızca API
pnpm dev --filter=web                 # Yalnızca Web

# Kalite
pnpm build                            # Tüm paketleri + uygulamaları derle
pnpm lint                             # Biome lint kontrolü
pnpm typecheck                        # TypeScript --noEmit
pnpm test                             # Tüm testler (Vitest)

# Veritabanı (apps/api/ içinden)
pnpm db:push                          # Şemayı DB'ye push et (Drizzle)
pnpm db:studio                        # Drizzle Studio aç
pnpm db:seed                          # Veritabanını seed et
pnpm db:generate                      # Migration üret

# Route üretimi
pnpm --filter web exec tsr generate   # TanStack Router route ağacını yeniden üret

# Üretim
pm2 start ecosystem.config.js
pm2 reload all
pm2 logs sase-api

Ö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