@stacksjs/ts-cloud 0.7.119 → 0.7.121

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.
Files changed (75) hide show
  1. package/dist/bin/cli.js +567 -566
  2. package/dist/bin/dashboard-server.js +426 -425
  3. package/dist/{chunk-8xezrv4z.js → chunk-0cx84wm2.js} +5 -5
  4. package/dist/{chunk-19bs955w.js → chunk-8ynjhsh3.js} +1 -1
  5. package/dist/{chunk-wmbep3b5.js → chunk-b801ga2t.js} +1 -1
  6. package/dist/{chunk-53jatzaw.js → chunk-w2zd56qm.js} +2 -2
  7. package/dist/{chunk-t2s3gdq6.js → chunk-xes5vst2.js} +70 -22
  8. package/dist/{chunk-nm1d3a75.js → chunk-zcjc5y9f.js} +171 -29
  9. package/dist/deploy/index.js +5 -5
  10. package/dist/deploy/release-content.d.ts +37 -0
  11. package/dist/deploy/release-content.test.d.ts +1 -0
  12. package/dist/drivers/aws/driver.d.ts +19 -1
  13. package/dist/drivers/aws/provision.d.ts +46 -0
  14. package/dist/drivers/hetzner/client.d.ts +5 -0
  15. package/dist/drivers/hetzner/driver.d.ts +6 -2
  16. package/dist/drivers/hetzner/instance-sizes.d.ts +10 -0
  17. package/dist/drivers/index.js +2 -2
  18. package/dist/drivers/shared/deploy-script.d.ts +7 -0
  19. package/dist/drivers/shared/sftp-provision.d.ts +45 -0
  20. package/dist/drivers/shared/sftp-provision.test.d.ts +1 -0
  21. package/dist/generators/infrastructure.d.ts +10 -0
  22. package/dist/index.js +39 -7
  23. package/dist/protection/index.js +1 -1
  24. package/dist/spend/index.js +2 -2
  25. package/dist/ui/access-denied.html +2 -2
  26. package/dist/ui/account/automation.html +4 -4
  27. package/dist/ui/account/security.html +2 -2
  28. package/dist/ui/applications/compose.html +4 -4
  29. package/dist/ui/applications/new.html +2 -2
  30. package/dist/ui/data/backups.html +4 -4
  31. package/dist/ui/data/services.html +4 -4
  32. package/dist/ui/data/volumes.html +3 -3
  33. package/dist/ui/index.html +3 -3
  34. package/dist/ui/infrastructure/topology.html +4 -4
  35. package/dist/ui/operations/alerts.html +4 -4
  36. package/dist/ui/operations/configuration.html +4 -4
  37. package/dist/ui/operations/jobs.html +4 -4
  38. package/dist/ui/operations/maintenance.html +4 -4
  39. package/dist/ui/operations/observability.html +4 -4
  40. package/dist/ui/operations/previews.html +4 -4
  41. package/dist/ui/operations/queue.html +4 -4
  42. package/dist/ui/operations/regions.html +4 -4
  43. package/dist/ui/operations/releases.html +4 -4
  44. package/dist/ui/operations/spend.html +4 -4
  45. package/dist/ui/operations/workloads.html +3 -3
  46. package/dist/ui/security.html +2 -2
  47. package/dist/ui/server/actions.html +4 -4
  48. package/dist/ui/server/activity.html +2 -2
  49. package/dist/ui/server/capacity.html +4 -4
  50. package/dist/ui/server/database.html +3 -3
  51. package/dist/ui/server/deployments.html +4 -4
  52. package/dist/ui/server/diagnostics.html +2 -2
  53. package/dist/ui/server/firewall.html +4 -4
  54. package/dist/ui/server/fleet.html +4 -4
  55. package/dist/ui/server/logs.html +4 -4
  56. package/dist/ui/server/metrics.html +4 -4
  57. package/dist/ui/server/security.html +2 -2
  58. package/dist/ui/server/services.html +2 -2
  59. package/dist/ui/server/sites.html +4 -4
  60. package/dist/ui/server/ssh-keys.html +4 -4
  61. package/dist/ui/server/team.html +4 -4
  62. package/dist/ui/server/terminal.html +2 -2
  63. package/dist/ui/serverless/alarms.html +4 -4
  64. package/dist/ui/serverless/assets.html +2 -2
  65. package/dist/ui/serverless/data.html +4 -4
  66. package/dist/ui/serverless/deployments.html +2 -2
  67. package/dist/ui/serverless/firewall.html +2 -2
  68. package/dist/ui/serverless/functions.html +3 -3
  69. package/dist/ui/serverless/logs.html +4 -4
  70. package/dist/ui/serverless/metrics.html +2 -2
  71. package/dist/ui/serverless/queues.html +4 -4
  72. package/dist/ui/serverless/secrets.html +4 -4
  73. package/dist/ui/serverless/traces.html +4 -4
  74. package/dist/ui/serverless.html +4 -4
  75. package/package.json +3 -3
@@ -1,6 +1,15 @@
1
1
  import type { CloudDriver, ComputeStackOutputs, ComputeTarget, FindComputeTargetsOptions, ProvisionComputeOptions, RemoteDeployResult, RunRemoteDeployOptions, UploadReleaseOptions, UploadReleaseResult } from '@ts-cloud/core';
2
+ import { CloudFormationClient } from '../../aws/cloudformation';
3
+ import { EC2Client } from '../../aws/ec2';
2
4
  export interface AwsDriverOptions {
3
5
  region?: string;
6
+ /**
7
+ * Build the EC2 client for a region. Defaults to a real client; tests supply
8
+ * a fake so the driver's decisions can be exercised without AWS.
9
+ */
10
+ ec2Client?: (region: string) => EC2Client;
11
+ /** Build the CloudFormation client for a region. Same purpose as {@link ec2Client}. */
12
+ cfnClient?: (region: string) => CloudFormationClient;
4
13
  }
5
14
  /**
6
15
  * Local-state pin (parity with the Hetzner driver's shared-box support): a
@@ -14,6 +23,8 @@ export declare class AwsDriver implements CloudDriver {
14
23
  readonly name: 'aws';
15
24
  readonly usesCloudFormation = true;
16
25
  private region;
26
+ private readonly ec2Client;
27
+ private readonly cfnClient;
17
28
  constructor(options?: AwsDriverOptions);
18
29
  private resolveRegion;
19
30
  /**
@@ -27,7 +38,14 @@ export declare class AwsDriver implements CloudDriver {
27
38
  * `compute.server.iamInstanceProfile` (live-verified step).
28
39
  */
29
40
  provisionComputeInfrastructure(options: ProvisionComputeOptions): Promise<ComputeStackOutputs>;
30
- /** Terminate the lightweight EC2 box + delete its security group. */
41
+ /**
42
+ * Terminate the lightweight EC2 box and delete the security group ts-cloud
43
+ * created for it.
44
+ *
45
+ * Only this project's own resources are removed: the group must sit in the
46
+ * VPC the instances ran in, and must not be tagged for another project. A
47
+ * group ts-cloud found rather than created is left where it is.
48
+ */
31
49
  destroyCompute(options: ProvisionComputeOptions): Promise<{
32
50
  destroyed: string[];
33
51
  }>;
@@ -39,3 +39,49 @@ export declare function encodeUserData(userData: string): string;
39
39
  * or `null` to signal "resolve Ubuntu via SSM".
40
40
  */
41
41
  export declare function resolveAwsImageId(config: CloudConfig): string | null;
42
+ /** The security group name ts-cloud gives a project's app box. */
43
+ export declare function awsSecurityGroupName(slug: string, environment: string): string;
44
+ /**
45
+ * Tags ts-cloud puts on every AWS resource it creates for a project — the same
46
+ * vocabulary the instances are tagged with, so teardown can tell what it owns
47
+ * from what it merely found.
48
+ */
49
+ export declare function awsResourceTags(slug: string, environment: string, role?: string): {
50
+ Key: string;
51
+ Value: string;
52
+ }[];
53
+ /** Whether a resource's tags mark it as belonging to this project and environment. */
54
+ export declare function matchesAwsProjectTags(tags: {
55
+ Key?: string;
56
+ Value?: string;
57
+ }[] | undefined, slug: string, environment: string): boolean;
58
+ /** A security group as teardown needs to judge it. */
59
+ export interface AwsSecurityGroupCandidate {
60
+ GroupId?: string;
61
+ GroupName?: string;
62
+ VpcId?: string;
63
+ Tags?: {
64
+ Key?: string;
65
+ Value?: string;
66
+ }[];
67
+ }
68
+ export interface SelectSecurityGroupOptions {
69
+ slug: string;
70
+ environment: string;
71
+ /** VPC the project's instances were in, when it could still be read. */
72
+ vpcId?: string;
73
+ }
74
+ /**
75
+ * Pick the security group teardown should delete: the one provisioning would
76
+ * have reused.
77
+ *
78
+ * Provisioning scopes its lookup to a VPC, because a same-named group in
79
+ * another VPC belongs to something else — teardown has to be at least as
80
+ * careful, or it deletes another VPC's group (or fails forever against one
81
+ * still in use, reporting nothing).
82
+ *
83
+ * A group tagged for a different project or environment is never a candidate.
84
+ * Groups created before ts-cloud tagged them carry no tags at all; those are
85
+ * still deleted, but only when the VPC pins them to this project's box.
86
+ */
87
+ export declare function selectProjectSecurityGroup(groups: AwsSecurityGroupCandidate[], options: SelectSecurityGroupOptions): AwsSecurityGroupCandidate | undefined;
@@ -292,6 +292,11 @@ export declare class HetznerClient {
292
292
  }>): Promise<HetznerAction[]>;
293
293
  listSshKeys(): Promise<HetznerSshKey[]>;
294
294
  createSshKey(options: CreateSshKeyOptions): Promise<HetznerSshKey>;
295
+ /**
296
+ * Delete an SSH key. Running servers are unaffected — the key is only read
297
+ * when a server is created, and lives in the box's authorized_keys after.
298
+ */
299
+ deleteSshKey(id: number): Promise<void>;
295
300
  getAction(actionId: number): Promise<HetznerAction>;
296
301
  shutdownServer(serverId: number): Promise<HetznerAction>;
297
302
  powerOnServer(serverId: number): Promise<HetznerAction>;
@@ -81,8 +81,12 @@ export declare class HetznerDriver implements CloudDriver {
81
81
  private provisionBunFleet;
82
82
  /**
83
83
  * Tear down the compute — single server or full fleet (load balancer, all
84
- * app + services servers, firewalls, and the private network) and clear
85
- * local state.
84
+ * app + services servers, firewalls, the private network, and the SSH key
85
+ * this project created) — and clear local state.
86
+ *
87
+ * Only resources ts-cloud created for this project and environment are
88
+ * removed; anything it merely reused (an SSH key already on the account, say)
89
+ * is left where it is.
86
90
  */
87
91
  destroyCompute(options: ProvisionComputeOptions): Promise<{
88
92
  destroyed: string[];
@@ -8,3 +8,13 @@ export declare function resolveHetznerServerType(size?: string): string;
8
8
  export declare const TS_CLOUD_LABEL_PREFIX = "ts-cloud";
9
9
  export declare function tsCloudLabels(slug: string, environment: string, role?: string): Record<string, string>;
10
10
  export declare function matchesTsCloudLabels(labels: Record<string, string> | undefined, slug: string, environment: string, role?: string): boolean;
11
+ /**
12
+ * Whether a resource belongs to this project and environment, whatever role it
13
+ * plays. Use this for resources that are not per-role — an SSH key is created
14
+ * once and used by app, services, and load balancer boxes alike.
15
+ *
16
+ * A resource ts-cloud merely *reused* (an SSH key already on the account under
17
+ * someone else's name, say) carries no ts-cloud labels, so it never matches —
18
+ * which is what keeps teardown from deleting something it did not create.
19
+ */
20
+ export declare function matchesTsCloudProject(labels: Record<string, string> | undefined, slug: string, environment: string): boolean;
@@ -68,14 +68,14 @@ import {
68
68
  waitForSsh,
69
69
  wrapCloudInitUserData,
70
70
  writeResizeCheckpoint
71
- } from "../chunk-nm1d3a75.js";
71
+ } from "../chunk-zcjc5y9f.js";
72
72
  import"../chunk-7m60qnc8.js";
73
73
  import"../chunk-4cjrg98a.js";
74
74
  import"../chunk-rds0sy87.js";
75
75
  import"../chunk-zqtpg06c.js";
76
76
  import"../chunk-va2yd85b.js";
77
77
  import"../chunk-aa2dkn7b.js";
78
- import"../chunk-t2s3gdq6.js";
78
+ import"../chunk-xes5vst2.js";
79
79
  import"../chunk-v0bahtg2.js";
80
80
  export {
81
81
  writeResizeCheckpoint,
@@ -74,6 +74,13 @@ export interface BuildSiteDeployScriptOptions {
74
74
  * @default 5
75
75
  */
76
76
  healthGateSeconds?: number;
77
+ /**
78
+ * systemd `MemoryHigh` for the app unit. See {@link SiteConfig.memoryHigh}.
79
+ * @default '2G'
80
+ */
81
+ memoryHigh?: string;
82
+ /** systemd `MemoryMax` for the app unit. Unset by default. */
83
+ memoryMax?: string;
77
84
  }
78
85
  /**
79
86
  * Build the remote shell commands that install/refresh a server-app site on a
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Run an SFTP server on a box.
3
+ *
4
+ * AWS deploys get Transfer Family, which is a managed service with no box to
5
+ * put anything on. Every other driver — Hetzner, local — provisions
6
+ * [ts-sftp](https://github.com/stacksjs/ts-sftp) as a systemd unit instead, so
7
+ * the same `infrastructure.sftp` config produces a working SFTP endpoint
8
+ * whichever provider is behind it.
9
+ *
10
+ * Storage is a directory on the server. Bucket-backed storage is an AWS-only
11
+ * option (see {@link assertSftpSupported}).
12
+ */
13
+ import type { SftpConfig } from '@ts-cloud/core';
14
+ /** Where the server's own files live on the box. */
15
+ export declare const SFTP_CONFIG_DIR = "/etc/ts-sftp";
16
+ /** Where ts-sftp itself is installed. */
17
+ export declare const SFTP_INSTALL_DIR = "/opt/ts-sftp";
18
+ /** Bun interpreter the service runs, copied out of /root when it lives there. */
19
+ export declare const SFTP_BUN_BIN = "/usr/local/bin/ts-sftp-bun";
20
+ /** Default port. 2222 rather than 22, which sshd already owns. */
21
+ export declare const DEFAULT_SFTP_PORT = 2222;
22
+ export interface SftpProvisionOptions {
23
+ slug: string;
24
+ sftp: SftpConfig;
25
+ }
26
+ /** The systemd unit name for a project's SFTP server. */
27
+ export declare function sftpUnitName(slug: string): string;
28
+ /** The port the box serves SFTP on. */
29
+ export declare function sftpPort(sftp: SftpConfig): number;
30
+ /** The directory served to users. */
31
+ export declare function sftpRoot(options: SftpProvisionOptions): string;
32
+ /**
33
+ * Check that an SFTP config can be delivered by the given provider, and explain
34
+ * the gap when it cannot. Bucket-backed storage needs S3 behind it, which only
35
+ * the AWS path provides.
36
+ */
37
+ export declare function assertSftpSupported(sftp: SftpConfig, provider: string): void;
38
+ /**
39
+ * Build the commands that install and run ts-sftp on the box. Idempotent: the
40
+ * host key is generated once and kept, so a redeploy does not change the
41
+ * fingerprint clients have pinned.
42
+ */
43
+ export declare function buildSftpProvisionScript(options: SftpProvisionOptions): string[];
44
+ /** Ports the SFTP server needs open, for the firewall builders. */
45
+ export declare function sftpFirewallPorts(sftp: SftpConfig | undefined): number[];
@@ -0,0 +1 @@
1
+ export {};
@@ -27,6 +27,16 @@ export declare class InfrastructureGenerator {
27
27
  private resolveApiOriginPort;
28
28
  private defaultComputeCachePathPatterns;
29
29
  private shouldRouteStorageBucketToCompute;
30
+ /** Physical name of the bucket generated for an `infrastructure.storage` entry. */
31
+ private storageBucketName;
32
+ /** Logical ID of the EFS file system generated for an `infrastructure.fileSystem` entry. */
33
+ private fileSystemLogicalId;
34
+ /**
35
+ * Resolve a `storageBucket`/`fileSystem` name reference in `sftp.storage` to the
36
+ * resource this stack generates for it. Returns undefined when the storage config
37
+ * already names a concrete backend, which the Sftp module handles on its own.
38
+ */
39
+ private resolveSftpStorage;
30
40
  private resolveComputeCachePathPatterns;
31
41
  private createComputeCacheBehavior;
32
42
  private appendComputeAppOrigin;
package/dist/index.js CHANGED
@@ -208,7 +208,7 @@ import {
208
208
  verifyUpdateSignature,
209
209
  webhookEndpoint,
210
210
  zeroCapacity
211
- } from "./chunk-8xezrv4z.js";
211
+ } from "./chunk-0cx84wm2.js";
212
212
  import {
213
213
  deleteStaticSite,
214
214
  deployStaticSite,
@@ -222,7 +222,7 @@ import {
222
222
  } from "./chunk-v0vpamnf.js";
223
223
  import {
224
224
  buildAndPushServerlessImage
225
- } from "./chunk-wmbep3b5.js";
225
+ } from "./chunk-b801ga2t.js";
226
226
  import {
227
227
  ApplicationAutoScalingClient,
228
228
  BedrockClient,
@@ -341,7 +341,7 @@ import {
341
341
  waitForCloudInit,
342
342
  waitForSsh,
343
343
  wrapCloudInitUserData
344
- } from "./chunk-nm1d3a75.js";
344
+ } from "./chunk-zcjc5y9f.js";
345
345
  import {
346
346
  EC2Client,
347
347
  SSMClient
@@ -514,7 +514,7 @@ import {
514
514
  zeroFill,
515
515
  zoneOffsetMs,
516
516
  zonedParts
517
- } from "./chunk-19bs955w.js";
517
+ } from "./chunk-8ynjhsh3.js";
518
518
  import {
519
519
  DEFAULT_ATTACK_MODE_HOURS,
520
520
  MAX_CONTROL_HOURS,
@@ -978,7 +978,7 @@ import {
978
978
  withSecurity,
979
979
  withTimeout,
980
980
  xrayManager
981
- } from "./chunk-t2s3gdq6.js";
981
+ } from "./chunk-xes5vst2.js";
982
982
  import {
983
983
  __require
984
984
  } from "./chunk-v0bahtg2.js";
@@ -1216,6 +1216,32 @@ class InfrastructureGenerator {
1216
1216
  shouldRouteStorageBucketToCompute(name, storageConfig) {
1217
1217
  return name === "public" || storageConfig.routeCompute === true;
1218
1218
  }
1219
+ storageBucketName(slug, env, name) {
1220
+ return this.mergedConfig.infrastructure?.storage?.[name]?.bucket ?? `${slug}-${env}-${name}`;
1221
+ }
1222
+ fileSystemLogicalId(slug, env, name) {
1223
+ return generateLogicalId(generateResourceName({ slug: `${slug}-${name}`, environment: env, resourceType: "efs" }));
1224
+ }
1225
+ resolveSftpStorage(sftp, slug, env) {
1226
+ const storage = sftp.storage;
1227
+ if (storage?.type === "efs" && storage.fileSystem) {
1228
+ if (!this.mergedConfig.infrastructure?.fileSystem?.[storage.fileSystem]) {
1229
+ throw new Error(`sftp: storage.fileSystem "${storage.fileSystem}" is not defined in infrastructure.fileSystem`);
1230
+ }
1231
+ return {
1232
+ type: "efs",
1233
+ fileSystemId: { Ref: this.fileSystemLogicalId(slug, env, storage.fileSystem) },
1234
+ posixProfile: storage.posixProfile
1235
+ };
1236
+ }
1237
+ if (storage?.type === "s3" && storage.storageBucket) {
1238
+ if (!this.mergedConfig.infrastructure?.storage?.[storage.storageBucket]) {
1239
+ throw new Error(`sftp: storage.storageBucket "${storage.storageBucket}" is not defined in infrastructure.storage`);
1240
+ }
1241
+ return { type: "s3", bucket: this.storageBucketName(slug, env, storage.storageBucket) };
1242
+ }
1243
+ return;
1244
+ }
1219
1245
  resolveComputeCachePathPatterns(routes) {
1220
1246
  if (routes && routes.length > 0) {
1221
1247
  return routes;
@@ -2216,7 +2242,7 @@ ${handles.join(`
2216
2242
  })).filter((bucket) => bucket.name !== "public" && bucket.config.website && bucket.mountPath);
2217
2243
  for (const [name, storageConfig] of Object.entries(this.mergedConfig.infrastructure.storage)) {
2218
2244
  const serveViaCloudFront = !!(storageConfig.website && sharedOacLogicalId);
2219
- const physicalBucketName = storageConfig.bucket ?? `${slug}-${env}-${name}`;
2245
+ const physicalBucketName = this.storageBucketName(slug, env, name);
2220
2246
  const { bucket, logicalId } = Storage.createBucket({
2221
2247
  slug,
2222
2248
  name,
@@ -2501,8 +2527,10 @@ else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`
2501
2527
  }
2502
2528
  }
2503
2529
  if (this.mergedConfig.infrastructure?.sftp) {
2530
+ const sftpConfig = this.mergedConfig.infrastructure.sftp;
2504
2531
  const result = Sftp.create({
2505
- ...this.mergedConfig.infrastructure.sftp,
2532
+ ...sftpConfig,
2533
+ storage: this.resolveSftpStorage(sftpConfig, slug, env) ?? sftpConfig.storage,
2506
2534
  slug,
2507
2535
  environment: env
2508
2536
  });
@@ -2516,6 +2544,10 @@ else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`
2516
2544
  Value: { "Fn::Sub": `\${${result.serverLogicalId}}.server.transfer.\${AWS::Region}.amazonaws.com` },
2517
2545
  Description: "AWS Transfer Family SFTP endpoint"
2518
2546
  });
2547
+ this.builder.addOutput("SftpStorageDomain", {
2548
+ Value: result.domain,
2549
+ Description: "Storage backing the SFTP server (EFS or S3)"
2550
+ });
2519
2551
  }
2520
2552
  if (hostedZoneId && websiteBucketDistributions.length > 0) {
2521
2553
  for (const { name, distLogicalId, aliases } of websiteBucketDistributions) {
@@ -44,7 +44,7 @@ import {
44
44
  parseChain,
45
45
  propagationHeaders,
46
46
  recursionBlockedResponse
47
- } from "../chunk-t2s3gdq6.js";
47
+ } from "../chunk-xes5vst2.js";
48
48
  import"../chunk-v0bahtg2.js";
49
49
  export {
50
50
  wafEventTelemetry,
@@ -82,9 +82,9 @@ import {
82
82
  zeroFill,
83
83
  zoneOffsetMs,
84
84
  zonedParts
85
- } from "../chunk-19bs955w.js";
85
+ } from "../chunk-8ynjhsh3.js";
86
86
  import"../chunk-aa2dkn7b.js";
87
- import"../chunk-t2s3gdq6.js";
87
+ import"../chunk-xes5vst2.js";
88
88
  import"../chunk-v0bahtg2.js";
89
89
  export {
90
90
  zonedParts,