Шаг 0.13 плана production-готовности: - интерфейс PaymentProvider (createPayment/verifyWebhook/getPaymentStatus) - MockPaymentProvider с настоящим webhook (sha256-подпись, идемпотентность) - applySucceededPayment: транзакция с guard, credit, авто-снятие billingBlocked - роуты payments + confirm-test (только mock) + публичный webhook - Billing.tsx: Пополнить/Подтвердить (тест), фикс setLocation в рендере - миграция 0080 billing_payments, 13 новых тестов (78/78)
134 lines
4.9 KiB
TypeScript
134 lines
4.9 KiB
TypeScript
import type { Express } from "express";
|
|
import { createServer, type Server } from "http";
|
|
import cors from 'cors';
|
|
import express from 'express';
|
|
import { pool } from "../db";
|
|
import { apiLimiter, publishNotificationSSE } from "./shared";
|
|
import authRouter from "./auth.routes";
|
|
import filesRouter from "./files.routes";
|
|
import taskRouter from "./task.routes";
|
|
import chatRouter from "./chat.routes";
|
|
import automationRouter from "./automation.routes";
|
|
import adminRouter from "./admin.routes";
|
|
import billingPaymentsRouter from "./billing-payments.routes";
|
|
import messengerRouter from "./messenger.routes";
|
|
import reactionsRouter from "./reactions.routes";
|
|
import pollsRouter from "./polls.routes";
|
|
import orgConnectionsRouter from "./org-connections.routes";
|
|
import rolesRouter from "./roles.routes";
|
|
import homeRouter from "./home.routes";
|
|
import taskInboxHiddenRouter from "./task-inbox-hidden.routes";
|
|
import taskBulkRouter from "./task-bulk.routes";
|
|
import ragRouter from "./rag.routes";
|
|
import llmProvidersRouter from "./llm-providers.routes";
|
|
import errorLogsRouter from "./error-logs.routes";
|
|
import { registerBotRoutes } from "./bots.routes";
|
|
import { registerExternalRoutes } from "./external.routes";
|
|
import { registerBotApiRoutes } from "./bot-api.routes";
|
|
import { registerDataTableRoutes } from "./data-tables.routes";
|
|
import { registerDataTableSyncRoutes } from "./data-tables-sync.routes";
|
|
import { registerContentRoutes } from "./content.routes";
|
|
import { registerPushRoutes } from "./push.routes";
|
|
import { registerMcpRagRoutes } from "./mcp-rag.routes";
|
|
import { registerFieldTemplateInsertRoutes } from "./field-template-insert.routes";
|
|
import { registerDi2Routes } from "../finance-di2";
|
|
import { registerDocumentRoutes } from "../documents/routes";
|
|
import { registerDashboardRoutes } from "./dashboard.routes";
|
|
import formAccessRouter from "./form-access.routes";
|
|
import formOfflineRouter from "./form-offline.routes";
|
|
import { calendarRouter } from "./calendar.routes";
|
|
import { ganttRouter } from "./gantt.routes";
|
|
import organizationSkinsRouter from "./organization-skins.routes";
|
|
import { registerSyncRoutes } from "./sync.routes";
|
|
import { registerMedScheduleRoutes } from "../medschedule/routes";
|
|
import { registerMedScheduleBotRoutes } from "../medschedule/bot.routes";
|
|
import { registerGpsRoutes } from "../gps/routes";
|
|
import { registerVpnRoutes } from "../vpn";
|
|
import { FIELD_TYPE_DEFINITIONS } from "../../shared/field-types";
|
|
export { publishNotificationSSE };
|
|
|
|
export async function registerRoutes(app: Express): Promise<Server> {
|
|
// CORS configuration
|
|
app.use(cors({
|
|
origin: process.env.FRONTEND_URL || 'http://localhost:5000',
|
|
credentials: true,
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
|
allowedHeaders: ['Content-Type', 'Authorization']
|
|
}));
|
|
|
|
// Apply rate limiting
|
|
app.use('/api', apiLimiter);
|
|
|
|
// Health check endpoint
|
|
app.get('/api/health', async (_req, res) => {
|
|
const uptime = process.uptime();
|
|
let dbStatus = 'ok';
|
|
try {
|
|
await pool.query('SELECT 1');
|
|
} catch {
|
|
dbStatus = 'error';
|
|
}
|
|
const status = dbStatus === 'ok' ? 'ok' : 'degraded';
|
|
res.status(dbStatus === 'ok' ? 200 : 503).json({
|
|
status,
|
|
uptime: Math.floor(uptime),
|
|
db: dbStatus,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
});
|
|
|
|
// Field types registry (used by frontend editors)
|
|
app.get('/api/field-types', (_req, res) => {
|
|
res.json({ success: true, fieldTypes: FIELD_TYPE_DEFINITIONS });
|
|
});
|
|
|
|
// Route modules
|
|
app.use(filesRouter);
|
|
app.use(authRouter);
|
|
app.use(taskBulkRouter);
|
|
app.use(taskRouter);
|
|
app.use(chatRouter);
|
|
app.use(automationRouter);
|
|
app.use(adminRouter);
|
|
app.use(billingPaymentsRouter);
|
|
app.use(messengerRouter);
|
|
app.use(reactionsRouter);
|
|
app.use(pollsRouter);
|
|
app.use(orgConnectionsRouter);
|
|
app.use(rolesRouter);
|
|
app.use(homeRouter);
|
|
app.use(taskInboxHiddenRouter);
|
|
app.use(ragRouter);
|
|
app.use(llmProvidersRouter);
|
|
app.use(errorLogsRouter);
|
|
|
|
// Domain route registrations
|
|
registerBotRoutes(app);
|
|
registerExternalRoutes(app);
|
|
registerBotApiRoutes(app);
|
|
registerDataTableRoutes(app);
|
|
registerDataTableSyncRoutes(app);
|
|
registerContentRoutes(app);
|
|
registerPushRoutes(app);
|
|
registerMcpRagRoutes(app);
|
|
registerFieldTemplateInsertRoutes(app);
|
|
// Data-Insight2 (финансы v2) — старый модуль финансов удалён, DI2 единственный источник
|
|
await registerDi2Routes(app);
|
|
await registerDocumentRoutes(app);
|
|
registerDashboardRoutes(app);
|
|
app.use(formAccessRouter);
|
|
app.use(formOfflineRouter);
|
|
app.use(calendarRouter);
|
|
app.use(ganttRouter);
|
|
app.use(organizationSkinsRouter);
|
|
registerSyncRoutes(app);
|
|
registerMedScheduleRoutes(app);
|
|
registerMedScheduleBotRoutes(app);
|
|
// GPS-модуль: публичный /api/gps/ingest регистрируется внутри до authenticateToken
|
|
registerGpsRoutes(app);
|
|
registerVpnRoutes(app);
|
|
|
|
const httpServer = createServer(app);
|
|
return httpServer;
|
|
}
|