project setup with core files including configuration, package management, and basic structure. Added .gitignore, README, and various TypeScript types for CMS components. Implemented initial components and layouts for the application.

This commit is contained in:
Peter Meier
2025-12-13 23:26:13 +01:00
parent ea288a5bbc
commit b1a556dc6d
167 changed files with 19057 additions and 131 deletions

119
middlelayer/index.ts Normal file
View File

@@ -0,0 +1,119 @@
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);
});