feat(services): сделать multi-FQDN привязки first-class в UI
Build and Push CFDM Docker Image / build-and-push (push) Successful in 2m9s
Build and Push CFDM Docker Image / create-release (push) Skipped
Build and Push CFDM Docker Image / update-wiki (push) Successful in 7s

Исправить prune при domains: [], показать все FQDN в каталоге/kanban, улучшить sheet привязок и панель на домене; убрать мёртвый код.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-30 01:29:08 +07:00
co-authored by Cursor
parent 859ad23006
commit 3e4cacab42
18 changed files with 469 additions and 511 deletions
+50 -37
View File
@@ -4,6 +4,7 @@ import {
primaryKey,
sqliteTable,
text,
unique,
} from "drizzle-orm/sqlite-core";
export const groups = sqliteTable("groups", {
@@ -130,43 +131,55 @@ export const dnsRecords = sqliteTable("dns_records", {
.default(sql`datetime('now')`),
});
export const serviceBindings = sqliteTable("service_bindings", {
id: integer("id").primaryKey({ autoIncrement: true }),
domain_id: integer("domain_id")
.notNull()
.references(() => domains.id, { onDelete: "cascade" }),
service_id: integer("service_id")
.notNull()
.references(() => services.id, { onDelete: "cascade" }),
hostname: text("hostname").notNull().default("@"),
cname_target: text("cname_target"),
dns_record_id: integer("dns_record_id").references(() => dnsRecords.id, {
onDelete: "set null",
}),
lb_mode: text("lb_mode").notNull().default("round_robin"),
health_check_enabled: integer("health_check_enabled", { mode: "boolean" })
.notNull()
.default(false),
health_check_type: text("health_check_type").notNull().default("tcp"),
health_check_port: integer("health_check_port"),
health_check_path: text("health_check_path"),
health_check_expected_status: integer("health_check_expected_status"),
health_check_interval_sec: integer("health_check_interval_sec")
.notNull()
.default(30),
health_check_timeout_ms: integer("health_check_timeout_ms")
.notNull()
.default(3000),
health_check_verify_tls: integer("health_check_verify_tls", { mode: "boolean" })
.notNull()
.default(false),
created_at: text("created_at")
.notNull()
.default(sql`datetime('now')`),
updated_at: text("updated_at")
.notNull()
.default(sql`datetime('now')`),
});
export const serviceBindings = sqliteTable(
"service_bindings",
{
id: integer("id").primaryKey({ autoIncrement: true }),
domain_id: integer("domain_id")
.notNull()
.references(() => domains.id, { onDelete: "cascade" }),
service_id: integer("service_id")
.notNull()
.references(() => services.id, { onDelete: "cascade" }),
hostname: text("hostname").notNull().default("@"),
cname_target: text("cname_target"),
dns_record_id: integer("dns_record_id").references(() => dnsRecords.id, {
onDelete: "set null",
}),
lb_mode: text("lb_mode").notNull().default("round_robin"),
health_check_enabled: integer("health_check_enabled", { mode: "boolean" })
.notNull()
.default(false),
health_check_type: text("health_check_type").notNull().default("tcp"),
health_check_port: integer("health_check_port"),
health_check_path: text("health_check_path"),
health_check_expected_status: integer("health_check_expected_status"),
health_check_interval_sec: integer("health_check_interval_sec")
.notNull()
.default(30),
health_check_timeout_ms: integer("health_check_timeout_ms")
.notNull()
.default(3000),
health_check_verify_tls: integer("health_check_verify_tls", {
mode: "boolean",
})
.notNull()
.default(false),
created_at: text("created_at")
.notNull()
.default(sql`datetime('now')`),
updated_at: text("updated_at")
.notNull()
.default(sql`datetime('now')`),
},
(table) => [
unique("service_bindings_domain_service_hostname").on(
table.domain_id,
table.service_id,
table.hostname,
),
],
);
export const serviceIps = sqliteTable("service_ips", {
id: integer("id").primaryKey({ autoIncrement: true }),