Files
cloudflare-domain-manager/apps/api/src/routes/integrations-vps-tracker.ts
T
DenozordecandCursor c01c75116a
Build and Push CFDM Docker Image / build-and-push (push) Successful in 2m16s
Build and Push CFDM Docker Image / create-release (push) Skipped
Build and Push CFDM Docker Image / update-wiki (push) Successful in 6s
fix(integrations): разворачивать CNAME до IP при sync в VPS Tracker
CNAME-bindings отдавали пустой ips[]; теперь IP берутся из локальной цепочки bindings, DNS resolve4 или IP сервиса — чтобы vps-tracker мог матчить домен к VPS.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 00:59:01 +07:00

73 lines
2.5 KiB
TypeScript

import { timingSafeEqual } from "node:crypto";
import type { FastifyInstance } from "fastify";
import { vpsTrackerEventSchema } from "@cfdm/shared";
import { getAppSettingsSecrets } from "@cfdm/db";
import * as vpsTrackerEvents from "../services/vps-tracker-events.js";
import { buildAllSyncBindings } from "../services/vps-tracker-sync.js";
function verifyBearer(authHeader: string | undefined, expected: string): boolean {
if (!authHeader?.startsWith("Bearer ")) return false;
const token = authHeader.slice(7);
if (!token || !expected) return false;
const a = Buffer.from(token);
const b = Buffer.from(expected);
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}
export async function integrationsVpsTrackerRoutes(app: FastifyInstance) {
app.post("/integrations/vps-tracker/events", async (req, reply) => {
const secrets = getAppSettingsSecrets(app.db);
const token = secrets.vpsTrackerIntegrationToken;
if (!token) {
return reply.status(503).send({ error: "Integration not configured" });
}
if (!verifyBearer(req.headers.authorization, token)) {
return reply.status(401).send({ error: "Unauthorized" });
}
const parsed = vpsTrackerEventSchema.safeParse(req.body);
if (!parsed.success) {
return reply.status(400).send({ error: parsed.error.flatten() });
}
if (parsed.data.event !== "vps_down") {
return { ok: true, reconciled: 0 };
}
const reconciled = await vpsTrackerEvents.reconcileForVpsDown(
app.db,
app.cf,
parsed.data,
);
return { ok: true, reconciled };
});
/**
* Полная синхронизация по запросу VPS Tracker (pull).
* Bindings возвращаются в ответе — клиент применяет их локально.
* Не требует доступности VPS Tracker URL из CFDM.
*/
app.post("/integrations/vps-tracker/sync", async (req, reply) => {
const secrets = getAppSettingsSecrets(app.db);
const token = secrets.vpsTrackerIntegrationToken;
if (!token) {
return reply.status(503).send({
ok: false,
error: "Integration not configured",
});
}
if (!verifyBearer(req.headers.authorization, token)) {
return reply.status(401).send({ ok: false, error: "Unauthorized" });
}
const bindings = await buildAllSyncBindings(app.db);
return {
ok: true,
count: bindings.length,
bindings,
fullSync: true,
};
});
}