@stacksjs/buddy 0.70.197 → 0.70.199

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.
@@ -84,6 +84,7 @@ export declare function scrubLoopbackSitePortsForFirewall(tsCloudConfig: any): a
84
84
  * does not provide `config/email.ts`.
85
85
  */
86
86
  export declare function hasExplicitEmailConfig(projectRoot?: unknown): boolean;
87
+ export declare function mailServerOwnerFromConfig(config: any): string | undefined;
87
88
  /**
88
89
  * Everything is MERGE-based so a shared mail server keeps every other tenant's
89
90
  * domains, keys, users, and forward rules untouched. Best-effort — a hiccup is
@@ -819,9 +819,23 @@ async function runHetznerDeploy(args) {
819
819
  if (ok)
820
820
  await reconcileConfigDns(onlySite ? { [onlySite]: sites[onlySite] } : sites, log);
821
821
  if (ok) {
822
- const mailRes = await provisionMailTenant(ip, log);
822
+ const mailOwner = mailServerOwnerFromConfig(emailConfig);
823
+ let mailIp = ip;
824
+ if (mailOwner) {
825
+ const mailBox = await resolveAttachTargetBox(mailOwner, environment);
826
+ if (mailBox?.publicIp) {
827
+ mailIp = mailBox.publicIp;
828
+ log.info(`Mail: reconciling on '${mailOwner}' box '${mailBox.serverName}' (${mailIp})`);
829
+ } else {
830
+ mailIp = void 0;
831
+ log.warn(`Mail: attach target '${mailOwner}' has no reachable '${environment}' box; application deploy remains live`);
832
+ }
833
+ }
834
+ const mailRes = mailIp ? await provisionMailTenant(mailIp, log) : null;
835
+ if (mailOwner && mailIp && mailIp !== ip)
836
+ await cleanupDetachedMailHealth(ip, log);
823
837
  if (mailRes)
824
- await reconcileMailDns(mailRes, ip, log);
838
+ await reconcileMailDns(mailRes, mailIp, log);
825
839
  }
826
840
  for (const record of githubDeployments)
827
841
  await setGithubDeploymentStatus(record, ok ? "success" : "failure");
@@ -861,6 +875,34 @@ function resolveMailboxes(mailboxes, domain) {
861
875
  export function hasExplicitEmailConfig(projectRoot = p.projectPath()) {
862
876
  return existsSync(join(projectRoot, "config", "email.ts"));
863
877
  }
878
+ export function mailServerOwnerFromConfig(config) {
879
+ const owner = config?.server?.attachTo;
880
+ return typeof owner === "string" && owner.trim() ? owner.trim() : void 0;
881
+ }
882
+ async function cleanupDetachedMailHealth(ip, logger) {
883
+ const { execSync } = await import("node:child_process"), sshArgs = ["-o", "StrictHostKeyChecking=accept-new", "-o", "BatchMode=yes", "-o", "ConnectTimeout=20", `root@${ip}`], script = `set -e
884
+ if systemctl list-unit-files --type=service --no-legend | awk '{print $1}' | grep -qx mail.service; then
885
+ exit 0
886
+ fi
887
+ systemctl disable --now mail-health.timer >/dev/null 2>&1 || true
888
+ systemctl stop mail-health.service >/dev/null 2>&1 || true
889
+ systemctl reset-failed mail-health.service >/dev/null 2>&1 || true
890
+ rm -f /etc/systemd/system/mail-health.service /etc/systemd/system/mail-health.timer
891
+ rm -f /usr/local/sbin/mail-health-check /etc/systemd/system/mail.service.d/reliability.conf
892
+ rmdir /etc/systemd/system/mail.service.d 2>/dev/null || true
893
+ systemctl daemon-reload
894
+ systemctl reset-failed`;
895
+ try {
896
+ execSync(`ssh ${sshArgs.map((a) => `'${a}'`).join(" ")} bash -s`, {
897
+ input: script,
898
+ encoding: "utf8",
899
+ stdio: ["pipe", "pipe", "pipe"]
900
+ });
901
+ logger.success("Mail: removed the detached health timer from the application box");
902
+ } catch (err) {
903
+ logger.warn(`Mail: could not remove the detached health timer: ${getErrorMessage(err)}`);
904
+ }
905
+ }
864
906
  async function resolveAttachTargetBox(owner, environment) {
865
907
  const token = process.env.HCLOUD_TOKEN;
866
908
  if (!token)
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.197",
5
+ "version": "0.70.199",
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.79",
140
+ "@stacksjs/ts-cloud": "^0.7.82",
141
141
  "ajv": "^8.20.0",
142
142
  "ajv-formats": "^3.0.1"
143
143
  },