grants/server/index.ts
gdegelas a05331128b Atlas Green Morocco — grant strategy platform
- Full grant strategy framework for renewable energy & green hydrogen
- AI-powered grant studio, partner outreach, financial modeling
- Umami analytics with data-performance tracking
- Live Degelas metrics connected to solar.degelas.be
- Trilingual (EN/FR/AR) with i18n support
- Dockerized with Nginx frontend + Express API proxy
2026-06-01 09:44:03 +00:00

81 lines
2.9 KiB
TypeScript

import express from "express";
import cors from "cors";
import helmet from "helmet";
import "dotenv/config";
import { validateEnv, getEnv } from "./lib/env";
import { generateRoute } from "./routes/generate";
import { degelasMetricsRoute } from "./routes/degelas";
import { vaultSaveRoute, vaultDocsRoute } from "./routes/vault";
import { apiLimiter, healthLimiter } from "./middleware/rateLimit";
import { requestLogger } from "./middleware/logger";
import { errorHandler, notFoundHandler } from "./middleware/errorHandler";
// Validate environment on startup
validateEnv();
const { PORT, CLIENT_ORIGIN } = getEnv();
const app = express();
// ── Security Middleware ───────────────────────────────────────────────────────
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "blob:"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "blob:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
crossOriginEmbedderPolicy: false, // Needed for some frontend features
}));
// CORS with validated origin
app.use(cors({
origin: CLIENT_ORIGIN === "*" ? true : CLIENT_ORIGIN.split(",").map((o) => o.trim()),
methods: ["GET", "POST"],
allowedHeaders: ["Content-Type", "X-Request-ID"],
exposedHeaders: ["X-Request-ID"],
credentials: false,
maxAge: 600, // 10 minutes
}));
// Request logging
app.use(requestLogger);
// Body parser with size limit
app.use(express.json({ limit: "1mb" }));
app.use(express.urlencoded({ extended: true, limit: "1mb" }));
// ── Routes ────────────────────────────────────────────────────────────────────
// Health check (high rate limit)
app.get("/api/health", healthLimiter, (_req, res) => {
res.json({ status: "ok", timestamp: new Date().toISOString() });
});
// AI generation (rate limited)
app.post("/api/ai/generate", apiLimiter, generateRoute);
// Degelas metrics (rate limited)
app.get("/api/degelas/metrics", apiLimiter, degelasMetricsRoute);
// Vault persistence
app.post("/api/vault/save", apiLimiter, vaultSaveRoute);
app.get("/api/vault/docs", apiLimiter, vaultDocsRoute);
// 404 handler
app.use(notFoundHandler);
// Global error handler
app.use(errorHandler);
// ── Start Server ──────────────────────────────────────────────────────────────
app.listen(PORT, () => {
console.log(`✅ Atlas Green API proxy running on port ${PORT}`);
console.log(` Environment: ${process.env.NODE_ENV || "development"}`);
});