Phase9
Some checks failed
CI / Node — lint, typecheck, test, build (push) Failing after 9s
CI / Go — node-agent tests (push) Failing after 8s
CI / Go — edge-gateway build (push) Successful in 17s

This commit is contained in:
smueller
2026-06-26 13:46:25 +02:00
parent b335f6a497
commit ed8334328e
49 changed files with 2219 additions and 16 deletions

View File

@@ -1,6 +1,8 @@
import { prisma } from '@hexahost/database';
import type { GameNode, GameServer } from '@hexahost/database';
import { createDnsProvider, resolveDnsProviderName } from './providers';
import type { DnsRecordSpec } from './providers';
import { buildJoinSlug } from './slug';
export function getPlayDomain(): string {
@@ -21,6 +23,10 @@ function joinHostname(slug: string, playDomain = getPlayDomain()): string {
return `${slug}.${playDomain}`;
}
function initialDnsStatus(): 'PENDING' | 'ACTIVE' {
return resolveDnsProviderName() === 'rfc2136' ? 'PENDING' : 'ACTIVE';
}
async function upsertDnsRecord(input: {
serverId: string;
fqdn: string;
@@ -30,6 +36,8 @@ async function upsertDnsRecord(input: {
srvPriority?: number;
srvWeight?: number;
}): Promise<void> {
const status = initialDnsStatus();
await prisma.serverDnsRecord.upsert({
where: {
serverId_recordType_fqdn: {
@@ -46,21 +54,77 @@ async function upsertDnsRecord(input: {
port: input.port,
srvPriority: input.srvPriority,
srvWeight: input.srvWeight,
status: 'ACTIVE',
lastSyncedAt: new Date(),
status,
lastSyncedAt: status === 'ACTIVE' ? new Date() : null,
},
update: {
target: input.target,
port: input.port,
srvPriority: input.srvPriority,
srvWeight: input.srvWeight,
status: 'ACTIVE',
lastSyncedAt: new Date(),
status,
lastSyncedAt: status === 'ACTIVE' ? new Date() : null,
lastError: null,
},
});
}
function toDnsRecordSpec(record: {
serverId: string;
fqdn: string;
recordType: string;
target: string;
port: number | null;
srvPriority: number | null;
srvWeight: number | null;
}): DnsRecordSpec {
return {
serverId: record.serverId,
fqdn: record.fqdn,
recordType: record.recordType as 'A' | 'SRV',
target: record.target,
port: record.port ?? undefined,
srvPriority: record.srvPriority ?? undefined,
srvWeight: record.srvWeight ?? undefined,
};
}
export async function pushPendingDnsRecords(limit = 50): Promise<number> {
const provider = createDnsProvider();
const pending = await prisma.serverDnsRecord.findMany({
where: { status: 'PENDING' },
take: limit,
orderBy: { updatedAt: 'asc' },
});
let synced = 0;
for (const record of pending) {
try {
await provider.syncRecord(toDnsRecordSpec(record));
await prisma.serverDnsRecord.update({
where: { id: record.id },
data: {
status: 'ACTIVE',
lastSyncedAt: new Date(),
lastError: null,
},
});
synced += 1;
} catch (error) {
await prisma.serverDnsRecord.update({
where: { id: record.id },
data: {
status: 'FAILED',
lastError: error instanceof Error ? error.message : 'DNS sync failed',
},
});
}
}
return synced;
}
export async function syncServerDnsRecords(
server: GameServer & { node: GameNode | null },
playDomain = getPlayDomain(),
@@ -91,6 +155,12 @@ export async function syncServerDnsRecords(
srvWeight: 5,
});
}
if (resolveDnsProviderName() === 'local') {
return;
}
await pushPendingDnsRecords(10);
}
async function ensureUniqueSlug(serverId: string, serverName: string): Promise<string> {