Сервис ставится существующей группой с Badge типа резервирования; sync сохраняет lbMode. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -49,6 +49,39 @@ describe('vpsDomainsRepository', () => {
|
||||
expect(domains).toHaveLength(1)
|
||||
expect(domains[0]?.fqdn).toBe('vpn.example.com')
|
||||
expect(domains[0]?.matchStatus).toBe('matched')
|
||||
expect(domains[0]?.lbMode).toBeNull()
|
||||
})
|
||||
|
||||
it('сохраняет lbMode из binding', () => {
|
||||
const vps = vpsRepository.create({
|
||||
ip: '203.0.113.10',
|
||||
providerId: 'p1',
|
||||
providerAccountId: 'a1',
|
||||
status: 'active',
|
||||
tariffType: 'monthly',
|
||||
currency: 'RUB',
|
||||
vcpu: 1,
|
||||
ramGb: 1,
|
||||
diskGb: 10,
|
||||
})
|
||||
const created = Array.isArray(vps) ? vps[0]! : vps
|
||||
|
||||
vpsDomainsRepository.syncBindings([
|
||||
{
|
||||
bindingId: 1,
|
||||
serviceId: 10,
|
||||
serviceName: 'VPN Node',
|
||||
serviceSlug: 'vpn-node',
|
||||
fqdn: 'vpn.example.com',
|
||||
zoneName: 'example.com',
|
||||
hostname: 'vpn',
|
||||
ips: ['203.0.113.10'],
|
||||
lbMode: 'failover',
|
||||
},
|
||||
])
|
||||
|
||||
const domains = vpsDomainsRepository.listByVpsId(created.id)
|
||||
expect(domains[0]?.lbMode).toBe('failover')
|
||||
})
|
||||
|
||||
it('помечает unmatched без совпадения IP', () => {
|
||||
|
||||
@@ -63,6 +63,7 @@ type BindingFields = {
|
||||
cfdmBindingId: number
|
||||
source: 'cfdm'
|
||||
targetIps: string
|
||||
lbMode: string | null
|
||||
syncedAt: string
|
||||
}
|
||||
|
||||
@@ -239,6 +240,7 @@ export const vpsDomainsRepository = {
|
||||
cfdmBindingId: seed.cfdmBindingId,
|
||||
source: 'cfdm',
|
||||
targetIps: seed.targetIps ?? JSON.stringify(storedIps),
|
||||
lbMode: seed.lbMode ?? null,
|
||||
syncedAt: now,
|
||||
}
|
||||
const result = reconcileBindingToVpsIds(db, group, fields, wanted, emptyStatus)
|
||||
@@ -282,6 +284,7 @@ export const vpsDomainsRepository = {
|
||||
cfdmBindingId: item.bindingId,
|
||||
source: 'cfdm',
|
||||
targetIps: JSON.stringify(item.ips.filter(isIpLiteral)),
|
||||
lbMode: item.lbMode ?? null,
|
||||
syncedAt: now,
|
||||
})
|
||||
|
||||
|
||||
@@ -262,6 +262,7 @@ const CORE_TABLE_MIGRATIONS: string[] = [
|
||||
source TEXT NOT NULL DEFAULT 'cfdm',
|
||||
matchStatus TEXT NOT NULL DEFAULT 'unmatched',
|
||||
targetIps TEXT,
|
||||
lbMode TEXT,
|
||||
syncedAt TEXT NOT NULL,
|
||||
UNIQUE(cfdmBindingId, vpsId)
|
||||
)`,
|
||||
@@ -376,6 +377,7 @@ const COLUMN_MIGRATIONS: string[] = [
|
||||
`ALTER TABLE settings ADD COLUMN notifyLowBalanceEnabled INTEGER`,
|
||||
`ALTER TABLE settings ADD COLUMN notifySyncDigestEnabled INTEGER`,
|
||||
`ALTER TABLE vps_domains ADD COLUMN targetIps TEXT`,
|
||||
`ALTER TABLE vps_domains ADD COLUMN lbMode TEXT`,
|
||||
`ALTER TABLE providers ADD COLUMN spaceId TEXT`,
|
||||
`ALTER TABLE provider_accounts ADD COLUMN spaceId TEXT`,
|
||||
`ALTER TABLE provider_accounts ADD COLUMN balance_api REAL`,
|
||||
@@ -473,6 +475,10 @@ function rebuildVpsDomainsBindingUnique(sqlite: Database.Database): void {
|
||||
return
|
||||
}
|
||||
|
||||
const colRows = sqlite.prepare(`PRAGMA table_info(vps_domains)`).all() as { name: string }[]
|
||||
const hasLbMode = colRows.some((c) => c.name === 'lbMode')
|
||||
const lbSelect = hasLbMode ? 'lbMode' : 'NULL'
|
||||
|
||||
sqlite.exec('PRAGMA foreign_keys = OFF')
|
||||
sqlite.exec('BEGIN')
|
||||
try {
|
||||
@@ -490,15 +496,16 @@ function rebuildVpsDomainsBindingUnique(sqlite: Database.Database): void {
|
||||
source TEXT NOT NULL DEFAULT 'cfdm',
|
||||
matchStatus TEXT NOT NULL DEFAULT 'unmatched',
|
||||
targetIps TEXT,
|
||||
lbMode TEXT,
|
||||
syncedAt TEXT NOT NULL,
|
||||
UNIQUE(cfdmBindingId, vpsId)
|
||||
)`)
|
||||
sqlite.exec(`INSERT INTO vps_domains_new (
|
||||
id, spaceId, vpsId, fqdn, zoneName, hostname, serviceName, serviceSlug,
|
||||
cfdmServiceId, cfdmBindingId, source, matchStatus, targetIps, syncedAt
|
||||
cfdmServiceId, cfdmBindingId, source, matchStatus, targetIps, lbMode, syncedAt
|
||||
) SELECT
|
||||
id, COALESCE(spaceId, 'space-main'), vpsId, fqdn, zoneName, hostname, serviceName, serviceSlug,
|
||||
cfdmServiceId, cfdmBindingId, source, matchStatus, targetIps, syncedAt
|
||||
cfdmServiceId, cfdmBindingId, source, matchStatus, targetIps, ${lbSelect}, syncedAt
|
||||
FROM vps_domains`)
|
||||
sqlite.exec('DROP TABLE vps_domains')
|
||||
sqlite.exec('ALTER TABLE vps_domains_new RENAME TO vps_domains')
|
||||
|
||||
@@ -232,6 +232,7 @@ export const vpsDomains = sqliteTable(
|
||||
source: text('source').notNull().default('cfdm'),
|
||||
matchStatus: text('matchStatus').notNull().default('unmatched'),
|
||||
targetIps: text('targetIps'),
|
||||
lbMode: text('lbMode'),
|
||||
syncedAt: text('syncedAt').notNull(),
|
||||
},
|
||||
(t) => ({
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const topologyLbModeSchema = z.enum(['round_robin', 'failover', 'weighted'])
|
||||
export type TopologyLbMode = z.infer<typeof topologyLbModeSchema>
|
||||
|
||||
export const cfdmBindingSyncItemSchema = z.object({
|
||||
bindingId: z.number().int().positive(),
|
||||
serviceId: z.number().int().positive(),
|
||||
@@ -11,6 +14,8 @@ export const cfdmBindingSyncItemSchema = z.object({
|
||||
ips: z.array(z.string()),
|
||||
/** CNAME-цель (FQDN), если binding — CNAME; для матчинга по dns / цепочке. */
|
||||
cnameTarget: z.string().optional(),
|
||||
/** HA-режим binding (fallback — service group). Optional для старых payload. */
|
||||
lbMode: topologyLbModeSchema.optional(),
|
||||
deleted: z.boolean().optional(),
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user