feat: интеграция с VPS Tracker
Build, Test, and Push CFDM Docker Image / test (push) Failing after 3m36s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been skipped
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been skipped
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped

Исходящий sync bindings после updateConfig, настройки в app_settings, страница Интеграции, приём событий vps_down для DNS failover.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-06-30 16:45:17 +07:00
co-authored by Cursor
parent 859918fae2
commit 96783386e0
32 changed files with 2116 additions and 243 deletions
+180 -73
View File
@@ -184,6 +184,18 @@ var ipHealthStatus = sqliteTable(
},
(t) => [primaryKey({ columns: [t.scope, t.ref_id, t.ip] })]
);
var appSettings = sqliteTable("app_settings", {
id: text("id").primaryKey(),
app_switcher_json: text("app_switcher_json"),
vps_tracker_url: text("vps_tracker_url"),
vps_tracker_integration_token: text("vps_tracker_integration_token"),
vps_tracker_sync_enabled: integer("vps_tracker_sync_enabled", {
mode: "boolean"
}).notNull().default(false),
vps_tracker_last_sync_at: text("vps_tracker_last_sync_at"),
created_at: text("created_at").notNull().default(sql`datetime('now')`),
updated_at: text("updated_at").notNull().default(sql`datetime('now')`)
});
var schema = {
groups,
services,
@@ -198,7 +210,8 @@ var schema = {
serviceGroupDnsRecords,
certificates,
syncJobs,
ipHealthStatus
ipHealthStatus,
appSettings
};
// src/client.ts
@@ -263,6 +276,94 @@ var ConflictError = class extends Error {
}
};
// src/settings-repo.ts
import { eq } from "drizzle-orm";
import { appSwitcherConfigSchema } from "@cfdm/shared";
var SETTINGS_ID = "settings-main";
var DEFAULT_APP_SWITCHER = {
menuLabel: "\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F",
apps: [
{
id: "vps-tracker",
name: "VPS Tracker",
subtitle: "\u0423\u0447\u0451\u0442 \u0432\u0438\u0440\u0442\u0443\u0430\u043B\u044C\u043D\u044B\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u0432",
url: "http://192.168.100.67:3001",
icon: "server",
shortcut: "\u23181"
},
{
id: "cfdm",
name: "CF Domain Manager",
subtitle: "\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0434\u043E\u043C\u0435\u043D\u0430\u043C\u0438",
url: "http://192.168.100.67:6363",
icon: "cloud",
shortcut: "\u23182"
}
]
};
function parseAppSwitcher(raw) {
if (!raw?.trim()) return DEFAULT_APP_SWITCHER;
try {
return appSwitcherConfigSchema.parse(JSON.parse(raw));
} catch {
return DEFAULT_APP_SWITCHER;
}
}
function toDto(row) {
return {
id: row.id,
appSwitcher: parseAppSwitcher(row.app_switcher_json),
vpsTrackerUrl: row.vps_tracker_url?.trim() ?? "",
vpsTrackerIntegrationTokenSet: Boolean(
row.vps_tracker_integration_token?.trim()
),
vpsTrackerSyncEnabled: Boolean(row.vps_tracker_sync_enabled),
vpsTrackerLastSyncAt: row.vps_tracker_last_sync_at
};
}
function getAppSettings(db) {
const row = db.select().from(appSettings).where(eq(appSettings.id, SETTINGS_ID)).get();
if (!row) {
db.insert(appSettings).values({ id: SETTINGS_ID }).run();
return toDto(
db.select().from(appSettings).where(eq(appSettings.id, SETTINGS_ID)).get()
);
}
return toDto(row);
}
function getAppSettingsSecrets(db) {
const row = db.select().from(appSettings).where(eq(appSettings.id, SETTINGS_ID)).get();
return {
vpsTrackerUrl: row?.vps_tracker_url?.trim() ?? "",
vpsTrackerIntegrationToken: row?.vps_tracker_integration_token?.trim() ?? "",
vpsTrackerSyncEnabled: Boolean(row?.vps_tracker_sync_enabled)
};
}
function updateAppSettings(db, patch) {
const existing = db.select().from(appSettings).where(eq(appSettings.id, SETTINGS_ID)).get();
if (!existing) {
db.insert(appSettings).values({ id: SETTINGS_ID }).run();
}
const current = db.select().from(appSettings).where(eq(appSettings.id, SETTINGS_ID)).get();
db.update(appSettings).set({
app_switcher_json: patch.appSwitcher !== void 0 ? JSON.stringify(patch.appSwitcher) : current.app_switcher_json,
vps_tracker_url: patch.vpsTrackerUrl !== void 0 ? patch.vpsTrackerUrl : current.vps_tracker_url,
vps_tracker_integration_token: patch.vpsTrackerIntegrationToken !== void 0 && patch.vpsTrackerIntegrationToken.trim() !== "" ? patch.vpsTrackerIntegrationToken : current.vps_tracker_integration_token,
vps_tracker_sync_enabled: patch.vpsTrackerSyncEnabled !== void 0 ? patch.vpsTrackerSyncEnabled : current.vps_tracker_sync_enabled,
updated_at: (/* @__PURE__ */ new Date()).toISOString()
}).where(eq(appSettings.id, SETTINGS_ID)).run();
return getAppSettings(db);
}
function touchVpsTrackerSync(db) {
db.update(appSettings).set({
vps_tracker_last_sync_at: (/* @__PURE__ */ new Date()).toISOString(),
updated_at: (/* @__PURE__ */ new Date()).toISOString()
}).where(eq(appSettings.id, SETTINGS_ID)).run();
}
function getAppSwitcher(db) {
return getAppSettings(db).appSwitcher;
}
// src/repos.ts
var repos_exports = {};
__export(repos_exports, {
@@ -357,12 +458,12 @@ __export(repos_exports, {
upsertSubdomain: () => upsertSubdomain
});
import { dnsRecordNamesMatch } from "@cfdm/shared";
import { and, asc, count, eq, isNull, like, notInArray, or, sql as sql2 } from "drizzle-orm";
import { and, asc, count, eq as eq2, isNull, like, notInArray, or, sql as sql2 } from "drizzle-orm";
function listGroups(db) {
return db.select().from(groups).orderBy(asc(groups.name)).all();
}
function getGroup(db, id) {
const row = db.select().from(groups).where(eq(groups.id, id)).get();
const row = db.select().from(groups).where(eq2(groups.id, id)).get();
if (!row) throw new NotFoundError(`group ${id}`);
return row;
}
@@ -380,17 +481,17 @@ function createGroup(db, name, slug) {
return getGroup(db, id);
}
function updateGroup(db, id, name, slug) {
const result = db.update(groups).set({ name, slug, updated_at: sql2`datetime('now')` }).where(eq(groups.id, id)).run();
const result = db.update(groups).set({ name, slug, updated_at: sql2`datetime('now')` }).where(eq2(groups.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`group ${id}`);
return getGroup(db, id);
}
function deleteGroup(db, id) {
const result = db.delete(groups).where(eq(groups.id, id)).run();
const result = db.delete(groups).where(eq2(groups.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`group ${id}`);
}
function listDomains(db, groupId) {
if (groupId != null) {
return db.select().from(domains).where(eq(domains.group_id, groupId)).orderBy(asc(domains.zone_name)).all();
return db.select().from(domains).where(eq2(domains.group_id, groupId)).orderBy(asc(domains.zone_name)).all();
}
return db.select().from(domains).orderBy(asc(domains.zone_name)).all();
}
@@ -412,7 +513,7 @@ function findDomainByZoneName(db, zoneName) {
return rows[0] ?? null;
}
function getDomain(db, id) {
const row = db.select().from(domains).where(eq(domains.id, id)).get();
const row = db.select().from(domains).where(eq2(domains.id, id)).get();
if (!row) throw new NotFoundError(`domain ${id}`);
return row;
}
@@ -433,33 +534,33 @@ function updateDomain(db, id, groupId, status, certMonitoring) {
if (certMonitoring !== void 0) {
updates.cert_monitoring = certMonitoring;
}
const result = db.update(domains).set(updates).where(eq(domains.id, id)).run();
const result = db.update(domains).set(updates).where(eq2(domains.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`domain ${id}`);
return getDomain(db, id);
}
function deleteDomain(db, id) {
const result = db.delete(domains).where(eq(domains.id, id)).run();
const result = db.delete(domains).where(eq2(domains.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`domain ${id}`);
}
function setDomainLastSynced(db, id) {
db.update(domains).set({
last_synced_at: sql2`datetime('now')`,
updated_at: sql2`datetime('now')`
}).where(eq(domains.id, id)).run();
}).where(eq2(domains.id, id)).run();
}
function listAllDomains(db) {
return listDomains(db);
}
function listSubdomainsByDomain(db, domainId) {
return db.select().from(subdomains).where(eq(subdomains.domain_id, domainId)).orderBy(asc(subdomains.name)).all();
return db.select().from(subdomains).where(eq2(subdomains.domain_id, domainId)).orderBy(asc(subdomains.name)).all();
}
function getSubdomain(db, id) {
const row = db.select().from(subdomains).where(eq(subdomains.id, id)).get();
const row = db.select().from(subdomains).where(eq2(subdomains.id, id)).get();
if (!row) throw new NotFoundError(`subdomain ${id}`);
return row;
}
function findSubdomainByDomainAndName(db, domainId, name) {
const row = db.select().from(subdomains).where(and(eq(subdomains.domain_id, domainId), eq(subdomains.name, name))).get();
const row = db.select().from(subdomains).where(and(eq2(subdomains.domain_id, domainId), eq2(subdomains.name, name))).get();
return row ?? null;
}
function upsertSubdomain(db, domainId, name, fqdn) {
@@ -483,12 +584,12 @@ function updateSubdomain(db, id, patch) {
if (patch.cert_monitoring !== void 0) {
updates.cert_monitoring = patch.cert_monitoring;
}
const result = db.update(subdomains).set(updates).where(eq(subdomains.id, id)).run();
const result = db.update(subdomains).set(updates).where(eq2(subdomains.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`subdomain ${id}`);
return getSubdomain(db, id);
}
function deleteSubdomain(db, id) {
const result = db.delete(subdomains).where(eq(subdomains.id, id)).run();
const result = db.delete(subdomains).where(eq2(subdomains.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`subdomain ${id}`);
}
function listAllSubdomains(db) {
@@ -498,9 +599,9 @@ function mapDnsRecord(row) {
return row;
}
function listDnsRecords(db, domainId, filter = {}) {
const conditions = [eq(dnsRecords.domain_id, domainId)];
const conditions = [eq2(dnsRecords.domain_id, domainId)];
if (filter.record_type) {
conditions.push(eq(dnsRecords.record_type, filter.record_type.toUpperCase()));
conditions.push(eq2(dnsRecords.record_type, filter.record_type.toUpperCase()));
}
if (filter.name) {
conditions.push(like(dnsRecords.name, `%${filter.name}%`));
@@ -509,10 +610,10 @@ function listDnsRecords(db, domainId, filter = {}) {
conditions.push(like(dnsRecords.content, `%${filter.content}%`));
}
if (filter.proxied != null) {
conditions.push(eq(dnsRecords.proxied, filter.proxied));
conditions.push(eq2(dnsRecords.proxied, filter.proxied));
}
if (filter.sync_status) {
conditions.push(eq(dnsRecords.sync_status, filter.sync_status));
conditions.push(eq2(dnsRecords.sync_status, filter.sync_status));
}
if (filter.q) {
const pat = `%${filter.q}%`;
@@ -531,7 +632,7 @@ function listDnsRecords(db, domainId, filter = {}) {
return db.select().from(dnsRecords).where(and(...conditions)).orderBy(asc(sortCol)).limit(limit).offset(offset).all().map(mapDnsRecord);
}
function getDnsRecord(db, domainId, id) {
const row = db.select().from(dnsRecords).where(and(eq(dnsRecords.id, id), eq(dnsRecords.domain_id, domainId))).get();
const row = db.select().from(dnsRecords).where(and(eq2(dnsRecords.id, id), eq2(dnsRecords.domain_id, domainId))).get();
if (!row) throw new NotFoundError(`dns record ${id}`);
return mapDnsRecord(row);
}
@@ -562,7 +663,7 @@ function updateDnsFields(db, id, recordType, name, content, ttl, proxied, priori
sync_status: syncStatus,
last_error: lastError,
updated_at: sql2`datetime('now')`
}).where(eq(dnsRecords.id, id)).run();
}).where(eq2(dnsRecords.id, id)).run();
}
function setDnsSyncStatus(db, id, syncStatus, cfRecordId, lastError) {
db.update(dnsRecords).set({
@@ -570,19 +671,19 @@ function setDnsSyncStatus(db, id, syncStatus, cfRecordId, lastError) {
cf_record_id: cfRecordId,
last_error: lastError,
updated_at: sql2`datetime('now')`
}).where(eq(dnsRecords.id, id)).run();
}).where(eq2(dnsRecords.id, id)).run();
}
function deleteDnsRecord(db, id) {
db.delete(dnsRecords).where(eq(dnsRecords.id, id)).run();
db.delete(dnsRecords).where(eq2(dnsRecords.id, id)).run();
}
function listDnsByDomain(db, domainId) {
return db.select().from(dnsRecords).where(eq(dnsRecords.domain_id, domainId)).all().map(mapDnsRecord);
return db.select().from(dnsRecords).where(eq2(dnsRecords.domain_id, domainId)).all().map(mapDnsRecord);
}
function findDnsByCfId(db, domainId, cfRecordId) {
const row = db.select().from(dnsRecords).where(
and(
eq(dnsRecords.domain_id, domainId),
eq(dnsRecords.cf_record_id, cfRecordId)
eq2(dnsRecords.domain_id, domainId),
eq2(dnsRecords.cf_record_id, cfRecordId)
)
).get();
return row ? mapDnsRecord(row) : null;
@@ -591,7 +692,7 @@ function markDnsPendingDelete(db, id) {
setDnsSyncStatus(db, id, "pending_delete", null, null);
}
function maxSortOrderInGroup(db, groupId) {
const condition = groupId === null ? isNull(services.service_group_id) : eq(services.service_group_id, groupId);
const condition = groupId === null ? isNull(services.service_group_id) : eq2(services.service_group_id, groupId);
const row = db.select({ maxOrder: sql2`coalesce(max(${services.sort_order}), -1)` }).from(services).where(condition).get();
return row?.maxOrder ?? -1;
}
@@ -599,13 +700,13 @@ function listServices(db) {
return db.select().from(services).orderBy(asc(services.sort_order), asc(services.name)).all();
}
function listServicesByGroup(db, groupId) {
return db.select().from(services).where(eq(services.service_group_id, groupId)).orderBy(asc(services.sort_order), asc(services.name)).all();
return db.select().from(services).where(eq2(services.service_group_id, groupId)).orderBy(asc(services.sort_order), asc(services.name)).all();
}
function listUngroupedServices(db) {
return db.select().from(services).where(isNull(services.service_group_id)).orderBy(asc(services.sort_order), asc(services.name)).all();
}
function getService(db, id) {
const row = db.select().from(services).where(eq(services.id, id)).get();
const row = db.select().from(services).where(eq2(services.id, id)).get();
if (!row) throw new NotFoundError(`service ${id}`);
return row;
}
@@ -620,11 +721,11 @@ function updateService(db, id, name, slug) {
slug,
subdomain: slug,
updated_at: sql2`datetime('now')`
}).where(eq(services.id, id)).run();
}).where(eq2(services.id, id)).run();
return getService(db, id);
}
function setServiceEnabled(db, id, enabled) {
db.update(services).set({ enabled, updated_at: sql2`datetime('now')` }).where(eq(services.id, id)).run();
db.update(services).set({ enabled, updated_at: sql2`datetime('now')` }).where(eq2(services.id, id)).run();
return getService(db, id);
}
function setServiceLb(db, id, weight, priority) {
@@ -632,7 +733,7 @@ function setServiceLb(db, id, weight, priority) {
lb_weight: weight,
lb_priority: priority,
updated_at: sql2`datetime('now')`
}).where(eq(services.id, id)).run();
}).where(eq2(services.id, id)).run();
}
function setServiceGroup(db, id, groupId) {
const sortOrder = maxSortOrderInGroup(db, groupId) + 1;
@@ -640,14 +741,14 @@ function setServiceGroup(db, id, groupId) {
service_group_id: groupId,
sort_order: sortOrder,
updated_at: sql2`datetime('now')`
}).where(eq(services.id, id)).run();
}).where(eq2(services.id, id)).run();
}
function reorderServices(db, groupId, orderedIds) {
const uniqueIds = new Set(orderedIds);
if (uniqueIds.size !== orderedIds.length) {
throw new Error("duplicate service ids in reorder request");
}
const condition = groupId === null ? isNull(services.service_group_id) : eq(services.service_group_id, groupId);
const condition = groupId === null ? isNull(services.service_group_id) : eq2(services.service_group_id, groupId);
const existing = db.select({ id: services.id }).from(services).where(condition).all().map((row) => row.id);
const existingSet = new Set(existing);
for (const serviceId of orderedIds) {
@@ -657,12 +758,12 @@ function reorderServices(db, groupId, orderedIds) {
}
db.transaction((tx) => {
for (let index = 0; index < orderedIds.length; index++) {
tx.update(services).set({ sort_order: index, updated_at: sql2`datetime('now')` }).where(eq(services.id, orderedIds[index])).run();
tx.update(services).set({ sort_order: index, updated_at: sql2`datetime('now')` }).where(eq2(services.id, orderedIds[index])).run();
}
});
}
function deleteService(db, id) {
const result = db.delete(services).where(eq(services.id, id)).run();
const result = db.delete(services).where(eq2(services.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`service ${id}`);
}
function mapServiceGroup(row) {
@@ -689,7 +790,7 @@ function listServiceGroups(db) {
return db.select().from(serviceGroups).orderBy(asc(serviceGroups.name)).all().map(mapServiceGroup);
}
function getServiceGroup(db, id) {
const row = db.select().from(serviceGroups).where(eq(serviceGroups.id, id)).get();
const row = db.select().from(serviceGroups).where(eq2(serviceGroups.id, id)).get();
if (!row) throw new NotFoundError(`service group ${id}`);
return mapServiceGroup(row);
}
@@ -735,37 +836,37 @@ function updateServiceGroup(db, id, name, groupType, icon, domain, lbPatch) {
if (lbPatch.health_check_timeout_ms !== void 0)
update.health_check_timeout_ms = lbPatch.health_check_timeout_ms;
}
const result = db.update(serviceGroups).set(update).where(eq(serviceGroups.id, id)).run();
const result = db.update(serviceGroups).set(update).where(eq2(serviceGroups.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`service group ${id}`);
return getServiceGroup(db, id);
}
function setServiceGroupEnabled(db, id, enabled) {
const result = db.update(serviceGroups).set({ enabled, updated_at: sql2`datetime('now')` }).where(eq(serviceGroups.id, id)).run();
const result = db.update(serviceGroups).set({ enabled, updated_at: sql2`datetime('now')` }).where(eq2(serviceGroups.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`service group ${id}`);
return getServiceGroup(db, id);
}
function deleteServiceGroup(db, id) {
const result = db.delete(serviceGroups).where(eq(serviceGroups.id, id)).run();
const result = db.delete(serviceGroups).where(eq2(serviceGroups.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`service group ${id}`);
}
function listServiceIps(db, serviceId) {
return db.select({ ip: serviceIps.ip }).from(serviceIps).where(eq(serviceIps.service_id, serviceId)).all().map((r) => r.ip);
return db.select({ ip: serviceIps.ip }).from(serviceIps).where(eq2(serviceIps.service_id, serviceId)).all().map((r) => r.ip);
}
function replaceServiceIps(db, serviceId, ips) {
db.delete(serviceIps).where(eq(serviceIps.service_id, serviceId)).run();
db.delete(serviceIps).where(eq2(serviceIps.service_id, serviceId)).run();
for (const ip of ips) {
db.insert(serviceIps).values({ service_id: serviceId, ip }).run();
}
}
function listBindingIps(db, bindingId) {
return db.select({ ip: serviceBindingIps.ip }).from(serviceBindingIps).where(eq(serviceBindingIps.binding_id, bindingId)).all().map((r) => r.ip);
return db.select({ ip: serviceBindingIps.ip }).from(serviceBindingIps).where(eq2(serviceBindingIps.binding_id, bindingId)).all().map((r) => r.ip);
}
function listBindingIpsWithMeta(db, bindingId) {
return db.select({
ip: serviceBindingIps.ip,
weight: serviceBindingIps.weight,
priority: serviceBindingIps.priority
}).from(serviceBindingIps).where(eq(serviceBindingIps.binding_id, bindingId)).all();
}).from(serviceBindingIps).where(eq2(serviceBindingIps.binding_id, bindingId)).all();
}
function replaceBindingIps(db, bindingId, ips) {
replaceBindingIpsWithMeta(
@@ -775,7 +876,7 @@ function replaceBindingIps(db, bindingId, ips) {
);
}
function replaceBindingIpsWithMeta(db, bindingId, entries) {
db.delete(serviceBindingIps).where(eq(serviceBindingIps.binding_id, bindingId)).run();
db.delete(serviceBindingIps).where(eq2(serviceBindingIps.binding_id, bindingId)).run();
for (const entry of entries) {
db.insert(serviceBindingIps).values({
binding_id: bindingId,
@@ -804,13 +905,13 @@ function updateBindingLbConfig(db, bindingId, patch) {
update.health_check_interval_sec = patch.health_check_interval_sec;
if (patch.health_check_timeout_ms !== void 0)
update.health_check_timeout_ms = patch.health_check_timeout_ms;
db.update(serviceBindings).set(update).where(eq(serviceBindings.id, bindingId)).run();
db.update(serviceBindings).set(update).where(eq2(serviceBindings.id, bindingId)).run();
}
function setBindingCnameTarget(db, bindingId, target) {
db.update(serviceBindings).set({
cname_target: target,
updated_at: sql2`datetime('now')`
}).where(eq(serviceBindings.id, bindingId)).run();
}).where(eq2(serviceBindings.id, bindingId)).run();
}
function listRecordsForBinding(db, bindingId) {
return db.all(sql2`
@@ -829,8 +930,8 @@ function linkBindingRecord(db, bindingId, dnsRecordId) {
function unlinkBindingRecord(db, bindingId, dnsRecordId) {
db.delete(serviceBindingRecords).where(
and(
eq(serviceBindingRecords.binding_id, bindingId),
eq(serviceBindingRecords.dns_record_id, dnsRecordId)
eq2(serviceBindingRecords.binding_id, bindingId),
eq2(serviceBindingRecords.dns_record_id, dnsRecordId)
)
).run();
}
@@ -851,8 +952,8 @@ function linkGroupDnsRecord(db, groupId, dnsRecordId) {
function unlinkGroupDnsRecord(db, groupId, dnsRecordId) {
db.delete(serviceGroupDnsRecords).where(
and(
eq(serviceGroupDnsRecords.group_id, groupId),
eq(serviceGroupDnsRecords.dns_record_id, dnsRecordId)
eq2(serviceGroupDnsRecords.group_id, groupId),
eq2(serviceGroupDnsRecords.dns_record_id, dnsRecordId)
)
).run();
}
@@ -942,7 +1043,7 @@ function listBindingsByService(db, serviceId) {
`);
}
function getBinding(db, id) {
const row = db.select().from(serviceBindings).where(eq(serviceBindings.id, id)).get();
const row = db.select().from(serviceBindings).where(eq2(serviceBindings.id, id)).get();
if (!row) throw new NotFoundError(`service binding ${id}`);
return row;
}
@@ -962,9 +1063,9 @@ function getBindingView(db, id) {
function findBinding(db, serviceId, domainId, hostname) {
const row = db.select().from(serviceBindings).where(
and(
eq(serviceBindings.service_id, serviceId),
eq(serviceBindings.domain_id, domainId),
eq(serviceBindings.hostname, hostname)
eq2(serviceBindings.service_id, serviceId),
eq2(serviceBindings.domain_id, domainId),
eq2(serviceBindings.hostname, hostname)
)
).get();
return row ?? null;
@@ -984,43 +1085,43 @@ function updateBindingFields(db, id, serviceId, hostname, dnsRecordId) {
hostname,
dns_record_id: dnsRecordId,
updated_at: sql2`datetime('now')`
}).where(eq(serviceBindings.id, id)).run();
}).where(eq2(serviceBindings.id, id)).run();
}
function setBindingDnsRecordId(db, bindingId, dnsRecordId) {
db.update(serviceBindings).set({
dns_record_id: dnsRecordId,
updated_at: sql2`datetime('now')`
}).where(eq(serviceBindings.id, bindingId)).run();
}).where(eq2(serviceBindings.id, bindingId)).run();
}
function bindingsToRemove(db, serviceId, keepIds) {
const all = db.select().from(serviceBindings).where(eq(serviceBindings.service_id, serviceId)).all();
const all = db.select().from(serviceBindings).where(eq2(serviceBindings.service_id, serviceId)).all();
return all.filter((b) => !keepIds.includes(b.id));
}
function deleteBindingsExcept(db, serviceId, keepIds) {
const all = db.select().from(serviceBindings).where(eq(serviceBindings.service_id, serviceId)).all();
const all = db.select().from(serviceBindings).where(eq2(serviceBindings.service_id, serviceId)).all();
for (const binding of all) {
if (!keepIds.includes(binding.id)) {
db.delete(serviceBindings).where(eq(serviceBindings.id, binding.id)).run();
db.delete(serviceBindings).where(eq2(serviceBindings.id, binding.id)).run();
}
}
}
function deleteBinding(db, id) {
const result = db.delete(serviceBindings).where(eq(serviceBindings.id, id)).run();
const result = db.delete(serviceBindings).where(eq2(serviceBindings.id, id)).run();
if (result.changes === 0) throw new NotFoundError(`service binding ${id}`);
}
function listCertificates(db, status) {
if (status) {
return db.select().from(certificates).where(eq(certificates.status, status)).orderBy(asc(certificates.expires_at)).all();
return db.select().from(certificates).where(eq2(certificates.status, status)).orderBy(asc(certificates.expires_at)).all();
}
return db.select().from(certificates).orderBy(asc(certificates.expires_at)).all();
}
function getCertificate(db, id) {
const row = db.select().from(certificates).where(eq(certificates.id, id)).get();
const row = db.select().from(certificates).where(eq2(certificates.id, id)).get();
if (!row) throw new NotFoundError(`certificate ${id}`);
return row;
}
function upsertCertificateCheck(db, domainId, subdomainId, hostname, expiresAt, status, lastError) {
const existing = db.select().from(certificates).where(eq(certificates.hostname, hostname)).get();
const existing = db.select().from(certificates).where(eq2(certificates.hostname, hostname)).get();
if (existing) {
db.update(certificates).set({
domain_id: domainId,
@@ -1030,7 +1131,7 @@ function upsertCertificateCheck(db, domainId, subdomainId, hostname, expiresAt,
last_error: lastError,
status,
updated_at: sql2`datetime('now')`
}).where(eq(certificates.id, existing.id)).run();
}).where(eq2(certificates.id, existing.id)).run();
return getCertificate(db, existing.id);
}
const id = db.insert(certificates).values({
@@ -1063,7 +1164,7 @@ function createSyncJob(db, id, domainId) {
db.insert(syncJobs).values({ id, domain_id: domainId, status: "pending" }).run();
}
function getSyncJob(db, id) {
const row = db.select().from(syncJobs).where(eq(syncJobs.id, id)).get();
const row = db.select().from(syncJobs).where(eq2(syncJobs.id, id)).get();
if (!row) throw new NotFoundError(`sync job ${id}`);
return row;
}
@@ -1072,7 +1173,7 @@ function finishSyncJob(db, id, status, message) {
status,
message,
finished_at: sql2`datetime('now')`
}).where(eq(syncJobs.id, id)).run();
}).where(eq2(syncJobs.id, id)).run();
}
function listIpHealthStatus(db, scope, refId) {
return db.all(sql2`
@@ -1111,17 +1212,17 @@ function upsertIpHealthStatus(db, scope, refId, ip, status, latencyMs, consecuti
function deleteIpHealthStatusForRef(db, scope, refId) {
db.delete(ipHealthStatus).where(
and(
eq(ipHealthStatus.scope, scope),
eq(ipHealthStatus.ref_id, refId)
eq2(ipHealthStatus.scope, scope),
eq2(ipHealthStatus.ref_id, refId)
)
).run();
}
function deleteIpHealthStatusForIp(db, scope, refId, ip) {
db.delete(ipHealthStatus).where(
and(
eq(ipHealthStatus.scope, scope),
eq(ipHealthStatus.ref_id, refId),
eq(ipHealthStatus.ip, ip)
eq2(ipHealthStatus.scope, scope),
eq2(ipHealthStatus.ref_id, refId),
eq2(ipHealthStatus.ip, ip)
)
).run();
}
@@ -1222,11 +1323,15 @@ function listHealthCheckTargets(db) {
export {
ConflictError,
NotFoundError,
appSettings,
certificates,
createDb,
createMemoryDb,
dnsRecords,
domains,
getAppSettings,
getAppSettingsSecrets,
getAppSwitcher,
groups,
healthCheck,
ipHealthStatus,
@@ -1242,5 +1347,7 @@ export {
serviceIps,
services,
subdomains,
syncJobs
syncJobs,
touchVpsTrackerSync,
updateAppSettings
};