quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 10s
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m8s
quality / api (push) Successful in 1m9s
CD / quality (push) Successful in 2m31s
CD / publish (push) Successful in 1m35s
- Added new endpoints for listing and checking service certificates, improving visibility into SSL status. - Integrated certificate monitoring options into service binding updates, allowing for flexible SSL management. - Updated the service detail grid to include SSL monitoring controls, enhancing user interaction with certificate settings. - Refactored related components and schemas to support the new certificate features, ensuring consistency across the application. - Improved test coverage for certificate functionalities, validating the new features and ensuring reliability.
545 lines
18 KiB
TypeScript
545 lines
18 KiB
TypeScript
import { sql } from "drizzle-orm";
|
|
import {
|
|
integer,
|
|
primaryKey,
|
|
sqliteTable,
|
|
text,
|
|
unique,
|
|
} from "drizzle-orm/sqlite-core";
|
|
|
|
export const groups = sqliteTable("groups", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
name: text("name").notNull(),
|
|
slug: text("slug").notNull().unique(),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
updated_at: text("updated_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const services = sqliteTable("services", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
name: text("name").notNull(),
|
|
slug: text("slug").notNull().unique(),
|
|
service_group_id: integer("service_group_id").references(
|
|
() => serviceGroups.id,
|
|
{ onDelete: "set null" },
|
|
),
|
|
subdomain: text("subdomain"),
|
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
sort_order: integer("sort_order").notNull().default(0),
|
|
lb_weight: integer("lb_weight").notNull().default(1),
|
|
lb_priority: integer("lb_priority").notNull().default(1),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
updated_at: text("updated_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const serviceGroups = sqliteTable("service_groups", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
name: text("name").notNull(),
|
|
type: text("type").notNull().default("custom"),
|
|
icon: text("icon"),
|
|
domain: text("domain"),
|
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
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),
|
|
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')`),
|
|
});
|
|
|
|
export const domains = sqliteTable("domains", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
group_id: integer("group_id").references(() => groups.id, {
|
|
onDelete: "set null",
|
|
}),
|
|
zone_name: text("zone_name").notNull().unique(),
|
|
cf_zone_id: text("cf_zone_id").notNull(),
|
|
status: text("status").notNull().default("active"),
|
|
cert_monitoring: text("cert_monitoring").notNull().default("auto"),
|
|
environment: text("environment"),
|
|
last_synced_at: text("last_synced_at"),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
updated_at: text("updated_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const subdomains = sqliteTable("subdomains", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
domain_id: integer("domain_id")
|
|
.notNull()
|
|
.references(() => domains.id, { onDelete: "cascade" }),
|
|
name: text("name").notNull(),
|
|
fqdn: text("fqdn").notNull(),
|
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
cert_monitoring: text("cert_monitoring").notNull().default("auto"),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
updated_at: text("updated_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const dnsRecords = sqliteTable("dns_records", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
domain_id: integer("domain_id")
|
|
.notNull()
|
|
.references(() => domains.id, { onDelete: "cascade" }),
|
|
cf_record_id: text("cf_record_id"),
|
|
record_type: text("record_type").notNull(),
|
|
name: text("name").notNull(),
|
|
content: text("content").notNull(),
|
|
ttl: integer("ttl").notNull().default(1),
|
|
proxied: integer("proxied", { mode: "boolean" }).notNull().default(false),
|
|
priority: integer("priority"),
|
|
sync_status: text("sync_status").notNull().default("pending_push"),
|
|
origin: text("origin").notNull().default("local"),
|
|
last_error: text("last_error"),
|
|
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),
|
|
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')`),
|
|
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 healthChecks = sqliteTable("health_checks", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
provider: text("provider").notNull().default("local"),
|
|
cf_healthcheck_id: text("cf_healthcheck_id"),
|
|
cf_zone_id: text("cf_zone_id"),
|
|
name: text("name").notNull(),
|
|
protocol: text("protocol").notNull().default("tcp"),
|
|
path: text("path"),
|
|
method: text("method"),
|
|
timeout: integer("timeout").notNull().default(5),
|
|
interval_sec: integer("interval_sec").notNull().default(30),
|
|
retries: integer("retries").notNull().default(2),
|
|
expected_status: integer("expected_status"),
|
|
consecutive_fails: integer("consecutive_fails").notNull().default(2),
|
|
consecutive_successes: integer("consecutive_successes").notNull().default(2),
|
|
suspended: integer("suspended", { 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 nodes = sqliteTable(
|
|
"nodes",
|
|
{
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
service_id: integer("service_id")
|
|
.notNull()
|
|
.references(() => services.id, { onDelete: "cascade" }),
|
|
address: text("address").notNull(),
|
|
protocol: text("protocol").notNull().default("tcp"),
|
|
port: integer("port"),
|
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
priority: integer("priority").notNull().default(1),
|
|
weight: integer("weight").notNull().default(1),
|
|
health_status: text("health_status").notNull().default("unknown"),
|
|
health_check_id: integer("health_check_id").references(() => healthChecks.id, {
|
|
onDelete: "set null",
|
|
}),
|
|
consecutive_failures: integer("consecutive_failures").notNull().default(0),
|
|
consecutive_successes: integer("consecutive_successes").notNull().default(0),
|
|
last_check_at: text("last_check_at"),
|
|
last_failure_reason: text("last_failure_reason"),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
updated_at: text("updated_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
},
|
|
(t) => [unique("nodes_service_address").on(t.service_id, t.address)],
|
|
);
|
|
|
|
export const bindingNodes = sqliteTable(
|
|
"binding_nodes",
|
|
{
|
|
binding_id: integer("binding_id")
|
|
.notNull()
|
|
.references(() => serviceBindings.id, { onDelete: "cascade" }),
|
|
node_id: integer("node_id")
|
|
.notNull()
|
|
.references(() => nodes.id, { onDelete: "cascade" }),
|
|
weight: integer("weight").notNull().default(1),
|
|
priority: integer("priority").notNull().default(1),
|
|
},
|
|
(t) => [primaryKey({ columns: [t.binding_id, t.node_id] })],
|
|
);
|
|
|
|
export const serviceIps = sqliteTable("service_ips", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
service_id: integer("service_id")
|
|
.notNull()
|
|
.references(() => services.id, { onDelete: "cascade" }),
|
|
ip: text("ip").notNull(),
|
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const serviceBindingRecords = sqliteTable(
|
|
"service_binding_records",
|
|
{
|
|
binding_id: integer("binding_id")
|
|
.notNull()
|
|
.references(() => serviceBindings.id, { onDelete: "cascade" }),
|
|
dns_record_id: integer("dns_record_id")
|
|
.notNull()
|
|
.references(() => dnsRecords.id, { onDelete: "cascade" }),
|
|
},
|
|
(t) => [primaryKey({ columns: [t.binding_id, t.dns_record_id] })],
|
|
);
|
|
|
|
export const serviceBindingIps = sqliteTable(
|
|
"service_binding_ips",
|
|
{
|
|
binding_id: integer("binding_id")
|
|
.notNull()
|
|
.references(() => serviceBindings.id, { onDelete: "cascade" }),
|
|
ip: text("ip").notNull(),
|
|
weight: integer("weight").notNull().default(1),
|
|
priority: integer("priority").notNull().default(1),
|
|
},
|
|
(t) => [primaryKey({ columns: [t.binding_id, t.ip] })],
|
|
);
|
|
|
|
export const serviceGroupDnsRecords = sqliteTable(
|
|
"service_group_dns_records",
|
|
{
|
|
group_id: integer("group_id")
|
|
.notNull()
|
|
.references(() => serviceGroups.id, { onDelete: "cascade" }),
|
|
dns_record_id: integer("dns_record_id")
|
|
.notNull()
|
|
.references(() => dnsRecords.id, { onDelete: "cascade" }),
|
|
},
|
|
(t) => [primaryKey({ columns: [t.group_id, t.dns_record_id] })],
|
|
);
|
|
|
|
export const certificates = sqliteTable("certificates", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
domain_id: integer("domain_id")
|
|
.notNull()
|
|
.references(() => domains.id, { onDelete: "cascade" }),
|
|
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"),
|
|
last_error: text("last_error"),
|
|
status: text("status").notNull().default("unknown"),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
updated_at: text("updated_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const syncJobs = sqliteTable("sync_jobs", {
|
|
id: text("id").primaryKey(),
|
|
status: text("status").notNull().default("pending"),
|
|
domain_id: integer("domain_id").references(() => domains.id, {
|
|
onDelete: "set null",
|
|
}),
|
|
message: text("message"),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
finished_at: text("finished_at"),
|
|
});
|
|
|
|
export const ipHealthStatus = sqliteTable(
|
|
"ip_health_status",
|
|
{
|
|
scope: text("scope").notNull(),
|
|
ref_id: integer("ref_id").notNull(),
|
|
ip: text("ip").notNull(),
|
|
status: text("status").notNull().default("unknown"),
|
|
latency_ms: integer("latency_ms"),
|
|
consecutive_failures: integer("consecutive_failures")
|
|
.notNull()
|
|
.default(0),
|
|
consecutive_successes: integer("consecutive_successes")
|
|
.notNull()
|
|
.default(0),
|
|
last_checked_at: text("last_checked_at"),
|
|
last_error: text("last_error"),
|
|
colo: text("colo"),
|
|
provider: text("provider").notNull().default("local"),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
updated_at: text("updated_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
},
|
|
(t) => [primaryKey({ columns: [t.scope, t.ref_id, t.ip] })],
|
|
);
|
|
|
|
export const appSettings = sqliteTable("app_settings", {
|
|
id: text("id").primaryKey(),
|
|
app_switcher_json: text("app_switcher_json"), // deprecated: portal is SoT; column kept for migrate compat
|
|
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"),
|
|
show_quick_actions: integer("show_quick_actions", {
|
|
mode: "boolean",
|
|
})
|
|
.notNull()
|
|
.default(true),
|
|
health_check_cron: text("health_check_cron"),
|
|
health_degraded_failures: integer("health_degraded_failures"),
|
|
health_down_failures: integer("health_down_failures"),
|
|
health_latency_warn_ms: integer("health_latency_warn_ms"),
|
|
health_success_recoveries: integer("health_success_recoveries"),
|
|
health_worker_url: text("health_worker_url"),
|
|
health_worker_token: text("health_worker_token"),
|
|
health_worker_account_id: text("health_worker_account_id"),
|
|
health_worker_kv_namespace_id: text("health_worker_kv_namespace_id"),
|
|
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')`),
|
|
});
|
|
|
|
export const domainTags = sqliteTable(
|
|
"domain_tags",
|
|
{
|
|
domain_id: integer("domain_id")
|
|
.notNull()
|
|
.references(() => domains.id, { onDelete: "cascade" }),
|
|
tag: text("tag").notNull(),
|
|
},
|
|
(t) => [primaryKey({ columns: [t.domain_id, t.tag] })],
|
|
);
|
|
|
|
export const domainMonitors = sqliteTable("domain_monitors", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
domain_id: integer("domain_id")
|
|
.notNull()
|
|
.references(() => domains.id, { onDelete: "cascade" }),
|
|
hostname: text("hostname").notNull(),
|
|
type: text("type").notNull().default("http"),
|
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
interval_sec: integer("interval_sec").notNull().default(60),
|
|
timeout_ms: integer("timeout_ms").notNull().default(5000),
|
|
path: text("path"),
|
|
expected_status: integer("expected_status"),
|
|
last_status: text("last_status").notNull().default("unknown"),
|
|
last_latency_ms: integer("last_latency_ms"),
|
|
last_checked_at: text("last_checked_at"),
|
|
last_error: text("last_error"),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
updated_at: text("updated_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const domainMonitorResults = sqliteTable("domain_monitor_results", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
monitor_id: integer("monitor_id")
|
|
.notNull()
|
|
.references(() => domainMonitors.id, { onDelete: "cascade" }),
|
|
status: text("status").notNull(),
|
|
latency_ms: integer("latency_ms"),
|
|
error: text("error"),
|
|
checked_at: text("checked_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const healthProbeLog = sqliteTable("health_probe_log", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
scope: text("scope").notNull(),
|
|
ref_id: integer("ref_id").notNull(),
|
|
ip: text("ip").notNull(),
|
|
provider: text("provider").notNull(),
|
|
status: text("status").notNull(),
|
|
ok: integer("ok", { mode: "boolean" }).notNull(),
|
|
latency_ms: integer("latency_ms"),
|
|
colo: text("colo"),
|
|
error: text("error"),
|
|
checked_at: text("checked_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const notificationLog = sqliteTable("notification_log", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
kind: text("kind").notNull(),
|
|
ref_type: text("ref_type").notNull(),
|
|
ref_id: integer("ref_id"),
|
|
title: text("title").notNull(),
|
|
message: text("message").notNull(),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const auditLog = sqliteTable("audit_log", {
|
|
id: text("id").primaryKey(),
|
|
event_id: text("event_id"),
|
|
source_app: text("source_app").notNull().default("cfdm"),
|
|
action: text("action").notNull(),
|
|
severity: text("severity").notNull().default("info"),
|
|
actor_user_id: text("actor_user_id"),
|
|
actor_email: text("actor_email"),
|
|
actor_name: text("actor_name"),
|
|
target_type: text("target_type"),
|
|
target_id: text("target_id"),
|
|
summary: text("summary").notNull(),
|
|
details_json: text("details_json"),
|
|
ip: text("ip"),
|
|
created_at: text("created_at")
|
|
.notNull()
|
|
.default(sql`datetime('now')`),
|
|
});
|
|
|
|
export const schema = {
|
|
groups,
|
|
services,
|
|
serviceGroups,
|
|
domains,
|
|
subdomains,
|
|
dnsRecords,
|
|
serviceBindings,
|
|
healthChecks,
|
|
nodes,
|
|
bindingNodes,
|
|
serviceIps,
|
|
serviceBindingRecords,
|
|
serviceBindingIps,
|
|
serviceGroupDnsRecords,
|
|
certificates,
|
|
syncJobs,
|
|
ipHealthStatus,
|
|
appSettings,
|
|
domainTags,
|
|
domainMonitors,
|
|
domainMonitorResults,
|
|
healthProbeLog,
|
|
notificationLog,
|
|
auditLog,
|
|
};
|