@stacksjs/ts-cloud 0.7.85 → 0.7.87
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.
- package/dist/aws/ec2.d.ts +13 -0
- package/dist/aws/index.js +1 -1
- package/dist/bin/cli.js +533 -533
- package/dist/{chunk-1qjyqrc5.js → chunk-24kwnpe5.js} +2 -0
- package/dist/{chunk-703nkybg.js → chunk-7m60qnc8.js} +10 -0
- package/dist/{chunk-d2aw5vyv.js → chunk-aqmdexc3.js} +1 -1
- package/dist/{chunk-qbj74thz.js → chunk-cj3qrktr.js} +5 -5
- package/dist/{chunk-gx4288t5.js → chunk-tcr4xba6.js} +123 -62
- package/dist/{chunk-tq4v58zp.js → chunk-w61hh7n3.js} +2 -2
- package/dist/deploy/index.js +17 -9
- package/dist/deploy/server-dns.d.ts +66 -7
- package/dist/drivers/index.js +3 -3
- package/dist/drivers/shared/rpx-gateway.d.ts +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +15 -7
- package/dist/ui/account/automation.html +3 -3
- package/dist/ui/account/security.html +2 -2
- package/dist/ui/applications/compose.html +4 -4
- package/dist/ui/applications/new.html +2 -2
- package/dist/ui/data/backups.html +4 -4
- package/dist/ui/data/services.html +4 -4
- package/dist/ui/data/volumes.html +4 -4
- package/dist/ui/index.html +4 -4
- package/dist/ui/operations/alerts.html +3 -3
- package/dist/ui/operations/configuration.html +4 -4
- package/dist/ui/operations/jobs.html +4 -4
- package/dist/ui/operations/maintenance.html +4 -4
- package/dist/ui/operations/observability.html +4 -4
- package/dist/ui/operations/previews.html +4 -4
- package/dist/ui/operations/queue.html +4 -4
- package/dist/ui/operations/regions.html +4 -4
- package/dist/ui/operations/releases.html +4 -4
- package/dist/ui/operations/workloads.html +4 -4
- package/dist/ui/server/actions.html +4 -4
- package/dist/ui/server/capacity.html +4 -4
- package/dist/ui/server/database.html +4 -4
- package/dist/ui/server/deployments.html +4 -4
- package/dist/ui/server/diagnostics.html +2 -2
- package/dist/ui/server/firewall.html +4 -4
- package/dist/ui/server/fleet.html +4 -4
- package/dist/ui/server/logs.html +4 -4
- package/dist/ui/server/metrics.html +4 -4
- package/dist/ui/server/services.html +2 -2
- package/dist/ui/server/sites.html +4 -4
- package/dist/ui/server/ssh-keys.html +4 -4
- package/dist/ui/server/team.html +4 -4
- package/dist/ui/server/terminal.html +2 -2
- package/dist/ui/serverless/alarms.html +3 -3
- package/dist/ui/serverless/cost.html +2 -2
- package/dist/ui/serverless/data.html +4 -4
- package/dist/ui/serverless/firewall.html +2 -2
- package/dist/ui/serverless/functions.html +4 -4
- package/dist/ui/serverless/logs.html +4 -4
- package/dist/ui/serverless/metrics.html +2 -2
- package/dist/ui/serverless/queues.html +4 -4
- package/dist/ui/serverless/secrets.html +4 -4
- package/dist/ui/serverless/traces.html +4 -4
- package/dist/ui/serverless.html +3 -3
- package/package.json +3 -3
|
@@ -22000,6 +22000,8 @@ function resolveManagementDashboardSites(config, environment, opts) {
|
|
|
22000
22000
|
start: `bun ${DASHBOARD_ENTRY} dashboard:serve --box --host 127.0.0.1 --port ${port}`,
|
|
22001
22001
|
port,
|
|
22002
22002
|
env: {
|
|
22003
|
+
APP_ENV: "production",
|
|
22004
|
+
NODE_ENV: "production",
|
|
22003
22005
|
TS_CLOUD_DASHBOARD_TELEMETRY: "1"
|
|
22004
22006
|
},
|
|
22005
22007
|
zeroDowntime: false,
|
|
@@ -1087,6 +1087,15 @@ class EC2Client {
|
|
|
1087
1087
|
return [];
|
|
1088
1088
|
return Array.isArray(item) ? item : [item];
|
|
1089
1089
|
}
|
|
1090
|
+
parseFirstIpv6Address(item) {
|
|
1091
|
+
for (const eni of this.parseArray(item)) {
|
|
1092
|
+
for (const entry of this.parseArray(eni?.ipv6AddressesSet?.item)) {
|
|
1093
|
+
if (entry?.ipv6Address)
|
|
1094
|
+
return entry.ipv6Address;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1090
1099
|
parseTags(item) {
|
|
1091
1100
|
return this.parseArray(item).map((t) => ({
|
|
1092
1101
|
Key: t.key,
|
|
@@ -1110,6 +1119,7 @@ class EC2Client {
|
|
|
1110
1119
|
} : undefined,
|
|
1111
1120
|
PrivateIpAddress: i.privateIpAddress,
|
|
1112
1121
|
PublicIpAddress: i.ipAddress,
|
|
1122
|
+
Ipv6Address: this.parseFirstIpv6Address(i.networkInterfaceSet?.item),
|
|
1113
1123
|
SubnetId: i.subnetId,
|
|
1114
1124
|
VpcId: i.vpcId,
|
|
1115
1125
|
SecurityGroups: this.parseArray(i.groupSet?.item).map((g) => ({
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
step,
|
|
10
10
|
success,
|
|
11
11
|
warn
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-aqmdexc3.js";
|
|
13
13
|
import {
|
|
14
14
|
AwsBackupClient,
|
|
15
15
|
CloudWatchLogsClient,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
resolveSiteKind,
|
|
46
46
|
resolveUiSource,
|
|
47
47
|
siteInstallBase
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-tcr4xba6.js";
|
|
49
49
|
import {
|
|
50
50
|
artifactKey,
|
|
51
51
|
buildCloudFormationTemplate,
|
|
@@ -67,10 +67,10 @@ import {
|
|
|
67
67
|
resolveStatePath,
|
|
68
68
|
setStateDir,
|
|
69
69
|
statePath
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-24kwnpe5.js";
|
|
71
71
|
import {
|
|
72
72
|
SSMClient
|
|
73
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-7m60qnc8.js";
|
|
74
74
|
import {
|
|
75
75
|
CloudFormationClient
|
|
76
76
|
} from "./chunk-4cjrg98a.js";
|
|
@@ -8324,7 +8324,7 @@ async function deployServerlessApp(config, environment2, opts = {}) {
|
|
|
8324
8324
|
}
|
|
8325
8325
|
if (imageMode) {
|
|
8326
8326
|
step("Building + pushing container image");
|
|
8327
|
-
const { buildAndPushServerlessImage } = await import("./chunk-
|
|
8327
|
+
const { buildAndPushServerlessImage } = await import("./chunk-w61hh7n3.js");
|
|
8328
8328
|
const built = await buildAndPushServerlessImage({
|
|
8329
8329
|
app,
|
|
8330
8330
|
projectRoot,
|
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
resolveProjectStackName,
|
|
9
9
|
resolveStatePath,
|
|
10
10
|
statePath
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-24kwnpe5.js";
|
|
12
12
|
import {
|
|
13
13
|
EC2Client,
|
|
14
14
|
SSMClient
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-7m60qnc8.js";
|
|
16
16
|
import {
|
|
17
17
|
CloudFormationClient
|
|
18
18
|
} from "./chunk-4cjrg98a.js";
|
|
@@ -27,6 +27,98 @@ import {
|
|
|
27
27
|
import { readFileSync } from "node:fs";
|
|
28
28
|
import { join } from "node:path";
|
|
29
29
|
|
|
30
|
+
// src/deploy/server-dns.ts
|
|
31
|
+
function collectServerDnsDomains(sites = {}) {
|
|
32
|
+
const domains = new Set;
|
|
33
|
+
for (const site of Object.values(sites)) {
|
|
34
|
+
if (!site.domain)
|
|
35
|
+
continue;
|
|
36
|
+
if (site.redirect || site.deploy === "server" || site.start)
|
|
37
|
+
domains.add(site.domain);
|
|
38
|
+
}
|
|
39
|
+
return domains;
|
|
40
|
+
}
|
|
41
|
+
function normalizeName(name) {
|
|
42
|
+
return name.replace(/\.$/, "").toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
function matchesHostname(record, zone, hostname) {
|
|
45
|
+
const recordName = normalizeName(record.name);
|
|
46
|
+
const normalizedZone = normalizeName(zone);
|
|
47
|
+
const normalizedHostname = normalizeName(hostname);
|
|
48
|
+
const relativeName = normalizedHostname === normalizedZone ? "@" : normalizedHostname.endsWith(`.${normalizedZone}`) ? normalizedHostname.slice(0, -(normalizedZone.length + 1)) : normalizedHostname;
|
|
49
|
+
return recordName === normalizedHostname || recordName === relativeName || relativeName === "@" && (recordName === "" || recordName === normalizedZone);
|
|
50
|
+
}
|
|
51
|
+
async function removeStaleServerAddressRecords(provider, zone, hostname, desiredAddress, recordType = "A") {
|
|
52
|
+
const listed = await provider.listRecords(zone);
|
|
53
|
+
if (!listed.success)
|
|
54
|
+
return [`could not list ${recordType} records: ${listed.message || "unknown provider error"}`];
|
|
55
|
+
const matching = listed.records.filter((record) => record.type === recordType && matchesHostname(record, zone, hostname));
|
|
56
|
+
const desiredIndex = matching.findIndex((record) => record.content === desiredAddress);
|
|
57
|
+
if (matching.length <= 1 || desiredIndex === -1)
|
|
58
|
+
return [];
|
|
59
|
+
const warnings = [];
|
|
60
|
+
for (const [index, record] of matching.entries()) {
|
|
61
|
+
if (index === desiredIndex)
|
|
62
|
+
continue;
|
|
63
|
+
const result = await provider.deleteRecord(zone, record);
|
|
64
|
+
if (!result.success)
|
|
65
|
+
warnings.push(`could not remove stale ${record.name} ${recordType} ${record.content}: ${result.message || "unknown provider error"}`);
|
|
66
|
+
}
|
|
67
|
+
return warnings;
|
|
68
|
+
}
|
|
69
|
+
function normalizePublicIpv6(reported) {
|
|
70
|
+
if (!reported)
|
|
71
|
+
return;
|
|
72
|
+
const trimmed = reported.trim();
|
|
73
|
+
if (!trimmed)
|
|
74
|
+
return;
|
|
75
|
+
if (!trimmed.includes("/"))
|
|
76
|
+
return trimmed;
|
|
77
|
+
const [block] = trimmed.split("/");
|
|
78
|
+
if (!block.endsWith("::"))
|
|
79
|
+
return block || undefined;
|
|
80
|
+
return `${block}1`;
|
|
81
|
+
}
|
|
82
|
+
var IPV6_EXCLUDED_HOST_LABELS = new Set(["mail", "smtp", "imap", "mx"]);
|
|
83
|
+
function hostAcceptsIpv6(fqdn) {
|
|
84
|
+
return !IPV6_EXCLUDED_HOST_LABELS.has(fqdn.split(".")[0] ?? "");
|
|
85
|
+
}
|
|
86
|
+
async function reconcileAddressRecords(options) {
|
|
87
|
+
const { provider, zone, fqdn, ipv4, ipv6, ttl = 600 } = options;
|
|
88
|
+
const report = { published: [], warnings: [] };
|
|
89
|
+
const families = [{ type: "A", content: ipv4 }];
|
|
90
|
+
if (ipv6 && hostAcceptsIpv6(fqdn))
|
|
91
|
+
families.push({ type: "AAAA", content: ipv6 });
|
|
92
|
+
for (const { type, content } of families) {
|
|
93
|
+
const result = await provider.upsertRecord(zone, { name: fqdn, type, content, ttl });
|
|
94
|
+
if (result?.success === false) {
|
|
95
|
+
report.warnings.push(`${fqdn} → ${content} failed: ${result.error || result.message || "unknown error"}`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
report.warnings.push(...(await removeStaleServerAddressRecords(provider, zone, fqdn, content, type)).map((warning) => `${fqdn} cleanup: ${warning}`));
|
|
99
|
+
if (await verifyAddressRecord(provider, zone, fqdn, content, type))
|
|
100
|
+
report.published.push({ fqdn, type, content });
|
|
101
|
+
else
|
|
102
|
+
report.warnings.push(`${fqdn} → ${content} reported success at ${provider.name} but no matching ${type} record exists — create it manually: ${type} ${fqdn} → ${content}`);
|
|
103
|
+
}
|
|
104
|
+
return report;
|
|
105
|
+
}
|
|
106
|
+
async function verifyAddressRecord(provider, zone, fqdn, content, recordType) {
|
|
107
|
+
try {
|
|
108
|
+
if (typeof provider?.listRecords !== "function")
|
|
109
|
+
return true;
|
|
110
|
+
const listed = await provider.listRecords(zone);
|
|
111
|
+
if (!listed?.success || !Array.isArray(listed.records))
|
|
112
|
+
return true;
|
|
113
|
+
return listed.records.some((record) => {
|
|
114
|
+
const name = typeof record?.name === "string" ? record.name.replace(/\.$/, "") : "";
|
|
115
|
+
return record?.type === recordType && name === fqdn && record?.content === content;
|
|
116
|
+
});
|
|
117
|
+
} catch {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
30
122
|
// src/drivers/shared/package-manager.ts
|
|
31
123
|
var PANTRY_PACKAGES = {
|
|
32
124
|
php: "php.net",
|
|
@@ -1163,6 +1255,7 @@ function usesRpxProxy(compute) {
|
|
|
1163
1255
|
var RPX_DIR = "/etc/rpx";
|
|
1164
1256
|
var RPX_INSTALL_DIR = "/opt/rpx-gateway";
|
|
1165
1257
|
var RPX_LAUNCHER_PATH = "/etc/rpx/gateway.ts";
|
|
1258
|
+
var RPX_BINARY_PATH = "/etc/rpx/gateway";
|
|
1166
1259
|
var RPX_SERVICE_NAME = "rpx-gateway.service";
|
|
1167
1260
|
var RPX_SITES_DIR = "/etc/rpx/sites.d";
|
|
1168
1261
|
function renderRpxAssembler(sitesDir = RPX_SITES_DIR, defaultCertsDir = DEFAULT_RPX_CERTS_DIR) {
|
|
@@ -1368,6 +1461,9 @@ function buildRpxProvisionScript(options) {
|
|
|
1368
1461
|
`ln -sfn ${RPX_INSTALL_DIR}/node_modules ${RPX_DIR}/node_modules`,
|
|
1369
1462
|
...writeFileHeredoc(`${RPX_SITES_DIR}/${slug}.json`, fragment, "TS_CLOUD_RPX_FRAGMENT_EOF", "0600"),
|
|
1370
1463
|
...writeFileHeredoc(RPX_LAUNCHER_PATH, assembler, "TS_CLOUD_RPX_EOF"),
|
|
1464
|
+
`${bunBin} build --production --compile --outfile ${RPX_BINARY_PATH}.next ${RPX_LAUNCHER_PATH}`,
|
|
1465
|
+
`chmod 0755 ${RPX_BINARY_PATH}.next`,
|
|
1466
|
+
`mv -f ${RPX_BINARY_PATH}.next ${RPX_BINARY_PATH}`,
|
|
1371
1467
|
...writeFileHeredoc(`/etc/systemd/system/${RPX_SERVICE_NAME}`, [
|
|
1372
1468
|
"[Unit]",
|
|
1373
1469
|
"Description=rpx reverse-proxy gateway (managed by ts-cloud)",
|
|
@@ -1376,9 +1472,11 @@ function buildRpxProvisionScript(options) {
|
|
|
1376
1472
|
"",
|
|
1377
1473
|
"[Service]",
|
|
1378
1474
|
"Type=simple",
|
|
1379
|
-
`ExecStart=${
|
|
1475
|
+
`ExecStart=${RPX_BINARY_PATH}`,
|
|
1380
1476
|
`WorkingDirectory=${RPX_INSTALL_DIR}`,
|
|
1381
1477
|
`Environment=BUN_INSTALL=/root/.bun`,
|
|
1478
|
+
`Environment=APP_ENV=production`,
|
|
1479
|
+
`Environment=NODE_ENV=production`,
|
|
1382
1480
|
...poolEnv,
|
|
1383
1481
|
"MemoryAccounting=true",
|
|
1384
1482
|
`MemoryHigh=${memoryHigh}`,
|
|
@@ -1752,6 +1850,7 @@ class AwsDriver {
|
|
|
1752
1850
|
return {
|
|
1753
1851
|
appInstanceId: first.id,
|
|
1754
1852
|
appPublicIp: first.publicIp,
|
|
1853
|
+
appPublicIpv6: first.publicIpv6,
|
|
1755
1854
|
sshUser: "ubuntu",
|
|
1756
1855
|
deployStoragePath: "/var/ts-cloud/staging"
|
|
1757
1856
|
};
|
|
@@ -1834,6 +1933,7 @@ class AwsDriver {
|
|
|
1834
1933
|
return {
|
|
1835
1934
|
appInstanceId: instanceId,
|
|
1836
1935
|
appPublicIp: running.PublicIpAddress,
|
|
1936
|
+
appPublicIpv6: normalizePublicIpv6(running.Ipv6Address),
|
|
1837
1937
|
sshUser: "ubuntu",
|
|
1838
1938
|
deployStoragePath: "/var/ts-cloud/staging"
|
|
1839
1939
|
};
|
|
@@ -1892,6 +1992,7 @@ class AwsDriver {
|
|
|
1892
1992
|
return {
|
|
1893
1993
|
appInstanceId: first?.id,
|
|
1894
1994
|
appPublicIp: first?.publicIp,
|
|
1995
|
+
appPublicIpv6: first?.publicIpv6,
|
|
1895
1996
|
sshUser: "ubuntu",
|
|
1896
1997
|
deployStoragePath: "/var/ts-cloud/staging"
|
|
1897
1998
|
};
|
|
@@ -1961,6 +2062,7 @@ class AwsDriver {
|
|
|
1961
2062
|
id: instance.InstanceId,
|
|
1962
2063
|
name: nameTag,
|
|
1963
2064
|
publicIp: instance.PublicIpAddress,
|
|
2065
|
+
publicIpv6: normalizePublicIpv6(instance.Ipv6Address),
|
|
1964
2066
|
privateIp: instance.PrivateIpAddress,
|
|
1965
2067
|
status: instance.State?.Name
|
|
1966
2068
|
});
|
|
@@ -2045,59 +2147,6 @@ import { execSync } from "node:child_process";
|
|
|
2045
2147
|
import { randomUUID } from "node:crypto";
|
|
2046
2148
|
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
2047
2149
|
|
|
2048
|
-
// src/deploy/server-dns.ts
|
|
2049
|
-
function collectServerDnsDomains(sites = {}) {
|
|
2050
|
-
const domains = new Set;
|
|
2051
|
-
for (const site of Object.values(sites)) {
|
|
2052
|
-
if (!site.domain)
|
|
2053
|
-
continue;
|
|
2054
|
-
if (site.redirect || site.deploy === "server" || site.start)
|
|
2055
|
-
domains.add(site.domain);
|
|
2056
|
-
}
|
|
2057
|
-
return domains;
|
|
2058
|
-
}
|
|
2059
|
-
function normalizeName(name) {
|
|
2060
|
-
return name.replace(/\.$/, "").toLowerCase();
|
|
2061
|
-
}
|
|
2062
|
-
function matchesHostname(record, zone, hostname) {
|
|
2063
|
-
const recordName = normalizeName(record.name);
|
|
2064
|
-
const normalizedZone = normalizeName(zone);
|
|
2065
|
-
const normalizedHostname = normalizeName(hostname);
|
|
2066
|
-
const relativeName = normalizedHostname === normalizedZone ? "@" : normalizedHostname.endsWith(`.${normalizedZone}`) ? normalizedHostname.slice(0, -(normalizedZone.length + 1)) : normalizedHostname;
|
|
2067
|
-
return recordName === normalizedHostname || recordName === relativeName || relativeName === "@" && (recordName === "" || recordName === normalizedZone);
|
|
2068
|
-
}
|
|
2069
|
-
async function removeStaleServerAddressRecords(provider, zone, hostname, desiredAddress, recordType = "A") {
|
|
2070
|
-
const listed = await provider.listRecords(zone);
|
|
2071
|
-
if (!listed.success)
|
|
2072
|
-
return [`could not list ${recordType} records: ${listed.message || "unknown provider error"}`];
|
|
2073
|
-
const matching = listed.records.filter((record) => record.type === recordType && matchesHostname(record, zone, hostname));
|
|
2074
|
-
const desiredIndex = matching.findIndex((record) => record.content === desiredAddress);
|
|
2075
|
-
if (matching.length <= 1 || desiredIndex === -1)
|
|
2076
|
-
return [];
|
|
2077
|
-
const warnings = [];
|
|
2078
|
-
for (const [index, record] of matching.entries()) {
|
|
2079
|
-
if (index === desiredIndex)
|
|
2080
|
-
continue;
|
|
2081
|
-
const result = await provider.deleteRecord(zone, record);
|
|
2082
|
-
if (!result.success)
|
|
2083
|
-
warnings.push(`could not remove stale ${record.name} ${recordType} ${record.content}: ${result.message || "unknown provider error"}`);
|
|
2084
|
-
}
|
|
2085
|
-
return warnings;
|
|
2086
|
-
}
|
|
2087
|
-
function hetznerBoxIpv6(reported) {
|
|
2088
|
-
if (!reported)
|
|
2089
|
-
return;
|
|
2090
|
-
const trimmed = reported.trim();
|
|
2091
|
-
if (!trimmed)
|
|
2092
|
-
return;
|
|
2093
|
-
if (!trimmed.includes("/"))
|
|
2094
|
-
return trimmed;
|
|
2095
|
-
const [block] = trimmed.split("/");
|
|
2096
|
-
if (!block.endsWith("::"))
|
|
2097
|
-
return block || undefined;
|
|
2098
|
-
return `${block}1`;
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
2150
|
// src/drivers/shared/fleet.ts
|
|
2102
2151
|
function resolveFleetTopology(compute = {}) {
|
|
2103
2152
|
const appServers = Math.max(1, compute.appServers ?? compute.instances ?? 1);
|
|
@@ -2666,7 +2715,7 @@ class HetznerDriver {
|
|
|
2666
2715
|
serverId: alreadyRunning.id,
|
|
2667
2716
|
serverName: alreadyRunning.name,
|
|
2668
2717
|
publicIp: alreadyRunning.public_net.ipv4?.ip,
|
|
2669
|
-
publicIpv6:
|
|
2718
|
+
publicIpv6: normalizePublicIpv6(alreadyRunning.public_net.ipv6?.ip),
|
|
2670
2719
|
deployStoragePath: "/var/ts-cloud/staging",
|
|
2671
2720
|
sshUser: this.sshUser
|
|
2672
2721
|
};
|
|
@@ -2714,7 +2763,7 @@ class HetznerDriver {
|
|
|
2714
2763
|
serverName: running.name,
|
|
2715
2764
|
firewallId: firewall.id,
|
|
2716
2765
|
publicIp: running.public_net.ipv4?.ip,
|
|
2717
|
-
publicIpv6:
|
|
2766
|
+
publicIpv6: normalizePublicIpv6(running.public_net.ipv6?.ip),
|
|
2718
2767
|
deployStoragePath: "/var/ts-cloud/staging",
|
|
2719
2768
|
sshUser: this.sshUser
|
|
2720
2769
|
};
|
|
@@ -3216,6 +3265,7 @@ class HetznerDriver {
|
|
|
3216
3265
|
id: String(server.id),
|
|
3217
3266
|
name: server.name,
|
|
3218
3267
|
publicIp: server.public_net.ipv4?.ip,
|
|
3268
|
+
publicIpv6: normalizePublicIpv6(server.public_net.ipv6?.ip),
|
|
3219
3269
|
privateIp: server.private_net?.[0]?.ip,
|
|
3220
3270
|
status: server.status
|
|
3221
3271
|
});
|
|
@@ -3428,7 +3478,7 @@ ${out}`);
|
|
|
3428
3478
|
deployStoragePath: state.deployStoragePath || "/var/ts-cloud/staging",
|
|
3429
3479
|
appInstanceId: state.serverId ? String(state.serverId) : undefined,
|
|
3430
3480
|
appPublicIp: server?.public_net.ipv4?.ip || state.publicIp,
|
|
3431
|
-
appPublicIpv6:
|
|
3481
|
+
appPublicIpv6: normalizePublicIpv6(server?.public_net.ipv6?.ip) || state.publicIpv6,
|
|
3432
3482
|
sshUser: state.sshUser || this.sshUser,
|
|
3433
3483
|
servicesPrivateIp: state.servicesPrivateIp
|
|
3434
3484
|
};
|
|
@@ -5172,7 +5222,12 @@ var STACKS_CLI = "storage/framework/core/buddy/src/cli.ts";
|
|
|
5172
5222
|
var stacksDriver = {
|
|
5173
5223
|
id: "stacks",
|
|
5174
5224
|
wrapExec: (command) => command,
|
|
5175
|
-
execEnv: {
|
|
5225
|
+
execEnv: {
|
|
5226
|
+
PATH: "/usr/local/bin:/usr/bin:/bin",
|
|
5227
|
+
BUN_INSTALL: "/root/.bun",
|
|
5228
|
+
APP_ENV: "production",
|
|
5229
|
+
NODE_ENV: "production"
|
|
5230
|
+
},
|
|
5176
5231
|
schedulerMode: "daemon",
|
|
5177
5232
|
schedulerCommand: () => `${BUN_BIN} ${STACKS_CLI} schedule:run`,
|
|
5178
5233
|
queueWorkerCommand: (worker, { current }) => {
|
|
@@ -5658,7 +5713,13 @@ async function deploySiteRelease(driver, options, logger = noopLogger2) {
|
|
|
5658
5713
|
const dbEnv = outputs.servicesPrivateIp ? buildFleetServicesEnv(outputs.servicesPrivateIp, resolveAppDatabase(config)) : buildManagedDbEnv(resolveAppDatabase(config));
|
|
5659
5714
|
const compute = config.infrastructure?.compute;
|
|
5660
5715
|
const proxyEnv = usesRpxProxy(compute) ? { HOST: "127.0.0.1" } : {};
|
|
5661
|
-
const
|
|
5716
|
+
const productionEnv = environment === "production" ? { APP_ENV: "production", NODE_ENV: "production" } : {};
|
|
5717
|
+
const envWithServices = {
|
|
5718
|
+
...dbEnv,
|
|
5719
|
+
...proxyEnv,
|
|
5720
|
+
...site.env || {},
|
|
5721
|
+
...productionEnv
|
|
5722
|
+
};
|
|
5662
5723
|
if (isPhpSite(site)) {
|
|
5663
5724
|
const phpVersion = site.phpVersion ?? compute?.php?.default ?? compute?.php?.versions?.[0];
|
|
5664
5725
|
const appBase2 = siteInstallBase(slug, siteName);
|
|
@@ -6254,4 +6315,4 @@ function buildCloudFrontOriginConfig(options) {
|
|
|
6254
6315
|
CustomErrorResponses: { Quantity: 0 }
|
|
6255
6316
|
};
|
|
6256
6317
|
}
|
|
6257
|
-
export { PANTRY_PROJECT_DIR, pgAdminCommand, BACKUP_RUNNER_PATH, buildBackupRestoreScript, siteInstallBase, isPhpSite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, shipsARelease, DEFAULT_RPX_CERTS_DIR, normalizeRoutePath, deriveRouteId, buildRpxConfig, buildRpxLbConfig, renderRpxLauncher, RPX_DIR, RPX_LAUNCHER_PATH, RPX_SERVICE_NAME, buildRpxProvisionScript, buildRpxFragmentRefreshScript, buildUbuntuBootstrapScript, AwsDriver,
|
|
6318
|
+
export { collectServerDnsDomains, removeStaleServerAddressRecords, normalizePublicIpv6, IPV6_EXCLUDED_HOST_LABELS, hostAcceptsIpv6, reconcileAddressRecords, verifyAddressRecord, PANTRY_PROJECT_DIR, pgAdminCommand, BACKUP_RUNNER_PATH, buildBackupRestoreScript, siteInstallBase, isPhpSite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, shipsARelease, DEFAULT_RPX_CERTS_DIR, normalizeRoutePath, deriveRouteId, buildRpxConfig, buildRpxLbConfig, renderRpxLauncher, RPX_DIR, RPX_LAUNCHER_PATH, RPX_SERVICE_NAME, buildRpxProvisionScript, buildRpxFragmentRefreshScript, buildUbuntuBootstrapScript, AwsDriver, resolveHetznerLocation, HetznerClient, resolveHetznerApiToken2 as resolveHetznerApiToken, normalizeSshPublicKey, wrapCloudInitUserData, HetznerDriver, LocalBoxDriver, isBoxMode, createCloudDriver, CloudDriverFactory, cloudDrivers, planHetznerServerResize, isHetznerCapacityError, executeHetznerServerResize, buildSshArgs, sshExec, sshExecOrThrow, scpUpload, waitForSsh, waitForCloudInit, collectHetznerResizeManifest, prepareHetznerResize, verifyHetznerResize, resolveHetznerHostOptimizationPlan, buildHetznerHostOptimizationScript, applyHetznerHostOptimization, collectHetznerHostOptimizationReport, verifyHetznerHostOptimization, resizeCheckpointPath, resizeLockPath, readResizeCheckpoint, writeResizeCheckpoint, acquireResizeLock, ensureSshKey, ensureFirewall, ensureServer, serverPublicIpv4, UBUNTU_2404_AMI_PARAM, buildBoxUserData, HetznerBoxProvisioner, AwsBoxProvisioner, createBoxProvisioner, releasePaths, buildRollbackScript, resolveExecStart, buildSiteDeployScript, buildStaticSiteDeployScript, releaseTarballTmpPath, buildAwsArtifactFetch, buildLocalArtifactFetch, buildHostCleanupScript, MANAGEMENT_DASHBOARD_SITE, dashboardCredentialsFile, resolveDashboardAuth, resolveUiSource, ensureManagementDashboard, buildManagementDashboardArtifact, resolveSiteFramework, deploySiteRelease, deployAllComputeSites, reloadRpxGateway, renewRpxCertificates, MANAGED_CACHE_POLICY_OPTIMIZED, MANAGED_CACHE_POLICY_DISABLED, MANAGED_ORIGIN_REQUEST_POLICY_ALL_VIEWER, buildCloudFrontOriginConfig };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildAndPushServerlessImage
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-aqmdexc3.js";
|
|
4
4
|
import"./chunk-50jpda9q.js";
|
|
5
|
-
import"./chunk-
|
|
5
|
+
import"./chunk-24kwnpe5.js";
|
|
6
6
|
import"./chunk-zqtpg06c.js";
|
|
7
7
|
import"./chunk-v0bahtg2.js";
|
|
8
8
|
export {
|
package/dist/deploy/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
synchronizeDashboardUsers,
|
|
31
31
|
trackDashboardOperation,
|
|
32
32
|
verifyStaticApiOrigin
|
|
33
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-cj3qrktr.js";
|
|
34
34
|
import {
|
|
35
35
|
deleteStaticSite,
|
|
36
36
|
deployStaticSite,
|
|
@@ -44,20 +44,23 @@ import {
|
|
|
44
44
|
} from "../chunk-ef2dwfrg.js";
|
|
45
45
|
import {
|
|
46
46
|
buildAndPushServerlessImage
|
|
47
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-aqmdexc3.js";
|
|
48
48
|
import"../chunk-ybcz6sxc.js";
|
|
49
49
|
import"../chunk-01d86gt1.js";
|
|
50
50
|
import"../chunk-50jpda9q.js";
|
|
51
51
|
import"../chunk-he9a874b.js";
|
|
52
52
|
import"../chunk-gttvakpv.js";
|
|
53
53
|
import {
|
|
54
|
+
IPV6_EXCLUDED_HOST_LABELS,
|
|
54
55
|
MANAGEMENT_DASHBOARD_SITE,
|
|
55
56
|
buildManagementDashboardArtifact,
|
|
56
57
|
collectServerDnsDomains,
|
|
57
58
|
dashboardCredentialsFile,
|
|
58
59
|
ensureManagementDashboard,
|
|
59
|
-
|
|
60
|
+
hostAcceptsIpv6,
|
|
60
61
|
isPhpSite,
|
|
62
|
+
normalizePublicIpv6,
|
|
63
|
+
reconcileAddressRecords,
|
|
61
64
|
removeStaleServerAddressRecords,
|
|
62
65
|
resolveDashboardAuth,
|
|
63
66
|
resolveSiteDeployTarget,
|
|
@@ -65,10 +68,11 @@ import {
|
|
|
65
68
|
resolveUiSource,
|
|
66
69
|
shipsARelease,
|
|
67
70
|
siteInstallBase,
|
|
68
|
-
validateDeploymentConfig
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
import"../chunk-
|
|
71
|
+
validateDeploymentConfig,
|
|
72
|
+
verifyAddressRecord
|
|
73
|
+
} from "../chunk-tcr4xba6.js";
|
|
74
|
+
import"../chunk-24kwnpe5.js";
|
|
75
|
+
import"../chunk-7m60qnc8.js";
|
|
72
76
|
import"../chunk-4cjrg98a.js";
|
|
73
77
|
import"../chunk-hpv68b4a.js";
|
|
74
78
|
import"../chunk-32e7ya18.js";
|
|
@@ -77,6 +81,7 @@ import"../chunk-zqtpg06c.js";
|
|
|
77
81
|
import"../chunk-v0bahtg2.js";
|
|
78
82
|
export {
|
|
79
83
|
verifyStaticApiOrigin,
|
|
84
|
+
verifyAddressRecord,
|
|
80
85
|
validateDeploymentConfig,
|
|
81
86
|
uploadStaticFiles,
|
|
82
87
|
trackDashboardOperation,
|
|
@@ -98,11 +103,13 @@ export {
|
|
|
98
103
|
resolveDashboardAction,
|
|
99
104
|
removeStaleServerAddressRecords,
|
|
100
105
|
redeployServerlessApp,
|
|
106
|
+
reconcileAddressRecords,
|
|
107
|
+
normalizePublicIpv6,
|
|
101
108
|
isPhpSite,
|
|
102
109
|
invalidateCache,
|
|
103
110
|
initializeDashboardControlPlane,
|
|
104
111
|
infraEnvFromOutputs,
|
|
105
|
-
|
|
112
|
+
hostAcceptsIpv6,
|
|
106
113
|
hashContainerContext,
|
|
107
114
|
generateStaticSiteTemplate,
|
|
108
115
|
generateExternalDnsStaticSiteTemplate,
|
|
@@ -131,5 +138,6 @@ export {
|
|
|
131
138
|
buildFunctionEnv,
|
|
132
139
|
buildAndPushServerlessImage,
|
|
133
140
|
buildAndPushContainerImage,
|
|
134
|
-
MANAGEMENT_DASHBOARD_SITE
|
|
141
|
+
MANAGEMENT_DASHBOARD_SITE,
|
|
142
|
+
IPV6_EXCLUDED_HOST_LABELS
|
|
135
143
|
};
|
|
@@ -19,13 +19,72 @@ export declare function collectServerDnsDomains(sites?: Record<string, ServerSit
|
|
|
19
19
|
*/
|
|
20
20
|
export declare function removeStaleServerAddressRecords(provider: DnsProvider, zone: string, hostname: string, desiredAddress: string, recordType?: 'A' | 'AAAA'): Promise<string[]>;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* the address. Publishing the block verbatim as an AAAA record yields a host
|
|
25
|
-
* nothing answers on, so turn it into the address the box actually holds.
|
|
22
|
+
* Turn whatever a provider reports as a box's IPv6 into an address an AAAA
|
|
23
|
+
* record can point at.
|
|
26
24
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
25
|
+
* Providers disagree on what "the server's IPv6" means. Hetzner hands out a
|
|
26
|
+
* routed /64 and reports the block (`2a01:4f8:c014:6186::/64`) while the
|
|
27
|
+
* interface actually holds `::1` inside it; AWS reports a plain address per
|
|
28
|
+
* network interface. Publishing a block verbatim gives an AAAA record nothing
|
|
29
|
+
* answers on, which is the kind of failure that only shows up for the fraction
|
|
30
|
+
* of visitors whose network prefers IPv6 — so every driver runs its value
|
|
31
|
+
* through here rather than reinventing the narrowing.
|
|
32
|
+
*
|
|
33
|
+
* A plain address passes through unchanged.
|
|
34
|
+
*/
|
|
35
|
+
export declare function normalizePublicIpv6(reported: string | undefined | null): string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Hostname labels that must stay IPv4-only.
|
|
38
|
+
*
|
|
39
|
+
* A box typically runs its mail server on IPv4 alone, and its IPv6 address
|
|
40
|
+
* usually has no PTR — both of which make an AAAA on a mail host actively
|
|
41
|
+
* harmful: senders try the v6 address first, find nothing listening (or get
|
|
42
|
+
* rejected for the missing reverse record) and defer the message. Web traffic
|
|
43
|
+
* has no such constraint, so the exclusion is per-host rather than per-zone.
|
|
44
|
+
*/
|
|
45
|
+
export declare const IPV6_EXCLUDED_HOST_LABELS: ReadonlySet<string>;
|
|
46
|
+
/** Whether `fqdn` should get an AAAA record pointing at the box. */
|
|
47
|
+
export declare function hostAcceptsIpv6(fqdn: string): boolean;
|
|
48
|
+
export interface AddressRecordReport {
|
|
49
|
+
/** Records confirmed present at the provider after the write. */
|
|
50
|
+
published: Array<{
|
|
51
|
+
fqdn: string;
|
|
52
|
+
type: 'A' | 'AAAA';
|
|
53
|
+
content: string;
|
|
54
|
+
}>;
|
|
55
|
+
/** Anything the caller should surface: failed writes, unverified writes, stale-record cleanup problems. */
|
|
56
|
+
warnings: string[];
|
|
57
|
+
}
|
|
58
|
+
interface ReconcileAddressRecordsOptions {
|
|
59
|
+
provider: DnsProvider;
|
|
60
|
+
zone: string;
|
|
61
|
+
fqdn: string;
|
|
62
|
+
ipv4: string;
|
|
63
|
+
/** Omit when the box has no public IPv6; the AAAA pass is then skipped entirely. */
|
|
64
|
+
ipv6?: string;
|
|
65
|
+
ttl?: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Point one hostname at a box on every address family the box actually has.
|
|
69
|
+
*
|
|
70
|
+
* Shared by every driver and by the framework's deploy command so the rules
|
|
71
|
+
* live in one place: upsert, then remove the *other* records of that family for
|
|
72
|
+
* the same hostname (a leftover address round-robins traffic to a dead host —
|
|
73
|
+
* and a stale AAAA is the worse of the two, because dual-stack clients prefer
|
|
74
|
+
* IPv6), then verify against the provider rather than trusting the write.
|
|
75
|
+
*/
|
|
76
|
+
export declare function reconcileAddressRecords(options: ReconcileAddressRecordsOptions): Promise<AddressRecordReport>;
|
|
77
|
+
/**
|
|
78
|
+
* Best-effort post-write check that the record exists at the provider.
|
|
79
|
+
*
|
|
80
|
+
* Returns true when the provider offers no list API or the listing itself
|
|
81
|
+
* fails: verification must never turn a possibly-good write into a false
|
|
82
|
+
* alarm. It exists to catch phantom successes — an upsert that reported OK
|
|
83
|
+
* while editing the wrong record — at providers that can list their zone.
|
|
84
|
+
*
|
|
85
|
+
* The listing is deliberately untyped. Typed listings map to endpoints like
|
|
86
|
+
* Porkbun's retrieveByNameType, whose subdomain-less form returns apex records
|
|
87
|
+
* only, which made verification blind to every non-apex record.
|
|
29
88
|
*/
|
|
30
|
-
export declare function
|
|
89
|
+
export declare function verifyAddressRecord(provider: DnsProvider, zone: string, fqdn: string, content: string, recordType: 'A' | 'AAAA'): Promise<boolean>;
|
|
31
90
|
export {};
|
package/dist/drivers/index.js
CHANGED
|
@@ -68,9 +68,9 @@ import {
|
|
|
68
68
|
waitForSsh,
|
|
69
69
|
wrapCloudInitUserData,
|
|
70
70
|
writeResizeCheckpoint
|
|
71
|
-
} from "../chunk-
|
|
72
|
-
import"../chunk-
|
|
73
|
-
import"../chunk-
|
|
71
|
+
} from "../chunk-tcr4xba6.js";
|
|
72
|
+
import"../chunk-24kwnpe5.js";
|
|
73
|
+
import"../chunk-7m60qnc8.js";
|
|
74
74
|
import"../chunk-4cjrg98a.js";
|
|
75
75
|
import"../chunk-wj3s95p9.js";
|
|
76
76
|
import"../chunk-zqtpg06c.js";
|
|
@@ -230,6 +230,7 @@ export declare function usesRpxProxy(compute?: {
|
|
|
230
230
|
export declare const RPX_DIR = "/etc/rpx";
|
|
231
231
|
export declare const RPX_INSTALL_DIR = "/opt/rpx-gateway";
|
|
232
232
|
export declare const RPX_LAUNCHER_PATH = "/etc/rpx/gateway.ts";
|
|
233
|
+
export declare const RPX_BINARY_PATH = "/etc/rpx/gateway";
|
|
233
234
|
export declare const RPX_SERVICE_NAME = "rpx-gateway.service";
|
|
234
235
|
/**
|
|
235
236
|
* Per-app gateway registry. Each project's deploy writes ONLY its own fragment
|
package/dist/index.d.ts
CHANGED
|
@@ -30,7 +30,8 @@ export { keyMatchesFilters, migrateObjectStorage, remapKey } from './object-stor
|
|
|
30
30
|
export type { MigrateEndpoint, MigrateError, MigrateOptions, MigratePlanItem, MigrateProgress, MigrateResult, MigrateVerification, } from './object-storage/migrate';
|
|
31
31
|
export * from './ssl';
|
|
32
32
|
export { deployStaticSite, deployStaticSiteFull, uploadStaticFiles, invalidateCache, deleteStaticSite, generateStaticSiteTemplate, deployStaticSiteWithExternalDns, deployStaticSiteWithExternalDnsFull, generateExternalDnsStaticSiteTemplate, deploySite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, buildAndPushServerlessImage, buildFunctionEnv, deployServerlessApp, infraEnvFromOutputs, redeployServerlessApp, rollbackServerlessApp, runRemoteCommand, setMaintenance, buildManagementDashboardArtifact, dashboardCredentialsFile, ensureManagementDashboard, MANAGEMENT_DASHBOARD_SITE, resolveDashboardAuth, resolveUiSource, } from './deploy';
|
|
33
|
-
export { collectServerDnsDomains,
|
|
33
|
+
export { collectServerDnsDomains, hostAcceptsIpv6, IPV6_EXCLUDED_HOST_LABELS, normalizePublicIpv6, reconcileAddressRecords, removeStaleServerAddressRecords, verifyAddressRecord, } from './deploy/server-dns';
|
|
34
|
+
export type { AddressRecordReport } from './deploy/server-dns';
|
|
34
35
|
export type { EnsureDashboardLogger, ResolvedDashboardAuth, StaticSiteConfig, DeployResult, UploadOptions, ExternalDnsStaticSiteConfig, ExternalDnsDeployResult, DeploySiteConfig, DeploySiteResult, StaticSiteDnsProvider, SiteDeployKind, DeploymentValidationResult, BuildImageOptions, BuiltImage, CodeSource, DeployServerlessOptions, ResolvedContext, } from './deploy';
|
|
35
36
|
export { createCloudDriver, CloudDriverFactory, cloudDrivers, AwsDriver, HetznerDriver, HetznerClient, resolveHetznerApiToken, normalizeSshPublicKey, ensureFirewall, ensureServer, ensureSshKey, serverPublicIpv4, sshExec, sshExecOrThrow, scpUpload, waitForSsh, waitForCloudInit, buildSshArgs, generateUbuntuAppCloudInit, wrapCloudInitUserData, buildHostCleanupScript, buildSiteDeployScript, buildStaticSiteDeployScript, resolveExecStart, deployAllComputeSites, deploySiteRelease, } from './drivers';
|
|
36
37
|
export type { CreateCloudDriverOptions } from './drivers/factory';
|