Плашка «без привязки» оставалась для CNAME: sync шёл, но матч был только по IP. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -14,6 +14,10 @@ function normalizeIp(ip: string): string {
|
|||||||
return ip.trim().toLowerCase()
|
return ip.trim().toLowerCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeHost(host: string): string {
|
||||||
|
return host.trim().toLowerCase().replace(/\.+$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
function collectVpsIps(vps: { ip?: string | null; additionalIps?: string[] }): string[] {
|
function collectVpsIps(vps: { ip?: string | null; additionalIps?: string[] }): string[] {
|
||||||
const ips: string[] = []
|
const ips: string[] = []
|
||||||
if (vps.ip?.trim()) ips.push(normalizeIp(vps.ip))
|
if (vps.ip?.trim()) ips.push(normalizeIp(vps.ip))
|
||||||
@@ -41,6 +45,29 @@ function findVpsIdByIps(
|
|||||||
return null
|
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' {
|
function resolveMatchStatus(vpsId: string | null): 'matched' | 'unmatched' {
|
||||||
return vpsId ? 'matched' : 'unmatched'
|
return vpsId ? 'matched' : 'unmatched'
|
||||||
}
|
}
|
||||||
@@ -107,6 +134,9 @@ export const vpsDomainsRepository = {
|
|||||||
if (!vpsId && storedIps.length > 0) {
|
if (!vpsId && storedIps.length > 0) {
|
||||||
vpsId = findVpsIdByIps(allVps, storedIps)
|
vpsId = findVpsIdByIps(allVps, storedIps)
|
||||||
}
|
}
|
||||||
|
if (!vpsId) {
|
||||||
|
vpsId = findVpsIdByDns(allVps, row.fqdn)
|
||||||
|
}
|
||||||
const matchStatus =
|
const matchStatus =
|
||||||
vpsId && vpsIds.has(vpsId)
|
vpsId && vpsIds.has(vpsId)
|
||||||
? 'matched'
|
? 'matched'
|
||||||
@@ -143,6 +173,7 @@ export const vpsDomainsRepository = {
|
|||||||
let deleted = 0
|
let deleted = 0
|
||||||
let upserted = 0
|
let upserted = 0
|
||||||
const keptBindingIds = new Set<number>()
|
const keptBindingIds = new Set<number>()
|
||||||
|
const fqdnToVpsId = new Map<string, string>()
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (item.deleted) {
|
if (item.deleted) {
|
||||||
@@ -152,10 +183,14 @@ export const vpsDomainsRepository = {
|
|||||||
|
|
||||||
keptBindingIds.add(item.bindingId)
|
keptBindingIds.add(item.bindingId)
|
||||||
|
|
||||||
const vpsId = findVpsIdByIps(allVps, item.ips)
|
const vpsId = findVpsIdForBinding(allVps, item)
|
||||||
const matchStatus = resolveMatchStatus(vpsId)
|
const matchStatus = resolveMatchStatus(vpsId)
|
||||||
if (matchStatus === 'matched') matched++
|
if (matchStatus === 'matched') {
|
||||||
else unmatched++
|
matched++
|
||||||
|
fqdnToVpsId.set(normalizeHost(item.fqdn), vpsId!)
|
||||||
|
} else {
|
||||||
|
unmatched++
|
||||||
|
}
|
||||||
|
|
||||||
const existing = this.getByCfdmBindingId(item.bindingId)
|
const existing = this.getByCfdmBindingId(item.bindingId)
|
||||||
const values = {
|
const values = {
|
||||||
@@ -182,6 +217,27 @@ export const vpsDomainsRepository = {
|
|||||||
upserted++
|
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) {
|
if (opts?.fullSync) {
|
||||||
const rows = db
|
const rows = db
|
||||||
.select()
|
.select()
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export const cfdmBindingSyncItemSchema = z.object({
|
|||||||
zoneName: z.string().min(1),
|
zoneName: z.string().min(1),
|
||||||
hostname: z.string(),
|
hostname: z.string(),
|
||||||
ips: z.array(z.string()),
|
ips: z.array(z.string()),
|
||||||
|
/** CNAME-цель (FQDN), если binding — CNAME; для матчинга по dns / цепочке. */
|
||||||
|
cnameTarget: z.string().optional(),
|
||||||
deleted: z.boolean().optional(),
|
deleted: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user