@stacksjs/ts-cloud 0.7.63 → 0.7.65
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/bin/cli.js +1556 -1284
- package/dist/{chunk-0zbqbrqg.js → chunk-0z7ry8fm.js} +889 -89
- package/dist/{chunk-t092y37p.js → chunk-267f6qd0.js} +525 -145
- package/dist/{chunk-zvk0f89t.js → chunk-28vh1h91.js} +1 -1
- package/dist/{chunk-tq46tnxv.js → chunk-ecsyc3p0.js} +4 -1
- package/dist/{chunk-rxhqny6z.js → chunk-zcvdzvmz.js} +2 -2
- package/dist/deploy/dashboard-data-server.d.ts +27 -1
- package/dist/deploy/index.js +4 -4
- package/dist/deploy/telemetry-collection.d.ts +2 -0
- package/dist/drivers/hetzner/client.d.ts +84 -2
- package/dist/drivers/hetzner/host-optimization.d.ts +33 -0
- package/dist/drivers/hetzner/monitoring.d.ts +46 -0
- package/dist/drivers/hetzner/resize-remote.d.ts +38 -0
- package/dist/drivers/hetzner/resize-state.d.ts +24 -0
- package/dist/drivers/hetzner/resize.d.ts +86 -0
- package/dist/drivers/index.d.ts +8 -0
- package/dist/drivers/index.js +35 -3
- package/dist/drivers/shared/compute-deploy.d.ts +15 -0
- package/dist/drivers/shared/ufw.d.ts +4 -2
- package/dist/index.js +4 -4
- package/dist/security/pre-deploy-scanner.test.d.ts +1 -0
- package/dist/ui/access-denied.html +2 -2
- package/dist/ui/account/automation.html +4 -4
- 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 +12 -7
- package/dist/ui/integrations.html +2 -2
- package/dist/ui/operations/alerts.html +4 -4
- package/dist/ui/operations/configuration.html +4 -4
- package/dist/ui/operations/jobs.html +4 -4
- package/dist/ui/operations/maintenance.html +3 -3
- package/dist/ui/operations/observability.html +13 -7
- 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/security.html +2 -2
- package/dist/ui/server/actions.html +3 -3
- package/dist/ui/server/activity.html +2 -2
- 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/firewall.html +3 -3
- package/dist/ui/server/fleet.html +4 -4
- package/dist/ui/server/logs.html +4 -4
- package/dist/ui/server/metrics.html +203 -3
- package/dist/ui/server/services.html +2 -2
- package/dist/ui/server/sites.html +102 -6
- 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 +4 -4
- package/dist/ui/serverless/assets.html +2 -2
- 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 +3 -3
- 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 +4 -4
- package/dist/ui-src/pages/index.stx +12 -10
- package/dist/ui-src/pages/operations/observability.stx +6 -4
- package/dist/ui-src/pages/server/metrics.stx +262 -106
- package/dist/ui-src/pages/server/sites.stx +98 -6
- package/package.json +3 -3
|
@@ -21928,7 +21928,7 @@ function dashboardHostFor(domain, ownedDomains) {
|
|
|
21928
21928
|
function collectDomains(config, environment) {
|
|
21929
21929
|
const candidates = [];
|
|
21930
21930
|
const dnsDomain = config.infrastructure?.dns?.domain;
|
|
21931
|
-
if (dnsDomain)
|
|
21931
|
+
if (dnsDomain && !config.cloud?.attachTo)
|
|
21932
21932
|
candidates.push(dnsDomain);
|
|
21933
21933
|
if (environment && config.environments?.[environment]?.domain)
|
|
21934
21934
|
candidates.push(config.environments[environment].domain);
|
|
@@ -21997,6 +21997,9 @@ function resolveManagementDashboardSites(config, environment, opts) {
|
|
|
21997
21997
|
preStart: ["bun install --production --no-save"],
|
|
21998
21998
|
start: `bun ${DASHBOARD_ENTRY} dashboard:serve --box --host 127.0.0.1 --port ${port}`,
|
|
21999
21999
|
port,
|
|
22000
|
+
env: {
|
|
22001
|
+
TS_CLOUD_DASHBOARD_TELEMETRY: config.cloud?.attachTo ? "0" : "1"
|
|
22002
|
+
},
|
|
22000
22003
|
zeroDowntime: false,
|
|
22001
22004
|
sharedPaths: [DASHBOARD_STATE_DIR],
|
|
22002
22005
|
healthCheck: { path: "/login" },
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildAndPushServerlessImage
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-28vh1h91.js";
|
|
4
4
|
import"./chunk-50jpda9q.js";
|
|
5
|
-
import"./chunk-
|
|
5
|
+
import"./chunk-ecsyc3p0.js";
|
|
6
6
|
import"./chunk-zqtpg06c.js";
|
|
7
7
|
import"./chunk-v0bahtg2.js";
|
|
8
8
|
export {
|
|
@@ -12,9 +12,33 @@ interface ProbedService {
|
|
|
12
12
|
enabled?: string;
|
|
13
13
|
since?: string;
|
|
14
14
|
}
|
|
15
|
+
export interface DiscoveredSite {
|
|
16
|
+
name: string;
|
|
17
|
+
project: string;
|
|
18
|
+
route: string;
|
|
19
|
+
href: string;
|
|
20
|
+
domain: string;
|
|
21
|
+
path: string;
|
|
22
|
+
kind: 'app' | 'static' | 'redirect';
|
|
23
|
+
runtime: string;
|
|
24
|
+
deploy: string;
|
|
25
|
+
tls: 'https';
|
|
26
|
+
root: string;
|
|
27
|
+
branch: string;
|
|
28
|
+
status: 'live' | 'degraded' | 'failed';
|
|
29
|
+
managedBy?: 'owner' | 'attached';
|
|
30
|
+
httpStatus: number;
|
|
31
|
+
responseMs: number;
|
|
32
|
+
tlsDaysRemaining?: number;
|
|
33
|
+
checkedAt: string;
|
|
34
|
+
release?: string;
|
|
35
|
+
deployedAt?: string;
|
|
36
|
+
}
|
|
15
37
|
export declare function parseBlock(output: string): Record<string, string> & {
|
|
16
38
|
services: ProbedService[];
|
|
39
|
+
discoveredSites: DiscoveredSite[];
|
|
17
40
|
};
|
|
41
|
+
export declare function mergeDiscoveredSites(configured: Array<Record<string, any>>, discovered: DiscoveredSite[], ownerSlug: string): Array<Record<string, any>>;
|
|
18
42
|
interface ServerLogSource {
|
|
19
43
|
/** journalctl `-u` pattern (a unit name or a glob like `acme-web-queue-*`). */
|
|
20
44
|
pattern: string;
|
|
@@ -32,5 +56,7 @@ export declare function parseServerLogs(output: string): Array<Record<string, an
|
|
|
32
56
|
export declare function parseServerSecurity(output: string): Record<string, any>;
|
|
33
57
|
export declare function parseDeployHistory(output: string, sites?: Record<string, any>): Array<Record<string, any>>;
|
|
34
58
|
export declare function resolveConfigOnlyServerDashboardData(config: CloudConfig, environment: EnvironmentType): Record<string, any>;
|
|
35
|
-
export declare function resolveServerDashboardData(config: CloudConfig, environment: EnvironmentType
|
|
59
|
+
export declare function resolveServerDashboardData(config: CloudConfig, environment: EnvironmentType, options?: {
|
|
60
|
+
telemetryOnly?: boolean;
|
|
61
|
+
}): Promise<Record<string, any> | null>;
|
|
36
62
|
export {};
|
package/dist/deploy/index.js
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
synchronizeDashboardUsers,
|
|
33
33
|
trackDashboardOperation,
|
|
34
34
|
verifyStaticApiOrigin
|
|
35
|
-
} from "../chunk-
|
|
35
|
+
} from "../chunk-267f6qd0.js";
|
|
36
36
|
import {
|
|
37
37
|
deleteStaticSite,
|
|
38
38
|
deployStaticSite,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
} from "../chunk-gcsv7g83.js";
|
|
47
47
|
import {
|
|
48
48
|
buildAndPushServerlessImage
|
|
49
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-28vh1h91.js";
|
|
50
50
|
import"../chunk-ybcz6sxc.js";
|
|
51
51
|
import"../chunk-01d86gt1.js";
|
|
52
52
|
import"../chunk-50jpda9q.js";
|
|
@@ -65,8 +65,8 @@ import {
|
|
|
65
65
|
shipsARelease,
|
|
66
66
|
siteInstallBase,
|
|
67
67
|
validateDeploymentConfig
|
|
68
|
-
} from "../chunk-
|
|
69
|
-
import"../chunk-
|
|
68
|
+
} from "../chunk-0z7ry8fm.js";
|
|
69
|
+
import"../chunk-ecsyc3p0.js";
|
|
70
70
|
import"../chunk-703nkybg.js";
|
|
71
71
|
import"../chunk-4cjrg98a.js";
|
|
72
72
|
import"../chunk-hpv68b4a.js";
|
|
@@ -29,6 +29,8 @@ export interface TelemetryCollectionContext {
|
|
|
29
29
|
config: CloudConfig;
|
|
30
30
|
environment: EnvironmentType;
|
|
31
31
|
force?: boolean;
|
|
32
|
+
/** Collect host and site metrics without expensive runtime logs or inventory. */
|
|
33
|
+
lightweight?: boolean;
|
|
32
34
|
}
|
|
33
35
|
export declare function telemetryRecordsFromLog(input: {
|
|
34
36
|
projectId: string;
|
|
@@ -10,6 +10,54 @@ export interface HetznerApiErrorBody {
|
|
|
10
10
|
details?: unknown;
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
+
export declare class HetznerApiError extends Error {
|
|
14
|
+
readonly status: number;
|
|
15
|
+
readonly code?: string | undefined;
|
|
16
|
+
readonly details?: unknown;
|
|
17
|
+
constructor(message: string, status: number, code?: string | undefined, details?: unknown);
|
|
18
|
+
}
|
|
19
|
+
export declare class HetznerActionError extends Error {
|
|
20
|
+
readonly action: HetznerAction;
|
|
21
|
+
constructor(message: string, action: HetznerAction);
|
|
22
|
+
}
|
|
23
|
+
export interface HetznerServerTypeLocation {
|
|
24
|
+
id: number;
|
|
25
|
+
name: string;
|
|
26
|
+
available: boolean;
|
|
27
|
+
recommended: boolean;
|
|
28
|
+
deprecation?: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface HetznerServerTypePrice {
|
|
31
|
+
location: string;
|
|
32
|
+
price_hourly: {
|
|
33
|
+
net: string;
|
|
34
|
+
gross: string;
|
|
35
|
+
};
|
|
36
|
+
price_monthly: {
|
|
37
|
+
net: string;
|
|
38
|
+
gross: string;
|
|
39
|
+
};
|
|
40
|
+
included_traffic?: number;
|
|
41
|
+
price_per_tb_traffic?: {
|
|
42
|
+
net: string;
|
|
43
|
+
gross: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export interface HetznerServerType {
|
|
47
|
+
id?: number;
|
|
48
|
+
name: string;
|
|
49
|
+
architecture?: string;
|
|
50
|
+
cores?: number;
|
|
51
|
+
cpu_type?: string;
|
|
52
|
+
category?: string;
|
|
53
|
+
deprecated?: boolean;
|
|
54
|
+
description?: string;
|
|
55
|
+
disk?: number;
|
|
56
|
+
memory?: number;
|
|
57
|
+
prices?: HetznerServerTypePrice[];
|
|
58
|
+
storage_type?: string;
|
|
59
|
+
locations?: HetznerServerTypeLocation[];
|
|
60
|
+
}
|
|
13
61
|
export interface HetznerServer {
|
|
14
62
|
id: number;
|
|
15
63
|
name: string;
|
|
@@ -26,15 +74,24 @@ export interface HetznerServer {
|
|
|
26
74
|
ip: string;
|
|
27
75
|
}>;
|
|
28
76
|
labels?: Record<string, string>;
|
|
29
|
-
server_type:
|
|
77
|
+
server_type: HetznerServerType;
|
|
78
|
+
/** Current API shape. */
|
|
79
|
+
location?: {
|
|
80
|
+
id?: number;
|
|
30
81
|
name: string;
|
|
82
|
+
description?: string;
|
|
83
|
+
city?: string;
|
|
84
|
+
country?: string;
|
|
31
85
|
};
|
|
32
|
-
|
|
86
|
+
/** Legacy API shape retained for compatibility with recorded fixtures. */
|
|
87
|
+
datacenter?: {
|
|
33
88
|
name: string;
|
|
34
89
|
location: {
|
|
35
90
|
name: string;
|
|
36
91
|
};
|
|
37
92
|
};
|
|
93
|
+
primary_disk_size?: number;
|
|
94
|
+
locked?: boolean;
|
|
38
95
|
}
|
|
39
96
|
export interface HetznerFirewall {
|
|
40
97
|
id: number;
|
|
@@ -65,6 +122,15 @@ export interface HetznerAction {
|
|
|
65
122
|
message: string;
|
|
66
123
|
};
|
|
67
124
|
}
|
|
125
|
+
export type HetznerServerMetricType = 'cpu' | 'disk' | 'network';
|
|
126
|
+
export interface HetznerServerMetrics {
|
|
127
|
+
start: string;
|
|
128
|
+
end: string;
|
|
129
|
+
step: number;
|
|
130
|
+
time_series: Record<string, {
|
|
131
|
+
values: Array<[number, string]>;
|
|
132
|
+
}>;
|
|
133
|
+
}
|
|
68
134
|
export interface CreateServerOptions {
|
|
69
135
|
name: string;
|
|
70
136
|
serverType: string;
|
|
@@ -154,6 +220,14 @@ export declare class HetznerClient {
|
|
|
154
220
|
private requestAll;
|
|
155
221
|
listServers(): Promise<HetznerServer[]>;
|
|
156
222
|
getServer(id: number): Promise<HetznerServer>;
|
|
223
|
+
getServerMetrics(id: number, options: {
|
|
224
|
+
types: HetznerServerMetricType[];
|
|
225
|
+
start: Date;
|
|
226
|
+
end: Date;
|
|
227
|
+
step?: number;
|
|
228
|
+
}): Promise<HetznerServerMetrics>;
|
|
229
|
+
listServerTypes(name?: string): Promise<HetznerServerType[]>;
|
|
230
|
+
getServerType(name: string): Promise<HetznerServerType | null>;
|
|
157
231
|
createServer(options: CreateServerOptions): Promise<{
|
|
158
232
|
server: HetznerServer;
|
|
159
233
|
action: HetznerAction;
|
|
@@ -183,6 +257,10 @@ export declare class HetznerClient {
|
|
|
183
257
|
}>): Promise<HetznerAction[]>;
|
|
184
258
|
listSshKeys(): Promise<HetznerSshKey[]>;
|
|
185
259
|
createSshKey(options: CreateSshKeyOptions): Promise<HetznerSshKey>;
|
|
260
|
+
getAction(actionId: number): Promise<HetznerAction>;
|
|
261
|
+
shutdownServer(serverId: number): Promise<HetznerAction>;
|
|
262
|
+
powerOnServer(serverId: number): Promise<HetznerAction>;
|
|
263
|
+
changeServerType(serverId: number, serverType: string, upgradeDisk: boolean): Promise<HetznerAction>;
|
|
186
264
|
waitForAction(actionId: number, options?: {
|
|
187
265
|
pollIntervalMs?: number;
|
|
188
266
|
maxWaitMs?: number;
|
|
@@ -191,6 +269,10 @@ export declare class HetznerClient {
|
|
|
191
269
|
pollIntervalMs?: number;
|
|
192
270
|
maxWaitMs?: number;
|
|
193
271
|
}): Promise<HetznerServer>;
|
|
272
|
+
waitForServerStatus(serverId: number, status: string, options?: {
|
|
273
|
+
pollIntervalMs?: number;
|
|
274
|
+
maxWaitMs?: number;
|
|
275
|
+
}): Promise<HetznerServer>;
|
|
194
276
|
}
|
|
195
277
|
/**
|
|
196
278
|
* The Hetzner API token, or a clear error when none is configured.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CloudConfig } from '@ts-cloud/core';
|
|
2
|
+
import type { RemoteExecOptions } from '../shared/remote-exec';
|
|
3
|
+
export interface HetznerHostOptimizationPlan {
|
|
4
|
+
firewallPorts: number[];
|
|
5
|
+
monitoring: boolean;
|
|
6
|
+
autoUpdates: boolean;
|
|
7
|
+
swapGb: number;
|
|
8
|
+
sshPasswordAuthentication: false;
|
|
9
|
+
journalMaxUse: string;
|
|
10
|
+
journalRetention: string;
|
|
11
|
+
}
|
|
12
|
+
export interface HetznerHostOptimizationReport {
|
|
13
|
+
firewallActive: boolean;
|
|
14
|
+
firewallPorts: number[];
|
|
15
|
+
passwordAuthentication: boolean;
|
|
16
|
+
rootPasswordLogin: boolean;
|
|
17
|
+
fail2banActive: boolean;
|
|
18
|
+
unattendedUpgradesActive: boolean;
|
|
19
|
+
metricsTimerActive: boolean;
|
|
20
|
+
metricsSnapshotFresh: boolean;
|
|
21
|
+
swapBytes: number;
|
|
22
|
+
journalBytes: number;
|
|
23
|
+
failedUnits: string[];
|
|
24
|
+
publicTcpPorts: number[];
|
|
25
|
+
}
|
|
26
|
+
export interface HetznerHostOptimizationOptions extends RemoteExecOptions {
|
|
27
|
+
host: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function resolveHetznerHostOptimizationPlan(config: CloudConfig): HetznerHostOptimizationPlan;
|
|
30
|
+
export declare function buildHetznerHostOptimizationScript(config: CloudConfig): string[];
|
|
31
|
+
export declare function applyHetznerHostOptimization(config: CloudConfig, options: HetznerHostOptimizationOptions): Promise<void>;
|
|
32
|
+
export declare function collectHetznerHostOptimizationReport(options: HetznerHostOptimizationOptions): Promise<HetznerHostOptimizationReport>;
|
|
33
|
+
export declare function verifyHetznerHostOptimization(plan: HetznerHostOptimizationPlan, report: HetznerHostOptimizationReport): string[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { HetznerClient, HetznerServerMetrics } from './client';
|
|
2
|
+
import type { RemoteExecOptions } from '../shared/remote-exec';
|
|
3
|
+
export interface HetznerHostHistoryPoint {
|
|
4
|
+
timestamp: string;
|
|
5
|
+
cpuUsedPercent: number;
|
|
6
|
+
cpuUserPercent: number;
|
|
7
|
+
cpuSystemPercent: number;
|
|
8
|
+
cpuIowaitPercent: number;
|
|
9
|
+
memoryUsedPercent: number;
|
|
10
|
+
memoryUsedBytes: number;
|
|
11
|
+
memoryAvailableBytes: number;
|
|
12
|
+
swapUsedPercent: number;
|
|
13
|
+
swapUsedBytes: number;
|
|
14
|
+
}
|
|
15
|
+
export interface HetznerProviderMetricPoint {
|
|
16
|
+
timestamp: string;
|
|
17
|
+
value: number;
|
|
18
|
+
}
|
|
19
|
+
export interface HetznerServerMonitoring {
|
|
20
|
+
from: string;
|
|
21
|
+
to: string;
|
|
22
|
+
cores: number;
|
|
23
|
+
host: HetznerHostHistoryPoint[];
|
|
24
|
+
provider: Record<string, HetznerProviderMetricPoint[]>;
|
|
25
|
+
summary: {
|
|
26
|
+
cpuAveragePercent: number | null;
|
|
27
|
+
cpuPeakPercent: number | null;
|
|
28
|
+
memoryAveragePercent: number | null;
|
|
29
|
+
memoryPeakPercent: number | null;
|
|
30
|
+
memoryMinimumAvailableBytes: number | null;
|
|
31
|
+
swapPeakPercent: number | null;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare function parseSadfHostHistory(raw: string, from: Date, to: Date): HetznerHostHistoryPoint[];
|
|
35
|
+
export declare function providerMetricSeries(metrics: HetznerServerMetrics): Record<string, HetznerProviderMetricPoint[]>;
|
|
36
|
+
export declare function collectHetznerServerMonitoring(options: {
|
|
37
|
+
client: HetznerClient;
|
|
38
|
+
serverId: number;
|
|
39
|
+
cores: number;
|
|
40
|
+
from: Date;
|
|
41
|
+
to: Date;
|
|
42
|
+
step?: number;
|
|
43
|
+
remote: RemoteExecOptions & {
|
|
44
|
+
host: string;
|
|
45
|
+
};
|
|
46
|
+
}): Promise<HetznerServerMonitoring>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { RemoteExecOptions } from '../shared/remote-exec';
|
|
2
|
+
import type { HetznerServerType } from './client';
|
|
3
|
+
import type { HetznerResizeVerification } from './resize';
|
|
4
|
+
export interface HetznerRouteProbe {
|
|
5
|
+
domain: string;
|
|
6
|
+
ok: boolean;
|
|
7
|
+
status?: number;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface HetznerResizeManifest {
|
|
11
|
+
capturedAt: string;
|
|
12
|
+
hostname: string;
|
|
13
|
+
cpuCores: number;
|
|
14
|
+
memoryBytes: number;
|
|
15
|
+
rootSource: string;
|
|
16
|
+
rootFsType: string;
|
|
17
|
+
rootDiskBytes: number;
|
|
18
|
+
rootFilesystemBytes: number;
|
|
19
|
+
failedUnits: string[];
|
|
20
|
+
runningServices: string[];
|
|
21
|
+
releaseLinks: string[];
|
|
22
|
+
routeFragments: string[];
|
|
23
|
+
routeIds: string[];
|
|
24
|
+
routeDomains: string[];
|
|
25
|
+
persistentData: string[];
|
|
26
|
+
dataCatalog: string[];
|
|
27
|
+
routeProbes: HetznerRouteProbe[];
|
|
28
|
+
}
|
|
29
|
+
export interface HetznerRemoteResizeOptions extends RemoteExecOptions {
|
|
30
|
+
host: string;
|
|
31
|
+
sshTimeoutMs?: number;
|
|
32
|
+
routeTimeoutMs?: number;
|
|
33
|
+
}
|
|
34
|
+
export declare function collectHetznerResizeManifest(options: HetznerRemoteResizeOptions): Promise<HetznerResizeManifest>;
|
|
35
|
+
export declare function prepareHetznerResize(options: HetznerRemoteResizeOptions): Promise<HetznerResizeManifest>;
|
|
36
|
+
export declare function verifyHetznerResize(options: HetznerRemoteResizeOptions, target: HetznerServerType, before?: HetznerResizeManifest, context?: {
|
|
37
|
+
recovered: boolean;
|
|
38
|
+
}): Promise<HetznerResizeVerification>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HetznerResizePhase } from './resize';
|
|
2
|
+
import type { HetznerResizeManifest } from './resize-remote';
|
|
3
|
+
export interface HetznerResizeCheckpoint {
|
|
4
|
+
schemaVersion: 1;
|
|
5
|
+
operationId: string;
|
|
6
|
+
stackName: string;
|
|
7
|
+
serverId: number;
|
|
8
|
+
serverName: string;
|
|
9
|
+
sourceType: string;
|
|
10
|
+
targetType: string;
|
|
11
|
+
upgradeDisk: boolean;
|
|
12
|
+
phase: HetznerResizePhase;
|
|
13
|
+
status: 'running' | 'waiting-capacity' | 'completed' | 'recovered' | 'failed';
|
|
14
|
+
attempts: number;
|
|
15
|
+
startedAt: string;
|
|
16
|
+
updatedAt: string;
|
|
17
|
+
manifest?: HetznerResizeManifest;
|
|
18
|
+
lastError?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function resizeCheckpointPath(stackName: string): string;
|
|
21
|
+
export declare function resizeLockPath(stackName: string): string;
|
|
22
|
+
export declare function readResizeCheckpoint(stackName: string): Promise<HetznerResizeCheckpoint | null>;
|
|
23
|
+
export declare function writeResizeCheckpoint(checkpoint: HetznerResizeCheckpoint): Promise<void>;
|
|
24
|
+
export declare function acquireResizeLock(stackName: string, staleAfterMs?: number): Promise<() => Promise<void>>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { HetznerAction, HetznerClient, HetznerServer, HetznerServerType } from './client';
|
|
2
|
+
export type HetznerResizePhase = 'planning' | 'waiting-capacity' | 'preflight' | 'shutting-down' | 'resizing' | 'powering-on' | 'verifying' | 'recovering' | 'complete';
|
|
3
|
+
export interface HetznerResizePlan {
|
|
4
|
+
serverId: number;
|
|
5
|
+
serverName: string;
|
|
6
|
+
location: string;
|
|
7
|
+
currentType: HetznerServerType;
|
|
8
|
+
targetType: HetznerServerType;
|
|
9
|
+
capacityAvailable: boolean;
|
|
10
|
+
alreadyComplete: boolean;
|
|
11
|
+
upgradeDisk: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface HetznerResizeVerification {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
checks?: Record<string, boolean | number | string>;
|
|
16
|
+
failures?: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface HetznerResizeHooks<TManifest = unknown> {
|
|
19
|
+
preflight?: (server: HetznerServer, target: HetznerServerType) => Promise<TManifest> | TManifest;
|
|
20
|
+
afterBoot?: (server: HetznerServer, target: HetznerServerType, manifest: TManifest | undefined, context: {
|
|
21
|
+
recovered: boolean;
|
|
22
|
+
}) => Promise<HetznerResizeVerification> | HetznerResizeVerification;
|
|
23
|
+
onPhase?: (phase: HetznerResizePhase, detail?: Record<string, unknown>) => Promise<void> | void;
|
|
24
|
+
}
|
|
25
|
+
export interface HetznerResizeClient {
|
|
26
|
+
getServer(id: number): Promise<HetznerServer>;
|
|
27
|
+
getServerType(name: string): Promise<HetznerServerType | null>;
|
|
28
|
+
shutdownServer(id: number): Promise<HetznerAction>;
|
|
29
|
+
powerOnServer(id: number): Promise<HetznerAction>;
|
|
30
|
+
changeServerType(id: number, serverType: string, upgradeDisk: boolean): Promise<HetznerAction>;
|
|
31
|
+
waitForAction(actionId: number, options?: {
|
|
32
|
+
pollIntervalMs?: number;
|
|
33
|
+
maxWaitMs?: number;
|
|
34
|
+
}): Promise<HetznerAction>;
|
|
35
|
+
waitForServerRunning(serverId: number, options?: {
|
|
36
|
+
pollIntervalMs?: number;
|
|
37
|
+
maxWaitMs?: number;
|
|
38
|
+
}): Promise<HetznerServer>;
|
|
39
|
+
waitForServerStatus(serverId: number, status: string, options?: {
|
|
40
|
+
pollIntervalMs?: number;
|
|
41
|
+
maxWaitMs?: number;
|
|
42
|
+
}): Promise<HetznerServer>;
|
|
43
|
+
}
|
|
44
|
+
export interface ExecuteHetznerResizeOptions<TManifest = unknown> {
|
|
45
|
+
client: HetznerResizeClient;
|
|
46
|
+
serverId: number;
|
|
47
|
+
targetType: string;
|
|
48
|
+
upgradeDisk?: boolean;
|
|
49
|
+
/** Continue a recorded resize after the process stopped with the server off. */
|
|
50
|
+
resumeFromOff?: boolean;
|
|
51
|
+
/** Manifest restored from a durable checkpoint when resuming. */
|
|
52
|
+
manifest?: TManifest;
|
|
53
|
+
/** Ignore the best-effort stock flag and ask the placement API anyway. */
|
|
54
|
+
attemptUnavailable?: boolean;
|
|
55
|
+
hooks?: HetznerResizeHooks<TManifest>;
|
|
56
|
+
actionPollIntervalMs?: number;
|
|
57
|
+
actionTimeoutMs?: number;
|
|
58
|
+
serverPollIntervalMs?: number;
|
|
59
|
+
serverTimeoutMs?: number;
|
|
60
|
+
}
|
|
61
|
+
export type HetznerResizeResult<TManifest = unknown> = {
|
|
62
|
+
status: 'waiting-capacity';
|
|
63
|
+
plan: HetznerResizePlan;
|
|
64
|
+
} | {
|
|
65
|
+
status: 'already-complete';
|
|
66
|
+
plan: HetznerResizePlan;
|
|
67
|
+
verification?: HetznerResizeVerification;
|
|
68
|
+
} | {
|
|
69
|
+
status: 'completed';
|
|
70
|
+
plan: HetznerResizePlan;
|
|
71
|
+
server: HetznerServer;
|
|
72
|
+
manifest?: TManifest;
|
|
73
|
+
verification?: HetznerResizeVerification;
|
|
74
|
+
} | {
|
|
75
|
+
status: 'recovered';
|
|
76
|
+
plan: HetznerResizePlan;
|
|
77
|
+
server: HetznerServer;
|
|
78
|
+
manifest?: TManifest;
|
|
79
|
+
verification?: HetznerResizeVerification;
|
|
80
|
+
retryable: boolean;
|
|
81
|
+
error: string;
|
|
82
|
+
};
|
|
83
|
+
export declare function planHetznerServerResize(client: Pick<HetznerResizeClient, 'getServer' | 'getServerType'>, serverId: number, targetTypeName: string, upgradeDisk?: boolean): Promise<HetznerResizePlan>;
|
|
84
|
+
export declare function isHetznerCapacityError(error: unknown): boolean;
|
|
85
|
+
export declare function executeHetznerServerResize<TManifest = unknown>(options: ExecuteHetznerResizeOptions<TManifest>): Promise<HetznerResizeResult<TManifest>>;
|
|
86
|
+
export type HetznerResizeClientInstance = Pick<HetznerClient, 'getServer' | 'getServerType' | 'shutdownServer' | 'powerOnServer' | 'changeServerType' | 'waitForAction' | 'waitForServerRunning' | 'waitForServerStatus'>;
|
package/dist/drivers/index.d.ts
CHANGED
|
@@ -3,6 +3,14 @@ export { AwsDriver } from './aws/driver';
|
|
|
3
3
|
export { HetznerDriver } from './hetzner/driver';
|
|
4
4
|
export { isBoxMode, LocalBoxDriver } from './local-box/driver';
|
|
5
5
|
export { HetznerClient, normalizeSshPublicKey, resolveHetznerApiToken } from './hetzner/client';
|
|
6
|
+
export { executeHetznerServerResize, isHetznerCapacityError, planHetznerServerResize, } from './hetzner/resize';
|
|
7
|
+
export type { ExecuteHetznerResizeOptions, HetznerResizeHooks, HetznerResizePhase, HetznerResizePlan, HetznerResizeResult, HetznerResizeVerification, } from './hetzner/resize';
|
|
8
|
+
export { collectHetznerResizeManifest, prepareHetznerResize, verifyHetznerResize, } from './hetzner/resize-remote';
|
|
9
|
+
export type { HetznerRemoteResizeOptions, HetznerResizeManifest, HetznerRouteProbe, } from './hetzner/resize-remote';
|
|
10
|
+
export { applyHetznerHostOptimization, buildHetznerHostOptimizationScript, collectHetznerHostOptimizationReport, resolveHetznerHostOptimizationPlan, verifyHetznerHostOptimization, } from './hetzner/host-optimization';
|
|
11
|
+
export type { HetznerHostOptimizationOptions, HetznerHostOptimizationPlan, HetznerHostOptimizationReport, } from './hetzner/host-optimization';
|
|
12
|
+
export { acquireResizeLock, readResizeCheckpoint, resizeCheckpointPath, resizeLockPath, writeResizeCheckpoint, } from './hetzner/resize-state';
|
|
13
|
+
export type { HetznerResizeCheckpoint } from './hetzner/resize-state';
|
|
6
14
|
export { generateUbuntuAppCloudInit, wrapCloudInitUserData } from './hetzner/cloud-init';
|
|
7
15
|
export { ensureFirewall, ensureServer, ensureSshKey, serverPublicIpv4 } from './hetzner/provision';
|
|
8
16
|
export type { EnsuredResource, EnsuredServer, EnsureFirewallOptions, EnsureServerOptions, EnsureSshKeyOptions, } from './hetzner/provision';
|
package/dist/drivers/index.js
CHANGED
|
@@ -14,9 +14,12 @@ import {
|
|
|
14
14
|
RPX_LAUNCHER_PATH,
|
|
15
15
|
RPX_SERVICE_NAME,
|
|
16
16
|
UBUNTU_2404_AMI_PARAM,
|
|
17
|
+
acquireResizeLock,
|
|
18
|
+
applyHetznerHostOptimization,
|
|
17
19
|
buildAwsArtifactFetch,
|
|
18
20
|
buildBoxUserData,
|
|
19
21
|
buildCloudFrontOriginConfig,
|
|
22
|
+
buildHetznerHostOptimizationScript,
|
|
20
23
|
buildHostCleanupScript,
|
|
21
24
|
buildLocalArtifactFetch,
|
|
22
25
|
buildRpxConfig,
|
|
@@ -28,6 +31,8 @@ import {
|
|
|
28
31
|
buildStaticSiteDeployScript,
|
|
29
32
|
buildUbuntuBootstrapScript,
|
|
30
33
|
cloudDrivers,
|
|
34
|
+
collectHetznerHostOptimizationReport,
|
|
35
|
+
collectHetznerResizeManifest,
|
|
31
36
|
createBoxProvisioner,
|
|
32
37
|
createCloudDriver,
|
|
33
38
|
deployAllComputeSites,
|
|
@@ -36,47 +41,69 @@ import {
|
|
|
36
41
|
ensureFirewall,
|
|
37
42
|
ensureServer,
|
|
38
43
|
ensureSshKey,
|
|
44
|
+
executeHetznerServerResize,
|
|
39
45
|
isBoxMode,
|
|
46
|
+
isHetznerCapacityError,
|
|
40
47
|
normalizeRoutePath,
|
|
41
48
|
normalizeSshPublicKey,
|
|
49
|
+
planHetznerServerResize,
|
|
50
|
+
prepareHetznerResize,
|
|
51
|
+
readResizeCheckpoint,
|
|
42
52
|
releaseTarballTmpPath,
|
|
43
53
|
reloadRpxGateway,
|
|
44
54
|
renderRpxLauncher,
|
|
45
55
|
renewRpxCertificates,
|
|
56
|
+
resizeCheckpointPath,
|
|
57
|
+
resizeLockPath,
|
|
46
58
|
resolveExecStart,
|
|
47
59
|
resolveHetznerApiToken,
|
|
60
|
+
resolveHetznerHostOptimizationPlan,
|
|
48
61
|
scpUpload,
|
|
49
62
|
serverPublicIpv4,
|
|
50
63
|
sshExec,
|
|
51
64
|
sshExecOrThrow,
|
|
65
|
+
verifyHetznerHostOptimization,
|
|
66
|
+
verifyHetznerResize,
|
|
52
67
|
waitForCloudInit,
|
|
53
68
|
waitForSsh,
|
|
54
|
-
wrapCloudInitUserData
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
wrapCloudInitUserData,
|
|
70
|
+
writeResizeCheckpoint
|
|
71
|
+
} from "../chunk-0z7ry8fm.js";
|
|
72
|
+
import"../chunk-ecsyc3p0.js";
|
|
57
73
|
import"../chunk-703nkybg.js";
|
|
58
74
|
import"../chunk-4cjrg98a.js";
|
|
59
75
|
import"../chunk-wj3s95p9.js";
|
|
60
76
|
import"../chunk-zqtpg06c.js";
|
|
61
77
|
import"../chunk-v0bahtg2.js";
|
|
62
78
|
export {
|
|
79
|
+
writeResizeCheckpoint,
|
|
63
80
|
wrapCloudInitUserData,
|
|
64
81
|
waitForSsh,
|
|
65
82
|
waitForCloudInit,
|
|
83
|
+
verifyHetznerResize,
|
|
84
|
+
verifyHetznerHostOptimization,
|
|
66
85
|
sshExecOrThrow,
|
|
67
86
|
sshExec,
|
|
68
87
|
serverPublicIpv4,
|
|
69
88
|
scpUpload,
|
|
89
|
+
resolveHetznerHostOptimizationPlan,
|
|
70
90
|
resolveHetznerApiToken,
|
|
71
91
|
resolveExecStart,
|
|
92
|
+
resizeLockPath,
|
|
93
|
+
resizeCheckpointPath,
|
|
72
94
|
renewRpxCertificates,
|
|
73
95
|
renderRpxLauncher,
|
|
74
96
|
reloadRpxGateway,
|
|
75
97
|
releaseTarballTmpPath,
|
|
98
|
+
readResizeCheckpoint,
|
|
99
|
+
prepareHetznerResize,
|
|
100
|
+
planHetznerServerResize,
|
|
76
101
|
normalizeSshPublicKey,
|
|
77
102
|
normalizeRoutePath,
|
|
103
|
+
isHetznerCapacityError,
|
|
78
104
|
isBoxMode,
|
|
79
105
|
buildUbuntuBootstrapScript as generateUbuntuAppCloudInit,
|
|
106
|
+
executeHetznerServerResize,
|
|
80
107
|
ensureSshKey,
|
|
81
108
|
ensureServer,
|
|
82
109
|
ensureFirewall,
|
|
@@ -85,6 +112,8 @@ export {
|
|
|
85
112
|
deployAllComputeSites,
|
|
86
113
|
createCloudDriver,
|
|
87
114
|
createBoxProvisioner,
|
|
115
|
+
collectHetznerResizeManifest,
|
|
116
|
+
collectHetznerHostOptimizationReport,
|
|
88
117
|
cloudDrivers,
|
|
89
118
|
buildStaticSiteDeployScript,
|
|
90
119
|
buildSshArgs,
|
|
@@ -95,9 +124,12 @@ export {
|
|
|
95
124
|
buildRpxConfig,
|
|
96
125
|
buildLocalArtifactFetch,
|
|
97
126
|
buildHostCleanupScript,
|
|
127
|
+
buildHetznerHostOptimizationScript,
|
|
98
128
|
buildCloudFrontOriginConfig,
|
|
99
129
|
buildBoxUserData,
|
|
100
130
|
buildAwsArtifactFetch,
|
|
131
|
+
applyHetznerHostOptimization,
|
|
132
|
+
acquireResizeLock,
|
|
101
133
|
UBUNTU_2404_AMI_PARAM,
|
|
102
134
|
RPX_SERVICE_NAME,
|
|
103
135
|
RPX_LAUNCHER_PATH,
|
|
@@ -20,6 +20,13 @@ export interface DeployAllSitesOptions {
|
|
|
20
20
|
logger?: ComputeDeployLogger;
|
|
21
21
|
/** Project root used to resolve/build the management dashboard UI. Defaults to `process.cwd()`. */
|
|
22
22
|
cwd?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Auto-inject the management dashboard when the caller has not already
|
|
25
|
+
* prepared it. Framework integrations that inject before provisioning must
|
|
26
|
+
* disable this for narrowed single-site deploys, otherwise the dashboard is
|
|
27
|
+
* unexpectedly added back to the narrowed site set.
|
|
28
|
+
*/
|
|
29
|
+
managementDashboard?: boolean;
|
|
23
30
|
/**
|
|
24
31
|
* The FULL site model for regenerating the rpx gateway, distinct from `config`
|
|
25
32
|
* which may be narrowed to a single site for a partial deploy. The rpx gateway
|
|
@@ -28,6 +35,14 @@ export interface DeployAllSitesOptions {
|
|
|
28
35
|
*/
|
|
29
36
|
rpxConfig?: CloudConfig;
|
|
30
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Reconcile only the auto-managed dashboard units for one project. Dashboard
|
|
40
|
+
* host derivation can legitimately change when ownership metadata is fixed;
|
|
41
|
+
* without this bounded cleanup, the superseded unit remains enabled forever
|
|
42
|
+
* and can keep crash-looping on its old port. Release data and shared dashboard
|
|
43
|
+
* state stay on disk for recovery.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildManagementDashboardServiceReconciliationScript(slug: string, desiredSiteNames: string[]): string[];
|
|
31
46
|
/**
|
|
32
47
|
* Deploy every site that targets the compute server — both dynamic apps
|
|
33
48
|
* (`server` + `start`, run as systemd services) and static sites (`server`
|
|
@@ -11,7 +11,9 @@ import type { ComputeFirewallConfig } from '@ts-cloud/core';
|
|
|
11
11
|
/** Ports always allowed so SSH deploys + web traffic are never locked out. */
|
|
12
12
|
export declare const UFW_BASE_PORTS: readonly number[];
|
|
13
13
|
/**
|
|
14
|
-
* Build the UFW provisioning commands.
|
|
15
|
-
*
|
|
14
|
+
* Build the UFW provisioning commands. The ts-cloud declaration owns the host
|
|
15
|
+
* firewall, so every reconciliation resets stale/manual rules before applying
|
|
16
|
+
* the exact desired set. This prevents a removed app port from remaining
|
|
17
|
+
* publicly reachable forever.
|
|
16
18
|
*/
|
|
17
19
|
export declare function buildUfwScript(firewall?: ComputeFirewallConfig): string[];
|