import { logInfo } from "@dpu/shared/dist/logger.js"; import fastifySwagger from "@fastify/swagger"; import fastifySwaggerUi from "@fastify/swagger-ui"; import type { FastifyReply, FastifyRequest } from "fastify"; import Fastify from "fastify"; import fastifyAxios from "fastify-axios"; import { jsonSchemaTransform, serializerCompiler, validatorCompiler, type ZodTypeProvider, } from "fastify-type-provider-zod"; import { SwaggerTheme, SwaggerThemeNameEnum } from "swagger-themes"; import { z } from "zod"; import { Config } from "./config.js"; import { HomeAssistantClient } from "./homeassistant/client.js"; import { homeAssistantRoutes } from "./homeassistant/routes.js"; import { HomeAssistantService } from "./homeassistant/service.js"; import { TidalClient } from "./tidal/client.js"; import { TidalService } from "./tidal/service.js"; import { tidalRoutes } from "./tidal/routes.js"; import { sseRoutes } from "./sse/routes.js"; import { SseService } from "@dpu/shared"; import { HomepageService } from "./homepage/service.js"; import { homepageRoutes } from "./homepage/routes.js"; import fastifySSE from "@fastify/sse"; const fastify = Fastify().withTypeProvider(); fastify.setValidatorCompiler(validatorCompiler); fastify.setSerializerCompiler(serializerCompiler); await fastify.register(fastifySwagger, { openapi: { info: { title: "DPU API", description: "API Documentation", version: "1.0.0", }, servers: [ { url: "http://localhost:8080", description: "dev" }, { url: "https://dpu.dariusbag.dev/api", description: "prod" }, ], }, transform: jsonSchemaTransform, }); const theme = new SwaggerTheme(); const content = theme.getBuffer(SwaggerThemeNameEnum.ONE_DARK); await fastify.register(fastifySwaggerUi, { routePrefix: "/docs", indexPrefix: `${Config.env_dev ? "" : "/api"}`, uiConfig: { docExpansion: "list", deepLinking: false, }, theme: { css: [{ filename: "theme.css", content: content }], }, }); await fastify.register(fastifyAxios, { clients: { homeassistant: { baseURL: Config.homeassistant.api_url, headers: { Authorization: `Bearer ${Config.homeassistant.api_token}`, }, }, tidal: { baseURL: `${Config.tidal.host}:${Config.tidal.port}`, }, }, }); await fastify.register(fastifySSE); const haClient = new HomeAssistantClient(fastify.axios.homeassistant); const haService = new HomeAssistantService(haClient); const tidalClient = new TidalClient(fastify.axios.tidal); const tidalService = new TidalService(tidalClient); const sseService = new SseService(); const hpService = new HomepageService(haService, tidalService, sseService); async function verifyAPIKey( request: FastifyRequest, reply: FastifyReply, ): Promise { const apiKey = request.headers["x-api-key"]; if (!apiKey || apiKey !== Config.api_key) { logInfo("POST Request with wrong API key received"); return reply.code(401).send({ error: "Invalid API key" }); } } const port = parseInt(Config.port, 10); // Register routes await fastify.register(homeAssistantRoutes, { haService, verifyAPIKey }); await fastify.register(tidalRoutes, { tidalService, verifyAPIKey }); await fastify.register(sseRoutes, { sseService, verifyAPIKey }); await fastify.register(homepageRoutes, { hpService, verifyAPIKey }); fastify.get( "/ping", { schema: { description: "Health check endpoint", tags: ["default"], response: { 200: z.literal("pong"), }, }, }, async (_request, _reply) => { return "pong" as const; }, ); await fastify.ready(); fastify.listen({ port: port, host: "0.0.0.0" }, (err, address) => { if (err) { console.error(err); process.exit(1); } console.log(`Server listening at ${address}`); });