diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..939a2c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/data/ +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fa32d46 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM denoland/deno:2.2.2 AS builder +WORKDIR /app +COPY deno.json . +COPY src/ src/ +RUN deno compile --allow-net --allow-read --allow-write --allow-env --output reconciler src/main.ts + +FROM gcr.io/distroless/cc-debian12 +COPY --from=builder /app/reconciler /usr/local/bin/reconciler +ENTRYPOINT ["reconciler"] diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..58086cf --- /dev/null +++ b/deno.json @@ -0,0 +1,18 @@ +{ + "name": "@blastpilot/netbird-reconciler", + "version": "0.1.0", + "tasks": { + "dev": "deno run --allow-net --allow-read --allow-write --allow-env --watch src/main.ts", + "start": "deno run --allow-net --allow-read --allow-write --allow-env src/main.ts", + "test": "deno test --allow-net --allow-read --allow-write --allow-env", + "check": "deno check src/main.ts", + "lint": "deno lint", + "fmt": "deno fmt" + }, + "imports": { + "zod": "npm:zod@^3.23.0" + }, + "compilerOptions": { + "strict": true + } +} diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..14f9d72 --- /dev/null +++ b/deno.lock @@ -0,0 +1,16 @@ +{ + "version": "5", + "specifiers": { + "npm:zod@^3.23.0": "3.25.76" + }, + "npm": { + "zod@3.25.76": { + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==" + } + }, + "workspace": { + "dependencies": [ + "npm:zod@^3.23.0" + ] + } +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..3696b23 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; + +const ConfigSchema = z.object({ + netbirdApiUrl: z.string().url(), + netbirdApiToken: z.string().min(1), + giteaUrl: z.string().url(), + giteaToken: z.string().min(1), + giteaRepo: z.string().regex(/^[^/]+\/[^/]+$/), // owner/repo + reconcilerToken: z.string().min(1), + pollIntervalSeconds: z.coerce.number().int().positive().default(30), + port: z.coerce.number().int().positive().default(8080), + dataDir: z.string().default("/data"), +}); + +export type Config = z.infer; + +export function loadConfig(): Config { + return ConfigSchema.parse({ + netbirdApiUrl: Deno.env.get("NETBIRD_API_URL"), + netbirdApiToken: Deno.env.get("NETBIRD_API_TOKEN"), + giteaUrl: Deno.env.get("GITEA_URL"), + giteaToken: Deno.env.get("GITEA_TOKEN"), + giteaRepo: Deno.env.get("GITEA_REPO"), + reconcilerToken: Deno.env.get("RECONCILER_TOKEN"), + pollIntervalSeconds: Deno.env.get("POLL_INTERVAL_SECONDS"), + port: Deno.env.get("PORT"), + dataDir: Deno.env.get("DATA_DIR"), + }); +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..bd93efe --- /dev/null +++ b/src/main.ts @@ -0,0 +1,4 @@ +import { loadConfig } from "./config.ts"; + +const config = loadConfig(); +console.log(JSON.stringify({ msg: "starting", port: config.port }));