feat: propagate PL24 part metadata, improve auth UX with Google sign-in prominence
- Add unavailable/remark/modelCodes/presel fields to categories and parts DB schema - Pass PL24 unavailable flag through pipeline instead of filtering out records - Show unavailable categories/parts at reduced opacity in grid, tree, and parts panel - Display part remark and model codes as secondary info in parts table - Move Google sign-in button above email form on login/register pages with branded icon - Add public email existence check endpoint for better login error messages - Update INDEX.md documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
652
docs/INDEX.md
652
docs/INDEX.md
@@ -1,6 +1,9 @@
|
||||
# Sase.tr - Project Documentation Index
|
||||
|
||||
> **Automotive parts search platform** for the Turkish market with VIN decoding, subscription-based access, and multi-source parts catalog integration.
|
||||
> **URL:** https://sase.tr | **Repo:** `/home/s/ss`
|
||||
|
||||
Generated: 2026-02-17
|
||||
|
||||
---
|
||||
|
||||
@@ -9,25 +12,29 @@
|
||||
- [Architecture Overview](#architecture-overview)
|
||||
- [Tech Stack](#tech-stack)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Entry Points](#entry-points)
|
||||
- [Backend API](#backend-api)
|
||||
- [Modules & Services](#modules--services)
|
||||
- [API Endpoints](#api-endpoints)
|
||||
- [Authentication](#authentication)
|
||||
- [Guards, Interceptors & Pipes](#guards-interceptors--pipes)
|
||||
- [Common Infrastructure](#common-infrastructure)
|
||||
- [Integrations](#integrations)
|
||||
- [Database Schema](#database-schema)
|
||||
- [Job Queues](#job-queues)
|
||||
- [Integrations](#integrations)
|
||||
- [Telemetry](#telemetry)
|
||||
- [API Endpoints](#api-endpoints)
|
||||
- [Authentication](#authentication)
|
||||
- [Frontend Web](#frontend-web)
|
||||
- [Routes & Pages](#routes--pages)
|
||||
- [Components](#components)
|
||||
- [State Management](#state-management)
|
||||
- [Data Fetching](#data-fetching)
|
||||
- [Hooks & Stores](#hooks--stores)
|
||||
- [Lib Utilities](#lib-utilities)
|
||||
- [Internationalization](#internationalization)
|
||||
- [Shared Packages](#shared-packages)
|
||||
- [Testing](#testing)
|
||||
- [Infrastructure & Deployment](#infrastructure--deployment)
|
||||
- [Environment Variables](#environment-variables)
|
||||
- [Testing](#testing)
|
||||
- [Scripts & Commands](#scripts--commands)
|
||||
- [Key Commands](#key-commands)
|
||||
- [Key Dependencies](#key-dependencies)
|
||||
- [Quick Start](#quick-start)
|
||||
|
||||
---
|
||||
|
||||
@@ -70,11 +77,13 @@
|
||||
| **Payments** | Iyzico (card), EFT (bank transfer with receipt upload) |
|
||||
| **Storage** | MinIO (S3-compatible) |
|
||||
| **Jobs** | BullMQ (Redis-backed queues) |
|
||||
| **Email** | Postal (transactional email) |
|
||||
| **Analytics** | PostHog (product analytics) |
|
||||
| **Observability** | OpenTelemetry (API), Grafana Faro (frontend) |
|
||||
| **Testing** | Vitest 3, Playwright 1.50 |
|
||||
| **Linting** | Biome |
|
||||
| **CI/CD** | GitHub Actions |
|
||||
| **Process Mgmt** | PM2 |
|
||||
| **Package Mgmt** | pnpm 10.29, Turborepo |
|
||||
| **Linting** | Biome (2-space, double quotes, semicolons, trailing commas) |
|
||||
| **CI/CD** | GitHub Actions → SSH deploy → PM2 |
|
||||
| **Package Mgmt** | pnpm 10.29, Turborepo 2 |
|
||||
|
||||
---
|
||||
|
||||
@@ -88,6 +97,7 @@ sase.tr/
|
||||
│ │ │ ├── main.ts # Bootstrap (global prefix /api, CORS, Helmet, rate limiting)
|
||||
│ │ │ ├── app.module.ts # Root module (global guards, interceptors, filters)
|
||||
│ │ │ ├── worker.ts # Standalone worker process
|
||||
│ │ │ ├── health.controller.ts # Health check endpoint
|
||||
│ │ │ ├── auth/ # Better Auth integration
|
||||
│ │ │ ├── users/ # User account management
|
||||
│ │ │ ├── brands/ # Brand CRUD
|
||||
@@ -100,52 +110,53 @@ sase.tr/
|
||||
│ │ │ ├── parts/ # Auto parts catalog
|
||||
│ │ │ ├── translations/ # Automotive term translations
|
||||
│ │ │ ├── admin/ # Admin dashboard endpoints
|
||||
│ │ │ ├── analytics/ # Usage analytics tracking
|
||||
│ │ │ ├── common/ # Shared guards, pipes, interceptors, filters, decorators
|
||||
│ │ │ ├── config/ # Runtime configuration
|
||||
│ │ │ ├── database/ # Drizzle ORM setup + schemas
|
||||
│ │ │ ├── redis/ # Redis client module
|
||||
│ │ │ ├── storage/ # MinIO/S3 service
|
||||
│ │ │ ├── email/ # Email service
|
||||
│ │ │ ├── email/ # Postal email service
|
||||
│ │ │ ├── jobs/ # BullMQ queues + processors
|
||||
│ │ │ ├── telemetry/ # OpenTelemetry SDK (tracing, metrics)
|
||||
│ │ │ └── integrations/ # External API integrations
|
||||
│ │ │ ├── corgi/ # Offline VIN WMI decoder
|
||||
│ │ │ ├── pl24/ # PL24 parts catalog API
|
||||
│ │ │ ├── emex/ # EMEX scraper (Puppeteer)
|
||||
│ │ │ ├── pl24/ # PL24 parts catalog API + parsers
|
||||
│ │ │ ├── emex/ # EMEX scraper (Playwright)
|
||||
│ │ │ └── vin-api/ # NHTSA VIN API fallback
|
||||
│ │ ├── drizzle.config.ts
|
||||
│ │ └── vitest.config.ts
|
||||
│ │
|
||||
│ └── web/ # Vite + React frontend
|
||||
│ └── src/
|
||||
│ ├── main.tsx # Entry point (RouterProvider, QueryClient)
|
||||
│ ├── routeTree.gen.ts # Auto-generated TanStack route tree
|
||||
│ ├── main.tsx # Entry point (RouterProvider, QueryClient, Faro, PostHog)
|
||||
│ ├── routeTree.gen.ts # Auto-generated TanStack route tree (DO NOT EDIT)
|
||||
│ ├── routes/ # TanStack Router file-based routes
|
||||
│ │ ├── __root.tsx # Root layout
|
||||
│ │ ├── __root.tsx # Root layout (theme, toaster, PostHog tracking)
|
||||
│ │ ├── index.tsx # Landing page
|
||||
│ │ ├── _auth.tsx # Auth layout (login, register, etc.)
|
||||
│ │ ├── dashboard.tsx # Dashboard layout (protected)
|
||||
│ │ └── dashboard/ # Dashboard subroutes
|
||||
│ ├── components/ # React components
|
||||
│ │ ├── admin/ # DailyChart
|
||||
│ │ ├── schema/ # SchemaViewer, HotspotOverlay, PartsPanel
|
||||
│ │ ├── schema/ # SchemaViewer, HotspotOverlay, PartsPanel, SchemaToolbar
|
||||
│ │ ├── subscription/ # BrandSelector
|
||||
│ │ ├── vehicles/ # VehicleCard, VinInput
|
||||
│ │ ├── categories/ # CategoryTree, CategoryGrid
|
||||
│ │ ├── payment/ # PaymentContent
|
||||
│ │ └── settings/ # SettingsContent
|
||||
│ ├── hooks/ # Custom hooks (useAuth, useParts, useSchemaInteraction)
|
||||
│ ├── lib/ # api-client, auth-client, i18n, toast, user-settings, category-icons
|
||||
│ ├── hooks/ # useAuth, useParts, useSchemaInteraction
|
||||
│ ├── lib/ # api-client, auth-client, i18n, posthog, faro, toast, user-settings, category-icons
|
||||
│ ├── stores/ # Zustand stores (auth, schema)
|
||||
│ ├── messages/ # i18n JSON (tr.json, en.json)
|
||||
│ └── remotion/ # Animated demo video generation
|
||||
│ └── messages/ # i18n JSON (tr.json, en.json)
|
||||
│
|
||||
├── packages/
|
||||
│ ├── shared/ # Shared types, Zod schemas, utilities
|
||||
│ ├── config/ # Environment validation (Zod)
|
||||
│ └── ui/ # Reusable UI components (shadcn/ui)
|
||||
│ ├── shared/ # @sase/shared — types, Zod schemas, constants, utils
|
||||
│ ├── config/ # @sase/config — Zod env validation
|
||||
│ └── ui/ # @sase/ui — shadcn/Radix component library
|
||||
│
|
||||
├── docker/ # Docker Compose (PostgreSQL, Redis, MinIO, nginx)
|
||||
├── scripts/ # Build/deployment scripts
|
||||
├── scripts/ # Build/deployment/debug scripts
|
||||
├── .github/workflows/ # CI (lint, typecheck, test, build) + Deploy (SSH, PM2)
|
||||
├── ecosystem.config.js # PM2 process configuration
|
||||
├── turbo.json # Turborepo task pipeline
|
||||
@@ -155,32 +166,194 @@ sase.tr/
|
||||
|
||||
---
|
||||
|
||||
## Entry Points
|
||||
|
||||
| Process | Path | Description |
|
||||
|---------|------|-------------|
|
||||
| API Server | `apps/api/src/main.ts` | NestJS bootstrap (Helmet, CORS, rate limiting) |
|
||||
| Root Module | `apps/api/src/app.module.ts` | Global guards, interceptors, filters |
|
||||
| Worker | `apps/api/src/worker.ts` | BullMQ background job processor |
|
||||
| Health | `apps/api/src/health.controller.ts` | Health check endpoint |
|
||||
| Frontend | `apps/web/src/main.tsx` | React 19 + TanStack Router + Query + Faro + PostHog |
|
||||
| Root Layout | `apps/web/src/routes/__root.tsx` | Theme, Toaster, PostHog tracking |
|
||||
|
||||
---
|
||||
|
||||
## Backend API
|
||||
|
||||
### Modules & Services
|
||||
|
||||
| Module | Service | Purpose |
|
||||
|--------|---------|---------|
|
||||
| **AuthModule** | AuthService | Better Auth instance, session management |
|
||||
| **UsersModule** | UsersService | Profile CRUD, password change, account deletion, OAuth connections |
|
||||
| **BrandsModule** | BrandsService | Brand CRUD (cached, admin-managed) |
|
||||
| **PlansModule** | PlansService | Pricing plan CRUD (cached, admin-managed) |
|
||||
| **SubscriptionsModule** | SubscriptionsService | Create, activate, cancel, resume, extend subscriptions |
|
||||
| **PaymentsModule** | PaymentsService | Iyzico card payments, EFT with receipt upload, admin approval |
|
||||
| **ReferralsModule** | ReferralsService | Referral code generation, application, tier-based rewards |
|
||||
| **VehiclesModule** | VehiclesService | VIN decode (multi-source fallback), vehicle history, brand access check |
|
||||
| **CategoriesModule** | CategoriesService | Hierarchical category tree, schema pictures |
|
||||
| **PartsModule** | PartsService | Parts by category, OEM code search |
|
||||
| **TranslationsModule** | TranslationsService | Automotive term translation (Redis → DB → Dictionary fallback) |
|
||||
| **AdminModule** | AdminService | Dashboard stats, user management, payment approval, analytics |
|
||||
| **EmailModule** | EmailService | Password reset, welcome, payment confirmation emails |
|
||||
| **StorageModule** | StorageService | S3/MinIO file upload/download |
|
||||
| **RedisModule** | RedisService | Key-value cache operations |
|
||||
| **JobsModule** | — | BullMQ queue registration + processors |
|
||||
| **CorgiModule** | CorgiService | Offline WMI-based VIN decoder |
|
||||
| **PL24Module** | PL24Service | PL24 parts catalog API (with brand-specific parsers) |
|
||||
| **EmexModule** | EmexService | EMEX browser scraper via Puppeteer |
|
||||
| **VinApiModule** | VinApiService | NHTSA VIN API (fallback decoder) |
|
||||
| Module | Files | Purpose |
|
||||
|--------|-------|---------|
|
||||
| **AuthModule** | module, service, controller, auth.ts | Better Auth (email/password + Google OAuth) |
|
||||
| **UsersModule** | module, service, controller, spec | Profile CRUD, password change, account deletion, OAuth connections |
|
||||
| **BrandsModule** | module, service, controller, spec | Brand CRUD (cached, admin-managed) |
|
||||
| **PlansModule** | module, service, controller, spec | Pricing plan CRUD (cached, admin-managed) |
|
||||
| **SubscriptionsModule** | module, service, controller, spec | Create, activate, cancel, resume, extend subscriptions |
|
||||
| **PaymentsModule** | module, service, controller, spec | Iyzico card payments, EFT with receipt upload, admin approval |
|
||||
| **ReferralsModule** | module, service, controller, spec | Referral code generation, application, tier-based rewards |
|
||||
| **VehiclesModule** | module, service, controller, spec | VIN decode (multi-source fallback), vehicle history, brand access check |
|
||||
| **CategoriesModule** | module, service, controller, spec | Hierarchical category tree, schema pictures |
|
||||
| **PartsModule** | module, service, controller, spec | Parts by category, OEM code search |
|
||||
| **TranslationsModule** | module, service, controller, spec | Automotive term translation (Redis → DB → Dictionary fallback) |
|
||||
| **AdminModule** | module, service, controller, spec | Dashboard stats, user management, payment approval, analytics |
|
||||
| **AnalyticsModule** | module, service, controller | Usage analytics tracking |
|
||||
| **EmailModule** | module, service | Postal transactional emails (password reset, welcome, payment confirmation) |
|
||||
| **StorageModule** | module, service | S3/MinIO file upload/download |
|
||||
| **RedisModule** | module, service, provider | Key-value cache operations |
|
||||
|
||||
### Common Infrastructure
|
||||
|
||||
| Type | Name | Behavior |
|
||||
|------|------|----------|
|
||||
| **Guard** | `AuthGuard` (global) | Validates Better Auth session; skip with `@Public()` |
|
||||
| **Guard** | `RolesGuard` (global) | Checks `@Roles("admin")` metadata against `user.role` |
|
||||
| **Guard** | `BrandAccessGuard` (per-route) | Verifies user's subscription includes the target brand |
|
||||
| **Guard** | `ThrottlerGuard` (global) | Rate limiting (100/min default) |
|
||||
| **Interceptor** | `TransformInterceptor` (global) | Wraps responses: `{success: true, data: ...}` |
|
||||
| **Interceptor** | `LoggingInterceptor` (global) | Logs method, URL, status, response time |
|
||||
| **Interceptor** | `TimeoutInterceptor` (global) | 30s request timeout |
|
||||
| **Pipe** | `VinValidationPipe` (per-route) | Validates VIN: 17 chars, alphanumeric, no I/O/Q |
|
||||
| **Filter** | `HttpExceptionFilter` (global) | Returns `{success: false, error: {code, message}}` |
|
||||
| **Filter** | `DrizzleExceptionFilter` (global) | Catches unique constraint violations → 409 Conflict |
|
||||
| **Middleware** | `FileUploadValidation` | PNG/JPG/PDF only, max 5MB |
|
||||
|
||||
**Custom Decorators:**
|
||||
- `@Public()` — Skip authentication
|
||||
- `@CurrentUser(field?)` — Inject authenticated user (or specific field)
|
||||
- `@Roles(...roles)` — Require role(s)
|
||||
- `@ThrottleAuth()` — 5 req/min
|
||||
- `@ThrottleVinDecode()` — 20 req/min
|
||||
- `@ThrottleGeneral()` — 100 req/min
|
||||
|
||||
**Shared DTOs:** `ApiResponseDto`, `PaginationDto`
|
||||
|
||||
### Integrations
|
||||
|
||||
**VIN Decode Fallback Chain:** Corgi (offline WMI) → PL24 API → EMEX Scraper → NHTSA VIN API
|
||||
|
||||
| Integration | Type | Path | Notes |
|
||||
|-------------|------|------|-------|
|
||||
| **Corgi** | Offline DB | `corgi/` | WMI database for brand identification (+ spec) |
|
||||
| **PL24** | REST API | `pl24/` | Multi-brand catalog API + auth + parsers (BMW, Mercedes, Generic, Ford Legacy) |
|
||||
| **EMEX** | Browser scraper | `emex/` | Playwright-based (emexdwc.ae), async via BullMQ. Files: service, browser, mapper, types |
|
||||
| **VIN-API** | REST API | `vin-api/` | NHTSA VIN decoder (last-resort fallback) |
|
||||
| **Iyzico** | Payment API | — | Turkish payment processor for card payments |
|
||||
| **MinIO** | S3 API | — | Receipt uploads, schema images |
|
||||
|
||||
### Database Schema
|
||||
|
||||
**ORM:** Drizzle ORM 0.41 with PostgreSQL
|
||||
|
||||
**Schema files:** `apps/api/src/database/schema/`
|
||||
- `core.ts` — Main application tables
|
||||
- `emex.ts` — EMEX scraper cache tables
|
||||
- `pl24.ts` — PL24 catalog cache tables
|
||||
- `relations.ts` — Drizzle ORM relationships
|
||||
|
||||
#### Core Tables
|
||||
|
||||
```
|
||||
users
|
||||
├── id (uuid, PK)
|
||||
├── name, email (unique), emailVerified, image
|
||||
├── role (default: "user"), referralCode (unique), referredBy
|
||||
└── createdAt, updatedAt
|
||||
|
||||
sessions / accounts / verifications
|
||||
└── Better Auth managed tables
|
||||
|
||||
brands
|
||||
├── id (uuid, PK), name, slug (unique), logoUrl, isActive
|
||||
└── createdAt, updatedAt
|
||||
|
||||
plans
|
||||
├── id (uuid, PK), name, brandCount
|
||||
├── priceMonthly, priceYearly, isActive
|
||||
└── createdAt, updatedAt
|
||||
|
||||
userSubscriptions
|
||||
├── id (uuid, PK), userId → users, planId → plans
|
||||
├── status (pending/active/cancelled/expired)
|
||||
├── billingPeriod (monthly/yearly), startDate, endDate, cancelledAt
|
||||
└── Indexes: userId, status
|
||||
|
||||
userBrands (junction)
|
||||
├── userId → users, subscriptionId → userSubscriptions, brandId → brands
|
||||
└── Unique: (userId, subscriptionId, brandId)
|
||||
|
||||
payments
|
||||
├── id (uuid, PK), userId → users, subscriptionId → userSubscriptions
|
||||
├── amount, currency, method (iyzico/eft), status (pending/completed/failed/refunded)
|
||||
├── iyzicoPaymentId, eftReceiptUrl, adminNote
|
||||
└── Indexes: userId, status
|
||||
|
||||
vehicles
|
||||
├── id (uuid, PK), userId → users, vin, brandId → brands
|
||||
├── brandName, model, year, engine, transmission, bodyType, market
|
||||
├── rawData (jsonb), source
|
||||
└── Indexes: userId, vin, unique(userId, vin)
|
||||
|
||||
categories
|
||||
├── id (uuid, PK), vehicleId → vehicles, name, nameOriginal
|
||||
├── parentId (self-ref), externalId, source
|
||||
└── Indexes: vehicleId, parentId
|
||||
|
||||
parts
|
||||
├── id (uuid, PK), vehicleId → vehicles, categoryId → categories
|
||||
├── oemCode, name, nameOriginal, description, quantity, position, hotspotIndex
|
||||
└── Indexes: vehicleId, categoryId, oemCode
|
||||
|
||||
schemaPics
|
||||
├── id (uuid, PK), categoryId → categories
|
||||
├── imageUrl, hotspots (jsonb), source
|
||||
└── Indexes: categoryId
|
||||
|
||||
queryLogs
|
||||
├── id (uuid, PK), userId → users, vin, brandId → brands
|
||||
├── source, success, errorMessage, responseTimeMs
|
||||
└── Indexes: (userId, createdAt), vin
|
||||
|
||||
referrals
|
||||
├── id (uuid, PK), referrerId → users, referredId → users (unique)
|
||||
├── rewardApplied, createdAt
|
||||
└── Indexes: referrerId, referredId
|
||||
|
||||
passwordResetTokens
|
||||
└── id, userId → users, token (unique), expiresAt, usedAt
|
||||
|
||||
emexCategoryTranslations
|
||||
└── id, originalName (unique), translatedName, isManual
|
||||
```
|
||||
|
||||
#### Integration Tables
|
||||
- `pl24_*` — PL24 catalog cache (catalogs, vehicles, VINs, part groups, parts, schemas)
|
||||
- `emex_*` — EMEX scraper cache (similar structure with translations)
|
||||
|
||||
### Job Queues
|
||||
|
||||
**Framework:** BullMQ with Redis
|
||||
|
||||
| Queue | Trigger | Schedule | Action |
|
||||
|-------|---------|----------|--------|
|
||||
| `EMEX_SCRAPE` | On-demand (VIN decode) | — | Scrapes EMEX via Playwright, stores results |
|
||||
| `SUBSCRIPTION_EXPIRY` | Cron | Daily 3:00 AM | Expires ended subscriptions, removes brand access |
|
||||
| `QUERY_CLEANUP` | Cron | Weekly Sun 4:00 AM | Cleans old query log entries |
|
||||
|
||||
**Files:** `apps/api/src/jobs/` — `jobs.module.ts`, `bull.config.ts`, `processors/`, `queues/`
|
||||
|
||||
### Telemetry
|
||||
|
||||
**Location:** `apps/api/src/telemetry/`
|
||||
|
||||
OpenTelemetry SDK with:
|
||||
- `sdk-factory.ts` — SDK initialization
|
||||
- `tracing.ts` — Distributed tracing (API)
|
||||
- `worker-tracing.ts` — Worker process tracing
|
||||
- `metrics.ts` — Prometheus metrics
|
||||
- `__tests__/telemetry.spec.ts` — Tests
|
||||
|
||||
Instrumentation: Express, HTTP, ioredis, NestJS Core, BullMQ, Drizzle ORM
|
||||
|
||||
### API Endpoints
|
||||
|
||||
@@ -302,149 +475,18 @@ Flow:
|
||||
|
||||
**User model extensions:** `role` (default: "user"), `referralCode`, `referredBy`
|
||||
|
||||
**Frontend middleware** (`proxy.ts`): Checks session cookie, redirects `/dashboard/*` to `/login` if missing.
|
||||
|
||||
### Guards, Interceptors & Pipes
|
||||
|
||||
| Type | Name | Scope | Behavior |
|
||||
|------|------|-------|----------|
|
||||
| **Guard** | `AuthGuard` | Global | Validates Better Auth session; skip with `@Public()` |
|
||||
| **Guard** | `RolesGuard` | Global | Checks `@Roles("admin")` metadata against `user.role` |
|
||||
| **Guard** | `BrandAccessGuard` | Per-route | Verifies user's subscription includes the target brand |
|
||||
| **Guard** | `ThrottlerGuard` | Global | Rate limiting (100/min default) |
|
||||
| **Interceptor** | `TransformInterceptor` | Global | Wraps responses: `{success: true, data: ...}` |
|
||||
| **Interceptor** | `LoggingInterceptor` | Global | Logs method, URL, status, response time |
|
||||
| **Interceptor** | `TimeoutInterceptor` | Global | 30s request timeout |
|
||||
| **Pipe** | `VinValidationPipe` | Per-route | Validates VIN: 17 chars, alphanumeric, no I/O/Q |
|
||||
| **Filter** | `HttpExceptionFilter` | Global | Returns `{success: false, error: {code, message}}` |
|
||||
| **Filter** | `DrizzleExceptionFilter` | Global | Catches unique constraint violations → 409 Conflict |
|
||||
|
||||
**Custom Decorators:**
|
||||
- `@Public()` — Skip authentication
|
||||
- `@CurrentUser(field?)` — Inject authenticated user (or specific field)
|
||||
- `@Roles(...roles)` — Require role(s)
|
||||
- `@ThrottleAuth()` — 5 req/min
|
||||
- `@ThrottleVinDecode()` — 20 req/min
|
||||
- `@ThrottleGeneral()` — 100 req/min
|
||||
|
||||
### Database Schema
|
||||
|
||||
**ORM:** Drizzle ORM 0.41 with PostgreSQL
|
||||
|
||||
#### Core Tables
|
||||
|
||||
```
|
||||
users
|
||||
├── id (uuid, PK)
|
||||
├── name, email (unique), emailVerified, image
|
||||
├── role (default: "user"), referralCode (unique), referredBy
|
||||
└── createdAt, updatedAt
|
||||
|
||||
sessions / accounts / verifications
|
||||
└── Better Auth managed tables
|
||||
|
||||
brands
|
||||
├── id (uuid, PK), name, slug (unique), logoUrl, isActive
|
||||
└── createdAt, updatedAt
|
||||
|
||||
plans
|
||||
├── id (uuid, PK), name, brandCount
|
||||
├── priceMonthly, priceYearly, isActive
|
||||
└── createdAt, updatedAt
|
||||
|
||||
userSubscriptions
|
||||
├── id (uuid, PK), userId → users, planId → plans
|
||||
├── status (pending/active/cancelled/expired)
|
||||
├── billingPeriod (monthly/yearly), startDate, endDate, cancelledAt
|
||||
└── Indexes: userId, status
|
||||
|
||||
userBrands (junction)
|
||||
├── userId → users, subscriptionId → userSubscriptions, brandId → brands
|
||||
└── Unique: (userId, subscriptionId, brandId)
|
||||
|
||||
payments
|
||||
├── id (uuid, PK), userId → users, subscriptionId → userSubscriptions
|
||||
├── amount, currency, method (iyzico/eft), status (pending/completed/failed/refunded)
|
||||
├── iyzicoPaymentId, eftReceiptUrl, adminNote
|
||||
└── Indexes: userId, status
|
||||
|
||||
vehicles
|
||||
├── id (uuid, PK), userId → users, vin, brandId → brands
|
||||
├── brandName, model, year, engine, transmission, bodyType, market
|
||||
├── rawData (jsonb), source
|
||||
└── Indexes: userId, vin, unique(userId, vin)
|
||||
|
||||
categories
|
||||
├── id (uuid, PK), vehicleId → vehicles, name, nameOriginal
|
||||
├── parentId (self-ref), externalId, source
|
||||
└── Indexes: vehicleId, parentId
|
||||
|
||||
parts
|
||||
├── id (uuid, PK), vehicleId → vehicles, categoryId → categories
|
||||
├── oemCode, name, nameOriginal, description, quantity, position, hotspotIndex
|
||||
└── Indexes: vehicleId, categoryId, oemCode
|
||||
|
||||
schemaPics
|
||||
├── id (uuid, PK), categoryId → categories
|
||||
├── imageUrl, hotspots (jsonb), source
|
||||
└── Indexes: categoryId
|
||||
|
||||
queryLogs
|
||||
├── id (uuid, PK), userId → users, vin, brandId → brands
|
||||
├── source, success, errorMessage, responseTimeMs
|
||||
└── Indexes: (userId, createdAt), vin
|
||||
|
||||
referrals
|
||||
├── id (uuid, PK), referrerId → users, referredId → users (unique)
|
||||
├── rewardApplied, createdAt
|
||||
└── Indexes: referrerId, referredId
|
||||
|
||||
passwordResetTokens
|
||||
└── id, userId → users, token (unique), expiresAt, usedAt
|
||||
|
||||
emexCategoryTranslations
|
||||
└── id, originalName (unique), translatedName, isManual
|
||||
```
|
||||
|
||||
#### Integration Tables
|
||||
- `pl24_*` — PL24 catalog cache (catalogs, vehicles, VINs, part groups, parts, schemas)
|
||||
- `emex_*` — EMEX scraper cache (similar structure with translations)
|
||||
|
||||
### Job Queues
|
||||
|
||||
**Framework:** BullMQ with Redis
|
||||
|
||||
| Queue | Trigger | Schedule | Action |
|
||||
|-------|---------|----------|--------|
|
||||
| `EMEX_SCRAPE` | On-demand (VIN decode) | — | Scrapes EMEX via Puppeteer, stores results |
|
||||
| `SUBSCRIPTION_EXPIRY` | Cron | Daily 3:00 AM | Expires ended subscriptions, removes brand access |
|
||||
| `QUERY_CLEANUP` | Cron | Weekly Sun 4:00 AM | Cleans old query log entries |
|
||||
|
||||
### Integrations
|
||||
|
||||
**VIN Decode Fallback Chain:** Corgi (offline WMI) → PL24 API → EMEX Scraper → NHTSA VIN API
|
||||
|
||||
| Integration | Type | Caching | Notes |
|
||||
|-------------|------|---------|-------|
|
||||
| **Corgi** | Offline DB | — | WMI database for brand identification |
|
||||
| **PL24** | REST API | 24h vehicles, 1h catalogs | Brand-specific parsers (BMW, Mercedes, generic) |
|
||||
| **EMEX** | Browser scraper | Redis + DB | Puppeteer-based, async via BullMQ |
|
||||
| **NHTSA VIN API** | REST API | — | Last-resort fallback |
|
||||
| **Iyzico** | Payment API | — | Turkish payment processor for card payments |
|
||||
| **MinIO** | S3 API | — | Receipt uploads, schema images |
|
||||
|
||||
---
|
||||
|
||||
## Frontend Web
|
||||
|
||||
### Routes & Pages
|
||||
|
||||
**Router:** TanStack Router (file-based, auto-generated route tree)
|
||||
**Router:** TanStack Router (file-based, auto-generated route tree — 31 files)
|
||||
|
||||
#### Public
|
||||
| Path | Route File | Description |
|
||||
|------|------------|-------------|
|
||||
| `/` | `routes/index.tsx` | Landing page with features and pricing CTA |
|
||||
| `/` | `routes/index.tsx` | Landing page with VIN decode + features + pricing CTA |
|
||||
| `/pricing` | `routes/pricing.tsx` | Plan comparison (1/2/3 brand, full package) |
|
||||
| `/about` | `routes/about.tsx` | About page |
|
||||
| `/contact` | `routes/contact.tsx` | Contact page |
|
||||
@@ -483,6 +525,7 @@ emexCategoryTranslations
|
||||
| `/dashboard/admin/payments` | `routes/dashboard/admin/payments.tsx` | EFT approval workflow |
|
||||
| `/dashboard/admin/referrals` | `routes/dashboard/admin/referrals.tsx` | Referral program tracking |
|
||||
| `/dashboard/admin/analytics` | `routes/dashboard/admin/analytics.tsx` | Daily query statistics |
|
||||
| `/dashboard/admin/copy-logs` | `routes/dashboard/admin/copy-logs.tsx` | OEM code copy tracking |
|
||||
|
||||
### Components
|
||||
|
||||
@@ -503,31 +546,39 @@ emexCategoryTranslations
|
||||
|
||||
**UI primitives** from `@sase/ui`: Button, Card, Input, Label, Badge, Dialog, Tabs, Separator, Skeleton
|
||||
|
||||
### State Management
|
||||
### Hooks & Stores
|
||||
|
||||
**Framework:** Zustand
|
||||
**Hooks:**
|
||||
| Hook | Purpose |
|
||||
|------|---------|
|
||||
| `useAuth()` | Auth state + Better Auth client (`user`, `isAdmin`, `signIn`, `signUp`, `signOut`, `session`) |
|
||||
| `useCategoryParts(vehicleId, categoryId)` | TanStack Query for schema + parts + hotspots |
|
||||
| `useSchemaInteraction()` | Pan/zoom/pinch event handlers for schema viewer |
|
||||
|
||||
| Store | File | State |
|
||||
|-------|------|-------|
|
||||
| **Auth** | `stores/auth.store.ts` | `user`, `isLoading` |
|
||||
| **Schema** | `stores/schema.store.ts` | `highlightedPartId`, `selectedPartId`, `zoom`, `panX/Y`, `isFullscreen` |
|
||||
**Stores (Zustand):**
|
||||
| Store | State |
|
||||
|-------|-------|
|
||||
| `useAuthStore()` | `{ user: User | null, isLoading }` |
|
||||
| `useSchemaStore()` | `{ highlightedGroup, selectedGroup, zoom, panX, panY, isFullscreen }` |
|
||||
|
||||
**i18n** (`lib/i18n.ts`): `locale` ("tr" / "en"), persisted to localStorage
|
||||
|
||||
### Data Fetching
|
||||
|
||||
**Client:** Custom `ApiClient` class (`lib/api-client.ts`)
|
||||
**Data Fetching:** Custom `ApiClient` class (`lib/api-client.ts`)
|
||||
- Base URL: `/api` (proxied via Vite dev server)
|
||||
- Methods: `get<T>`, `post<T>`, `patch<T>`, `delete<T>`, `upload<T>`
|
||||
- Credentials: cookies (automatic)
|
||||
- Error handling: `ApiError` with code + status
|
||||
- Caching: TanStack React Query 5 (`staleTime: 60s`, `retry: 1`, no refetch on focus)
|
||||
|
||||
**Caching:** TanStack React Query 5 (`staleTime: 60s`, `retry: 1`, no refetch on focus)
|
||||
### Lib Utilities
|
||||
|
||||
**Key hooks:**
|
||||
- `useAuth()` — Session + Zustand sync, exposes `user`, `isAdmin`, `signIn`, `signUp`, `signOut`
|
||||
- `useCategoryParts()` — Fetches category schema, parts, hotspots
|
||||
- `useSchemaInteraction()` — Mouse/touch events for zoom, pan, part selection
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `api-client.ts` | HTTP client wrapper with Faro error integration |
|
||||
| `auth-client.ts` | Better Auth client (signIn, signUp, signOut, useSession) |
|
||||
| `i18n.ts` | Zustand-based i18n (tr/en), `useTranslation()` hook |
|
||||
| `posthog.ts` | PostHog analytics initialization and tracking |
|
||||
| `faro.ts` | Grafana Faro frontend observability |
|
||||
| `user-settings.ts` | Theme/settings persistence |
|
||||
| `toast.ts` | Toast notification export |
|
||||
| `category-icons.ts` | Category icon mappings |
|
||||
|
||||
### Internationalization
|
||||
|
||||
@@ -535,8 +586,8 @@ emexCategoryTranslations
|
||||
|
||||
| Locale | File | Coverage |
|
||||
|--------|------|----------|
|
||||
| Turkish (default) | `messages/tr.json` | Full (268 lines) |
|
||||
| English | `messages/en.json` | Full (268 lines) |
|
||||
| Turkish (default) | `messages/tr.json` | Full |
|
||||
| English | `messages/en.json` | Full |
|
||||
|
||||
**Usage:** `const { t, locale, setLocale } = useTranslation()` → `t("nav.search")`
|
||||
|
||||
@@ -546,11 +597,53 @@ emexCategoryTranslations
|
||||
|
||||
## Shared Packages
|
||||
|
||||
| Package | Path | Contents |
|
||||
|---------|------|----------|
|
||||
| `@sase/shared` | `packages/shared/` | Zod schemas, TypeScript types, utility functions (formatters, currency, VIN validator) |
|
||||
| `@sase/config` | `packages/config/` | Environment variable validation schemas (Zod) |
|
||||
| `@sase/ui` | `packages/ui/` | Reusable React components (shadcn/ui based) |
|
||||
### @sase/shared (`packages/shared/src/`)
|
||||
|
||||
| Directory | Exports |
|
||||
|-----------|---------|
|
||||
| `types/` | User, UserProfile, UserSubscriptionSummary, Vehicle, VinDecodeResult, CategoryNode, VehicleSource, Brand, Plan, Subscription, UserBrand, CreateSubscriptionInput, SubscriptionStatus, Payment, PaymentMethod, PaymentStatus, IyzicoInitializeInput, EftPaymentInput, Part, PartSource, PartSearchResult, Category, CategoryWithSchema, SchemaPic, Hotspot, ApiResponse, ApiError, PaginationMeta, PaginationInput, PaginatedResult |
|
||||
| `schemas/` | loginSchema, registerSchema, forgotPasswordSchema, resetPasswordSchema, vinSchema, paginationSchema (Zod) |
|
||||
| `constants/` | ERROR_CODES (30+, prefixed AUTH/VIN/SUB/PAY), PLANS (Single/Double/Triple/Full), REFERRAL_REWARDS (Tier 1: 3→7d, Tier 2: 5→30d), VIN_REGEX, EMAIL_REGEX, OEM_CODE_REGEX, CURRENCY |
|
||||
| `utils/` | VIN validator (check digit, WMI extraction, model year decode), currency (formatTRY, kurus↔lira), formatters (VIN, date, datetime, Turkish slug, referral code) |
|
||||
|
||||
**Dependency:** `zod ^3.24.0`
|
||||
|
||||
### @sase/config (`packages/config/src/index.ts`)
|
||||
|
||||
Zod env schema exporting `envSchema`, `Env` type, `validateEnv()`.
|
||||
Groups: DATABASE_URL, REDIS_*, BETTER_AUTH_*, GOOGLE_*, MINIO_*, CORS_ORIGIN, IYZICO_*, PL24_*, EMEX_*, ML_PREDICTION_ENABLED, POSTAL_*, OTEL_*
|
||||
|
||||
### @sase/ui (`packages/ui/src/`)
|
||||
|
||||
Components: Button (CVA variants), Input, Card (6 compound parts), Badge (CVA), Label, Skeleton, Separator, Dialog (10 compound parts), Tabs (4 compound parts), `cn()` utility
|
||||
|
||||
Dependencies: Radix UI (dialog, dropdown-menu, label, popover, select, separator, slot, tabs, tooltip), class-variance-authority, clsx, tailwind-merge, lucide-react
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests (Vitest 3)
|
||||
|
||||
| Category | Files | Total Lines |
|
||||
|----------|-------|-------------|
|
||||
| **Service specs** | 11 (admin, brands, categories, parts, payments, plans, referrals, subscriptions, translations, users, vehicles) | 2,521 |
|
||||
| **Guard specs** | 3 (auth, roles, brand-access) | 380 |
|
||||
| **Pipe specs** | 1 (vin-validation) | 77 |
|
||||
| **Integration specs** | 1 (corgi) | 121 |
|
||||
| **Telemetry specs** | 1 | 97 |
|
||||
| **Total API** | **17 test files** | **3,196 lines** |
|
||||
|
||||
**Test Config:**
|
||||
- API: `apps/api/vitest.config.ts` — `src/**/*.spec.ts`, v8 coverage (text + lcov)
|
||||
- Web: `apps/web/vitest.config.ts` — `src/**/*.{test,spec}.{ts,tsx}`, jsdom, `@/` alias, test-setup (jest-dom)
|
||||
|
||||
**Test Pattern:** Mock Drizzle DB with chainable query builder, `vi.mock()` for external deps
|
||||
|
||||
### Playwright 1.50
|
||||
- Installed at root level (`package.json`)
|
||||
- Used by EMEX integration (`emex.browser.ts`) for web scraping
|
||||
- Test scripts in `scripts/` (vin-e2e-test.js)
|
||||
|
||||
---
|
||||
|
||||
@@ -560,19 +653,24 @@ emexCategoryTranslations
|
||||
|
||||
| Service | Image | Port | Volume |
|
||||
|---------|-------|------|--------|
|
||||
| PostgreSQL 17 | `postgres:17-alpine` | 5432 | `pg_data` |
|
||||
| Redis 7.4 | `redis:7.4-alpine` | 6379 | `redis_data` |
|
||||
| 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/`)
|
||||
|
||||
- `sase.tr.conf` — Frontend SPA + `/api` proxy + `/collect/` Faro telemetry CORS proxy + gzip (level 6) + 1-year asset cache + security headers
|
||||
- `api.sase.tr.conf` — NestJS proxy (60s timeout for VIN decode) + SSL + blocked paths (.git, .env, node_modules)
|
||||
|
||||
### CI/CD (GitHub Actions)
|
||||
|
||||
**`ci.yml`** — Runs on all branches & PRs to main:
|
||||
**`ci.yml`** — Runs on all branches & PRs to main (15min timeout):
|
||||
1. Biome lint
|
||||
2. TypeScript type check
|
||||
3. Vitest unit tests
|
||||
4. Full build
|
||||
|
||||
**`deploy.yml`** — Runs on push to `main`:
|
||||
**`deploy.yml`** — Runs on push to `main` (10min timeout):
|
||||
1. SSH into production
|
||||
2. `git pull origin main`
|
||||
3. `pnpm install`
|
||||
@@ -582,11 +680,11 @@ emexCategoryTranslations
|
||||
|
||||
### PM2 Configuration
|
||||
|
||||
| Process | Instances | Mode | Port | Memory |
|
||||
|---------|-----------|------|------|--------|
|
||||
| `sase-api` | 1 | fork | 4000 | 512MB |
|
||||
| `sase-web` | 1 | fork | 3000 | 512MB |
|
||||
| `sase-worker` | 1 | fork | — | 256MB |
|
||||
| Process | Command | Port | Memory |
|
||||
|---------|---------|------|--------|
|
||||
| `sase-api` | `pnpm dev` (dev) / `dist/main.js` (prod) | 4000 | 512MB |
|
||||
| `sase-web` | `pnpm dev` (dev) / serve dist (prod) | 3000 | 512MB |
|
||||
| `sase-worker` | `dist/worker.js` | — | 256MB |
|
||||
|
||||
---
|
||||
|
||||
@@ -604,14 +702,14 @@ emexCategoryTranslations
|
||||
| `MINIO_ACCESS_KEY` | MinIO access key |
|
||||
| `MINIO_SECRET_KEY` | MinIO secret key |
|
||||
| `MINIO_PUBLIC_URL` | Public URL for stored files |
|
||||
| `CORS_ORIGIN` | Comma-separated allowed origins |
|
||||
| `CORS_ORIGIN` | Allowed origins |
|
||||
|
||||
### Optional
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `PORT` | 4000 | API server port |
|
||||
| `REDIS_HOST` | localhost | Redis host |
|
||||
| `REDIS_HOST` | 127.0.0.1 | Redis host |
|
||||
| `REDIS_PORT` | 6379 | Redis port |
|
||||
| `MINIO_BUCKET_NAME` | sase-schemas | Storage bucket |
|
||||
| `MINIO_USE_SSL` | false | HTTPS for MinIO |
|
||||
@@ -619,67 +717,94 @@ emexCategoryTranslations
|
||||
| `GOOGLE_CLIENT_SECRET` | — | Google OAuth secret |
|
||||
| `IYZICO_API_KEY` | — | Iyzico payment API key |
|
||||
| `IYZICO_SECRET_KEY` | — | Iyzico payment secret |
|
||||
| `PL24_API_URL` | — | PL24 catalog API URL |
|
||||
| `IYZICO_BASE_URL` | — | Iyzico API base URL |
|
||||
| `PL24_BASE_URL` | — | PL24 catalog API URL |
|
||||
| `PL24_COMPANY_CODE` | — | PL24 company code |
|
||||
| `PL24_USERNAME` | — | PL24 credentials |
|
||||
| `PL24_PASSWORD` | — | PL24 credentials |
|
||||
| `EMEX_USERNAME` | — | EMEX scraper credentials |
|
||||
| `EMEX_PASSWORD` | — | EMEX scraper credentials |
|
||||
| `ML_PREDICTION_ENABLED` | false | Enable ML predictions |
|
||||
| `POSTAL_API_URL` | — | Postal email service URL |
|
||||
| `POSTAL_API_KEY` | — | Postal API key |
|
||||
| `POSTAL_FROM_ADDRESS` | noreply@sase.tr | Sender email |
|
||||
| `POSTAL_FROM_NAME` | Sase.tr | Sender name |
|
||||
| `OTEL_ENABLED` | false | Enable OpenTelemetry |
|
||||
| `OTEL_EXPORTER_OTLP_ENDPOINT` | — | OTLP collector endpoint |
|
||||
| `OTEL_EXPORTER_OTLP_HEADERS` | — | OTLP auth headers |
|
||||
| `OTEL_SERVICE_NAME` | sase-api | OTel service name |
|
||||
| `OTEL_TRACE_SAMPLE_RATE` | 1.0 | Trace sampling rate (0-1) |
|
||||
|
||||
Full schema: `packages/config/src/index.ts`
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
**Framework:** Vitest 3 (all apps)
|
||||
|
||||
| App | Config | Location | Count |
|
||||
|-----|--------|----------|-------|
|
||||
| API | `apps/api/vitest.config.ts` | `src/**/*.spec.ts` | ~19 spec files |
|
||||
| Web | `apps/web/vitest.config.ts` | `src/**/*.test.ts` | Store tests + component tests |
|
||||
| E2E | Playwright | `apps/web/` | Browser tests |
|
||||
|
||||
**Coverage:** V8 provider, lcov + text reporters
|
||||
|
||||
### Test Categories (API)
|
||||
|
||||
- **Service tests:** admin, categories, translations, payments, vehicles, subscriptions, plans, brands, users, referrals, corgi integration
|
||||
- **Guard tests:** auth, roles, brand-access
|
||||
- **Pipe tests:** VIN validation
|
||||
|
||||
---
|
||||
|
||||
## Scripts & Commands
|
||||
## Key Commands
|
||||
|
||||
```bash
|
||||
# Development
|
||||
pnpm dev # Start all apps in dev mode (Turbo)
|
||||
pnpm dev --filter=api # Start API only
|
||||
pnpm dev --filter=web # Start web only
|
||||
pnpm dev # Start all apps (Turbo)
|
||||
pnpm dev --filter=api # API only
|
||||
pnpm dev --filter=web # Web only
|
||||
|
||||
# Build
|
||||
pnpm build # Build all packages + apps
|
||||
# Build & Quality
|
||||
pnpm build # Build all packages + apps
|
||||
pnpm lint # Biome lint check
|
||||
pnpm typecheck # TypeScript --noEmit
|
||||
pnpm test # Run all tests (Vitest)
|
||||
|
||||
# Testing
|
||||
pnpm test # Run all tests (Vitest)
|
||||
pnpm test:watch # Watch mode
|
||||
# Database (run from apps/api/)
|
||||
pnpm db:push # Push schema to DB (Drizzle)
|
||||
pnpm db:studio # Open Drizzle Studio
|
||||
pnpm db:seed # Seed database
|
||||
pnpm db:generate # Generate migration
|
||||
|
||||
# Linting
|
||||
pnpm lint # Biome lint check
|
||||
pnpm format # Biome format
|
||||
|
||||
# Database
|
||||
pnpm db:push # Push schema to database (Drizzle)
|
||||
pnpm db:studio # Open Drizzle Studio
|
||||
|
||||
# Type Checking
|
||||
pnpm typecheck # TypeScript --noEmit
|
||||
# Route generation
|
||||
pnpm --filter web exec tsr generate
|
||||
|
||||
# Production
|
||||
pm2 start ecosystem.config.js # Start all processes
|
||||
pm2 reload all # Zero-downtime reload
|
||||
pm2 start ecosystem.config.js
|
||||
pm2 reload all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| NestJS | 10.4 | Backend framework |
|
||||
| Drizzle ORM | 0.41 | Database ORM |
|
||||
| Better Auth | 1.2 | Cookie-based auth |
|
||||
| React | 19 | Frontend UI |
|
||||
| TanStack Router | 1.120 | File-based routing |
|
||||
| TanStack Query | 5 | Server state management |
|
||||
| Zustand | 5 | Client state management |
|
||||
| Tailwind CSS | 4 | Styling |
|
||||
| Vite | 6.3 | Build tool |
|
||||
| Vitest | 3.x | Unit testing |
|
||||
| Playwright | 1.50 | Browser automation |
|
||||
| BullMQ | 5.30 | Job queues |
|
||||
| Biome | latest | Linting/formatting |
|
||||
| Turborepo | 2.x | Monorepo orchestration |
|
||||
| OpenTelemetry | 0.212 | Backend observability |
|
||||
| Grafana Faro | 2.2 | Frontend observability |
|
||||
| PostHog | latest | Product analytics |
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. `docker compose -f docker/docker-compose.yml up -d` — Start PostgreSQL, Redis, MinIO
|
||||
2. `cp apps/api/.env.example apps/api/.env` — Configure env vars
|
||||
3. `pnpm install` — Install dependencies
|
||||
4. `pnpm --filter api db:push && pnpm --filter api db:seed` — Setup database
|
||||
5. `pnpm dev` — Start all services
|
||||
6. Open `http://localhost:3000` — Frontend
|
||||
7. Admin login: `admin@sase.tr` / `Sase2026`
|
||||
|
||||
---
|
||||
|
||||
## Pricing Model
|
||||
|
||||
| Plan | Brands | Monthly | Yearly |
|
||||
@@ -695,4 +820,11 @@ pm2 reload all # Zero-downtime reload
|
||||
|
||||
---
|
||||
|
||||
*Generated: 2026-02-15 | Branch: v2*
|
||||
## Documentation
|
||||
|
||||
| File | Topic |
|
||||
|------|-------|
|
||||
| `docs/INDEX.md` | This file — comprehensive project reference |
|
||||
| `CLAUDE.md` | Claude Code project guide |
|
||||
| `.claude/product-marketing-context.md` | Marketing context |
|
||||
| `docs/00-overview.md` — `docs/13-analytics-posthog.md` | Detailed topic docs |
|
||||
|
||||
Reference in New Issue
Block a user