feat: sase.tr v2 full application implementation
Complete rewrite of sase.tr VIN lookup platform with modern stack: Backend (NestJS 10 + Drizzle ORM + PostgreSQL + Redis + BullMQ): - 34 DB models (core + PL24 + EMEX schemas) - Auth via Better Auth (email/password + social) - Brands, Plans, Subscriptions, Payments (iyzico + EFT) - VIN decode orchestration (Corgi + PL24 + EMEX + NHTSA) - Interactive schema viewer backend (MinIO storage) - EMEX scraping integration (Puppeteer + BullMQ workers) - Translation module (EN→TR automotive dictionary) - Admin dashboard API (stats, user mgmt, payment approval) - Rate limiting, Helmet security, file upload validation Frontend (Next.js 15 + Tailwind v4 + shadcn/ui + TanStack Query + Zustand): - 20 routes: auth, dashboard, VIN search, schema viewer, admin - Interactive schema viewer with zoom/pan/hotspot highlighting - Subscription management with brand selector - Payment flow (iyzico 3D Secure + EFT with receipt upload) - i18n support (TR/EN) - Error boundaries, loading skeletons, 404 page Infrastructure: - 85 tests (52 backend + 33 frontend, Vitest) - CI/CD (GitHub Actions: lint, typecheck, test, build, deploy) - Zero-downtime deploy script (PM2) - Env validation script Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
70
apps/api/src/auth/auth.ts
Normal file
70
apps/api/src/auth/auth.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import * as schema from "../database/schema/core";
|
||||
|
||||
let authInstance: ReturnType<typeof betterAuth> | null = null;
|
||||
|
||||
export function createAuth(databaseUrl: string, secret: string, baseUrl: string) {
|
||||
if (authInstance) return authInstance;
|
||||
|
||||
const client = postgres(databaseUrl, { max: 5 });
|
||||
const db = drizzle(client, { schema });
|
||||
|
||||
authInstance = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
usePlural: true,
|
||||
}),
|
||||
secret,
|
||||
baseURL: baseUrl,
|
||||
basePath: "/api/auth",
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
minPasswordLength: 8,
|
||||
},
|
||||
socialProviders: {
|
||||
google: {
|
||||
clientId: process.env.GOOGLE_CLIENT_ID || "",
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
|
||||
enabled: !!(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET),
|
||||
},
|
||||
},
|
||||
session: {
|
||||
cookieCache: {
|
||||
enabled: true,
|
||||
maxAge: 60 * 5, // 5 minutes
|
||||
},
|
||||
},
|
||||
user: {
|
||||
additionalFields: {
|
||||
role: {
|
||||
type: "string",
|
||||
defaultValue: "user",
|
||||
input: false,
|
||||
},
|
||||
referralCode: {
|
||||
type: "string",
|
||||
required: false,
|
||||
input: false,
|
||||
},
|
||||
referredBy: {
|
||||
type: "string",
|
||||
required: false,
|
||||
input: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
trustedOrigins: (process.env.CORS_ORIGIN || "http://localhost:3000").split(","),
|
||||
});
|
||||
|
||||
return authInstance;
|
||||
}
|
||||
|
||||
export function getAuth() {
|
||||
if (!authInstance) {
|
||||
throw new Error("Auth not initialized. Call createAuth first.");
|
||||
}
|
||||
return authInstance;
|
||||
}
|
||||
Reference in New Issue
Block a user