@stacksjs/ts-cloud 0.2.22 → 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
@@ -6,10 +6,12 @@ export { AWSClient, CloudFormationClient, CloudFormationClient as AWSCloudFormat
6
6
  export type { AWSRequestOptions, AWSClientConfig, AWSError, AWSCredentials as AWSClientCredentials, StackParameter, StackTag, CreateStackOptions, UpdateStackOptions, DescribeStacksOptions, StackEvent, Stack, InvalidationOptions, Distribution, S3SyncOptions, S3CopyOptions, S3ListOptions, S3Object, CertificateDetail, Certificate as ELBv2Certificate, RekognitionS3Object, RekognitionBoundingBox, TextractS3Object, TextractBoundingBox, CountryCode, ContactType, ContactDetail, KendraCreateDataSourceCommandInput, KendraCreateDataSourceCommandOutput, KendraListDataSourcesCommandInput, KendraListDataSourcesCommandOutput, InvokeModelCommandInput, InvokeModelCommandOutput, InvokeModelWithResponseStreamCommandInput, InvokeModelWithResponseStreamCommandOutput, CreateModelCustomizationJobCommandInput, CreateModelCustomizationJobCommandOutput, GetModelCustomizationJobCommandInput, GetModelCustomizationJobCommandOutput, ListFoundationModelsCommandInput, ListFoundationModelsCommandOutput, AttributeValue as DynamoDBAttributeValue, KeySchemaElement, AttributeDefinition as DynamoDBAttributeDefinition, } from './aws';
7
7
  export { createObjectStorageClient, providerEndpoint, resolveObjectStorage, } from './object-storage';
8
8
  export type { ObjectStorageConfig, ObjectStorageCredentials, ObjectStorageProvider, ResolvedObjectStorage, } from './object-storage';
9
+ export { keyMatchesFilters, migrateObjectStorage, remapKey, } from './object-storage/migrate';
10
+ export type { MigrateEndpoint, MigrateError, MigrateOptions, MigratePlanItem, MigrateProgress, MigrateResult, MigrateVerification, } from './object-storage/migrate';
9
11
  export * from './ssl';
10
12
  export { deployStaticSite, deployStaticSiteFull, uploadStaticFiles, invalidateCache, deleteStaticSite, generateStaticSiteTemplate, deployStaticSiteWithExternalDns, deployStaticSiteWithExternalDnsFull, generateExternalDnsStaticSiteTemplate, deploySite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, } from './deploy';
11
13
  export type { StaticSiteConfig, DeployResult, UploadOptions, ExternalDnsStaticSiteConfig, ExternalDnsDeployResult, DeploySiteConfig, DeploySiteResult, StaticSiteDnsProvider, SiteDeployKind, DeploymentValidationResult, } from './deploy';
12
- 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';
13
15
  export type { CreateCloudDriverOptions } from './drivers/factory';
14
16
  export { createDnsProvider, detectDnsProvider, DnsProviderFactory, dnsProviders, PorkbunProvider, GoDaddyProvider, Route53Provider, UnifiedDnsValidator, createPorkbunValidator, createGoDaddyValidator, createRoute53Validator, } from './dns';
15
17
  export type { DnsProvider, DnsProviderConfig, DnsRecord, DnsRecordType, DnsRecordResult, CreateRecordResult, DeleteRecordResult, ListRecordsResult, } from './dns';
package/dist/index.js CHANGED
@@ -2123,6 +2123,67 @@ class S3Client2 {
2123
2123
  });
2124
2124
  return result;
2125
2125
  }
2126
+ async getObjectBytes(bucket, key) {
2127
+ const { accessKeyId, secretAccessKey, sessionToken } = this.getCredentials();
2128
+ const host = this.s3VirtualHost(bucket);
2129
+ const encodedKey = key.split("/").map((seg) => encodeURIComponent(seg)).join("/");
2130
+ const canonicalUri = this.forcePathStyle ? `/${bucket}/${encodedKey}` : `/${encodedKey}`;
2131
+ const url = `https://${host}${canonicalUri}`;
2132
+ const now = new Date;
2133
+ const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, "");
2134
+ const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, "");
2135
+ const payloadHash = crypto3.createHash("sha256").update("").digest("hex");
2136
+ const requestHeaders = {
2137
+ host,
2138
+ "x-amz-date": amzDate,
2139
+ "x-amz-content-sha256": payloadHash
2140
+ };
2141
+ if (sessionToken) {
2142
+ requestHeaders["x-amz-security-token"] = sessionToken;
2143
+ }
2144
+ const canonicalHeaders = Object.keys(requestHeaders).sort().map((k) => `${k.toLowerCase()}:${requestHeaders[k].trim()}
2145
+ `).join("");
2146
+ const signedHeaders = Object.keys(requestHeaders).sort().map((k) => k.toLowerCase()).join(";");
2147
+ const canonicalRequest = [
2148
+ "GET",
2149
+ canonicalUri,
2150
+ "",
2151
+ canonicalHeaders,
2152
+ signedHeaders,
2153
+ payloadHash
2154
+ ].join(`
2155
+ `);
2156
+ const algorithm = "AWS4-HMAC-SHA256";
2157
+ const credentialScope = `${dateStamp}/${this.region}/s3/aws4_request`;
2158
+ const stringToSign = [
2159
+ algorithm,
2160
+ amzDate,
2161
+ credentialScope,
2162
+ crypto3.createHash("sha256").update(canonicalRequest).digest("hex")
2163
+ ].join(`
2164
+ `);
2165
+ const kDate = crypto3.createHmac("sha256", `AWS4${secretAccessKey}`).update(dateStamp).digest();
2166
+ const kRegion = crypto3.createHmac("sha256", kDate).update(this.region).digest();
2167
+ const kService = crypto3.createHmac("sha256", kRegion).update("s3").digest();
2168
+ const kSigning = crypto3.createHmac("sha256", kService).update("aws4_request").digest();
2169
+ const signature = crypto3.createHmac("sha256", kSigning).update(stringToSign).digest("hex");
2170
+ const authorizationHeader = `${algorithm} Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
2171
+ const response = await fetch(url, {
2172
+ method: "GET",
2173
+ headers: { ...requestHeaders, Authorization: authorizationHeader }
2174
+ });
2175
+ if (!response.ok) {
2176
+ const errorText = await response.text();
2177
+ throw new Error(`S3 GET failed: ${response.status} ${errorText}`);
2178
+ }
2179
+ const buffer = await response.arrayBuffer();
2180
+ const contentLengthHeader = response.headers.get("content-length");
2181
+ return {
2182
+ body: new Uint8Array(buffer),
2183
+ contentType: response.headers.get("content-type") ?? undefined,
2184
+ contentLength: contentLengthHeader ? Number.parseInt(contentLengthHeader, 10) : undefined
2185
+ };
2186
+ }
2126
2187
  async copyObject(options) {
2127
2188
  const headers = {
2128
2189
  "x-amz-copy-source": `/${options.sourceBucket}/${options.sourceKey}`
@@ -65570,6 +65631,165 @@ init_client();
65570
65631
 
65571
65632
  // src/object-storage/index.ts
65572
65633
  init_s3();
65634
+
65635
+ // src/object-storage/migrate.ts
65636
+ function stripPrefix(key, prefix) {
65637
+ if (!prefix)
65638
+ return key;
65639
+ return key.startsWith(prefix) ? key.slice(prefix.length) : key;
65640
+ }
65641
+ function remapKey(sourceKey, fromPrefix, toPrefix) {
65642
+ const stripped = stripPrefix(sourceKey, fromPrefix);
65643
+ return `${toPrefix ?? ""}${stripped}`;
65644
+ }
65645
+ function keyMatchesFilters(key, include, exclude) {
65646
+ if (exclude && exclude.some((p) => key.startsWith(p)))
65647
+ return false;
65648
+ if (include && include.length > 0)
65649
+ return include.some((p) => key.startsWith(p));
65650
+ return true;
65651
+ }
65652
+ async function mapWithConcurrency(items, limit, worker) {
65653
+ let cursor = 0;
65654
+ const runners = [];
65655
+ const size = Math.max(1, Math.min(limit, items.length || 1));
65656
+ for (let i = 0;i < size; i++) {
65657
+ runners.push((async () => {
65658
+ while (true) {
65659
+ const index = cursor++;
65660
+ if (index >= items.length)
65661
+ return;
65662
+ await worker(items[index], index);
65663
+ }
65664
+ })());
65665
+ }
65666
+ await Promise.all(runners);
65667
+ }
65668
+ function clientFor(endpoint) {
65669
+ if (endpoint.client)
65670
+ return endpoint.client;
65671
+ return createObjectStorageClient({
65672
+ provider: endpoint.provider,
65673
+ region: endpoint.region,
65674
+ endpoint: endpoint.endpoint,
65675
+ forcePathStyle: endpoint.forcePathStyle,
65676
+ credentials: endpoint.credentials
65677
+ });
65678
+ }
65679
+ async function migrateObjectStorage(options) {
65680
+ const concurrency = options.concurrency ?? 8;
65681
+ const fromClient = clientFor(options.from);
65682
+ const toClient = clientFor(options.to);
65683
+ const sourceObjects = await fromClient.listAllObjects({
65684
+ bucket: options.from.bucket,
65685
+ prefix: options.from.prefix
65686
+ });
65687
+ const result = {
65688
+ copied: 0,
65689
+ skipped: 0,
65690
+ excluded: 0,
65691
+ bytesCopied: 0,
65692
+ errors: [],
65693
+ excludedKeys: [],
65694
+ deleted: []
65695
+ };
65696
+ const toCopy = [];
65697
+ for (const obj of sourceObjects) {
65698
+ if (!keyMatchesFilters(obj.Key, options.include, options.exclude)) {
65699
+ result.excluded++;
65700
+ result.excludedKeys.push(obj.Key);
65701
+ continue;
65702
+ }
65703
+ toCopy.push({ source: obj, destKey: remapKey(obj.Key, options.from.prefix, options.to.prefix) });
65704
+ }
65705
+ if (options.dryRun) {
65706
+ result.plan = toCopy.map(({ source, destKey }) => ({ key: source.Key, destKey, size: source.Size }));
65707
+ const total2 = sourceObjects.length;
65708
+ let index = 0;
65709
+ for (const obj of sourceObjects) {
65710
+ index++;
65711
+ const excluded = !keyMatchesFilters(obj.Key, options.include, options.exclude);
65712
+ options.onProgress?.({
65713
+ key: obj.Key,
65714
+ destKey: excluded ? "" : remapKey(obj.Key, options.from.prefix, options.to.prefix),
65715
+ size: obj.Size,
65716
+ action: excluded ? "excluded" : "planned",
65717
+ index,
65718
+ total: total2
65719
+ });
65720
+ }
65721
+ return result;
65722
+ }
65723
+ const total = toCopy.length;
65724
+ let processed = 0;
65725
+ await mapWithConcurrency(toCopy, concurrency, async ({ source, destKey }) => {
65726
+ const myIndex = ++processed;
65727
+ try {
65728
+ if (!options.force) {
65729
+ const head = await toClient.headObject(options.to.bucket, destKey);
65730
+ if (head && head.ContentLength === source.Size) {
65731
+ result.skipped++;
65732
+ options.onProgress?.({ key: source.Key, destKey, size: source.Size, action: "skipped", index: myIndex, total });
65733
+ return;
65734
+ }
65735
+ }
65736
+ const { body, contentType } = await fromClient.getObjectBytes(options.from.bucket, source.Key);
65737
+ await toClient.putObject({
65738
+ bucket: options.to.bucket,
65739
+ key: destKey,
65740
+ body,
65741
+ contentType
65742
+ });
65743
+ result.copied++;
65744
+ result.bytesCopied += body.byteLength;
65745
+ options.onProgress?.({ key: source.Key, destKey, size: source.Size, action: "copied", index: myIndex, total });
65746
+ } catch (err) {
65747
+ result.errors.push({ key: source.Key, message: err?.message ?? String(err) });
65748
+ options.onProgress?.({ key: source.Key, destKey, size: source.Size, action: "error", index: myIndex, total });
65749
+ }
65750
+ });
65751
+ const copiedDestKeys = new Set(toCopy.map((c) => c.destKey));
65752
+ if (options.deleteExtraneous) {
65753
+ const destObjects = await toClient.listAllObjects({ bucket: options.to.bucket, prefix: options.to.prefix });
65754
+ const extraneous = destObjects.filter((o) => !copiedDestKeys.has(o.Key)).map((o) => o.Key);
65755
+ for (const key of extraneous) {
65756
+ try {
65757
+ await toClient.deleteObject(options.to.bucket, key);
65758
+ result.deleted.push(key);
65759
+ } catch (err) {
65760
+ result.errors.push({ key, message: `delete failed: ${err?.message ?? String(err)}` });
65761
+ }
65762
+ }
65763
+ }
65764
+ if (options.verify) {
65765
+ const destObjects = await toClient.listAllObjects({ bucket: options.to.bucket, prefix: options.to.prefix });
65766
+ const destBySizeKey = new Map(destObjects.map((o) => [o.Key, o.Size]));
65767
+ const missing = [];
65768
+ const sizeMismatches = [];
65769
+ let matched = 0;
65770
+ for (const { source, destKey } of toCopy) {
65771
+ if (!destBySizeKey.has(destKey)) {
65772
+ missing.push(destKey);
65773
+ continue;
65774
+ }
65775
+ const actual = destBySizeKey.get(destKey);
65776
+ if (actual !== source.Size) {
65777
+ sizeMismatches.push({ key: destKey, expected: source.Size, actual });
65778
+ continue;
65779
+ }
65780
+ matched++;
65781
+ }
65782
+ result.verification = {
65783
+ ok: missing.length === 0 && sizeMismatches.length === 0,
65784
+ matched,
65785
+ missing,
65786
+ sizeMismatches
65787
+ };
65788
+ }
65789
+ return result;
65790
+ }
65791
+
65792
+ // src/object-storage/index.ts
65573
65793
  var DEFAULT_REGION = {
65574
65794
  aws: "us-east-1",
65575
65795
  backblaze: "us-west-004",
@@ -81229,204 +81449,6 @@ import { homedir as homedir7 } from "node:os";
81229
81449
  import { join as join13 } from "node:path";
81230
81450
  import { execSync } from "node:child_process";
81231
81451
 
81232
- // src/drivers/shared/caddyfile.ts
81233
- function isOnDemandDomain(domain) {
81234
- return domain === "*" || domain.includes("*");
81235
- }
81236
- function isStaticApp(app) {
81237
- return typeof app.root === "string" && app.root.length > 0;
81238
- }
81239
- function isProxyApp(app) {
81240
- return typeof app.port === "number";
81241
- }
81242
- function staticSiteServerRoot(name) {
81243
- return `/var/www/${name}`;
81244
- }
81245
- function normalizeOnDemandTls(onDemandTls) {
81246
- if (!onDemandTls)
81247
- return;
81248
- if (onDemandTls === true)
81249
- return {};
81250
- return onDemandTls;
81251
- }
81252
- function wrapInHandle(body, path, indent) {
81253
- const inner = body.split(`
81254
- `).map((line) => ` ${line}`).join(`
81255
- `);
81256
- return `${indent}handle ${path} {
81257
- ${inner}
81258
- ${indent}}`;
81259
- }
81260
- function renderUpstreamBlock(app, indent) {
81261
- const host = app.upstreamHost || "localhost";
81262
- const upstream = `${host}:${app.port}`;
81263
- const directives = app.reverseProxyDirectives ?? [];
81264
- const proxyLine = directives.length === 0 ? `${indent}reverse_proxy ${upstream}` : [
81265
- `${indent}reverse_proxy ${upstream} {`,
81266
- ...directives.map((d) => `${indent} ${d}`),
81267
- `${indent}}`
81268
- ].join(`
81269
- `);
81270
- const isCatchAll = !app.path || app.path === "/";
81271
- if (isCatchAll)
81272
- return proxyLine;
81273
- return wrapInHandle(proxyLine, app.path, indent);
81274
- }
81275
- function renderStaticBlock(app, indent) {
81276
- const root = app.root;
81277
- const lines = [`${indent}root * ${root}`];
81278
- if (app.cache?.enabled) {
81279
- const maxAge = app.cache.maxAge ?? 3600;
81280
- lines.push(`${indent}header Cache-Control "public, max-age=${maxAge}"`);
81281
- }
81282
- if (app.spa) {
81283
- lines.push(`${indent}try_files {path} /index.html`);
81284
- } else if (app.pathRewriteStyle === "flat") {
81285
- lines.push(`${indent}try_files {path} {path}.html {path}/index.html`);
81286
- } else {
81287
- lines.push(`${indent}try_files {path} {path}/index.html {path}.html`);
81288
- }
81289
- lines.push(`${indent}file_server`);
81290
- const body = lines.join(`
81291
- `);
81292
- const isCatchAll = !app.path || app.path === "/";
81293
- if (isCatchAll)
81294
- return body;
81295
- return wrapInHandle(body, app.path, indent);
81296
- }
81297
- function renderAppBlock(app, indent) {
81298
- return isStaticApp(app) ? renderStaticBlock(app, indent) : renderUpstreamBlock(app, indent);
81299
- }
81300
- function groupAppsByDomains(apps) {
81301
- const groups = new Map;
81302
- for (const app of apps) {
81303
- const domains = [...app.domains].filter(Boolean);
81304
- if (domains.length === 0)
81305
- continue;
81306
- const key = [...domains].sort().join(" ");
81307
- const group = groups.get(key) ?? { domains, apps: [] };
81308
- group.apps.push(app);
81309
- groups.set(key, group);
81310
- }
81311
- return [...groups.values()];
81312
- }
81313
- function sortAppsByPath(apps) {
81314
- return [...apps].sort((a, b) => {
81315
- const aCatchAll = !a.path || a.path === "/";
81316
- const bCatchAll = !b.path || b.path === "/";
81317
- if (aCatchAll && !bCatchAll)
81318
- return 1;
81319
- if (!aCatchAll && bCatchAll)
81320
- return -1;
81321
- return (b.path?.length ?? 0) - (a.path?.length ?? 0);
81322
- });
81323
- }
81324
- function buildCaddyfileFromProxy(proxy) {
81325
- if (proxy.raw && proxy.raw.trim())
81326
- return proxy.raw.trim();
81327
- const apps = (proxy.apps ?? []).filter((app) => app.domains.length > 0 && (isProxyApp(app) || isStaticApp(app)));
81328
- if (apps.length === 0)
81329
- return;
81330
- const onDemand = normalizeOnDemandTls(proxy.onDemandTls);
81331
- const hasOnDemandDomain = apps.some((app) => app.domains.some(isOnDemandDomain));
81332
- const globalLines = [];
81333
- if (proxy.email)
81334
- globalLines.push(`email ${proxy.email}`);
81335
- if (proxy.staging)
81336
- globalLines.push("acme_ca https://acme-staging-v02.api.letsencrypt.org/directory");
81337
- if (onDemand) {
81338
- if (onDemand.ask || onDemand.interval || onDemand.burst != null) {
81339
- const inner = [];
81340
- if (onDemand.ask)
81341
- inner.push(` ask ${onDemand.ask}`);
81342
- if (onDemand.interval || onDemand.burst != null) {
81343
- const rl = [];
81344
- if (onDemand.interval)
81345
- rl.push(` interval ${onDemand.interval}`);
81346
- if (onDemand.burst != null)
81347
- rl.push(` burst ${onDemand.burst}`);
81348
- inner.push(` rate_limit {
81349
- ${rl.join(`
81350
- `)}
81351
- }`);
81352
- }
81353
- globalLines.push(`on_demand_tls {
81354
- ${inner.join(`
81355
- `)}
81356
- }`);
81357
- } else {
81358
- globalLines.push(`on_demand_tls {
81359
- }`);
81360
- }
81361
- }
81362
- for (const directive of proxy.globalDirectives ?? [])
81363
- globalLines.push(directive);
81364
- const blocks = [];
81365
- if (globalLines.length > 0)
81366
- blocks.push(`{
81367
- ${globalLines.map((line) => ` ${line}`).join(`
81368
- `)}
81369
- }`);
81370
- for (const group of groupAppsByDomains(apps)) {
81371
- const sorted = sortAppsByPath(group.apps);
81372
- const body = sorted.map((app) => renderAppBlock(app, " ")).join(`
81373
- `);
81374
- const needsOnDemand = onDemand && group.domains.some(isOnDemandDomain);
81375
- const tlsBlock = needsOnDemand ? `
81376
- tls {
81377
- on_demand
81378
- }` : "";
81379
- blocks.push(`${group.domains.join(", ")} {
81380
- ${body}${tlsBlock}
81381
- }`);
81382
- }
81383
- if (hasOnDemandDomain && !onDemand) {
81384
- blocks.unshift(`# WARNING: wildcard/catch-all domain present but on_demand_tls is not enabled.
81385
- ` + "# Caddy cannot provision TLS for these hosts. Set compute.proxy.onDemandTls.");
81386
- }
81387
- return blocks.join(`
81388
-
81389
- `);
81390
- }
81391
- function proxyConfigFromSites(sites) {
81392
- const apps = [];
81393
- for (const [name, site] of Object.entries(sites)) {
81394
- if (typeof site.domain !== "string" || !site.domain)
81395
- continue;
81396
- if (typeof site.port === "number" && site.deploy !== "bucket") {
81397
- apps.push({
81398
- name,
81399
- domains: [site.domain],
81400
- port: site.port,
81401
- path: site.path
81402
- });
81403
- } else if (resolveSiteKind(site) === "server-static") {
81404
- apps.push({
81405
- name,
81406
- domains: [site.domain],
81407
- root: staticSiteServerRoot(name),
81408
- path: site.path,
81409
- spa: site.spa,
81410
- pathRewriteStyle: site.pathRewriteStyle,
81411
- cache: site.cache
81412
- });
81413
- }
81414
- }
81415
- return { apps };
81416
- }
81417
- function resolveCaddyfile(sites, proxy) {
81418
- if (proxy) {
81419
- if (proxy.raw && proxy.raw.trim())
81420
- return proxy.raw.trim();
81421
- const resolved = proxy.apps && proxy.apps.length > 0 ? proxy : { ...proxy, apps: proxyConfigFromSites(sites).apps };
81422
- return buildCaddyfileFromProxy(resolved);
81423
- }
81424
- return buildCaddyfileFromProxy(proxyConfigFromSites(sites));
81425
- }
81426
- function buildCaddyfile(sites) {
81427
- return buildCaddyfileFromProxy(proxyConfigFromSites(sites));
81428
- }
81429
-
81430
81452
  // src/drivers/hetzner/client.ts
81431
81453
  var DEFAULT_API_URL = "https://api.hetzner.cloud/v1";
81432
81454
 
@@ -81829,14 +81851,12 @@ class HetznerDriver {
81829
81851
  return this.outputsFromState(rehydrated, alreadyRunning);
81830
81852
  }
81831
81853
  const sites = config6.sites || {};
81832
- const caddyfile = resolveCaddyfile(sites, compute.proxy);
81833
- const sitePorts = caddyfile ? [] : this.collectUpstreamPorts(sites, compute.proxy);
81854
+ const sitePorts = this.collectUpstreamPorts(sites);
81834
81855
  const bootstrap = generateUbuntuAppCloudInit({
81835
81856
  runtime: compute.runtime || "bun",
81836
81857
  runtimeVersion: compute.runtimeVersion || "latest",
81837
81858
  systemPackages: compute.systemPackages,
81838
- database: config6.infrastructure?.database,
81839
- caddyfile
81859
+ database: config6.infrastructure?.database
81840
81860
  });
81841
81861
  const userData = wrapCloudInitUserData(bootstrap);
81842
81862
  const serverType = resolveHetznerServerType(compute.size);
@@ -82002,12 +82022,8 @@ class HetznerDriver {
82002
82022
  const { firewall } = await this.client.createFirewall({ name, labels, rules });
82003
82023
  return { firewall };
82004
82024
  }
82005
- collectUpstreamPorts(sites, proxy) {
82025
+ collectUpstreamPorts(sites) {
82006
82026
  const ports = new Set;
82007
- for (const app of proxy?.apps ?? []) {
82008
- if (typeof app.port === "number")
82009
- ports.add(app.port);
82010
- }
82011
82027
  for (const site of Object.values(sites)) {
82012
82028
  if (typeof site.port === "number")
82013
82029
  ports.add(site.port);
@@ -82361,7 +82377,6 @@ export {
82361
82377
  suggestFlags,
82362
82378
  suggestCommand,
82363
82379
  storageAdvancedManager,
82364
- staticSiteServerRoot,
82365
82380
  staticSiteManager,
82366
82381
  stackDependencyManager,
82367
82382
  signRequestAsync,
@@ -82393,13 +82408,12 @@ export {
82393
82408
  resolveDeployBucketName,
82394
82409
  resolveCredentials,
82395
82410
  resolveCloudProvider,
82396
- resolveCaddyfile,
82397
82411
  requiresReplacement,
82398
82412
  replicaManager,
82413
+ remapKey,
82399
82414
  regionPairManager,
82400
82415
  quickHash,
82401
82416
  queueManagementManager,
82402
- proxyConfigFromSites,
82403
82417
  providerEndpoint,
82404
82418
  progressiveDeploymentManager,
82405
82419
  processInChunks,
@@ -82417,6 +82431,7 @@ export {
82417
82431
  multiRegionManager,
82418
82432
  multiAccountManager,
82419
82433
  migrationManager,
82434
+ migrateObjectStorage,
82420
82435
  metricsManager,
82421
82436
  mergeInfrastructure,
82422
82437
  makeAWSRequestOnce,
@@ -82430,9 +82445,9 @@ export {
82430
82445
  lambdaDestinationsManager,
82431
82446
  lambdaDLQManager,
82432
82447
  lambdaConcurrencyManager,
82448
+ keyMatchesFilters,
82433
82449
  isWebCryptoAvailable,
82434
82450
  isValidRegion,
82435
- isOnDemandDomain,
82436
82451
  isNodeCryptoAvailable,
82437
82452
  isLocalDevelopment,
82438
82453
  isLikelyTypo,
@@ -82585,8 +82600,6 @@ export {
82585
82600
  buildSiteDeployScript,
82586
82601
  buildOptimizationManager,
82587
82602
  buildCloudFormationTemplate,
82588
- buildCaddyfileFromProxy,
82589
- buildCaddyfile,
82590
82603
  bounceComplaintHandler,
82591
82604
  blueGreenManager,
82592
82605
  batchProcessingManager,
@@ -23,6 +23,7 @@
23
23
  * ```
24
24
  */
25
25
  import { S3Client } from '../aws/s3';
26
+ export * from './migrate';
26
27
  export type ObjectStorageProvider = 'aws' | 'backblaze' | 'hetzner';
27
28
  export interface ObjectStorageCredentials {
28
29
  accessKeyId: string;