@stacksjs/ts-cloud 0.2.23 → 0.2.24

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.
@@ -2,10 +2,10 @@ import type { CloudConfig, SiteConfig, SiteDeployTarget } from '@ts-cloud/core';
2
2
  /**
3
3
  * The three resolved deployment kinds for a site:
4
4
  * - `'bucket'` — upload built `root` to object storage + CDN.
5
- * - `'server-app'` — `server` + `start`: dynamic app as a systemd service
6
- * behind the Caddy reverse proxy.
5
+ * - `'server-app'` — `server` + `start`: dynamic app as a systemd service.
7
6
  * - `'server-static'` — `server` + no `start` (has static `root`): a static
8
- * site built and served on the box via Caddy `file_server`.
7
+ * site built and shipped to `/var/www/<site>` on the box
8
+ * (served by the operator's own proxy, e.g. rpx + tlsx).
9
9
  */
10
10
  export type SiteDeployKind = 'bucket' | 'server-app' | 'server-static';
11
11
  /**
@@ -67,8 +67,9 @@ export declare class HetznerDriver implements CloudDriver {
67
67
  */
68
68
  private ensureFirewall;
69
69
  /**
70
- * Collect the upstream app ports that must be reachable when no reverse proxy
71
- * is fronting traffic (raw-port deploys). Drops 80/443 (handled separately).
70
+ * Collect the upstream app ports that must be reachable on the box. ts-cloud
71
+ * does not front traffic with its own proxy, so each site's app port is
72
+ * opened directly. Drops 80/443 (always handled by the firewall base rules).
72
73
  */
73
74
  private collectUpstreamPorts;
74
75
  private sleep;
@@ -81,8 +82,8 @@ export declare class HetznerDriver implements CloudDriver {
81
82
  private waitForSshReady;
82
83
  /**
83
84
  * Block until cloud-init finishes (`cloud-init status --wait`). cloud-init is
84
- * what installs the runtime + Caddy; deploying before it completes leaves the
85
- * release pointing at a half-provisioned box (missing `bun`, no Caddy).
85
+ * what installs the runtime; deploying before it completes leaves the
86
+ * release pointing at a half-provisioned box (missing `bun`).
86
87
  */
87
88
  private waitForCloudInit;
88
89
  private outputsFromState;
@@ -3,6 +3,5 @@ export { AwsDriver } from './aws/driver';
3
3
  export { HetznerDriver } from './hetzner/driver';
4
4
  export { HetznerClient, resolveHetznerApiToken } from './hetzner/client';
5
5
  export { generateUbuntuAppCloudInit, wrapCloudInitUserData } from './hetzner/cloud-init';
6
- export { buildCaddyfile, buildCaddyfileFromProxy, isOnDemandDomain, proxyConfigFromSites, resolveCaddyfile, staticSiteServerRoot, } from './shared/caddyfile';
7
6
  export { buildAwsArtifactFetch, buildLocalArtifactFetch, buildSiteDeployScript, buildStaticSiteDeployScript, resolveExecStart, } from './shared/deploy-script';
8
7
  export { deployAllComputeSites, deploySiteRelease } from './shared/compute-deploy';
@@ -22,6 +22,6 @@ export interface DeployAllSitesOptions {
22
22
  /**
23
23
  * Deploy every site that targets the compute server — both dynamic apps
24
24
  * (`server` + `start`, run as systemd services) and static sites (`server`
25
- * without `start`, served by Caddy `file_server`). Bucket sites are skipped.
25
+ * without `start`, shipped to `/var/www/<site>`). Bucket sites are skipped.
26
26
  */
27
27
  export declare function deployAllComputeSites(options: DeployAllSitesOptions): Promise<boolean>;
@@ -31,7 +31,7 @@ export interface BuildStaticSiteDeployScriptOptions {
31
31
  siteName: string;
32
32
  /** How the remote host obtains the release tarball */
33
33
  artifactFetch: string[];
34
- /** Target directory served by Caddy `file_server`. Default `/var/www/<site>`. */
34
+ /** Target directory the static site is shipped to. Default `/var/www/<site>`. */
35
35
  appDir?: string;
36
36
  /**
37
37
  * Commands run inside `appDir` after extraction — e.g. build the docs/blog on
@@ -43,8 +43,8 @@ export interface BuildStaticSiteDeployScriptOptions {
43
43
  /**
44
44
  * Build the remote shell commands that install/refresh a STATIC site on a
45
45
  * compute target. Unlike {@link buildSiteDeployScript}, there is no systemd
46
- * service — the extracted files are served directly by Caddy `file_server`
47
- * (Caddy is reloaded separately when the Caddyfile changes).
46
+ * service — the extracted files are shipped to `/var/www/<site>` and served by
47
+ * the operator's own proxy (e.g. rpx + tlsx), which ts-cloud does not manage.
48
48
  */
49
49
  export declare function buildStaticSiteDeployScript(options: BuildStaticSiteDeployScriptOptions): string[];
50
50
  export declare function buildAwsArtifactFetch(bucket: string, key: string, region: string, siteName: string): string[];
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export type { MigrateEndpoint, MigrateError, MigrateOptions, MigratePlanItem, Mi
11
11
  export * from './ssl';
12
12
  export { deployStaticSite, deployStaticSiteFull, uploadStaticFiles, invalidateCache, deleteStaticSite, generateStaticSiteTemplate, deployStaticSiteWithExternalDns, deployStaticSiteWithExternalDnsFull, generateExternalDnsStaticSiteTemplate, deploySite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, } from './deploy';
13
13
  export type { StaticSiteConfig, DeployResult, UploadOptions, ExternalDnsStaticSiteConfig, ExternalDnsDeployResult, DeploySiteConfig, DeploySiteResult, StaticSiteDnsProvider, SiteDeployKind, DeploymentValidationResult, } from './deploy';
14
- export { createCloudDriver, CloudDriverFactory, cloudDrivers, AwsDriver, HetznerDriver, HetznerClient, resolveHetznerApiToken, generateUbuntuAppCloudInit, wrapCloudInitUserData, buildCaddyfile, buildCaddyfileFromProxy, isOnDemandDomain, proxyConfigFromSites, resolveCaddyfile, staticSiteServerRoot, buildSiteDeployScript, buildStaticSiteDeployScript, resolveExecStart, deployAllComputeSites, deploySiteRelease, } from './drivers';
14
+ export { createCloudDriver, CloudDriverFactory, cloudDrivers, AwsDriver, HetznerDriver, HetznerClient, resolveHetznerApiToken, generateUbuntuAppCloudInit, wrapCloudInitUserData, buildSiteDeployScript, buildStaticSiteDeployScript, resolveExecStart, deployAllComputeSites, deploySiteRelease, } from './drivers';
15
15
  export type { CreateCloudDriverOptions } from './drivers/factory';
16
16
  export { createDnsProvider, detectDnsProvider, DnsProviderFactory, dnsProviders, PorkbunProvider, GoDaddyProvider, Route53Provider, UnifiedDnsValidator, createPorkbunValidator, createGoDaddyValidator, createRoute53Validator, } from './dns';
17
17
  export type { DnsProvider, DnsProviderConfig, DnsRecord, DnsRecordType, DnsRecordResult, CreateRecordResult, DeleteRecordResult, ListRecordsResult, } from './dns';
package/dist/index.js CHANGED
@@ -81449,204 +81449,6 @@ import { homedir as homedir7 } from "node:os";
81449
81449
  import { join as join13 } from "node:path";
81450
81450
  import { execSync } from "node:child_process";
81451
81451
 
81452
- // src/drivers/shared/caddyfile.ts
81453
- function isOnDemandDomain(domain) {
81454
- return domain === "*" || domain.includes("*");
81455
- }
81456
- function isStaticApp(app) {
81457
- return typeof app.root === "string" && app.root.length > 0;
81458
- }
81459
- function isProxyApp(app) {
81460
- return typeof app.port === "number";
81461
- }
81462
- function staticSiteServerRoot(name) {
81463
- return `/var/www/${name}`;
81464
- }
81465
- function normalizeOnDemandTls(onDemandTls) {
81466
- if (!onDemandTls)
81467
- return;
81468
- if (onDemandTls === true)
81469
- return {};
81470
- return onDemandTls;
81471
- }
81472
- function wrapInHandle(body, path, indent) {
81473
- const inner = body.split(`
81474
- `).map((line) => ` ${line}`).join(`
81475
- `);
81476
- return `${indent}handle ${path} {
81477
- ${inner}
81478
- ${indent}}`;
81479
- }
81480
- function renderUpstreamBlock(app, indent) {
81481
- const host = app.upstreamHost || "localhost";
81482
- const upstream = `${host}:${app.port}`;
81483
- const directives = app.reverseProxyDirectives ?? [];
81484
- const proxyLine = directives.length === 0 ? `${indent}reverse_proxy ${upstream}` : [
81485
- `${indent}reverse_proxy ${upstream} {`,
81486
- ...directives.map((d) => `${indent} ${d}`),
81487
- `${indent}}`
81488
- ].join(`
81489
- `);
81490
- const isCatchAll = !app.path || app.path === "/";
81491
- if (isCatchAll)
81492
- return proxyLine;
81493
- return wrapInHandle(proxyLine, app.path, indent);
81494
- }
81495
- function renderStaticBlock(app, indent) {
81496
- const root = app.root;
81497
- const lines = [`${indent}root * ${root}`];
81498
- if (app.cache?.enabled) {
81499
- const maxAge = app.cache.maxAge ?? 3600;
81500
- lines.push(`${indent}header Cache-Control "public, max-age=${maxAge}"`);
81501
- }
81502
- if (app.spa) {
81503
- lines.push(`${indent}try_files {path} /index.html`);
81504
- } else if (app.pathRewriteStyle === "flat") {
81505
- lines.push(`${indent}try_files {path} {path}.html {path}/index.html`);
81506
- } else {
81507
- lines.push(`${indent}try_files {path} {path}/index.html {path}.html`);
81508
- }
81509
- lines.push(`${indent}file_server`);
81510
- const body = lines.join(`
81511
- `);
81512
- const isCatchAll = !app.path || app.path === "/";
81513
- if (isCatchAll)
81514
- return body;
81515
- return wrapInHandle(body, app.path, indent);
81516
- }
81517
- function renderAppBlock(app, indent) {
81518
- return isStaticApp(app) ? renderStaticBlock(app, indent) : renderUpstreamBlock(app, indent);
81519
- }
81520
- function groupAppsByDomains(apps) {
81521
- const groups = new Map;
81522
- for (const app of apps) {
81523
- const domains = [...app.domains].filter(Boolean);
81524
- if (domains.length === 0)
81525
- continue;
81526
- const key = [...domains].sort().join(" ");
81527
- const group = groups.get(key) ?? { domains, apps: [] };
81528
- group.apps.push(app);
81529
- groups.set(key, group);
81530
- }
81531
- return [...groups.values()];
81532
- }
81533
- function sortAppsByPath(apps) {
81534
- return [...apps].sort((a, b) => {
81535
- const aCatchAll = !a.path || a.path === "/";
81536
- const bCatchAll = !b.path || b.path === "/";
81537
- if (aCatchAll && !bCatchAll)
81538
- return 1;
81539
- if (!aCatchAll && bCatchAll)
81540
- return -1;
81541
- return (b.path?.length ?? 0) - (a.path?.length ?? 0);
81542
- });
81543
- }
81544
- function buildCaddyfileFromProxy(proxy) {
81545
- if (proxy.raw && proxy.raw.trim())
81546
- return proxy.raw.trim();
81547
- const apps = (proxy.apps ?? []).filter((app) => app.domains.length > 0 && (isProxyApp(app) || isStaticApp(app)));
81548
- if (apps.length === 0)
81549
- return;
81550
- const onDemand = normalizeOnDemandTls(proxy.onDemandTls);
81551
- const hasOnDemandDomain = apps.some((app) => app.domains.some(isOnDemandDomain));
81552
- const globalLines = [];
81553
- if (proxy.email)
81554
- globalLines.push(`email ${proxy.email}`);
81555
- if (proxy.staging)
81556
- globalLines.push("acme_ca https://acme-staging-v02.api.letsencrypt.org/directory");
81557
- if (onDemand) {
81558
- if (onDemand.ask || onDemand.interval || onDemand.burst != null) {
81559
- const inner = [];
81560
- if (onDemand.ask)
81561
- inner.push(` ask ${onDemand.ask}`);
81562
- if (onDemand.interval || onDemand.burst != null) {
81563
- const rl = [];
81564
- if (onDemand.interval)
81565
- rl.push(` interval ${onDemand.interval}`);
81566
- if (onDemand.burst != null)
81567
- rl.push(` burst ${onDemand.burst}`);
81568
- inner.push(` rate_limit {
81569
- ${rl.join(`
81570
- `)}
81571
- }`);
81572
- }
81573
- globalLines.push(`on_demand_tls {
81574
- ${inner.join(`
81575
- `)}
81576
- }`);
81577
- } else {
81578
- globalLines.push(`on_demand_tls {
81579
- }`);
81580
- }
81581
- }
81582
- for (const directive of proxy.globalDirectives ?? [])
81583
- globalLines.push(directive);
81584
- const blocks = [];
81585
- if (globalLines.length > 0)
81586
- blocks.push(`{
81587
- ${globalLines.map((line) => ` ${line}`).join(`
81588
- `)}
81589
- }`);
81590
- for (const group of groupAppsByDomains(apps)) {
81591
- const sorted = sortAppsByPath(group.apps);
81592
- const body = sorted.map((app) => renderAppBlock(app, " ")).join(`
81593
- `);
81594
- const needsOnDemand = onDemand && group.domains.some(isOnDemandDomain);
81595
- const tlsBlock = needsOnDemand ? `
81596
- tls {
81597
- on_demand
81598
- }` : "";
81599
- blocks.push(`${group.domains.join(", ")} {
81600
- ${body}${tlsBlock}
81601
- }`);
81602
- }
81603
- if (hasOnDemandDomain && !onDemand) {
81604
- blocks.unshift(`# WARNING: wildcard/catch-all domain present but on_demand_tls is not enabled.
81605
- ` + "# Caddy cannot provision TLS for these hosts. Set compute.proxy.onDemandTls.");
81606
- }
81607
- return blocks.join(`
81608
-
81609
- `);
81610
- }
81611
- function proxyConfigFromSites(sites) {
81612
- const apps = [];
81613
- for (const [name, site] of Object.entries(sites)) {
81614
- if (typeof site.domain !== "string" || !site.domain)
81615
- continue;
81616
- if (typeof site.port === "number" && site.deploy !== "bucket") {
81617
- apps.push({
81618
- name,
81619
- domains: [site.domain],
81620
- port: site.port,
81621
- path: site.path
81622
- });
81623
- } else if (resolveSiteKind(site) === "server-static") {
81624
- apps.push({
81625
- name,
81626
- domains: [site.domain],
81627
- root: staticSiteServerRoot(name),
81628
- path: site.path,
81629
- spa: site.spa,
81630
- pathRewriteStyle: site.pathRewriteStyle,
81631
- cache: site.cache
81632
- });
81633
- }
81634
- }
81635
- return { apps };
81636
- }
81637
- function resolveCaddyfile(sites, proxy) {
81638
- if (proxy) {
81639
- if (proxy.raw && proxy.raw.trim())
81640
- return proxy.raw.trim();
81641
- const resolved = proxy.apps && proxy.apps.length > 0 ? proxy : { ...proxy, apps: proxyConfigFromSites(sites).apps };
81642
- return buildCaddyfileFromProxy(resolved);
81643
- }
81644
- return buildCaddyfileFromProxy(proxyConfigFromSites(sites));
81645
- }
81646
- function buildCaddyfile(sites) {
81647
- return buildCaddyfileFromProxy(proxyConfigFromSites(sites));
81648
- }
81649
-
81650
81452
  // src/drivers/hetzner/client.ts
81651
81453
  var DEFAULT_API_URL = "https://api.hetzner.cloud/v1";
81652
81454
 
@@ -82049,14 +81851,12 @@ class HetznerDriver {
82049
81851
  return this.outputsFromState(rehydrated, alreadyRunning);
82050
81852
  }
82051
81853
  const sites = config6.sites || {};
82052
- const caddyfile = resolveCaddyfile(sites, compute.proxy);
82053
- const sitePorts = caddyfile ? [] : this.collectUpstreamPorts(sites, compute.proxy);
81854
+ const sitePorts = this.collectUpstreamPorts(sites);
82054
81855
  const bootstrap = generateUbuntuAppCloudInit({
82055
81856
  runtime: compute.runtime || "bun",
82056
81857
  runtimeVersion: compute.runtimeVersion || "latest",
82057
81858
  systemPackages: compute.systemPackages,
82058
- database: config6.infrastructure?.database,
82059
- caddyfile
81859
+ database: config6.infrastructure?.database
82060
81860
  });
82061
81861
  const userData = wrapCloudInitUserData(bootstrap);
82062
81862
  const serverType = resolveHetznerServerType(compute.size);
@@ -82222,12 +82022,8 @@ class HetznerDriver {
82222
82022
  const { firewall } = await this.client.createFirewall({ name, labels, rules });
82223
82023
  return { firewall };
82224
82024
  }
82225
- collectUpstreamPorts(sites, proxy) {
82025
+ collectUpstreamPorts(sites) {
82226
82026
  const ports = new Set;
82227
- for (const app of proxy?.apps ?? []) {
82228
- if (typeof app.port === "number")
82229
- ports.add(app.port);
82230
- }
82231
82027
  for (const site of Object.values(sites)) {
82232
82028
  if (typeof site.port === "number")
82233
82029
  ports.add(site.port);
@@ -82581,7 +82377,6 @@ export {
82581
82377
  suggestFlags,
82582
82378
  suggestCommand,
82583
82379
  storageAdvancedManager,
82584
- staticSiteServerRoot,
82585
82380
  staticSiteManager,
82586
82381
  stackDependencyManager,
82587
82382
  signRequestAsync,
@@ -82613,14 +82408,12 @@ export {
82613
82408
  resolveDeployBucketName,
82614
82409
  resolveCredentials,
82615
82410
  resolveCloudProvider,
82616
- resolveCaddyfile,
82617
82411
  requiresReplacement,
82618
82412
  replicaManager,
82619
82413
  remapKey,
82620
82414
  regionPairManager,
82621
82415
  quickHash,
82622
82416
  queueManagementManager,
82623
- proxyConfigFromSites,
82624
82417
  providerEndpoint,
82625
82418
  progressiveDeploymentManager,
82626
82419
  processInChunks,
@@ -82655,7 +82448,6 @@ export {
82655
82448
  keyMatchesFilters,
82656
82449
  isWebCryptoAvailable,
82657
82450
  isValidRegion,
82658
- isOnDemandDomain,
82659
82451
  isNodeCryptoAvailable,
82660
82452
  isLocalDevelopment,
82661
82453
  isLikelyTypo,
@@ -82808,8 +82600,6 @@ export {
82808
82600
  buildSiteDeployScript,
82809
82601
  buildOptimizationManager,
82810
82602
  buildCloudFormationTemplate,
82811
- buildCaddyfileFromProxy,
82812
- buildCaddyfile,
82813
82603
  bounceComplaintHandler,
82814
82604
  blueGreenManager,
82815
82605
  batchProcessingManager,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/ts-cloud",
3
3
  "type": "module",
4
- "version": "0.2.23",
4
+ "version": "0.2.24",
5
5
  "description": "A lightweight, performant infrastructure-as-code library and CLI for deploying both server-based (EC2) and serverless applications.",
6
6
  "author": "Chris Breuer <chris@stacksjs.com>",
7
7
  "license": "MIT",
@@ -89,8 +89,8 @@
89
89
  "test": "bun test"
90
90
  },
91
91
  "dependencies": {
92
- "@ts-cloud/aws-types": "0.2.23",
93
- "@ts-cloud/core": "0.2.23",
92
+ "@ts-cloud/aws-types": "0.2.24",
93
+ "@ts-cloud/core": "0.2.24",
94
94
  "@stacksjs/ts-xml": "^0.1.0"
95
95
  },
96
96
  "devDependencies": {
@@ -1,46 +0,0 @@
1
- import type { CaddyAppConfig, CaddyProxyConfig, SiteConfig } from '@ts-cloud/core';
2
- /** A domain is "on-demand" (needs lazy TLS) if it's a wildcard or bare catch-all. */
3
- export declare function isOnDemandDomain(domain: string): boolean;
4
- /**
5
- * The on-server install path a static site's `root` is shipped to. Mirrors the
6
- * release layout used by the systemd app deploy (`/var/www/<name>`), so a box
7
- * can host both proxied apps and file-served static sites side by side.
8
- */
9
- export declare function staticSiteServerRoot(name: string): string;
10
- /**
11
- * Build a complete Caddyfile from a typed {@link CaddyProxyConfig}.
12
- *
13
- * Produces:
14
- * - a global options block (ACME email, on-demand TLS `ask`, staging CA, extras);
15
- * - one site block per unique domain set, each performing host-based routing to
16
- * its upstream app(s);
17
- * - `tls { on_demand }` inside any block whose domains are wildcards/catch-all.
18
- *
19
- * Returns `undefined` when there's nothing to route (no apps, no raw).
20
- */
21
- export declare function buildCaddyfileFromProxy(proxy: CaddyProxyConfig): string | undefined;
22
- /**
23
- * Derive a {@link CaddyProxyConfig} from the legacy `sites` map: every site
24
- * that declares a `domain` + `port` becomes a Caddy app. Keeps single-app /
25
- * sites-driven deploys working without an explicit `compute.proxy` block.
26
- */
27
- export declare function proxyConfigFromSites(sites: Record<string, SiteConfig>): CaddyProxyConfig & {
28
- apps: CaddyAppConfig[];
29
- };
30
- /**
31
- * Resolve the final Caddyfile for a deploy. Prefers the typed `compute.proxy`
32
- * config (merging in `sites`-derived apps when `proxy.apps` is omitted), and
33
- * falls back to deriving everything from `sites`.
34
- *
35
- * Returns `undefined` when there's nothing to route.
36
- */
37
- export declare function resolveCaddyfile(sites: Record<string, SiteConfig>, proxy?: CaddyProxyConfig): string | undefined;
38
- /**
39
- * Build a Caddyfile from site configs. Sites sharing a domain are grouped;
40
- * explicit paths are ordered before catch-all routes.
41
- *
42
- * @deprecated Prefer {@link resolveCaddyfile} / {@link buildCaddyfileFromProxy},
43
- * which support multi-app host routing and on-demand TLS. Retained for
44
- * backward compatibility.
45
- */
46
- export declare function buildCaddyfile(sites: Record<string, SiteConfig>): string | undefined;