Update ReUI skill documentation to reflect the addition of 3 new components, increasing the total from 17 to 20. Adjust descriptions and examples accordingly, including updates to the data-grid API to align with TanStack Table v9. Ensure all references to components and their usage are consistent across the documentation.
This commit is contained in:
Vendored
+18
-2
File diff suppressed because one or more lines are too long
Vendored
+88
-14
@@ -491,6 +491,7 @@ function touchVpsTrackerSync(db) {
|
||||
var repos_exports = {};
|
||||
__export(repos_exports, {
|
||||
addDomainTags: () => addDomainTags,
|
||||
aggregateGroupScopeHealthByIds: () => aggregateGroupScopeHealthByIds,
|
||||
aggregateIpHealthByRefs: () => aggregateIpHealthByRefs,
|
||||
aggregateIpHealthByServiceIds: () => aggregateIpHealthByServiceIds,
|
||||
bindingsToRemove: () => bindingsToRemove,
|
||||
@@ -559,6 +560,7 @@ __export(repos_exports, {
|
||||
listHealthCheckTargets: () => listHealthCheckTargets,
|
||||
listIpHealthStatus: () => listIpHealthStatus,
|
||||
listNotificationLog: () => listNotificationLog,
|
||||
listOriginIpsForFqdn: () => listOriginIpsForFqdn,
|
||||
listRecordsForBinding: () => listRecordsForBinding,
|
||||
listServiceGroups: () => listServiceGroups,
|
||||
listServiceIps: () => listServiceIps,
|
||||
@@ -597,7 +599,7 @@ __export(repos_exports, {
|
||||
upsertIpHealthStatus: () => upsertIpHealthStatus,
|
||||
upsertSubdomain: () => upsertSubdomain
|
||||
});
|
||||
import { dnsRecordNamesMatch } from "@cfdm/shared";
|
||||
import { dnsRecordNamesMatch, isIpLiteral } 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();
|
||||
@@ -870,6 +872,33 @@ function deleteDnsRecord(db, id) {
|
||||
function listDnsByDomain(db, domainId) {
|
||||
return db.select().from(dnsRecords).where(eq3(dnsRecords.domain_id, domainId)).all().map(mapDnsRecord);
|
||||
}
|
||||
function listOriginIpsForFqdn(db, fqdn, depth = 0) {
|
||||
if (depth > 8) return [];
|
||||
const normalized = fqdn.trim().toLowerCase().replace(/\.+$/, "");
|
||||
if (!normalized) return [];
|
||||
const aIps = [];
|
||||
let cnameNext = null;
|
||||
for (const domain of listAllDomains(db)) {
|
||||
const zone = domain.zone_name.trim().toLowerCase().replace(/\.+$/, "");
|
||||
if (!zone) continue;
|
||||
if (normalized !== zone && !normalized.endsWith(`.${zone}`)) continue;
|
||||
const hostLabel = normalized === zone ? "@" : normalized.slice(0, -(zone.length + 1));
|
||||
for (const record of listDnsByDomain(db, domain.id)) {
|
||||
const type = record.record_type.toUpperCase();
|
||||
if (!dnsRecordNamesMatch(record.name, hostLabel, zone)) continue;
|
||||
if (type === "A" || type === "AAAA") {
|
||||
const ip = record.content.trim();
|
||||
if (ip) aIps.push(ip);
|
||||
} else if (type === "CNAME" && !cnameNext) {
|
||||
const target = record.content.trim().replace(/\.+$/, "");
|
||||
if (target) cnameNext = target.includes(".") ? target : `${target}.${zone}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aIps.length > 0) return [...new Set(aIps)];
|
||||
if (cnameNext) return listOriginIpsForFqdn(db, cnameNext, depth + 1);
|
||||
return [];
|
||||
}
|
||||
function findDnsByCfId(db, domainId, cfRecordId) {
|
||||
const row = db.select().from(dnsRecords).where(
|
||||
and2(
|
||||
@@ -1169,17 +1198,22 @@ function enrichServiceBindingView(db, row) {
|
||||
const configured = listBindingIpsWithMeta(db, row.id);
|
||||
const configuredIps = configured.map((c) => c.ip);
|
||||
const linkedRecords = listRecordsForBinding(db, row.id);
|
||||
const linkedIps = linkedRecords.filter((record) => record.record_type.toUpperCase() === "A").map((record) => record.content);
|
||||
const linkedIps = linkedRecords.filter((record) => {
|
||||
const type = record.record_type.toUpperCase();
|
||||
return (type === "A" || type === "AAAA") && isIpLiteral(record.content);
|
||||
}).map((record) => record.content);
|
||||
const target_ips = [
|
||||
.../* @__PURE__ */ new Set([
|
||||
...configuredIps,
|
||||
...configuredIps.filter(isIpLiteral),
|
||||
...linkedIps,
|
||||
...row.target_ip ? [row.target_ip] : []
|
||||
...row.target_ip && isIpLiteral(row.target_ip) ? [row.target_ip] : []
|
||||
])
|
||||
].sort();
|
||||
if (target_ips.length === 0) {
|
||||
for (const record of listDnsByDomain(db, row.domain_id)) {
|
||||
if (record.record_type.toUpperCase() !== "A") continue;
|
||||
const type = record.record_type.toUpperCase();
|
||||
if (type !== "A" && type !== "AAAA") continue;
|
||||
if (!isIpLiteral(record.content)) continue;
|
||||
if (!dnsRecordMatchesHostname(record.name, row.hostname, row.zone_name)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1436,6 +1470,37 @@ function aggregateIpHealthByRefs(db, scope, refIds) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function aggregateGroupScopeHealthByIds(db, groupIds) {
|
||||
const result = /* @__PURE__ */ new Map();
|
||||
if (groupIds.length === 0) return result;
|
||||
const idList = sql2.join(
|
||||
groupIds.map((id) => sql2`${id}`),
|
||||
sql2`, `
|
||||
);
|
||||
const rows = db.all(sql2`
|
||||
SELECT ihs.ref_id AS ref_id,
|
||||
${WORST_HEALTH_SQL} AS health_status,
|
||||
MAX(ihs.latency_ms) AS health_latency_ms
|
||||
FROM ip_health_status ihs
|
||||
WHERE ihs.scope = 'group'
|
||||
AND ihs.ref_id IN (${idList})
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM service_binding_ips sbi
|
||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||
JOIN services s ON s.id = sb.service_id
|
||||
WHERE s.service_group_id = ihs.ref_id
|
||||
AND s.enabled = 1
|
||||
AND sbi.ip = ihs.ip
|
||||
AND (sb.cname_target IS NULL OR sb.cname_target = '')
|
||||
)
|
||||
GROUP BY ihs.ref_id
|
||||
`);
|
||||
for (const row of rows) {
|
||||
result.set(row.ref_id, parseHealthAggregateRow(row));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function aggregateIpHealthByServiceIds(db, serviceIds) {
|
||||
const result = /* @__PURE__ */ new Map();
|
||||
if (serviceIds.length === 0) return result;
|
||||
@@ -1538,12 +1603,18 @@ function pruneStaleIpHealthStatus(db, activeTargets) {
|
||||
}
|
||||
ips.add(t.ip);
|
||||
}
|
||||
const storedRefs = db.all(sql2`
|
||||
SELECT DISTINCT scope, ref_id FROM ip_health_status
|
||||
`);
|
||||
let deleted = 0;
|
||||
for (const [key, ips] of byRef) {
|
||||
const sep = key.indexOf(":");
|
||||
const scope = key.slice(0, sep);
|
||||
const refId = Number(key.slice(sep + 1));
|
||||
if (!Number.isFinite(refId)) continue;
|
||||
for (const { scope, ref_id: refId } of storedRefs) {
|
||||
const key = `${scope}:${refId}`;
|
||||
const ips = byRef.get(key);
|
||||
if (!ips || ips.size === 0) {
|
||||
deleteIpHealthStatusForRef(db, scope, refId);
|
||||
deleted += 1;
|
||||
continue;
|
||||
}
|
||||
for (const row of listIpHealthStatus(db, scope, refId)) {
|
||||
if (!ips.has(row.ip)) {
|
||||
deleteIpHealthStatusForIp(db, scope, refId, row.ip);
|
||||
@@ -1570,7 +1641,7 @@ function listHealthCheckTargets(db) {
|
||||
WHERE sb.health_check_enabled = 1
|
||||
`);
|
||||
const groupTargets = db.all(sql2`
|
||||
SELECT 'group' AS scope, sg.id AS ref_id, sip.ip,
|
||||
SELECT DISTINCT 'group' AS scope, sg.id AS ref_id, sbi.ip,
|
||||
sg.domain AS hostname,
|
||||
sg.health_check_type AS type,
|
||||
sg.health_check_port AS port,
|
||||
@@ -1578,13 +1649,16 @@ 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
|
||||
FROM services s
|
||||
JOIN service_ips sip ON sip.service_id = s.id
|
||||
FROM service_binding_ips sbi
|
||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||
JOIN services s ON s.id = sb.service_id
|
||||
JOIN service_groups sg ON sg.id = s.service_group_id
|
||||
WHERE sg.health_check_enabled = 1
|
||||
AND sg.domain IS NOT NULL
|
||||
AND sg.domain <> ''
|
||||
AND s.enabled = 1
|
||||
AND (sg.enabled = 1)
|
||||
AND sg.enabled = 1
|
||||
AND (sb.cname_target IS NULL OR sb.cname_target = '')
|
||||
`);
|
||||
const groupInheritedBindingTargets = db.all(sql2`
|
||||
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
||||
|
||||
+69
-10
@@ -1605,6 +1605,49 @@ export function aggregateIpHealthByRefs(
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group-scope badge health: only IPs that are currently published/probed
|
||||
* (A-record binding IPs of enabled services). Ignores stale pool-only rows.
|
||||
*/
|
||||
export function aggregateGroupScopeHealthByIds(
|
||||
db: Db,
|
||||
groupIds: number[],
|
||||
): Map<number, HealthAggregate> {
|
||||
const result = new Map<number, HealthAggregate>();
|
||||
if (groupIds.length === 0) return result;
|
||||
const idList = sql.join(
|
||||
groupIds.map((id) => sql`${id}`),
|
||||
sql`, `,
|
||||
);
|
||||
const rows = db.all<{
|
||||
ref_id: number;
|
||||
health_status: string | null;
|
||||
health_latency_ms: number | null;
|
||||
}>(sql`
|
||||
SELECT ihs.ref_id AS ref_id,
|
||||
${WORST_HEALTH_SQL} AS health_status,
|
||||
MAX(ihs.latency_ms) AS health_latency_ms
|
||||
FROM ip_health_status ihs
|
||||
WHERE ihs.scope = 'group'
|
||||
AND ihs.ref_id IN (${idList})
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM service_binding_ips sbi
|
||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||
JOIN services s ON s.id = sb.service_id
|
||||
WHERE s.service_group_id = ihs.ref_id
|
||||
AND s.enabled = 1
|
||||
AND sbi.ip = ihs.ip
|
||||
AND (sb.cname_target IS NULL OR sb.cname_target = '')
|
||||
)
|
||||
GROUP BY ihs.ref_id
|
||||
`);
|
||||
for (const row of rows) {
|
||||
result.set(row.ref_id, parseHealthAggregateRow(row));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Worst binding-scope health rolled up per service_id. */
|
||||
export function aggregateIpHealthByServiceIds(
|
||||
db: Db,
|
||||
@@ -1744,7 +1787,10 @@ export function deleteIpHealthStatusForIp(
|
||||
.run();
|
||||
}
|
||||
|
||||
/** Drop health rows whose IP is no longer a live probe target for that scope/ref. */
|
||||
/** Drop health rows whose IP is no longer a live probe target for that scope/ref.
|
||||
* Also clears refs that have stored rows but zero active targets (e.g. all
|
||||
* services disabled) — otherwise stale `down` keeps poisoning the group badge.
|
||||
*/
|
||||
export function pruneStaleIpHealthStatus(
|
||||
db: Db,
|
||||
activeTargets: Array<{ scope: HealthCheckScope; ref_id: number; ip: string }>,
|
||||
@@ -1760,12 +1806,19 @@ export function pruneStaleIpHealthStatus(
|
||||
ips.add(t.ip);
|
||||
}
|
||||
|
||||
const storedRefs = db.all<{ scope: HealthCheckScope; ref_id: number }>(sql`
|
||||
SELECT DISTINCT scope, ref_id FROM ip_health_status
|
||||
`);
|
||||
|
||||
let deleted = 0;
|
||||
for (const [key, ips] of byRef) {
|
||||
const sep = key.indexOf(":");
|
||||
const scope = key.slice(0, sep) as HealthCheckScope;
|
||||
const refId = Number(key.slice(sep + 1));
|
||||
if (!Number.isFinite(refId)) continue;
|
||||
for (const { scope, ref_id: refId } of storedRefs) {
|
||||
const key = `${scope}:${refId}`;
|
||||
const ips = byRef.get(key);
|
||||
if (!ips || ips.size === 0) {
|
||||
deleteIpHealthStatusForRef(db, scope, refId);
|
||||
deleted += 1;
|
||||
continue;
|
||||
}
|
||||
for (const row of listIpHealthStatus(db, scope, refId)) {
|
||||
if (!ips.has(row.ip)) {
|
||||
deleteIpHealthStatusForIp(db, scope, refId, row.ip);
|
||||
@@ -1799,8 +1852,11 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
||||
WHERE sb.health_check_enabled = 1
|
||||
`);
|
||||
|
||||
// Group FQDN probes the same A-record IPs that DNS publishes (binding IPs of
|
||||
// enabled services) — NOT the full service_ips pool. Pool-only dead IPs must
|
||||
// not mark the group Down while the published domain stays healthy.
|
||||
const groupTargets = db.all<HealthCheckTarget>(sql`
|
||||
SELECT 'group' AS scope, sg.id AS ref_id, sip.ip,
|
||||
SELECT DISTINCT 'group' AS scope, sg.id AS ref_id, sbi.ip,
|
||||
sg.domain AS hostname,
|
||||
sg.health_check_type AS type,
|
||||
sg.health_check_port AS port,
|
||||
@@ -1808,13 +1864,16 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
||||
sg.health_check_expected_status AS expected_status,
|
||||
sg.health_check_timeout_ms AS timeout_ms,
|
||||
sg.health_check_verify_tls AS verify_tls
|
||||
FROM services s
|
||||
JOIN service_ips sip ON sip.service_id = s.id
|
||||
FROM service_binding_ips sbi
|
||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||
JOIN services s ON s.id = sb.service_id
|
||||
JOIN service_groups sg ON sg.id = s.service_group_id
|
||||
WHERE sg.health_check_enabled = 1
|
||||
AND sg.domain IS NOT NULL
|
||||
AND sg.domain <> ''
|
||||
AND s.enabled = 1
|
||||
AND (sg.enabled = 1)
|
||||
AND sg.enabled = 1
|
||||
AND (sb.cname_target IS NULL OR sb.cname_target = '')
|
||||
`);
|
||||
|
||||
// IPs of A-bindings of enabled services in a group whose group has health-check enabled.
|
||||
|
||||
Reference in New Issue
Block a user