import { ApolloServer } from "@apollo/server"; import { startStandaloneServer } from "@apollo/server/standalone"; import { createServer } from "http"; import { typeDefs } from "./schema.js"; import { resolvers } from "./resolvers.js"; import { queryComplexityPlugin } from "./plugins/queryComplexity.js"; import { createResponseCachePlugin } from "./plugins/responseCache.js"; import { monitoringPlugin, queryComplexityMonitoringPlugin, } from "./plugins/monitoring.js"; import { createContext, type GraphQLContext } from "./utils/dataloaders.js"; import { logger } from "./monitoring/logger.js"; import { getMetrics } from "./monitoring/metrics.js"; import { extractTokenFromHeader } from "./auth/jwt.js"; import { userService } from "./auth/userService.js"; const PORT = process.env.PORT ? parseInt(process.env.PORT) : 4000; const METRICS_PORT = process.env.METRICS_PORT ? parseInt(process.env.METRICS_PORT) : 9090; // Konfiguration aus Environment Variables const MAX_QUERY_COMPLEXITY = process.env.MAX_QUERY_COMPLEXITY ? parseInt(process.env.MAX_QUERY_COMPLEXITY) : 1000; /** * Startet einen separaten HTTP-Server für Metrics (Prometheus) */ function startMetricsServer() { const server = createServer(async (req, res) => { if (req.url === "/metrics" && req.method === "GET") { try { const metrics = await getMetrics(); res.writeHead(200, { "Content-Type": "text/plain; version=0.0.4" }); res.end(metrics); } catch (error) { logger.error("Failed to get metrics", { error }); res.writeHead(500, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Failed to get metrics" })); } } else if (req.url === "/health" && req.method === "GET") { res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ status: "ok", service: "graphql-middlelayer" })); } else { res.writeHead(404, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Not found" })); } }); server.listen(METRICS_PORT, () => { logger.info( `📊 Metrics Server läuft auf: http://localhost:${METRICS_PORT}/metrics` ); logger.info( `❤️ Health Check verfügbar unter: http://localhost:${METRICS_PORT}/health` ); }); return server; } async function startServer() { const server = new ApolloServer({ typeDefs, resolvers, plugins: [ // Monitoring (muss zuerst sein für vollständiges Tracking) monitoringPlugin(), queryComplexityMonitoringPlugin(), // Query Complexity Limit queryComplexityPlugin({ maxComplexity: MAX_QUERY_COMPLEXITY, defaultComplexity: 1, }), // Response Caching createResponseCachePlugin(), ], }); const { url } = await startStandaloneServer(server, { listen: { port: PORT }, context: async ({ req }) => { // Extrahiere User aus Authorization Header let user = null; const authHeader = req.headers.authorization; const token = extractTokenFromHeader(authHeader || null); if (token) { try { user = await userService.getUserFromToken(token); } catch (error) { logger.warn("Token verification failed", { error }); } } // Erstelle Context mit Dataloadern und User return createContext(user); }, }); // Starte Metrics Server startMetricsServer(); logger.info(`🚀 GraphQL Middlelayer läuft auf: ${url}`); logger.info(`📊 GraphQL Playground verfügbar unter: ${url}`); logger.info(`⚡ Query Complexity Limit: ${MAX_QUERY_COMPLEXITY}`); logger.info(`💾 Response Caching: Aktiviert`); logger.info(`🔄 Dataloader: Aktiviert`); logger.info( `📈 Monitoring: Aktiviert (Structured Logging, Prometheus, Tracing)` ); } startServer().catch((error) => { logger.error("Fehler beim Starten des Servers", { error }); process.exit(1); });