feat(services): считать сервис живым при одном OK и писать историю failover
quality / commitlint (push) Skipped
quality / changes (push) Successful in 11s
quality / docker-check (push) Skipped
CD / update-wiki (push) Successful in 5s
quality / web (push) Successful in 1m11s
quality / api (push) Successful in 1m1s
CD / quality (push) Successful in 2m30s
CD / publish (push) Successful in 2m4s
quality / commitlint (push) Skipped
quality / changes (push) Successful in 11s
quality / docker-check (push) Skipped
CD / update-wiki (push) Successful in 5s
quality / web (push) Successful in 1m11s
quality / api (push) Successful in 1m1s
CD / quality (push) Successful in 2m30s
CD / publish (push) Successful in 2m4s
Сервис up, если жив хотя бы один IP. Failover показывает Down по FQDN и журнал add/remove A-записей. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Vendored
+295
-3
File diff suppressed because one or more lines are too long
Vendored
+40
-1
@@ -355,6 +355,15 @@ var notificationLog = sqliteTable("notification_log", {
|
||||
message: text("message").notNull(),
|
||||
created_at: text("created_at").notNull().default(sql`datetime('now')`)
|
||||
});
|
||||
var failoverLog = sqliteTable("failover_log", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
service_id: integer("service_id").notNull().references(() => services.id, { onDelete: "cascade" }),
|
||||
binding_id: integer("binding_id").notNull().references(() => serviceBindings.id, { onDelete: "cascade" }),
|
||||
fqdn: text("fqdn").notNull(),
|
||||
ip: text("ip").notNull(),
|
||||
action: text("action").notNull(),
|
||||
created_at: text("created_at").notNull().default(sql`datetime('now')`)
|
||||
});
|
||||
var auditLog = sqliteTable("audit_log", {
|
||||
id: text("id").primaryKey(),
|
||||
event_id: text("event_id"),
|
||||
@@ -395,6 +404,7 @@ var schema = {
|
||||
domainMonitorResults,
|
||||
healthProbeLog,
|
||||
notificationLog,
|
||||
failoverLog,
|
||||
auditLog
|
||||
};
|
||||
|
||||
@@ -715,6 +725,7 @@ __export(repos_exports, {
|
||||
getSyncJob: () => getSyncJob,
|
||||
insertBinding: () => insertBinding,
|
||||
insertDnsRecord: () => insertDnsRecord,
|
||||
insertFailoverLog: () => insertFailoverLog,
|
||||
insertHealthProbeLog: () => insertHealthProbeLog,
|
||||
insertNotificationLog: () => insertNotificationLog,
|
||||
linkBindingRecord: () => linkBindingRecord,
|
||||
@@ -738,6 +749,7 @@ __export(repos_exports, {
|
||||
listDomains: () => listDomains,
|
||||
listDomainsEnriched: () => listDomainsEnriched,
|
||||
listEnabledDomainMonitors: () => listEnabledDomainMonitors,
|
||||
listFailoverLogForService: () => listFailoverLogForService,
|
||||
listGroupDnsRecords: () => listGroupDnsRecords,
|
||||
listGroups: () => listGroups,
|
||||
listHealthCheckTargets: () => listHealthCheckTargets,
|
||||
@@ -1995,6 +2007,12 @@ var WORST_HEALTH_SQL = sql2.raw(`CASE
|
||||
END) = 1 THEN 'up'
|
||||
ELSE 'unknown'
|
||||
END`);
|
||||
var BEST_ALIVE_HEALTH_SQL = sql2.raw(`CASE
|
||||
WHEN MAX(CASE WHEN status = 'up' THEN 1 ELSE 0 END) = 1 THEN 'up'
|
||||
WHEN MAX(CASE WHEN status = 'degraded' THEN 1 ELSE 0 END) = 1 THEN 'degraded'
|
||||
WHEN MAX(CASE WHEN status = 'down' THEN 1 ELSE 0 END) = 1 THEN 'down'
|
||||
ELSE 'unknown'
|
||||
END`);
|
||||
function aggregateIpHealthByRefs(db, scope, refIds) {
|
||||
const result = /* @__PURE__ */ new Map();
|
||||
if (refIds.length === 0) return result;
|
||||
@@ -2055,7 +2073,7 @@ function aggregateIpHealthByServiceIds(db, serviceIds) {
|
||||
);
|
||||
const rows = db.all(sql2`
|
||||
SELECT sb.service_id AS service_id,
|
||||
${WORST_HEALTH_SQL} AS health_status,
|
||||
${BEST_ALIVE_HEALTH_SQL} AS health_status,
|
||||
MAX(ihs.latency_ms) AS health_latency_ms
|
||||
FROM ip_health_status ihs
|
||||
INNER JOIN service_bindings sb
|
||||
@@ -2545,6 +2563,26 @@ function listNotificationLog(db, limit = 50) {
|
||||
LIMIT ${limit}
|
||||
`);
|
||||
}
|
||||
function insertFailoverLog(db, input) {
|
||||
for (const entry of input.entries) {
|
||||
db.insert(failoverLog).values({
|
||||
service_id: input.serviceId,
|
||||
binding_id: input.bindingId,
|
||||
fqdn: input.fqdn,
|
||||
ip: entry.ip,
|
||||
action: entry.action
|
||||
}).run();
|
||||
}
|
||||
}
|
||||
function listFailoverLogForService(db, serviceId, limit = 100) {
|
||||
return db.all(sql2`
|
||||
SELECT id, service_id, binding_id, fqdn, ip, action, created_at
|
||||
FROM failover_log
|
||||
WHERE service_id = ${serviceId}
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT ${limit}
|
||||
`);
|
||||
}
|
||||
export {
|
||||
ConflictError,
|
||||
NotFoundError,
|
||||
@@ -2560,6 +2598,7 @@ export {
|
||||
domainMonitors,
|
||||
domainTags,
|
||||
domains,
|
||||
failoverLog,
|
||||
getAppSettings,
|
||||
getAppSettingsSecrets,
|
||||
groups,
|
||||
|
||||
Reference in New Issue
Block a user