privatization

This commit is contained in:
Darius
2026-02-09 00:07:53 +01:00
parent ff87c3b5ce
commit 92ff251021
9 changed files with 212 additions and 113 deletions

1
package-lock.json generated
View File

@@ -25,6 +25,7 @@
"@types/luxon": "^3.7.1", "@types/luxon": "^3.7.1",
"@types/node": "^24.10.1", "@types/node": "^24.10.1",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"openapi-types": "^12.1.3",
"tsx": "^4.20.6", "tsx": "^4.20.6",
"typescript": "^5.9.3" "typescript": "^5.9.3"
} }

View File

@@ -31,6 +31,7 @@
"@types/luxon": "^3.7.1", "@types/luxon": "^3.7.1",
"@types/node": "^24.10.1", "@types/node": "^24.10.1",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"openapi-types": "^12.1.3",
"tsx": "^4.20.6", "tsx": "^4.20.6",
"typescript": "^5.9.3" "typescript": "^5.9.3"
} }

View File

@@ -3,7 +3,7 @@ import type { FastifyInstance } from "fastify";
import { z } from "zod"; import { z } from "zod";
import type { GristService } from "./service"; import type { GristService } from "./service";
export async function gristRoutes( export async function privateGristRoutes(
fastify: FastifyInstance, fastify: FastifyInstance,
{ {
gristService, gristService,

View File

@@ -1,9 +1,9 @@
import type { HomeAssistantDeskPositionResult } from "@dpu/shared"; import type { HomeAssistantDeskPositionResult } from "@dpu/shared";
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { z } from "zod"; import { z } from "zod";
import type { HomeAssistantService } from "../homeassistant/service.js"; import type { HomeAssistantService } from "./service.js";
export async function homeAssistantRoutes( export async function privateHomeAssistantRoutes(
fastify: FastifyInstance, fastify: FastifyInstance,
{ {
haService, haService,

View File

@@ -1,5 +1,4 @@
import type { import type {
API_HA_DeskPosition,
ComponentUpdate, ComponentUpdate,
GristRecord_PersonalGoals, GristRecord_PersonalGoals,
HA_Update, HA_Update,
@@ -14,7 +13,7 @@ import type { GristService } from "../grist/service.js";
import type { HomeAssistantService } from "../homeassistant/service.js"; import type { HomeAssistantService } from "../homeassistant/service.js";
import type { HomepageService } from "./service.js"; import type { HomepageService } from "./service.js";
export async function homepageRoutes( export async function privateHomepageRoutes(
fastify: FastifyInstance, fastify: FastifyInstance,
{ {
hpService, hpService,
@@ -31,39 +30,6 @@ export async function homepageRoutes(
) => Promise<void>; ) => Promise<void>;
}, },
) { ) {
fastify.get(
"/homepage/status",
{
schema: {
description: "Get all current information for dpu status page",
tags: ["homepage"],
response: {
200: z.object({
ha_desk_position: z.custom<API_HA_DeskPosition>().nullable(),
ha_temp: z.string().nullable(),
tidal_current: z.custom<TidalGetCurrent>().nullable(),
grist_personal_goals: z
.custom<GristRecord_PersonalGoals>()
.nullable(),
}),
418: z.object({
error: z.string(),
}),
},
},
},
async (_request, reply) => {
const service_result = await hpService.getFullInformation();
if (!service_result.successful) {
reply.code(418);
return { error: service_result.result };
}
return service_result.result;
},
);
fastify.post( fastify.post(
"/homepage/update/grist", "/homepage/update/grist",
{ {

View File

@@ -0,0 +1,50 @@
import type {
API_HA_DeskPosition,
GristRecord_PersonalGoals,
TidalGetCurrent,
} from "@dpu/shared";
import type { FastifyInstance } from "fastify";
import { z } from "zod";
import type { HomepageService } from "./service.js";
export async function publicHomepageRoutes(
fastify: FastifyInstance,
{
hpService,
}: {
hpService: HomepageService;
},
) {
fastify.get(
"/homepage/status",
{
schema: {
description: "Get all current information for dpu status page",
tags: ["homepage"],
response: {
200: z.object({
ha_desk_position: z.custom<API_HA_DeskPosition>().nullable(),
ha_temp: z.string().nullable(),
tidal_current: z.custom<TidalGetCurrent>().nullable(),
grist_personal_goals: z
.custom<GristRecord_PersonalGoals>()
.nullable(),
}),
418: z.object({
error: z.string(),
}),
},
},
},
async (_request, reply) => {
const service_result = await hpService.getFullInformation();
if (!service_result.successful) {
reply.code(418);
return { error: service_result.result };
}
return service_result.result;
},
);
}

View File

@@ -1,104 +1,149 @@
import { WsService } from "@dpu/shared"; import { WsService } from "@dpu/shared";
import { logError } from "@dpu/shared/dist/logger.js"; import { logError } from "@dpu/shared/dist/logger.js";
import fastifyCors from "@fastify/cors"; import fastifyCors, { type FastifyCorsOptions } from "@fastify/cors";
import fastifySwagger from "@fastify/swagger"; import fastifySwagger, { type SwaggerOptions } from "@fastify/swagger";
import fastifySwaggerUi from "@fastify/swagger-ui"; import fastifySwaggerUi, {
type FastifySwaggerUiOptions,
} from "@fastify/swagger-ui";
import fastifyWebsocket from "@fastify/websocket"; import fastifyWebsocket from "@fastify/websocket";
import type { FastifyReply, FastifyRequest } from "fastify"; import type { FastifyReply, FastifyRequest } from "fastify";
import Fastify from "fastify"; import Fastify from "fastify";
import fastifyAxios from "fastify-axios"; import fastifyAxios, { type FastifyAxiosOptions } from "fastify-axios";
import { import {
jsonSchemaTransform, jsonSchemaTransform,
serializerCompiler, serializerCompiler,
validatorCompiler, validatorCompiler,
type ZodTypeProvider, type ZodTypeProvider,
} from "fastify-type-provider-zod"; } from "fastify-type-provider-zod";
import type { OpenAPIV3 } from "openapi-types";
import { SwaggerTheme, SwaggerThemeNameEnum } from "swagger-themes"; import { SwaggerTheme, SwaggerThemeNameEnum } from "swagger-themes";
import { z } from "zod"; import { z } from "zod";
import { Config } from "./config.js"; import { Config } from "./config.js";
import { GristClient } from "./grist/client.js"; import { GristClient } from "./grist/client.js";
import { gristRoutes } from "./grist/routes.js"; import { privateGristRoutes } from "./grist/private-routes.js";
import { GristService } from "./grist/service.js"; import { GristService } from "./grist/service.js";
import { HomeAssistantClient } from "./homeassistant/client.js"; import { HomeAssistantClient } from "./homeassistant/client.js";
import { homeAssistantRoutes } from "./homeassistant/routes.js"; import { privateHomeAssistantRoutes } from "./homeassistant/private-routes.js";
import { HomeAssistantService } from "./homeassistant/service.js"; import { HomeAssistantService } from "./homeassistant/service.js";
import { homepageRoutes } from "./homepage/routes.js"; import { privateHomepageRoutes } from "./homepage/private-routes.js";
import { publicHomepageRoutes } from "./homepage/public-routes.js";
import { HomepageService } from "./homepage/service.js"; import { HomepageService } from "./homepage/service.js";
import { TidalClient } from "./tidal/client.js"; import { TidalClient } from "./tidal/client.js";
import { tidalRoutes } from "./tidal/routes.js"; import { privateTidalRoutes } from "./tidal/private-routes.js";
import { TidalService } from "./tidal/service.js"; import { TidalService } from "./tidal/service.js";
import { wsRoutes } from "./websocket/routes.js"; import { publicWsRoutes } from "./websocket/public-routes.js";
const fastify = Fastify().withTypeProvider<ZodTypeProvider>(); // Initialize with Zod
const publicServer = Fastify().withTypeProvider<ZodTypeProvider>();
const privateServer = Fastify().withTypeProvider<ZodTypeProvider>();
publicServer.setValidatorCompiler(validatorCompiler);
publicServer.setSerializerCompiler(serializerCompiler);
privateServer.setValidatorCompiler(validatorCompiler);
privateServer.setSerializerCompiler(serializerCompiler);
fastify.setValidatorCompiler(validatorCompiler); // Cors
fastify.setSerializerCompiler(serializerCompiler); function getCorsObject(urls: string[]): FastifyCorsOptions {
return {
origin: urls,
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization"],
};
}
await fastify.register(fastifyCors, { await publicServer.register(
origin: ["http://localhost:4321", "https://dariusbag.dev"], fastifyCors,
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], getCorsObject(["https://dariusbag.dev"]),
allowedHeaders: ["Content-Type", "Authorization"], );
}); await privateServer.register(
fastifyCors,
getCorsObject(["http://localhost:4321"]),
);
await fastify.register(fastifySwagger, { // Swagger
openapi: { function getSwaggerObject(servers: OpenAPIV3.ServerObject[]): SwaggerOptions {
info: { return {
title: "DPU API", openapi: {
description: "API Documentation", info: {
version: "1.0.0", title: "DPU API",
description: "API Documentation",
version: "1.0.0",
},
servers: servers,
}, },
servers: [ transform: jsonSchemaTransform,
{ url: "http://localhost:8080", description: "dev" }, };
{ url: "https://dpu.dariusbag.dev/api", description: "prod" }, }
],
},
transform: jsonSchemaTransform,
});
await publicServer.register(
fastifySwagger,
getSwaggerObject([
{ url: "https://dpu.dariusbag.dev/api", description: "prod" },
]),
);
await privateServer.register(
fastifySwagger,
getSwaggerObject([
{ url: "http://192.168.178.161:20001", description: "prod" },
]),
);
// Swagger UI
const theme = new SwaggerTheme(); const theme = new SwaggerTheme();
const content = theme.getBuffer(SwaggerThemeNameEnum.ONE_DARK); const content = theme.getBuffer(SwaggerThemeNameEnum.ONE_DARK);
await fastify.register(fastifySwaggerUi, { function getSwaggerUiObject(indexPrefix: string): FastifySwaggerUiOptions {
routePrefix: "/docs", return {
indexPrefix: `${Config.env_dev ? "" : "/api"}`, routePrefix: "/docs",
uiConfig: { indexPrefix: indexPrefix,
docExpansion: "list", uiConfig: {
deepLinking: false, docExpansion: "list",
}, deepLinking: false,
theme: { },
css: [{ filename: "theme.css", content: content }], theme: {
}, css: [{ filename: "theme.css", content: content }],
}); },
};
}
await fastify.register(fastifyAxios, { await publicServer.register(fastifySwaggerUi, getSwaggerUiObject("/api"));
clients: { await privateServer.register(fastifySwaggerUi, getSwaggerUiObject(""));
grist: {
baseURL: Config.grist.api_url, // axios
headers: { function getAxiosConfig(): FastifyAxiosOptions {
Authorization: `Bearer ${Config.grist.api_token}`, return {
clients: {
grist: {
baseURL: Config.grist.api_url,
headers: {
Authorization: `Bearer ${Config.grist.api_token}`,
},
},
homeassistant: {
baseURL: Config.homeassistant.api_url,
headers: {
Authorization: `Bearer ${Config.homeassistant.api_token}`,
},
},
tidal: {
baseURL: `${Config.tidal.address}`,
}, },
}, },
homeassistant: { };
baseURL: Config.homeassistant.api_url, }
headers: {
Authorization: `Bearer ${Config.homeassistant.api_token}`,
},
},
tidal: {
baseURL: `${Config.tidal.address}`,
},
},
});
await fastify.register(fastifyWebsocket); await privateServer.register(fastifyAxios, getAxiosConfig());
const gristClient = new GristClient(fastify.axios.grist); // Websockets
await publicServer.register(fastifyWebsocket);
// Clients and Services
const gristClient = new GristClient(privateServer.axios.grist);
const gristService = new GristService(gristClient); const gristService = new GristService(gristClient);
const haClient = new HomeAssistantClient(fastify.axios.homeassistant); const haClient = new HomeAssistantClient(privateServer.axios.homeassistant);
const haService = new HomeAssistantService(haClient); const haService = new HomeAssistantService(haClient);
const tidalClient = new TidalClient(fastify.axios.tidal); const tidalClient = new TidalClient(privateServer.axios.tidal);
const tidalService = new TidalService(tidalClient); const tidalService = new TidalService(tidalClient);
const wsService = new WsService(); const wsService = new WsService();
@@ -122,21 +167,45 @@ async function verifyAPIKey(
} }
} }
const port = parseInt(Config.port, 10);
// Register routes // Register routes
await fastify.register(gristRoutes, { gristService }); await publicServer.register(publicWsRoutes, { wsService });
await fastify.register(homeAssistantRoutes, { haService, verifyAPIKey }); await publicServer.register(publicHomepageRoutes, {
await fastify.register(tidalRoutes, { tidalService, verifyAPIKey }); hpService,
await fastify.register(wsRoutes, { wsService }); });
await fastify.register(homepageRoutes, {
await privateServer.register(privateGristRoutes, { gristService });
await privateServer.register(privateHomeAssistantRoutes, {
haService,
verifyAPIKey,
});
await privateServer.register(privateTidalRoutes, {
tidalService,
verifyAPIKey,
});
await privateServer.register(privateHomepageRoutes, {
hpService, hpService,
gristService, gristService,
haService, haService,
verifyAPIKey, verifyAPIKey,
}); });
fastify.get( // Ping Routes
publicServer.get(
"/ping",
{
schema: {
description: "Health check endpoint",
tags: ["default"],
response: {
200: z.literal("pong"),
},
},
},
async (_request, _reply) => {
return "pong" as const;
},
);
privateServer.get(
"/ping", "/ping",
{ {
schema: { schema: {
@@ -152,8 +221,20 @@ fastify.get(
}, },
); );
await fastify.ready(); const port = parseInt(Config.port, 10);
fastify.listen({ port: port, host: "0.0.0.0" }, (err, address) => { const publicPort = port + 1;
await publicServer.ready();
await privateServer.ready();
publicServer.listen({ port: publicPort, host: "0.0.0.0" }, (err, address) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log(`Server listening at ${address}`);
});
privateServer.listen({ port: port, host: "0.0.0.0" }, (err, address) => {
if (err) { if (err) {
console.error(err); console.error(err);
process.exit(1); process.exit(1);

View File

@@ -1,8 +1,8 @@
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { z } from "zod"; import { z } from "zod";
import type { TidalService } from "../tidal/service.js"; import type { TidalService } from "./service.js";
export async function tidalRoutes( export async function privateTidalRoutes(
fastify: FastifyInstance, fastify: FastifyInstance,
{ {
tidalService, tidalService,

View File

@@ -1,7 +1,7 @@
import { logInfo, type WsService } from "@dpu/shared"; import { logInfo, type WsService } from "@dpu/shared";
import type { FastifyInstance } from "fastify"; import type { FastifyInstance } from "fastify";
export async function wsRoutes( export async function publicWsRoutes(
fastify: FastifyInstance, fastify: FastifyInstance,
{ {
wsService, wsService,