fix(services): сохранять источники health-check и агрегацию в форме сервиса
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 9s
quality / changes (push) Successful in 12s
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m14s
quality / api (push) Successful in 53s
CD / quality (push) Successful in 2m23s
CD / publish (push) Successful in 2m5s
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 9s
quality / changes (push) Successful in 12s
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m14s
quality / api (push) Successful in 53s
CD / quality (push) Successful in 2m23s
CD / publish (push) Successful in 2m5s
GET отдавал sqlite-типы (0/1 и JSON-строка), Zod отклонял PATCH; форма сбрасывалась на refetch. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Vendored
+74
-1
@@ -1613,6 +1613,25 @@ declare const serviceBindings: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
cert_monitoring: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "cert_monitoring";
|
||||
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";
|
||||
@@ -2682,6 +2701,23 @@ declare const certificates: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
service_id: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "service_id";
|
||||
tableName: "certificates";
|
||||
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;
|
||||
}, {}, {}>;
|
||||
hostname: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "hostname";
|
||||
tableName: "certificates";
|
||||
@@ -6296,6 +6332,25 @@ declare const schema: {
|
||||
}, {}, {
|
||||
length: number | undefined;
|
||||
}>;
|
||||
cert_monitoring: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "cert_monitoring";
|
||||
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";
|
||||
@@ -7365,6 +7420,23 @@ declare const schema: {
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
service_id: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "service_id";
|
||||
tableName: "certificates";
|
||||
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;
|
||||
}, {}, {}>;
|
||||
hostname: drizzle_orm_sqlite_core.SQLiteColumn<{
|
||||
name: "hostname";
|
||||
tableName: "certificates";
|
||||
@@ -9677,6 +9749,7 @@ interface BindingLbPatch {
|
||||
health_check_provider?: HealthCheckProvider;
|
||||
health_check_providers?: HealthCheckProvider[];
|
||||
health_check_aggregate?: HealthCheckAggregate;
|
||||
cert_monitoring?: string;
|
||||
}
|
||||
declare function updateBindingLbConfig(db: Db, bindingId: number, patch: BindingLbPatch): void;
|
||||
declare function setBindingCnameTarget(db: Db, bindingId: number, target: string | null): void;
|
||||
@@ -9702,7 +9775,7 @@ declare function deleteBindingsExcept(db: Db, serviceId: number, keepIds: number
|
||||
declare function deleteBinding(db: Db, id: number): void;
|
||||
declare function listCertificates(db: Db, status?: string): Certificate[];
|
||||
declare function getCertificate(db: Db, id: number): Certificate;
|
||||
declare function upsertCertificateCheck(db: Db, domainId: number, subdomainId: number | null, hostname: string, expiresAt: string | null, status: string, lastError: string | null): Certificate;
|
||||
declare function upsertCertificateCheck(db: Db, domainId: number, subdomainId: number | null, hostname: string, expiresAt: string | null, status: string, lastError: string | null, serviceId?: number | null): Certificate;
|
||||
declare function countCertificatesByStatus(db: Db): Array<[string, number]>;
|
||||
declare function deleteCertificatesNotIn(db: Db, hostnames: string[]): number;
|
||||
declare function createSyncJob(db: Db, id: string, domainId: number | null): void;
|
||||
|
||||
Vendored
+58
-11
@@ -123,6 +123,7 @@ var serviceBindings = sqliteTable(
|
||||
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"),
|
||||
cert_monitoring: text("cert_monitoring").notNull().default("auto"),
|
||||
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')`),
|
||||
@@ -228,6 +229,9 @@ var certificates = sqliteTable("certificates", {
|
||||
subdomain_id: integer("subdomain_id").references(() => subdomains.id, {
|
||||
onDelete: "set null"
|
||||
}),
|
||||
service_id: integer("service_id").references(() => services.id, {
|
||||
onDelete: "set null"
|
||||
}),
|
||||
hostname: text("hostname").notNull().unique(),
|
||||
expires_at: text("expires_at"),
|
||||
last_checked_at: text("last_checked_at"),
|
||||
@@ -1221,15 +1225,16 @@ function mapServiceBinding(row) {
|
||||
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_enabled: Boolean(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,
|
||||
health_check_verify_tls: Boolean(row.health_check_verify_tls),
|
||||
...mapHealthFields(row),
|
||||
cert_monitoring: row.cert_monitoring ?? "auto",
|
||||
routing_strategy: row.routing_strategy,
|
||||
operation_version: row.operation_version,
|
||||
created_at: row.created_at,
|
||||
@@ -1625,6 +1630,8 @@ 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.cert_monitoring !== void 0)
|
||||
update.cert_monitoring = patch.cert_monitoring;
|
||||
Object.assign(update, healthProviderColumns(patch));
|
||||
db.update(serviceBindings).set(update).where(eq3(serviceBindings.id, bindingId)).run();
|
||||
}
|
||||
@@ -1685,7 +1692,7 @@ var SERVICE_BINDING_SELECT_COLUMNS = `sb.id, sb.domain_id, sb.service_id, sb.hos
|
||||
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.health_check_providers, sb.health_check_aggregate, sb.cname_target,
|
||||
sb.health_check_providers, sb.health_check_aggregate, sb.cert_monitoring, 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,
|
||||
@@ -1733,6 +1740,7 @@ function enrichServiceBindingView(db, row) {
|
||||
return {
|
||||
...row,
|
||||
cname_target: row.cname_target ?? null,
|
||||
cert_monitoring: row.cert_monitoring ?? "auto",
|
||||
...mapHealthFields(row),
|
||||
target_ips,
|
||||
target_ip: target_ips[0] ?? null,
|
||||
@@ -1765,11 +1773,15 @@ function listBindingsByDomain(db, domainId) {
|
||||
`).map((row) => enrichServiceBindingView(db, row));
|
||||
}
|
||||
function listBindingsByService(db, serviceId) {
|
||||
return db.all(sql2`
|
||||
const rows = db.all(sql2`
|
||||
SELECT sb.*, d.zone_name FROM service_bindings sb
|
||||
JOIN domains d ON d.id = sb.domain_id
|
||||
WHERE sb.service_id = ${serviceId}
|
||||
`);
|
||||
return rows.map((row) => ({
|
||||
...mapServiceBinding(row),
|
||||
zone_name: row.zone_name
|
||||
}));
|
||||
}
|
||||
function getBinding(db, id) {
|
||||
const row = db.select().from(serviceBindings).where(eq3(serviceBindings.id, id)).get();
|
||||
@@ -1839,23 +1851,57 @@ function deleteBinding(db, id) {
|
||||
const result = db.delete(serviceBindings).where(eq3(serviceBindings.id, id)).run();
|
||||
if (result.changes === 0) throw new NotFoundError(`service binding ${id}`);
|
||||
}
|
||||
var CERTIFICATE_SELECT = `c.id, c.domain_id, c.subdomain_id, c.service_id, c.hostname,
|
||||
c.expires_at, c.last_checked_at, c.last_error, c.status, c.created_at, c.updated_at,
|
||||
s.name AS service_name`;
|
||||
function mapCertificate(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
domain_id: row.domain_id,
|
||||
subdomain_id: row.subdomain_id,
|
||||
service_id: row.service_id ?? null,
|
||||
service_name: row.service_name ?? null,
|
||||
hostname: row.hostname,
|
||||
expires_at: row.expires_at,
|
||||
last_checked_at: row.last_checked_at,
|
||||
last_error: row.last_error,
|
||||
status: row.status,
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at
|
||||
};
|
||||
}
|
||||
function listCertificates(db, status) {
|
||||
if (status) {
|
||||
return db.select().from(certificates).where(eq3(certificates.status, status)).orderBy(asc(certificates.expires_at)).all();
|
||||
}
|
||||
return db.select().from(certificates).orderBy(asc(certificates.expires_at)).all();
|
||||
const rows = status ? db.all(sql2`
|
||||
SELECT ${sql2.raw(CERTIFICATE_SELECT)}
|
||||
FROM certificates c
|
||||
LEFT JOIN services s ON s.id = c.service_id
|
||||
WHERE c.status = ${status}
|
||||
ORDER BY c.expires_at ASC
|
||||
`) : db.all(sql2`
|
||||
SELECT ${sql2.raw(CERTIFICATE_SELECT)}
|
||||
FROM certificates c
|
||||
LEFT JOIN services s ON s.id = c.service_id
|
||||
ORDER BY c.expires_at ASC
|
||||
`);
|
||||
return rows.map(mapCertificate);
|
||||
}
|
||||
function getCertificate(db, id) {
|
||||
const row = db.select().from(certificates).where(eq3(certificates.id, id)).get();
|
||||
const row = db.all(sql2`
|
||||
SELECT ${sql2.raw(CERTIFICATE_SELECT)}
|
||||
FROM certificates c
|
||||
LEFT JOIN services s ON s.id = c.service_id
|
||||
WHERE c.id = ${id}
|
||||
`)[0];
|
||||
if (!row) throw new NotFoundError(`certificate ${id}`);
|
||||
return row;
|
||||
return mapCertificate(row);
|
||||
}
|
||||
function upsertCertificateCheck(db, domainId, subdomainId, hostname, expiresAt, status, lastError) {
|
||||
function upsertCertificateCheck(db, domainId, subdomainId, hostname, expiresAt, status, lastError, serviceId) {
|
||||
const existing = db.select().from(certificates).where(eq3(certificates.hostname, hostname)).get();
|
||||
if (existing) {
|
||||
db.update(certificates).set({
|
||||
domain_id: domainId,
|
||||
subdomain_id: subdomainId,
|
||||
service_id: serviceId === void 0 ? existing.service_id : serviceId,
|
||||
expires_at: expiresAt,
|
||||
last_checked_at: sql2`datetime('now')`,
|
||||
last_error: lastError,
|
||||
@@ -1867,6 +1913,7 @@ function upsertCertificateCheck(db, domainId, subdomainId, hostname, expiresAt,
|
||||
const id = db.insert(certificates).values({
|
||||
domain_id: domainId,
|
||||
subdomain_id: subdomainId,
|
||||
service_id: serviceId ?? null,
|
||||
hostname,
|
||||
expires_at: expiresAt,
|
||||
last_checked_at: sql2`datetime('now')`,
|
||||
|
||||
@@ -842,14 +842,14 @@ function mapServiceBinding(
|
||||
cname_target: row.cname_target,
|
||||
dns_record_id: row.dns_record_id,
|
||||
lb_mode: row.lb_mode as LbMode,
|
||||
health_check_enabled: row.health_check_enabled,
|
||||
health_check_enabled: Boolean(row.health_check_enabled),
|
||||
health_check_type: row.health_check_type as HealthCheckType,
|
||||
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,
|
||||
health_check_verify_tls: Boolean(row.health_check_verify_tls),
|
||||
...mapHealthFields(row),
|
||||
cert_monitoring: row.cert_monitoring ?? "auto",
|
||||
routing_strategy: row.routing_strategy as LbMode,
|
||||
@@ -1777,11 +1777,15 @@ export function listBindingsByDomain(db: Db, domainId: number): ServiceBindingVi
|
||||
}
|
||||
|
||||
export function listBindingsByService(db: Db, serviceId: number): Array<ServiceBinding & { zone_name: string }> {
|
||||
return db.all(sql`
|
||||
const rows = db.all<typeof serviceBindings.$inferSelect & { zone_name: string }>(sql`
|
||||
SELECT sb.*, d.zone_name FROM service_bindings sb
|
||||
JOIN domains d ON d.id = sb.domain_id
|
||||
WHERE sb.service_id = ${serviceId}
|
||||
`);
|
||||
return rows.map((row) => ({
|
||||
...mapServiceBinding(row),
|
||||
zone_name: row.zone_name,
|
||||
}));
|
||||
}
|
||||
|
||||
export function getBinding(db: Db, id: number): ServiceBinding {
|
||||
|
||||
Reference in New Issue
Block a user