refractor code,

This commit is contained in:
root
2026-05-21 12:35:49 -04:00
parent e74681e810
commit f009ca10c6
158 changed files with 215801 additions and 5884 deletions
+4 -194
View File
@@ -1,7 +1,3 @@
import express from 'express'
import cors from 'cors'
import helmet from 'helmet'
import morgan from 'morgan'
import http from 'http'
import { Server as SocketIOServer } from 'socket.io'
import cron from 'node-cron'
@@ -9,72 +5,12 @@ import jwt from 'jsonwebtoken'
import { z } from 'zod'
import { redis } from './lib/redis'
import { prisma } from './lib/prisma'
import { getStorageRoot } from './lib/storage'
import { authLimiter, apiLimiter, publicLimiter, adminLimiter } from './middleware/rateLimiter'
import { assertStorageConfiguration } from './lib/storage'
import { createApp, corsOrigins } from './app'
// ─── Routes ───────────────────────────────────────────────────
import webhookRouter from './routes/webhooks'
import companyAuthRouter from './routes/auth.company'
import employeeAuthRouter from './routes/auth.employee'
import renterAuthRouter from './routes/auth.renter'
import vehiclesRouter from './routes/vehicles'
import reservationsRouter from './routes/reservations'
import teamRouter from './routes/team'
import customersRouter from './routes/customers'
import offersRouter from './routes/offers'
import analyticsRouter from './routes/analytics'
import notificationsRouter from './routes/notifications'
import marketplaceRouter from './routes/marketplace'
import adminRouter from './routes/admin'
import companiesRouter from './routes/companies'
import subscriptionsRouter from './routes/subscriptions'
import siteRouter from './routes/site'
import paymentsRouter from './routes/payments'
const app = express()
const app = createApp()
const server = http.createServer(app)
// Trust the first hop from a reverse proxy so req.ip and rate-limiting
// use the real client IP (from X-Forwarded-For) rather than the proxy's IP.
if (process.env.NODE_ENV === 'production') {
app.set('trust proxy', 1)
}
const v1 = '/api/v1'
const defaultCorsOrigins = [
'http://localhost:3000',
'http://localhost:3001',
'http://localhost:3002',
'http://localhost:3003',
'http://127.0.0.1:3000',
'http://127.0.0.1:3001',
'http://127.0.0.1:3002',
'http://127.0.0.1:3003',
]
const corsOrigins = process.env.CORS_ORIGINS
? process.env.CORS_ORIGINS.split(',').map((origin) => origin.trim()).filter(Boolean)
: defaultCorsOrigins
const routeDocs = [
{ method: 'GET', path: '/health', description: 'Health check' },
{ method: 'GET', path: `${v1}/docs`, description: 'Machine-readable API index' },
{ method: 'GET', path: `${v1}/auth/renter/me`, description: 'Current renter profile' },
{ method: 'GET', path: `${v1}/vehicles`, description: 'List company vehicles' },
{ method: 'POST', path: `${v1}/vehicles`, description: 'Create vehicle' },
{ method: 'GET', path: `${v1}/reservations`, description: 'List reservations' },
{ method: 'POST', path: `${v1}/reservations`, description: 'Create reservation' },
{ method: 'GET', path: `${v1}/customers`, description: 'List customers' },
{ method: 'POST', path: `${v1}/customers`, description: 'Create customer' },
{ method: 'GET', path: `${v1}/offers`, description: 'List offers' },
{ method: 'GET', path: `${v1}/analytics/dashboard`, description: 'Dashboard analytics' },
{ method: 'GET', path: `${v1}/analytics/report`, description: 'Analytics report' },
{ method: 'GET', path: `${v1}/notifications/company`, description: 'Company notifications' },
{ method: 'GET', path: `${v1}/marketplace/cities`, description: 'Marketplace cities' },
{ method: 'GET', path: `${v1}/marketplace/search`, description: 'Marketplace search' },
{ method: 'GET', path: `${v1}/site/:slug/brand`, description: 'Public site brand config' },
{ method: 'GET', path: `${v1}/companies/me`, description: 'Current company profile' },
{ method: 'GET', path: `${v1}/subscriptions/plans`, description: 'Subscription plans' },
{ method: 'POST', path: `${v1}/admin/auth/login`, description: 'Admin login' },
]
assertStorageConfiguration()
// ─── Socket.io ────────────────────────────────────────────────
const io = new SocketIOServer(server, {
@@ -122,132 +58,6 @@ subscriber.on('pmessage', (_pattern, channel, message) => {
}
})
// ─── Middleware ────────────────────────────────────────────────
// Serve uploaded assets from the configured storage root.
app.use('/storage', express.static(getStorageRoot()))
// Webhook must use raw body BEFORE express.json()
app.use('/api/v1/webhooks', express.raw({ type: 'application/json' }), webhookRouter)
app.use(helmet())
app.use(cors({ origin: corsOrigins, credentials: true }))
app.use(morgan('combined'))
app.use(express.json({ limit: '10mb' }))
// ─── API Routes ───────────────────────────────────────────────
// Auth routes: strict brute-force protection
app.use(`${v1}/auth/renter`, authLimiter, renterAuthRouter)
app.use(`${v1}/auth/company`, authLimiter, companyAuthRouter)
app.use(`${v1}/auth/employee`, authLimiter, employeeAuthRouter)
// Admin routes: dedicated limit
app.use(`${v1}/admin`, adminLimiter, adminRouter)
// Public unauthenticated routes
app.use(`${v1}/marketplace`, publicLimiter, marketplaceRouter)
app.use(`${v1}/site`, publicLimiter, siteRouter)
// Authenticated company/renter routes
app.use(`${v1}/vehicles`, apiLimiter, vehiclesRouter)
app.use(`${v1}/reservations`, apiLimiter, reservationsRouter)
app.use(`${v1}/team`, apiLimiter, teamRouter)
app.use(`${v1}/customers`, apiLimiter, customersRouter)
app.use(`${v1}/offers`, apiLimiter, offersRouter)
app.use(`${v1}/analytics`, apiLimiter, analyticsRouter)
app.use(`${v1}/notifications`, apiLimiter, notificationsRouter)
app.use(`${v1}/companies`, apiLimiter, companiesRouter)
app.use(`${v1}/subscriptions`, apiLimiter, subscriptionsRouter)
app.use(`${v1}/payments`, apiLimiter, paymentsRouter)
// ─── Health check ─────────────────────────────────────────────
app.get('/health', (_req, res) => {
res.json({ status: 'ok', version: '1.0.0', timestamp: new Date().toISOString() })
})
app.get(`${v1}/docs`, (_req, res) => {
res.json({
name: 'rentaldrivego-api',
version: '1.0.0',
baseUrl: v1,
docsUrl: '/docs',
routes: routeDocs,
})
})
app.get('/docs', (_req, res) => {
const rows = routeDocs
.map(
(route) => `
<tr>
<td>${route.method}</td>
<td><code>${route.path}</code></td>
<td>${route.description}</td>
</tr>`,
)
.join('')
res.type('html').send(`<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>RentalDriveGo API Docs</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; color: #0f172a; background: #f8fafc; }
h1 { margin-bottom: 8px; }
p { color: #475569; }
.meta { margin-bottom: 24px; }
table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 12px; overflow: hidden; }
th, td { text-align: left; padding: 12px 16px; border-bottom: 1px solid #e2e8f0; }
th { background: #e2e8f0; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
code { background: #f1f5f9; padding: 2px 6px; border-radius: 6px; }
</style>
</head>
<body>
<h1>RentalDriveGo API</h1>
<p class="meta">Base URL: <code>${v1}</code> · JSON index: <code>${v1}/docs</code></p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Path</th>
<th>Description</th>
</tr>
</thead>
<tbody>${rows}</tbody>
</table>
</body>
</html>`)
})
// ─── Error handler ────────────────────────────────────────────
app.use((err: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
const statusCode = err.statusCode ?? 500
const message = err.message ?? 'Internal server error'
const code = err.code ?? 'internal_error'
if (statusCode >= 500) {
console.error('[API Error]', err)
}
// Zod validation error
if (err.name === 'ZodError') {
return res.status(400).json({ error: 'validation_error', message: 'Invalid request body', issues: err.issues, statusCode: 400 })
}
// Prisma not found
if (err.code === 'P2025') {
return res.status(404).json({ error: 'not_found', message: 'Resource not found', statusCode: 404 })
}
// Prisma unique constraint
if (err.code === 'P2002') {
return res.status(409).json({ error: 'conflict', message: 'A resource with this value already exists', statusCode: 409 })
}
res.status(statusCode).json({ error: code, message, statusCode })
})
// ─── Scheduled jobs ───────────────────────────────────────────
// Daily: flag expiring/expired licenses