fix(integrations): матчить CFDM-домены по dns и CNAME-цели
Docker / build (push) Failing after 20s

Плашка «без привязки» оставалась для CNAME: sync шёл, но матч был только по IP.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-08-01 01:16:26 +07:00
co-authored by Cursor
parent d4d59f4b8e
commit 619385981c
2 changed files with 61 additions and 3 deletions
+59 -3
View File
@@ -14,6 +14,10 @@ function normalizeIp(ip: string): string {
return ip.trim().toLowerCase()
}
function normalizeHost(host: string): string {
return host.trim().toLowerCase().replace(/\.+$/, '')
}
function collectVpsIps(vps: { ip?: string | null; additionalIps?: string[] }): string[] {
const ips: string[] = []
if (vps.ip?.trim()) ips.push(normalizeIp(vps.ip))
@@ -41,6 +45,29 @@ function findVpsIdByIps(
return null
}
/** Точное совпадение hostname с полем VPS.dns. */
function findVpsIdByDns(
allVps: ReturnType<typeof vpsRepository.list>,
hostname: string,
): string | null {
const key = normalizeHost(hostname)
if (!key) return null
const matches = allVps.filter((v) => normalizeHost(v.dns ?? '') === key)
if (matches.length === 1) return matches[0]!.id
return null
}
function findVpsIdForBinding(
allVps: ReturnType<typeof vpsRepository.list>,
item: Pick<CfdmBindingSyncItem, 'ips' | 'fqdn' | 'cnameTarget'>,
): string | null {
return (
findVpsIdByIps(allVps, item.ips) ??
findVpsIdByDns(allVps, item.fqdn) ??
(item.cnameTarget ? findVpsIdByDns(allVps, item.cnameTarget) : null)
)
}
function resolveMatchStatus(vpsId: string | null): 'matched' | 'unmatched' {
return vpsId ? 'matched' : 'unmatched'
}
@@ -107,6 +134,9 @@ export const vpsDomainsRepository = {
if (!vpsId && storedIps.length > 0) {
vpsId = findVpsIdByIps(allVps, storedIps)
}
if (!vpsId) {
vpsId = findVpsIdByDns(allVps, row.fqdn)
}
const matchStatus =
vpsId && vpsIds.has(vpsId)
? 'matched'
@@ -143,6 +173,7 @@ export const vpsDomainsRepository = {
let deleted = 0
let upserted = 0
const keptBindingIds = new Set<number>()
const fqdnToVpsId = new Map<string, string>()
for (const item of items) {
if (item.deleted) {
@@ -152,10 +183,14 @@ export const vpsDomainsRepository = {
keptBindingIds.add(item.bindingId)
const vpsId = findVpsIdByIps(allVps, item.ips)
const vpsId = findVpsIdForBinding(allVps, item)
const matchStatus = resolveMatchStatus(vpsId)
if (matchStatus === 'matched') matched++
else unmatched++
if (matchStatus === 'matched') {
matched++
fqdnToVpsId.set(normalizeHost(item.fqdn), vpsId!)
} else {
unmatched++
}
const existing = this.getByCfdmBindingId(item.bindingId)
const values = {
@@ -182,6 +217,27 @@ export const vpsDomainsRepository = {
upserted++
}
// CNAME → уже matched FQDN (например imsk → ihome, а ihome привязан по IP/dns)
let inherited = true
while (inherited) {
inherited = false
for (const item of items) {
if (item.deleted || !item.cnameTarget) continue
const existing = this.getByCfdmBindingId(item.bindingId)
if (!existing || existing.vpsId) continue
const parentVpsId = fqdnToVpsId.get(normalizeHost(item.cnameTarget))
if (!parentVpsId) continue
db.update(schema.vpsDomains)
.set({ vpsId: parentVpsId, matchStatus: 'matched' })
.where(eq(schema.vpsDomains.id, existing.id))
.run()
fqdnToVpsId.set(normalizeHost(item.fqdn), parentVpsId)
matched++
unmatched = Math.max(0, unmatched - 1)
inherited = true
}
}
if (opts?.fullSync) {
const rows = db
.select()
@@ -9,6 +9,8 @@ export const cfdmBindingSyncItemSchema = z.object({
zoneName: z.string().min(1),
hostname: z.string(),
ips: z.array(z.string()),
/** CNAME-цель (FQDN), если binding — CNAME; для матчинга по dns / цепочке. */
cnameTarget: z.string().optional(),
deleted: z.boolean().optional(),
})