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:
56
packages/config/src/index.ts
Normal file
56
packages/config/src/index.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const envSchema = z.object({
|
||||
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
||||
PORT: z.coerce.number().default(4000),
|
||||
|
||||
DATABASE_URL: z.string().url(),
|
||||
|
||||
REDIS_HOST: z.string().default("127.0.0.1"),
|
||||
REDIS_PORT: z.coerce.number().default(6379),
|
||||
REDIS_PASSWORD: z.string(),
|
||||
|
||||
BETTER_AUTH_SECRET: z.string().min(32),
|
||||
BETTER_AUTH_URL: z.string().url(),
|
||||
|
||||
MINIO_ENDPOINT: z.string(),
|
||||
MINIO_ACCESS_KEY: z.string(),
|
||||
MINIO_SECRET_KEY: z.string(),
|
||||
MINIO_BUCKET_NAME: z.string().default("sase-schemas"),
|
||||
MINIO_PUBLIC_URL: z.string(),
|
||||
MINIO_USE_SSL: z
|
||||
.string()
|
||||
.transform((v) => v === "true")
|
||||
.default("false"),
|
||||
|
||||
CORS_ORIGIN: z.string().default("http://localhost:3000"),
|
||||
|
||||
IYZICO_API_KEY: z.string().optional(),
|
||||
IYZICO_SECRET_KEY: z.string().optional(),
|
||||
IYZICO_BASE_URL: z.string().optional(),
|
||||
|
||||
PL24_API_URL: z.string().optional(),
|
||||
PL24_USERNAME: z.string().optional(),
|
||||
PL24_PASSWORD: z.string().optional(),
|
||||
|
||||
EMEX_USERNAME: z.string().optional(),
|
||||
EMEX_PASSWORD: z.string().optional(),
|
||||
|
||||
ML_PREDICTION_ENABLED: z
|
||||
.string()
|
||||
.transform((v) => v === "true")
|
||||
.default("false"),
|
||||
});
|
||||
|
||||
export type Env = z.infer<typeof envSchema>;
|
||||
|
||||
export function validateEnv(env: Record<string, unknown> = process.env as Record<string, unknown>): Env {
|
||||
const result = envSchema.safeParse(env);
|
||||
if (!result.success) {
|
||||
const formatted = result.error.format();
|
||||
console.error("Environment validation failed:");
|
||||
console.error(JSON.stringify(formatted, null, 2));
|
||||
throw new Error("Invalid environment variables");
|
||||
}
|
||||
return result.data;
|
||||
}
|
||||
Reference in New Issue
Block a user