feat(health): добавить Globalping и мультивыбор источников проб
CD / update-wiki (push) Successful in 8s
quality / commitlint (push) Skipped
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 54s
quality / api (push) Successful in 46s
CD / quality (push) Successful in 1m49s
CD / publish (push) Successful in 1m40s
CD / update-wiki (push) Successful in 8s
quality / commitlint (push) Skipped
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 54s
quality / api (push) Successful in 46s
CD / quality (push) Successful in 1m49s
CD / publish (push) Successful in 1m40s
Несколько источников проб сразу и правило агрегации на сервисе вместо XOR Local/Cloudflare. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Vendored
+278
-3
@@ -1,7 +1,7 @@
|
||||
import * as drizzle_orm_sqlite_core from 'drizzle-orm/sqlite-core';
|
||||
import Database from 'better-sqlite3';
|
||||
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
||||
import { AuditSourceApp, AuditSeverity, AuditTargetType, AuditLogEntry, HealthWorkerStatus, LbMode, HealthCheckType, HealthCheckProvider, IpHealthState, HealthCheckScope, ServiceBinding, Domain, Group, OriginHealthCheck, ServiceNode, Service, ServiceGroup, Subdomain, DnsRecord, ServiceBindingView, Certificate, GroupWithStats, IpHealthStatus, SyncJob, DomainListItem, HealthCheckTarget } from '@cfdm/shared';
|
||||
import { AuditSourceApp, AuditSeverity, AuditTargetType, AuditLogEntry, HealthWorkerStatus, LbMode, HealthCheckType, HealthCheckProvider, HealthCheckAggregate, IpHealthState, HealthStatusProvider, HealthCheckScope, ServiceBinding, Domain, Group, OriginHealthCheck, ServiceNode, Service, ServiceGroup, Subdomain, DnsRecord, ServiceBindingView, Certificate, GroupWithStats, IpHealthStatus, SyncJob, DomainListItem, HealthCheckTarget } from '@cfdm/shared';
|
||||
|
||||
declare const groups: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
||||
name: "groups";
|
||||
@@ -599,6 +599,44 @@ declare const serviceGroups: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
health_check_providers: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "health_check_providers";
|
||||
tableName: "service_groups";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: true;
|
||||
hasDefault: true;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
health_check_aggregate: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "health_check_aggregate";
|
||||
tableName: "service_groups";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: true;
|
||||
hasDefault: true;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
created_at: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "created_at";
|
||||
tableName: "service_groups";
|
||||
@@ -1537,6 +1575,44 @@ declare const serviceBindings: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
health_check_providers: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "health_check_providers";
|
||||
tableName: "service_bindings";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: true;
|
||||
hasDefault: true;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
health_check_aggregate: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "health_check_aggregate";
|
||||
tableName: "service_bindings";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: true;
|
||||
hasDefault: true;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
routing_strategy: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "routing_strategy";
|
||||
tableName: "service_bindings";
|
||||
@@ -3460,6 +3536,61 @@ declare const appSettings: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
globalping_token: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "globalping_token";
|
||||
tableName: "app_settings";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: false;
|
||||
hasDefault: false;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
globalping_locations: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "globalping_locations";
|
||||
tableName: "app_settings";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: false;
|
||||
hasDefault: false;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
globalping_limit: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "globalping_limit";
|
||||
tableName: "app_settings";
|
||||
dataType: "number";
|
||||
columnType: "SQLiteInteger";
|
||||
data: number;
|
||||
driverParam: number;
|
||||
notNull: false;
|
||||
hasDefault: false;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: undefined;
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
created_at: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "created_at";
|
||||
tableName: "app_settings";
|
||||
@@ -5151,6 +5282,44 @@ declare const schema: {
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
health_check_providers: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "health_check_providers";
|
||||
tableName: "service_groups";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: true;
|
||||
hasDefault: true;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
health_check_aggregate: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "health_check_aggregate";
|
||||
tableName: "service_groups";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: true;
|
||||
hasDefault: true;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
created_at: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "created_at";
|
||||
tableName: "service_groups";
|
||||
@@ -6089,6 +6258,44 @@ declare const schema: {
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
health_check_providers: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "health_check_providers";
|
||||
tableName: "service_bindings";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: true;
|
||||
hasDefault: true;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
health_check_aggregate: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "health_check_aggregate";
|
||||
tableName: "service_bindings";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: true;
|
||||
hasDefault: true;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
routing_strategy: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "routing_strategy";
|
||||
tableName: "service_bindings";
|
||||
@@ -8012,6 +8219,61 @@ declare const schema: {
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
globalping_token: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "globalping_token";
|
||||
tableName: "app_settings";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: false;
|
||||
hasDefault: false;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
globalping_locations: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "globalping_locations";
|
||||
tableName: "app_settings";
|
||||
dataType: "string";
|
||||
columnType: "SQLiteText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: false;
|
||||
hasDefault: false;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
globalping_limit: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "globalping_limit";
|
||||
tableName: "app_settings";
|
||||
dataType: "number";
|
||||
columnType: "SQLiteInteger";
|
||||
data: number;
|
||||
driverParam: number;
|
||||
notNull: false;
|
||||
hasDefault: false;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: undefined;
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
created_at: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "created_at";
|
||||
tableName: "app_settings";
|
||||
@@ -9176,6 +9438,9 @@ type AppSettingsDto = {
|
||||
healthWorkerError: string | null;
|
||||
healthWorkerDeployedAt: string | null;
|
||||
healthWorkerLastIngestAt: string | null;
|
||||
globalpingTokenSet: boolean;
|
||||
globalpingLocations: string;
|
||||
globalpingLimit: number;
|
||||
} & HealthEngineSettings;
|
||||
type AppSettingsPatch = {
|
||||
vpsTrackerUrl?: string;
|
||||
@@ -9194,6 +9459,9 @@ type AppSettingsPatch = {
|
||||
healthWorkerError?: string | null;
|
||||
healthWorkerDeployedAt?: string | null;
|
||||
healthWorkerLastIngestAt?: string | null;
|
||||
globalpingToken?: string;
|
||||
globalpingLocations?: string;
|
||||
globalpingLimit?: number;
|
||||
};
|
||||
type HealthEngineFallbacks = HealthEngineSettings & {
|
||||
healthWorkerUrl: string;
|
||||
@@ -9206,6 +9474,9 @@ declare function getAppSettingsSecrets(db: Db): {
|
||||
vpsTrackerSyncEnabled: boolean;
|
||||
healthWorkerUrl: string;
|
||||
healthWorkerToken: string;
|
||||
globalpingToken: string;
|
||||
globalpingLocations: string;
|
||||
globalpingLimit: number;
|
||||
};
|
||||
declare function updateAppSettings(db: Db, patch: AppSettingsPatch, fallbacks?: HealthEngineFallbacks): AppSettingsDto;
|
||||
declare function touchVpsTrackerSync(db: Db): void;
|
||||
@@ -9294,6 +9565,8 @@ interface ServiceGroupLbPatch {
|
||||
health_check_timeout_ms?: number;
|
||||
health_check_verify_tls?: boolean;
|
||||
health_check_provider?: HealthCheckProvider;
|
||||
health_check_providers?: HealthCheckProvider[];
|
||||
health_check_aggregate?: HealthCheckAggregate;
|
||||
}
|
||||
declare function createServiceGroup(db: Db, name: string, groupType: string, icon: string | null, domain: string | null, lbPatch?: ServiceGroupLbPatch): ServiceGroup;
|
||||
declare function updateServiceGroup(db: Db, id: number, name: string, groupType: string, icon: string | null, domain: string | null, lbPatch?: ServiceGroupLbPatch): ServiceGroup;
|
||||
@@ -9402,6 +9675,8 @@ interface BindingLbPatch {
|
||||
health_check_timeout_ms?: number;
|
||||
health_check_verify_tls?: boolean;
|
||||
health_check_provider?: HealthCheckProvider;
|
||||
health_check_providers?: HealthCheckProvider[];
|
||||
health_check_aggregate?: HealthCheckAggregate;
|
||||
}
|
||||
declare function updateBindingLbConfig(db: Db, bindingId: number, patch: BindingLbPatch): void;
|
||||
declare function setBindingCnameTarget(db: Db, bindingId: number, target: string | null): void;
|
||||
@@ -9453,7 +9728,7 @@ type ServiceIpHealthRow = {
|
||||
latency_ms: number | null;
|
||||
last_checked_at: string | null;
|
||||
last_error: string | null;
|
||||
provider: HealthCheckProvider;
|
||||
provider: HealthStatusProvider;
|
||||
colo: string | null;
|
||||
};
|
||||
/** Per-IP binding-scope health, worst status if the same IP is on several bindings. */
|
||||
@@ -9462,7 +9737,7 @@ declare function mergeHealthAggregates(parts: Array<HealthAggregate | undefined
|
||||
declare function getIpHealthStatusRow(db: Db, scope: HealthCheckScope, refId: number, ip: string): IpHealthStatus | null;
|
||||
declare function upsertIpHealthStatus(db: Db, scope: HealthCheckScope, refId: number, ip: string, status: string, latencyMs: number | null, consecutiveFailures: number, lastError: string | null, consecutiveSuccesses?: number, extras?: {
|
||||
colo?: string | null;
|
||||
provider?: HealthCheckProvider;
|
||||
provider?: HealthStatusProvider;
|
||||
}): void;
|
||||
declare function deleteIpHealthStatusForRef(db: Db, scope: HealthCheckScope, refId: number): void;
|
||||
declare function deleteIpHealthStatusForIp(db: Db, scope: HealthCheckScope, refId: number, ip: string): void;
|
||||
|
||||
Vendored
+126
-22
@@ -53,6 +53,8 @@ var serviceGroups = sqliteTable("service_groups", {
|
||||
health_check_timeout_ms: integer("health_check_timeout_ms").notNull().default(3e3),
|
||||
health_check_verify_tls: integer("health_check_verify_tls", { mode: "boolean" }).notNull().default(false),
|
||||
health_check_provider: text("health_check_provider").notNull().default("local"),
|
||||
health_check_providers: text("health_check_providers").notNull().default('["local"]'),
|
||||
health_check_aggregate: text("health_check_aggregate").notNull().default("majority"),
|
||||
created_at: text("created_at").notNull().default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at").notNull().default(sql`datetime('now')`)
|
||||
});
|
||||
@@ -119,6 +121,8 @@ var serviceBindings = sqliteTable(
|
||||
mode: "boolean"
|
||||
}).notNull().default(false),
|
||||
health_check_provider: text("health_check_provider").notNull().default("local"),
|
||||
health_check_providers: text("health_check_providers").notNull().default('["local"]'),
|
||||
health_check_aggregate: text("health_check_aggregate").notNull().default("majority"),
|
||||
routing_strategy: text("routing_strategy").notNull().default("round_robin"),
|
||||
operation_version: integer("operation_version").notNull().default(0),
|
||||
created_at: text("created_at").notNull().default(sql`datetime('now')`),
|
||||
@@ -286,6 +290,9 @@ var appSettings = sqliteTable("app_settings", {
|
||||
health_worker_error: text("health_worker_error"),
|
||||
health_worker_deployed_at: text("health_worker_deployed_at"),
|
||||
health_worker_last_ingest_at: text("health_worker_last_ingest_at"),
|
||||
globalping_token: text("globalping_token"),
|
||||
globalping_locations: text("globalping_locations"),
|
||||
globalping_limit: integer("globalping_limit"),
|
||||
created_at: text("created_at").notNull().default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at").notNull().default(sql`datetime('now')`)
|
||||
});
|
||||
@@ -577,7 +584,10 @@ function toDto(row, fallbacks) {
|
||||
healthWorkerError: row.health_worker_error?.trim() || null,
|
||||
healthWorkerDeployedAt: row.health_worker_deployed_at ?? null,
|
||||
healthWorkerLastIngestAt: row.health_worker_last_ingest_at ?? null,
|
||||
healthWorkerStatus: workerStatus(row, env.healthWorkerUrl)
|
||||
healthWorkerStatus: workerStatus(row, env.healthWorkerUrl),
|
||||
globalpingTokenSet: Boolean(row.globalping_token?.trim()),
|
||||
globalpingLocations: row.globalping_locations?.trim() || "World",
|
||||
globalpingLimit: row.globalping_limit == null || Number.isNaN(row.globalping_limit) || row.globalping_limit < 1 ? 3 : Math.min(10, row.globalping_limit)
|
||||
};
|
||||
}
|
||||
function getAppSettings(db, fallbacks) {
|
||||
@@ -593,12 +603,16 @@ function getAppSettings(db, fallbacks) {
|
||||
}
|
||||
function getAppSettingsSecrets(db) {
|
||||
const row = db.select().from(appSettings).where(eq2(appSettings.id, SETTINGS_ID)).get();
|
||||
const limit = row?.globalping_limit;
|
||||
return {
|
||||
vpsTrackerUrl: row?.vps_tracker_url?.trim() ?? "",
|
||||
vpsTrackerIntegrationToken: row?.vps_tracker_integration_token?.trim() ?? "",
|
||||
vpsTrackerSyncEnabled: Boolean(row?.vps_tracker_sync_enabled),
|
||||
healthWorkerUrl: row?.health_worker_url?.trim() ?? "",
|
||||
healthWorkerToken: row?.health_worker_token?.trim() ?? ""
|
||||
healthWorkerToken: row?.health_worker_token?.trim() ?? "",
|
||||
globalpingToken: row?.globalping_token?.trim() ?? "",
|
||||
globalpingLocations: row?.globalping_locations?.trim() || "World",
|
||||
globalpingLimit: limit == null || Number.isNaN(limit) || limit < 1 ? 3 : Math.min(10, limit)
|
||||
};
|
||||
}
|
||||
function updateAppSettings(db, patch, fallbacks) {
|
||||
@@ -624,6 +638,9 @@ function updateAppSettings(db, patch, fallbacks) {
|
||||
health_worker_error: patch.healthWorkerError !== void 0 ? patch.healthWorkerError?.trim() || null : current.health_worker_error,
|
||||
health_worker_deployed_at: patch.healthWorkerDeployedAt !== void 0 ? patch.healthWorkerDeployedAt : current.health_worker_deployed_at,
|
||||
health_worker_last_ingest_at: patch.healthWorkerLastIngestAt !== void 0 ? patch.healthWorkerLastIngestAt : current.health_worker_last_ingest_at,
|
||||
globalping_token: patch.globalpingToken !== void 0 && patch.globalpingToken.trim() !== "" ? patch.globalpingToken : current.globalping_token,
|
||||
globalping_locations: patch.globalpingLocations !== void 0 ? patch.globalpingLocations.trim() || "World" : current.globalping_locations,
|
||||
globalping_limit: patch.globalpingLimit !== void 0 ? Math.min(10, Math.max(1, patch.globalpingLimit)) : current.globalping_limit,
|
||||
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
||||
}).where(eq2(appSettings.id, SETTINGS_ID)).run();
|
||||
return getAppSettings(db, fallbacks);
|
||||
@@ -771,7 +788,15 @@ __export(repos_exports, {
|
||||
upsertIpHealthStatus: () => upsertIpHealthStatus,
|
||||
upsertSubdomain: () => upsertSubdomain
|
||||
});
|
||||
import { dnsRecordNamesMatch, isIpLiteral } from "@cfdm/shared";
|
||||
import {
|
||||
derivePrimaryProvider,
|
||||
dnsRecordNamesMatch,
|
||||
isIpLiteral,
|
||||
parseHealthAggregate,
|
||||
parseHealthProviders,
|
||||
serializeHealthProviders,
|
||||
normalizeStatusProvider
|
||||
} from "@cfdm/shared";
|
||||
import { and as and2, asc, count, eq as eq3, isNull, like, notInArray, or as or2, sql as sql2 } from "drizzle-orm";
|
||||
function listGroups(db) {
|
||||
return db.select().from(groups).orderBy(asc(groups.name)).all();
|
||||
@@ -1158,8 +1183,58 @@ function deleteService(db, id) {
|
||||
const result = db.delete(services).where(eq3(services.id, id)).run();
|
||||
if (result.changes === 0) throw new NotFoundError(`service ${id}`);
|
||||
}
|
||||
function normalizeHealthProvider(value) {
|
||||
return value === "cloudflare" ? "cloudflare" : "local";
|
||||
function healthProviderColumns(patch) {
|
||||
const out = {};
|
||||
if (patch.health_check_providers !== void 0) {
|
||||
const list = parseHealthProviders(patch.health_check_providers);
|
||||
out.health_check_providers = serializeHealthProviders(list);
|
||||
out.health_check_provider = derivePrimaryProvider(list);
|
||||
} else if (patch.health_check_provider !== void 0) {
|
||||
const list = parseHealthProviders(null, patch.health_check_provider);
|
||||
out.health_check_providers = serializeHealthProviders(list);
|
||||
out.health_check_provider = derivePrimaryProvider(list);
|
||||
}
|
||||
if (patch.health_check_aggregate !== void 0) {
|
||||
out.health_check_aggregate = parseHealthAggregate(
|
||||
patch.health_check_aggregate
|
||||
);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function mapHealthFields(row) {
|
||||
const providers = parseHealthProviders(
|
||||
row.health_check_providers,
|
||||
row.health_check_provider
|
||||
);
|
||||
return {
|
||||
health_check_providers: providers,
|
||||
health_check_provider: derivePrimaryProvider(providers),
|
||||
health_check_aggregate: parseHealthAggregate(row.health_check_aggregate)
|
||||
};
|
||||
}
|
||||
function mapServiceBinding(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
domain_id: row.domain_id,
|
||||
service_id: row.service_id,
|
||||
hostname: row.hostname,
|
||||
cname_target: row.cname_target,
|
||||
dns_record_id: row.dns_record_id,
|
||||
lb_mode: row.lb_mode,
|
||||
health_check_enabled: row.health_check_enabled,
|
||||
health_check_type: row.health_check_type,
|
||||
health_check_port: row.health_check_port,
|
||||
health_check_path: row.health_check_path,
|
||||
health_check_expected_status: row.health_check_expected_status,
|
||||
health_check_interval_sec: row.health_check_interval_sec,
|
||||
health_check_timeout_ms: row.health_check_timeout_ms,
|
||||
health_check_verify_tls: row.health_check_verify_tls,
|
||||
...mapHealthFields(row),
|
||||
routing_strategy: row.routing_strategy,
|
||||
operation_version: row.operation_version,
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at
|
||||
};
|
||||
}
|
||||
function mapServiceGroup(row) {
|
||||
return {
|
||||
@@ -1178,7 +1253,7 @@ function mapServiceGroup(row) {
|
||||
health_check_interval_sec: row.health_check_interval_sec,
|
||||
health_check_timeout_ms: row.health_check_timeout_ms,
|
||||
health_check_verify_tls: row.health_check_verify_tls,
|
||||
health_check_provider: normalizeHealthProvider(row.health_check_provider),
|
||||
...mapHealthFields(row),
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at
|
||||
};
|
||||
@@ -1206,7 +1281,11 @@ function createServiceGroup(db, name, groupType, icon, domain, lbPatch) {
|
||||
health_check_interval_sec: lbPatch?.health_check_interval_sec ?? 30,
|
||||
health_check_timeout_ms: lbPatch?.health_check_timeout_ms ?? 3e3,
|
||||
health_check_verify_tls: lbPatch?.health_check_verify_tls ?? false,
|
||||
health_check_provider: lbPatch?.health_check_provider ?? "local"
|
||||
...healthProviderColumns({
|
||||
health_check_provider: lbPatch?.health_check_provider ?? "local",
|
||||
health_check_providers: lbPatch?.health_check_providers,
|
||||
health_check_aggregate: lbPatch?.health_check_aggregate ?? "majority"
|
||||
})
|
||||
}).returning({ id: serviceGroups.id }).get().id;
|
||||
return getServiceGroup(db, id);
|
||||
}
|
||||
@@ -1236,8 +1315,7 @@ function updateServiceGroup(db, id, name, groupType, icon, domain, lbPatch) {
|
||||
update.health_check_timeout_ms = lbPatch.health_check_timeout_ms;
|
||||
if (lbPatch.health_check_verify_tls !== void 0)
|
||||
update.health_check_verify_tls = lbPatch.health_check_verify_tls;
|
||||
if (lbPatch.health_check_provider !== void 0)
|
||||
update.health_check_provider = lbPatch.health_check_provider;
|
||||
Object.assign(update, healthProviderColumns(lbPatch));
|
||||
}
|
||||
const result = db.update(serviceGroups).set(update).where(eq3(serviceGroups.id, id)).run();
|
||||
if (result.changes === 0) throw new NotFoundError(`service group ${id}`);
|
||||
@@ -1547,8 +1625,7 @@ function updateBindingLbConfig(db, bindingId, patch) {
|
||||
update.health_check_timeout_ms = patch.health_check_timeout_ms;
|
||||
if (patch.health_check_verify_tls !== void 0)
|
||||
update.health_check_verify_tls = patch.health_check_verify_tls;
|
||||
if (patch.health_check_provider !== void 0)
|
||||
update.health_check_provider = patch.health_check_provider;
|
||||
Object.assign(update, healthProviderColumns(patch));
|
||||
db.update(serviceBindings).set(update).where(eq3(serviceBindings.id, bindingId)).run();
|
||||
}
|
||||
function setBindingCnameTarget(db, bindingId, target) {
|
||||
@@ -1607,7 +1684,8 @@ function dnsRecordMatchesHostname(recordName, hostname, zoneName) {
|
||||
var SERVICE_BINDING_SELECT_COLUMNS = `sb.id, sb.domain_id, sb.service_id, sb.hostname, sb.dns_record_id,
|
||||
sb.lb_mode, sb.health_check_enabled, sb.health_check_type, sb.health_check_port,
|
||||
sb.health_check_path, sb.health_check_expected_status, sb.health_check_interval_sec,
|
||||
sb.health_check_timeout_ms, sb.health_check_verify_tls, sb.health_check_provider, sb.cname_target,
|
||||
sb.health_check_timeout_ms, sb.health_check_verify_tls, sb.health_check_provider,
|
||||
sb.health_check_providers, sb.health_check_aggregate, sb.cname_target,
|
||||
d.zone_name, d.group_id, g.name AS group_name,
|
||||
s.name AS service_name, s.slug AS service_slug,
|
||||
dr.content AS target_ip, dr.sync_status,
|
||||
@@ -1655,6 +1733,7 @@ function enrichServiceBindingView(db, row) {
|
||||
return {
|
||||
...row,
|
||||
cname_target: row.cname_target ?? null,
|
||||
...mapHealthFields(row),
|
||||
target_ips,
|
||||
target_ip: target_ips[0] ?? null,
|
||||
target_ip_weights,
|
||||
@@ -1695,7 +1774,7 @@ function listBindingsByService(db, serviceId) {
|
||||
function getBinding(db, id) {
|
||||
const row = db.select().from(serviceBindings).where(eq3(serviceBindings.id, id)).get();
|
||||
if (!row) throw new NotFoundError(`service binding ${id}`);
|
||||
return row;
|
||||
return mapServiceBinding(row);
|
||||
}
|
||||
function getBindingView(db, id) {
|
||||
const rows = db.all(sql2`
|
||||
@@ -1718,7 +1797,8 @@ function findBinding(db, serviceId, domainId, hostname) {
|
||||
eq3(serviceBindings.hostname, hostname)
|
||||
)
|
||||
).get();
|
||||
return row ?? null;
|
||||
if (!row) return null;
|
||||
return mapServiceBinding(row);
|
||||
}
|
||||
function insertBinding(db, domainId, serviceId, hostname, dnsRecordId) {
|
||||
const id = db.insert(serviceBindings).values({
|
||||
@@ -1744,7 +1824,7 @@ function setBindingDnsRecordId(db, bindingId, dnsRecordId) {
|
||||
}).where(eq3(serviceBindings.id, bindingId)).run();
|
||||
}
|
||||
function bindingsToRemove(db, serviceId, keepIds) {
|
||||
const all = db.select().from(serviceBindings).where(eq3(serviceBindings.service_id, serviceId)).all();
|
||||
const all = db.select().from(serviceBindings).where(eq3(serviceBindings.service_id, serviceId)).all().map(mapServiceBinding);
|
||||
return all.filter((b) => !keepIds.includes(b.id));
|
||||
}
|
||||
function deleteBindingsExcept(db, serviceId, keepIds) {
|
||||
@@ -1975,7 +2055,7 @@ function listIpHealthByServiceIds(db, serviceIds) {
|
||||
latency_ms: parsed.health_latency_ms,
|
||||
last_checked_at: row.last_checked_at,
|
||||
last_error: row.last_error,
|
||||
provider: normalizeHealthProvider(row.provider),
|
||||
provider: normalizeStatusProvider(row.provider),
|
||||
colo: row.colo
|
||||
});
|
||||
result.set(row.service_id, list);
|
||||
@@ -2101,6 +2181,8 @@ function listHealthCheckTargets(db) {
|
||||
sb.health_check_expected_status AS expected_status,
|
||||
sb.health_check_timeout_ms AS timeout_ms,
|
||||
sb.health_check_verify_tls AS verify_tls,
|
||||
sb.health_check_providers AS providers_json,
|
||||
COALESCE(sb.health_check_aggregate, 'majority') AS aggregate,
|
||||
COALESCE(sb.health_check_provider, 'local') AS provider
|
||||
FROM service_binding_ips sbi
|
||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||
@@ -2116,6 +2198,8 @@ function listHealthCheckTargets(db) {
|
||||
sg.health_check_expected_status AS expected_status,
|
||||
sg.health_check_timeout_ms AS timeout_ms,
|
||||
sg.health_check_verify_tls AS verify_tls,
|
||||
sg.health_check_providers AS providers_json,
|
||||
COALESCE(sg.health_check_aggregate, 'majority') AS aggregate,
|
||||
COALESCE(sg.health_check_provider, 'local') AS provider
|
||||
FROM service_binding_ips sbi
|
||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||
@@ -2137,6 +2221,8 @@ function listHealthCheckTargets(db) {
|
||||
sg.health_check_expected_status AS expected_status,
|
||||
sg.health_check_timeout_ms AS timeout_ms,
|
||||
sg.health_check_verify_tls AS verify_tls,
|
||||
sg.health_check_providers AS providers_json,
|
||||
COALESCE(sg.health_check_aggregate, 'majority') AS aggregate,
|
||||
COALESCE(sg.health_check_provider, 'local') AS provider
|
||||
FROM service_binding_ips sbi
|
||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||
@@ -2158,6 +2244,8 @@ function listHealthCheckTargets(db) {
|
||||
sb.health_check_expected_status AS expected_status,
|
||||
sb.health_check_timeout_ms AS timeout_ms,
|
||||
sb.health_check_verify_tls AS verify_tls,
|
||||
sb.health_check_providers AS providers_json,
|
||||
COALESCE(sb.health_check_aggregate, 'majority') AS aggregate,
|
||||
COALESCE(sb.health_check_provider, 'local') AS provider
|
||||
FROM service_bindings sb
|
||||
JOIN domains d ON d.id = sb.domain_id
|
||||
@@ -2176,6 +2264,8 @@ function listHealthCheckTargets(db) {
|
||||
sg.health_check_expected_status AS expected_status,
|
||||
sg.health_check_timeout_ms AS timeout_ms,
|
||||
sg.health_check_verify_tls AS verify_tls,
|
||||
sg.health_check_providers AS providers_json,
|
||||
COALESCE(sg.health_check_aggregate, 'majority') AS aggregate,
|
||||
COALESCE(sg.health_check_provider, 'local') AS provider
|
||||
FROM service_bindings sb
|
||||
JOIN domains d ON d.id = sb.domain_id
|
||||
@@ -2195,11 +2285,25 @@ function listHealthCheckTargets(db) {
|
||||
...groupInheritedBindingTargets,
|
||||
...cnameBindingTargets,
|
||||
...groupInheritedCnameBindingTargets
|
||||
].map((t) => ({
|
||||
...t,
|
||||
verify_tls: Boolean(t.verify_tls),
|
||||
provider: normalizeHealthProvider(t.provider)
|
||||
}));
|
||||
].map((t) => {
|
||||
const row = t;
|
||||
const providers = parseHealthProviders(row.providers_json, row.provider);
|
||||
return {
|
||||
scope: row.scope,
|
||||
ref_id: row.ref_id,
|
||||
ip: row.ip,
|
||||
hostname: row.hostname,
|
||||
type: row.type,
|
||||
port: row.port,
|
||||
path: row.path,
|
||||
expected_status: row.expected_status,
|
||||
timeout_ms: row.timeout_ms,
|
||||
verify_tls: Boolean(row.verify_tls),
|
||||
providers,
|
||||
aggregate: parseHealthAggregate(row.aggregate),
|
||||
provider: derivePrimaryProvider(providers)
|
||||
};
|
||||
});
|
||||
}
|
||||
function listDomainTags(db, domainId) {
|
||||
return db.select({ tag: domainTags.tag }).from(domainTags).where(eq3(domainTags.domain_id, domainId)).all().map((r) => r.tag);
|
||||
@@ -2329,7 +2433,7 @@ function listHealthProbeLogForService(db, serviceId, limit = 50) {
|
||||
`);
|
||||
return rows.map((row) => ({
|
||||
...row,
|
||||
provider: normalizeHealthProvider(row.provider),
|
||||
provider: parseHealthProviders(null, row.provider)[0] ?? "local",
|
||||
ok: Boolean(row.ok)
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user