@stacksjs/buddy 0.70.205 → 0.70.207

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -690,7 +690,7 @@ async function runHetznerDeploy(args) {
690
690
  process.exit(ExitCode.FatalError);
691
691
  }
692
692
  const attachTo = tsCloudConfig.cloud?.attachTo;
693
- let ip;
693
+ let ip, ipv6;
694
694
  if (attachTo) {
695
695
  const box = await resolveAttachTargetBox(attachTo, environment);
696
696
  if (!box?.publicIp) {
@@ -698,6 +698,7 @@ async function runHetznerDeploy(args) {
698
698
  process.exit(ExitCode.FatalError);
699
699
  }
700
700
  ip = box.publicIp;
701
+ ipv6 = box.publicIpv6;
701
702
  log.info(`Attaching to '${attachTo}' box '${box.serverName}' (${ip}) \u2014 skipping provisioning`);
702
703
  const compute = (tsCloudConfig.infrastructure ??= {}).compute ??= {};
703
704
  compute.webServer = "rpx";
@@ -717,6 +718,7 @@ async function runHetznerDeploy(args) {
717
718
  log.info("Provisioning Hetzner compute infrastructure...");
718
719
  const outputs = await driver.provisionComputeInfrastructure({ config: scrubLoopbackSitePortsForFirewall(tsCloudConfig), environment });
719
720
  ip = outputs.appPublicIp;
721
+ ipv6 = outputs.appPublicIpv6;
720
722
  log.success("Hetzner compute infrastructure ready");
721
723
  if (outputs.appInstanceId)
722
724
  log.info(`Server ID: ${outputs.appInstanceId}`);
@@ -815,7 +817,7 @@ async function runHetznerDeploy(args) {
815
817
  }
816
818
  });
817
819
  if (ok)
818
- await reconcileHetznerDns(onlySite ? { [onlySite]: sites[onlySite] } : sites, ip, log);
820
+ await reconcileHetznerDns(onlySite ? { [onlySite]: sites[onlySite] } : sites, ip, log, ipv6);
819
821
  if (ok)
820
822
  await reconcileConfigDns(onlySite ? { [onlySite]: sites[onlySite] } : sites, log);
821
823
  if (ok) {
@@ -921,7 +923,13 @@ async function resolveAttachTargetBox(owner, environment) {
921
923
  }, byLabel = await req(`label_selector=${encodeURIComponent(`ts-cloud/project=${owner},ts-cloud/environment=${environment},ts-cloud/role=app`)}`), chosen = pick(byLabel) || pick(await req(`name=${encodeURIComponent(`${owner}-${environment}-app`)}`));
922
924
  if (!chosen)
923
925
  return null;
924
- return { serverId: chosen.id, serverName: chosen.name, publicIp: chosen.public_net?.ipv4?.ip };
926
+ const { hetznerBoxIpv6 } = await import("@stacksjs/ts-cloud");
927
+ return {
928
+ serverId: chosen.id,
929
+ serverName: chosen.name,
930
+ publicIp: chosen.public_net?.ipv4?.ip,
931
+ publicIpv6: hetznerBoxIpv6?.(chosen.public_net?.ipv6?.ip)
932
+ };
925
933
  }
926
934
  export async function provisionMailTenant(ip, logger) {
927
935
  if (!hasExplicitEmailConfig())
@@ -1291,22 +1299,7 @@ async function reconcileConfigDns(sites, logger) {
1291
1299
  logger.warn(`DNS (config/dns.ts) reconcile for ${domain} failed: ${err instanceof Error ? err.message : String(err)}`);
1292
1300
  }
1293
1301
  }
1294
- async function verifyDnsRecord(provider, domain, fqdn, ip) {
1295
- try {
1296
- if (typeof provider?.listRecords !== "function")
1297
- return !0;
1298
- const res = await provider.listRecords(domain);
1299
- if (!res?.success || !Array.isArray(res.records))
1300
- return !0;
1301
- return res.records.some((r) => {
1302
- const name = typeof r?.name === "string" ? r.name.replace(/\.$/, "") : "";
1303
- return r?.type === "A" && name === fqdn && r?.content === ip;
1304
- });
1305
- } catch {
1306
- return !0;
1307
- }
1308
- }
1309
- async function reconcileHetznerDns(sites, ip, logger) {
1302
+ async function reconcileHetznerDns(sites, ip, logger, ipv6) {
1310
1303
  const domains = new Set;
1311
1304
  for (const site of Object.values(sites))
1312
1305
  if (site?.domain && typeof site.domain === "string")
@@ -1331,7 +1324,7 @@ async function reconcileHetznerDns(sites, ip, logger) {
1331
1324
  const {
1332
1325
  createDnsProvider,
1333
1326
  detectDnsProvider,
1334
- removeStaleServerAddressRecords
1327
+ reconcileAddressRecords
1335
1328
  } = await import("@stacksjs/ts-cloud");
1336
1329
  logger.info("Reconciling DNS records...");
1337
1330
  const resolveA = async (fqdn) => {
@@ -1370,18 +1363,11 @@ async function reconcileHetznerDns(sites, ip, logger) {
1370
1363
  continue;
1371
1364
  }
1372
1365
  for (const sub of ["", "www"]) {
1373
- const fqdn = sub ? `${sub}.${domain}` : domain, res = await provider.upsertRecord(domain, { name: fqdn, type: "A", content: ip, ttl: 600 });
1374
- if (res?.success === !1) {
1375
- logger.warn(` DNS: ${fqdn} \u2192 ${ip} failed: ${res.error || res.message || "unknown error"}`);
1376
- continue;
1377
- }
1378
- const cleanupWarnings = await removeStaleServerAddressRecords(provider, domain, fqdn, ip);
1379
- for (const warning of cleanupWarnings)
1380
- logger.warn(` DNS: ${fqdn} cleanup: ${warning}`);
1381
- if (await verifyDnsRecord(provider, domain, fqdn, ip))
1382
- logger.success(` DNS: ${fqdn} \u2192 ${ip} (${provider.name})`);
1383
- else
1384
- logger.warn(` DNS: ${fqdn} \u2192 ${ip} reported success at ${provider.name} but no matching A record exists \u2014 create it manually: A ${fqdn} \u2192 ${ip}`);
1366
+ const fqdn = sub ? `${sub}.${domain}` : domain, report = await reconcileAddressRecords({ provider, zone: domain, fqdn, ipv4: ip, ipv6 });
1367
+ for (const record of report.published)
1368
+ logger.success(` DNS: ${record.fqdn} \u2192 ${record.content} (${provider.name})`);
1369
+ for (const warning of report.warnings)
1370
+ logger.warn(` DNS: ${warning}`);
1385
1371
  }
1386
1372
  } catch (err) {
1387
1373
  logger.warn(` DNS: ${domain} reconciliation failed: ${err?.message || err}`);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/buddy",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.205",
5
+ "version": "0.70.207",
6
6
  "description": "Meet Buddy. The Stacks runtime.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -137,7 +137,7 @@
137
137
  "@stacksjs/ui": "^0.70.180",
138
138
  "@stacksjs/utils": "^0.70.180",
139
139
  "@stacksjs/validation": "^0.70.180",
140
- "@stacksjs/ts-cloud": "^0.7.84",
140
+ "@stacksjs/ts-cloud": "^0.7.86",
141
141
  "ajv": "^8.20.0",
142
142
  "ajv-formats": "^3.0.1"
143
143
  },