Files
sell/middlelayer/index.ts

120 lines
3.9 KiB
TypeScript

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<GraphQLContext>({
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);
});