@h-ai/deploy 1.0.0-beta.20 → 1.0.0-beta.22

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/README.en.md CHANGED
@@ -163,6 +163,8 @@ In `hai deploy`, choose a URL or individual fields for custom PostgreSQL, MySQL,
163
163
 
164
164
  #### Direct private object storage access
165
165
 
166
+ Nginx serves a standalone HTTP 404 page when a presigned download reaches the static site fallback or the SSR/API gateway receives an application 404. The page uses Chinese, English, or Japanese based on the browser language. Update the proxy rules with a full deployment bundle. Rebuild independently run static web images from the updated CLI templates. [Download troubleshooting](../../docs/usage/configuration/data.md#下载链接无法访问)
167
+
166
168
  Self-hosted S3 uses an internal endpoint. Set `_storage.yml` publicEndpoint to an external address such as `https://app.example.com/delivery/files`. Nginx exposes `/delivery/files/<bucket>/...`, removes the proxy directory and forwards the original bucket URI, signature query and Host directly to storage:9000. Files bypass Serv; omitting the directory retains root bucket routes. A route may sit under the website directory; API, asset and health conflicts fail packaging. Storage ports/consoles remain private, buffering/signature logs are disabled, and S3 handles authorization and cross-origin CORS. External ingress must retain the full entry path and Host so only the SDK gateway removes the prefix.
167
169
 
168
170
  Managed deployments retain bucket/region/prefix/publicEndpoint/publicUrl/operationLog while replacing internal connections and managed credentials. Adding the gateway or changing access settings requires a full baseline; partial preflight rejects mismatching configuration fingerprints. App/storage partial updates refresh retained proxy DNS, and recovery restores the previous topology. External/kept storage gets no local sidecar route; Local does not expose private directories. See the [storage handbook](../../docs/usage/configuration/data.md).
package/README.ja.md CHANGED
@@ -163,6 +163,8 @@ SSH 設定は選択環境の `.env.<環境>.local` に保存します。`--bundl
163
163
 
164
164
  #### 非公開オブジェクトストレージへの直接アクセス
165
165
 
166
+ 署名付きダウンロードが静的サイトのフォールバックに到達した場合、または SSR/API ゲートウェイがアプリから 404 を受け取った場合、Nginx は専用ページを HTTP 404 で返します。ブラウザーの言語に応じて中国語、英語、日本語で表示します。プロキシのルールを更新するには完全デプロイバンドルを使用し、静的 Web イメージを単独で使う場合は更新後の CLI テンプレートから再構築してください。[ダウンロードのトラブルシューティング](../../docs/usage/configuration/data.md#下载链接无法访问)
167
+
166
168
  自前 S3 の endpoint は内部接続先です。publicEndpoint に `https://app.example.com/delivery/files` などの外部アドレスを設定できます。Nginx は `/delivery/files/<bucket>/...` を受け、入口パスだけを除去し、本来のバケット URI、署名クエリ、Host を保持して storage:9000 に転送します。Serv を経由せず、パス省略時は従来のルートバケット形式です。サイト配下に置けますが API、資源、ヘルスパスの競合は拒否します。ポートと管理コンソールは非公開、バッファリングと署名ログは無効です。認可と CORS は S3 に設定し、外部プロキシは入口パスと Host を保持してください。
167
169
 
168
170
  自前構成は bucket/region/prefix/publicEndpoint/publicUrl/operationLog を保持し、内部接続と受管認証情報だけを置き換えます。gateway 追加や公開設定変更には完全デプロイが必要で、部分更新の事前検証は設定摘要の不一致を拒否します。アプリ/ストレージ部分更新では保持プロキシの DNS を更新し、失敗時は以前の構成に復元します。外部/保持接続にローカルルートは生成せず、Local の非公開ディレクトリも公開しません。[ストレージ手引き](../../docs/usage/configuration/data.md)を参照してください。
package/README.md CHANGED
@@ -177,6 +177,8 @@ SSH 参数保存到选中环境的 `.env.<环境>.local`。`--bundle-only` 在
177
177
 
178
178
  #### 私有对象存储直连
179
179
 
180
+ 预签名下载请求落入静态网站回退,或 SSR/API 网关收到应用的 404 时,Nginx 返回 HTTP 404 和独立的下载提示页。提示按浏览器语言显示中文、英文或日文。现有容器部署使用完整交付包更新代理规则,独立静态 Web 镜像使用更新后的 CLI 模板重新构建。[下载排查](../../docs/usage/configuration/data.md#下载链接无法访问)
181
+
180
182
  自建 S3 的 `endpoint` 使用 Compose 内部地址,浏览器地址用 `_storage.yml` 的 `publicEndpoint` 配置,例如 `https://app.example.com/delivery/files`。Nginx 对外开放 `/delivery/files/<bucket>/...`,去掉 `/delivery/files` 后将原始桶 URI、签名查询与 Host 直接转发 storage:9000,文件不经过 Serv;省略目录时继续使用根桶路径。入口可位于网站目录内,API/资源/健康路径冲突会在打包前失败。存储端口与控制台不公开,对象缓冲和签名日志关闭;S3 负责私有桶授权和跨源 CORS。外层代理须保留完整入口路径与 Host,只由 SDK 网关去掉前缀。
181
183
 
182
184
  自建配置保留 bucket/region/prefix/publicEndpoint/publicUrl/operationLog,只替换内部连接与托管凭据。首次增加网关或改变这些访问设置须全量部署,增量预检按配置摘要拒绝不一致;普通应用/存储增量刷新保留代理的服务 DNS,失败恢复使用旧拓扑。外部/保持连接不生成本地存储路由,Local 不公开私有目录。详见[存储配置手册](../../docs/usage/configuration/data.md#配置人员区分三个访问地址).
package/dist/index.d.ts CHANGED
@@ -1077,7 +1077,7 @@ declare const deploy: DeployFunctions;
1077
1077
  * const message = deployM('deploy_sshRequired')
1078
1078
  * ```
1079
1079
  */
1080
- declare const deployM: (key: "deploy_composeHostAddressInvalid" | "deploy_composeHostnameInvalid" | "deploy_composeHostnameConflict" | "deploy_composeProviderRequired" | "deploy_composeServiceUnavailable" | "deploy_notInitialized" | "deploy_initFailed" | "deploy_unsupportedProviderType" | "deploy_unsupportedProvisionerType" | "deploy_configTokenRequired" | "deploy_authRequired" | "deploy_authFailed" | "deploy_projectCreateFailed" | "deploy_uploadFailed" | "deploy_deployFailed" | "deploy_provisionFailed" | "deploy_adapterMissing" | "deploy_scanFailed" | "deploy_envVarFailed" | "deploy_credentialError" | "deploy_credentialInvalidKey" | "deploy_credentialSaved" | "deploy_credentialLoaded" | "deploy_configError" | "deploy_credentialMissing" | "deploy_apiError" | "deploy_fileUploadFailed" | "deploy_provisionNoResult" | "deploy_recoveryFailure" | "deploy_recoverySuggestion" | "deploy_commandTimeout" | "deploy_commandFailed" | "deploy_remoteConnectFailed" | "deploy_remoteRuntimeMissing" | "deploy_imageTransferFailed" | "deploy_healthCheckFailed" | "deploy_invalidEnvironment" | "deploy_environmentMissing" | "deploy_environmentReadFailed" | "deploy_environmentWriteFailed" | "deploy_multipleApplications" | "deploy_purposeSqlite" | "deploy_purposeDatabase" | "deploy_purposeMemory" | "deploy_purposeLocal" | "deploy_purposeCache" | "deploy_purposeStorage" | "deploy_purposeVector" | "deploy_purposeDisabled" | "deploy_purposeLdap" | "deploy_purposeConsole" | "deploy_purposeEmail" | "deploy_purposeSms" | "deploy_webProxyInvalid" | "deploy_webBaseConflict" | "deploy_storageProxyInvalid" | "deploy_storagePublicEndpointInvalid" | "deploy_storageBackendRequired" | "deploy_artifactInvalid" | "deploy_sshRequired" | "deploy_sshPasswordInvalid" | "deploy_sshAskpassFailed" | "deploy_sshAskpassCleanupFailed" | "deploy_targetAuth" | "deploy_targetPasswordEnv" | "deploy_bundleInstructions" | "deploy_bundleApplyGuide" | "deploy_bundleMetadata" | "deploy_targetHost" | "deploy_targetPort" | "deploy_targetUsername" | "deploy_targetIdentityFile" | "deploy_targetKnownHostsFile" | "deploy_targetStrictHostKeyChecking" | "deploy_targetConnectTimeoutSeconds" | "deploy_targetDirectory" | "deploy_targetBundle" | "deploy_bundleOnlyContainer" | "deploy_selectedContainerOnly" | "deploy_selectionInvalid" | "deploy_imageVersionRequired" | "deploy_bundleRuntimeRequired" | "deploy_bundleFailed" | "deploy_configFileInvalid" | "deploy_configFileUnreadable" | "deploy_bundleNameInvalid" | "deploy_localStopped" | "deploy_localUnknown" | "deploy_localNotStarted" | "deploy_commandOutputFailed" | "deploy_commandInputFailed" | "deploy_commandTreeActive" | "deploy_commandExecutionFailed" | "deploy_remoteUnknown" | "deploy_remoteFinished" | "deploy_bundleDirectoryExists" | "deploy_bundleFull" | "deploy_bundlePartial" | "deploy_bundleSelection" | "deploy_transportBodyLimitInvalid" | "deploy_transportKeyRingInvalid" | "deploy_uploadPasswordPrompt" | "deploy_uploadPasswordUnavailable" | "deploy_uploadPasswordCancelled" | "deploy_uploadPasswordInvalid" | "deploy_managedSecretsInvalid" | "deploy_secretImportFailed" | "deploy_projectNameRequired" | "deploy_projectNameOverrideRemoved" | "deploy_requestAccessInvalid" | "deploy_httpsPortsInvalid" | "deploy_httpsCertificateInvalid" | "deploy_bundleRuntime" | "deploy_refreshCookieSecureInvalid" | "deploy_postgresPasswordConflict" | "deploy_bundleVariables" | "deploy_bundleHttpOnly" | "deploy_bundleHttps" | "deploy_bundleAcme" | "deploy_bundleAccounts" | "deploy_bundleDirectories" | "deploy_downloadInvalid" | "deploy_imageSourceMissing" | "deploy_proxySystemUnsupported" | "deploy_middlewareFieldInvalid" | "deploy_middlewareFieldError" | "deploy_middlewareRequired" | "deploy_middlewareInteger" | "deploy_middlewareBoolean" | "deploy_middlewareUrl" | "deploy_middlewareS3Endpoint" | "deploy_middlewareTlsSyntax" | "deploy_middlewareMysqlTls" | "deploy_middlewarePostgresTls" | "deploy_middlewareHeaders" | "deploy_middlewareMethod" | "deploy_configResolutionFailed" | "deploy_reachEmptyProviderOverride" | "deploy_s3Engine", options?: _h_ai_core.MessageOptions) => string;
1080
+ declare const deployM: (key: "deploy_composeHostAddressInvalid" | "deploy_composeHostnameInvalid" | "deploy_composeHostnameConflict" | "deploy_composeProviderRequired" | "deploy_composeServiceUnavailable" | "deploy_notInitialized" | "deploy_initFailed" | "deploy_unsupportedProviderType" | "deploy_unsupportedProvisionerType" | "deploy_configTokenRequired" | "deploy_authRequired" | "deploy_authFailed" | "deploy_projectCreateFailed" | "deploy_uploadFailed" | "deploy_deployFailed" | "deploy_provisionFailed" | "deploy_adapterMissing" | "deploy_scanFailed" | "deploy_envVarFailed" | "deploy_credentialError" | "deploy_credentialInvalidKey" | "deploy_credentialSaved" | "deploy_credentialLoaded" | "deploy_configError" | "deploy_credentialMissing" | "deploy_apiError" | "deploy_fileUploadFailed" | "deploy_provisionNoResult" | "deploy_recoveryFailure" | "deploy_recoverySuggestion" | "deploy_commandTimeout" | "deploy_commandFailed" | "deploy_remoteConnectFailed" | "deploy_remoteRuntimeMissing" | "deploy_imageTransferFailed" | "deploy_healthCheckFailed" | "deploy_invalidEnvironment" | "deploy_environmentMissing" | "deploy_environmentReadFailed" | "deploy_environmentWriteFailed" | "deploy_multipleApplications" | "deploy_purposeSqlite" | "deploy_purposeDatabase" | "deploy_purposeMemory" | "deploy_purposeLocal" | "deploy_purposeCache" | "deploy_purposeStorage" | "deploy_purposeVector" | "deploy_purposeDisabled" | "deploy_purposeLdap" | "deploy_purposeConsole" | "deploy_purposeEmail" | "deploy_purposeSms" | "deploy_webProxyInvalid" | "deploy_webBaseConflict" | "deploy_storageProxyInvalid" | "deploy_storagePublicEndpointInvalid" | "deploy_storageBackendRequired" | "deploy_artifactInvalid" | "deploy_sshRequired" | "deploy_sshPasswordInvalid" | "deploy_sshAskpassFailed" | "deploy_sshAskpassCleanupFailed" | "deploy_targetAuth" | "deploy_targetPasswordEnv" | "deploy_bundleInstructions" | "deploy_bundleApplyGuide" | "deploy_bundleMetadata" | "deploy_targetHost" | "deploy_targetPort" | "deploy_targetUsername" | "deploy_targetIdentityFile" | "deploy_targetKnownHostsFile" | "deploy_targetStrictHostKeyChecking" | "deploy_targetConnectTimeoutSeconds" | "deploy_targetDirectory" | "deploy_targetBundle" | "deploy_bundleOnlyContainer" | "deploy_selectedContainerOnly" | "deploy_selectionInvalid" | "deploy_imageVersionRequired" | "deploy_bundleRuntimeRequired" | "deploy_bundleFailed" | "deploy_configFileInvalid" | "deploy_configFileUnreadable" | "deploy_bundleNameInvalid" | "deploy_localStopped" | "deploy_localUnknown" | "deploy_localNotStarted" | "deploy_commandOutputFailed" | "deploy_commandInputFailed" | "deploy_commandTreeActive" | "deploy_commandExecutionFailed" | "deploy_remoteUnknown" | "deploy_remoteFinished" | "deploy_bundleDirectoryExists" | "deploy_bundleFull" | "deploy_bundlePartial" | "deploy_bundleSelection" | "deploy_transportBodyLimitInvalid" | "deploy_transportKeyRingInvalid" | "deploy_uploadPasswordPrompt" | "deploy_uploadPasswordUnavailable" | "deploy_uploadPasswordCancelled" | "deploy_uploadPasswordInvalid" | "deploy_managedSecretsInvalid" | "deploy_secretImportFailed" | "deploy_projectNameRequired" | "deploy_projectNameOverrideRemoved" | "deploy_requestAccessInvalid" | "deploy_httpsPortsInvalid" | "deploy_httpsCertificateInvalid" | "deploy_bundleRuntime" | "deploy_refreshCookieSecureInvalid" | "deploy_postgresPasswordConflict" | "deploy_bundleVariables" | "deploy_bundleHttpOnly" | "deploy_bundleHttps" | "deploy_bundleAcme" | "deploy_bundleAccounts" | "deploy_bundleDirectories" | "deploy_downloadInvalid" | "deploy_imageSourceMissing" | "deploy_proxySystemUnsupported" | "deploy_middlewareFieldInvalid" | "deploy_middlewareFieldError" | "deploy_middlewareRequired" | "deploy_middlewareInteger" | "deploy_middlewareBoolean" | "deploy_middlewareUrl" | "deploy_middlewareS3Endpoint" | "deploy_middlewareTlsSyntax" | "deploy_middlewareMysqlTls" | "deploy_middlewarePostgresTls" | "deploy_middlewareHeaders" | "deploy_middlewareMethod" | "deploy_configResolutionFailed" | "deploy_reachEmptyProviderOverride" | "deploy_s3Engine" | "deploy_downloadUnavailableTitle" | "deploy_downloadUnavailableInstruction", options?: _h_ai_core.MessageOptions) => string;
1081
1081
 
1082
1082
  /**
1083
1083
  * 部署中间件的后端与连接字段目录,供扫描、CLI 和容器规划共同使用。
package/dist/index.js CHANGED
@@ -221,7 +221,9 @@ var en_US_default = {
221
221
  deploy_middlewareMethod: "Enter POST or PUT.",
222
222
  deploy_configResolutionFailed: "Cannot resolve {path}. Check the configuration structure and array indices for {fields}.",
223
223
  deploy_reachEmptyProviderOverride: "While resolving {path}, {field} refers to a missing provider because HAI_REACH_PROVIDERS is []. Remove stale indexed variables and set HAI_REACH_PROVIDERS to the complete notification provider list in .env.<environment>.local.",
224
- deploy_s3Engine: "Choose the self-hosted S3 implementation"
224
+ deploy_s3Engine: "Choose the self-hosted S3 implementation",
225
+ deploy_downloadUnavailableTitle: "Download link unavailable",
226
+ deploy_downloadUnavailableInstruction: "Return to the download page to get a new link. If the download still fails, contact your administrator."
225
227
  };
226
228
 
227
229
  // messages/ja-JP.json
@@ -364,7 +366,9 @@ var ja_JP_default = {
364
366
  deploy_middlewareMethod: "POST \u307E\u305F\u306F PUT \u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
365
367
  deploy_configResolutionFailed: "{path} \u3092\u89E3\u6C7A\u3067\u304D\u307E\u305B\u3093\u3002{fields} \u306B\u5BFE\u5FDC\u3059\u308B\u8A2D\u5B9A\u306E\u69CB\u9020\u3068\u914D\u5217\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
366
368
  deploy_reachEmptyProviderOverride: "{path} \u306E\u6709\u52B9\u306A\u8A2D\u5B9A\u3092\u89E3\u6C7A\u3067\u304D\u307E\u305B\u3093\u3002HAI_REACH_PROVIDERS \u304C [] \u306E\u305F\u3081\u3001{field} \u306B\u5BFE\u5FDC\u3059\u308B\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u53E4\u3044\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u4ED8\u304D\u5909\u6570\u3092\u524A\u9664\u3057\u3001.env.<\u74B0\u5883>.local \u306E HAI_REACH_PROVIDERS \u306B\u901A\u77E5\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306E\u5B8C\u5168\u306A\u4E00\u89A7\u3092\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
367
- deploy_s3Engine: "\u30BB\u30EB\u30D5\u30DB\u30B9\u30C8 S3 \u306E\u5B9F\u88C5\u3092\u9078\u629E"
369
+ deploy_s3Engine: "\u30BB\u30EB\u30D5\u30DB\u30B9\u30C8 S3 \u306E\u5B9F\u88C5\u3092\u9078\u629E",
370
+ deploy_downloadUnavailableTitle: "\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u30EA\u30F3\u30AF\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093",
371
+ deploy_downloadUnavailableInstruction: "\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u30DA\u30FC\u30B8\u306B\u623B\u308A\u3001\u65B0\u3057\u3044\u30EA\u30F3\u30AF\u3092\u53D6\u5F97\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u89E3\u6C7A\u3057\u306A\u3044\u5834\u5408\u306F\u7BA1\u7406\u8005\u306B\u304A\u554F\u3044\u5408\u308F\u305B\u304F\u3060\u3055\u3044\u3002"
368
372
  };
369
373
 
370
374
  // messages/zh-CN.json
@@ -507,7 +511,9 @@ var zh_CN_default = {
507
511
  deploy_middlewareMethod: "\u8BF7\u8F93\u5165 POST \u6216 PUT\u3002",
508
512
  deploy_configResolutionFailed: "\u65E0\u6CD5\u89E3\u6790 {path}\u3002\u8BF7\u68C0\u67E5 {fields} \u5BF9\u5E94\u7684\u914D\u7F6E\u7ED3\u6784\u4E0E\u6570\u7EC4\u7D22\u5F15\u3002",
509
513
  deploy_reachEmptyProviderOverride: "\u89E3\u6790 {path} \u7684\u751F\u6548\u914D\u7F6E\u65F6\u53D1\u73B0 {field} \u6307\u5411\u4E0D\u5B58\u5728\u7684 Provider\u3002HAI_REACH_PROVIDERS \u5F53\u524D\u4E3A []\u3002\u8BF7\u5220\u9664\u65E7\u7684\u7D22\u5F15\u53D8\u91CF\uFF0C\u5E76\u5728 .env.<\u73AF\u5883>.local \u4E2D\u7528 HAI_REACH_PROVIDERS \u914D\u7F6E\u5B8C\u6574\u7684\u901A\u77E5 Provider \u5217\u8868\u3002",
510
- deploy_s3Engine: "\u9009\u62E9\u81EA\u5EFA S3 \u670D\u52A1\u5B9E\u73B0"
514
+ deploy_s3Engine: "\u9009\u62E9\u81EA\u5EFA S3 \u670D\u52A1\u5B9E\u73B0",
515
+ deploy_downloadUnavailableTitle: "\u4E0B\u8F7D\u94FE\u63A5\u65E0\u6CD5\u8BBF\u95EE",
516
+ deploy_downloadUnavailableInstruction: "\u8BF7\u8FD4\u56DE\u4E0B\u8F7D\u9875\u9762\u91CD\u65B0\u83B7\u53D6\u94FE\u63A5\u3002\u82E5\u4ECD\u65E0\u6CD5\u4E0B\u8F7D\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002"
511
517
  };
512
518
 
513
519
  // src/deploy-i18n.ts
@@ -1457,7 +1463,26 @@ var deploy_config_shapes_default = {
1457
1463
  type: "string",
1458
1464
  format: "uri"
1459
1465
  },
1466
+ multipart: {
1467
+ default: {},
1468
+ type: "object",
1469
+ properties: {
1470
+ partSizeBytes: {
1471
+ default: 5242880,
1472
+ type: "integer",
1473
+ minimum: 5242880,
1474
+ maximum: 5368709120
1475
+ },
1476
+ queueSize: {
1477
+ default: 2,
1478
+ type: "integer",
1479
+ minimum: 1,
1480
+ maximum: 9007199254740991
1481
+ }
1482
+ }
1483
+ },
1460
1484
  operationLog: {
1485
+ default: {},
1461
1486
  type: "object",
1462
1487
  properties: {
1463
1488
  read: {
@@ -1465,7 +1490,7 @@ var deploy_config_shapes_default = {
1465
1490
  type: "boolean"
1466
1491
  },
1467
1492
  write: {
1468
- default: false,
1493
+ default: true,
1469
1494
  type: "boolean"
1470
1495
  },
1471
1496
  maxLength: {
@@ -1473,15 +1498,6 @@ var deploy_config_shapes_default = {
1473
1498
  type: "integer",
1474
1499
  minimum: 0,
1475
1500
  maximum: 9007199254740991
1476
- },
1477
- level: {
1478
- default: "debug",
1479
- type: "string",
1480
- enum: [
1481
- "info",
1482
- "debug",
1483
- "trace"
1484
- ]
1485
1501
  }
1486
1502
  }
1487
1503
  }
@@ -1514,6 +1530,7 @@ var deploy_config_shapes_default = {
1514
1530
  type: "number"
1515
1531
  },
1516
1532
  operationLog: {
1533
+ default: {},
1517
1534
  type: "object",
1518
1535
  properties: {
1519
1536
  read: {
@@ -1521,7 +1538,7 @@ var deploy_config_shapes_default = {
1521
1538
  type: "boolean"
1522
1539
  },
1523
1540
  write: {
1524
- default: false,
1541
+ default: true,
1525
1542
  type: "boolean"
1526
1543
  },
1527
1544
  maxLength: {
@@ -1529,15 +1546,6 @@ var deploy_config_shapes_default = {
1529
1546
  type: "integer",
1530
1547
  minimum: 0,
1531
1548
  maximum: 9007199254740991
1532
- },
1533
- level: {
1534
- default: "debug",
1535
- type: "string",
1536
- enum: [
1537
- "info",
1538
- "debug",
1539
- "trace"
1540
- ]
1541
1549
  }
1542
1550
  }
1543
1551
  }
@@ -10354,6 +10362,87 @@ async function copyArchive(source, destination) {
10354
10362
  await pipeline(createReadStream(source), digest, createWriteStream(destination, { mode: 384 }));
10355
10363
  return hash.digest("hex");
10356
10364
  }
10365
+
10366
+ // src/container/deploy-download-error.ts
10367
+ var PAGE_STYLE = `
10368
+ * { box-sizing: border-box; }
10369
+ body {
10370
+ margin: 0;
10371
+ min-height: 100vh;
10372
+ display: grid;
10373
+ place-items: center;
10374
+ padding: 24px;
10375
+ background: #f1f5f9;
10376
+ color: #172033;
10377
+ font-family: Inter, system-ui, "Microsoft YaHei", sans-serif;
10378
+ line-height: 1.7;
10379
+ }
10380
+ main {
10381
+ width: min(100%, 640px);
10382
+ padding: clamp(24px, 6vw, 48px);
10383
+ border: 1px solid #dbe3ee;
10384
+ border-top: 4px solid #2563eb;
10385
+ border-radius: 12px;
10386
+ background: #fff;
10387
+ }
10388
+ h1 { margin: 0 0 16px; font-size: clamp(24px, 5vw, 32px); line-height: 1.4; }
10389
+ p { margin: 0; color: #475569; font-size: 16px; }
10390
+ `;
10391
+ function escapeHtml(value) {
10392
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
10393
+ }
10394
+ function renderPage() {
10395
+ return `<!doctype html>
10396
+ <html lang="$hai_download_language">
10397
+ <head>
10398
+ <meta charset="utf-8">
10399
+ <meta name="viewport" content="width=device-width, initial-scale=1">
10400
+ <meta name="robots" content="noindex">
10401
+ <meta name="referrer" content="no-referrer">
10402
+ <link rel="icon" href="data:,">
10403
+ <title>$hai_download_title</title>
10404
+ <style>${PAGE_STYLE}</style>
10405
+ </head>
10406
+ <body><main><h1>$hai_download_title</h1><p>$hai_download_instruction</p></main></body>
10407
+ </html>`;
10408
+ }
10409
+ function renderDownloadErrorMaps() {
10410
+ return `map "$request_method:$args" $hai_signed_download {
10411
+ default 0;
10412
+ "~*^(GET|HEAD):(?:[^&]*&)*X-Amz-Signature=[^&]+(?:&|$)" 1;
10413
+ }
10414
+ map $http_accept_language $hai_download_language {
10415
+ default zh-CN;
10416
+ ~*^en en-US;
10417
+ ~*^ja ja-JP;
10418
+ }
10419
+ map $hai_download_language $hai_download_title {
10420
+ default ${JSON.stringify(escapeHtml(deployM("deploy_downloadUnavailableTitle", { locale: "zh-CN" })))};
10421
+ en-US ${JSON.stringify(escapeHtml(deployM("deploy_downloadUnavailableTitle", { locale: "en-US" })))};
10422
+ ja-JP ${JSON.stringify(escapeHtml(deployM("deploy_downloadUnavailableTitle", { locale: "ja-JP" })))};
10423
+ }
10424
+ map $hai_download_language $hai_download_instruction {
10425
+ default ${JSON.stringify(escapeHtml(deployM("deploy_downloadUnavailableInstruction", { locale: "zh-CN" })))};
10426
+ en-US ${JSON.stringify(escapeHtml(deployM("deploy_downloadUnavailableInstruction", { locale: "en-US" })))};
10427
+ ja-JP ${JSON.stringify(escapeHtml(deployM("deploy_downloadUnavailableInstruction", { locale: "ja-JP" })))};
10428
+ }
10429
+ `;
10430
+ }
10431
+ function renderDownloadErrorLocation() {
10432
+ return ` location @hai_download_unavailable {
10433
+ access_log off;
10434
+ error_log /dev/null;
10435
+ types { }
10436
+ default_type text/html;
10437
+ charset utf-8;
10438
+ add_header Cache-Control "private, no-store" always;
10439
+ add_header Referrer-Policy no-referrer always;
10440
+ add_header X-Content-Type-Options nosniff always;
10441
+ add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'" always;
10442
+ return 404 ${JSON.stringify(renderPage())};
10443
+ }
10444
+ `;
10445
+ }
10357
10446
  var CERTBOT_IMAGE = "docker.io/certbot/certbot:v5.9.0";
10358
10447
  function createHttpsAssets(config, projectDirectory) {
10359
10448
  const assets = {
@@ -11088,6 +11177,7 @@ function generateWebProxyConfig(port, apiPrefix, options = {}) {
11088
11177
  }
11089
11178
  `).join("");
11090
11179
  return `include /tmp/hai-dns.conf;
11180
+ ${renderDownloadErrorMaps()}
11091
11181
  ${renderResolvedUpstream("hai_serv_backend", "serv", port)}${options.storageBucket ? renderStorageUpstream(options.storageBucket, options.storageBasePath) : ""}server {
11092
11182
  listen ${webPort};
11093
11183
  server_name _;
@@ -11171,17 +11261,7 @@ function generateApplicationProxyConfig(options) {
11171
11261
  const bodyLimits = resolveTransportBodyLimits(maxPlaintextBytes);
11172
11262
  const trustedEdges = renderTrustedEdgeProxies(options.trustedEdgeProxies);
11173
11263
  const forwardedProtocol = options.trustedEdgeProxies?.length ? "$hai_request_scheme" : "$scheme";
11174
- return `include /tmp/hai-dns.conf;
11175
- ${renderResolvedUpstream("hai_application_backend", primaryService, port)}${renderTrustedEdgeProtocol(options.trustedEdgeProxies)}${bucket ? renderStorageUpstream(bucket, options.storageBasePath) : ""}server {
11176
- listen 8080;
11177
- server_name _;
11178
- ${trustedEdges} large_client_header_buffers 4 32k;
11179
- ${bucket ? renderStorageLocation(bucket, reservedPaths, options.storageBasePath) : ""} location = /_hai/gateway-health {
11180
- access_log off;
11181
- return 200 'ok';
11182
- }
11183
- location / {
11184
- client_max_body_size ${bodyLimits.encryptedPayloadBytes};
11264
+ const proxySettings = ` client_max_body_size ${bodyLimits.encryptedPayloadBytes};
11185
11265
  proxy_pass http://hai_application_backend;
11186
11266
  proxy_http_version 1.1;
11187
11267
  proxy_set_header Host $http_host;
@@ -11195,7 +11275,29 @@ ${bucket ? renderStorageLocation(bucket, reservedPaths, options.storageBasePath)
11195
11275
  proxy_request_buffering off;
11196
11276
  proxy_buffering off;
11197
11277
  proxy_buffer_size 32k;
11198
- proxy_buffers 8 32k;
11278
+ proxy_buffers 8 32k;`;
11279
+ return `include /tmp/hai-dns.conf;
11280
+ ${renderDownloadErrorMaps()}
11281
+ ${renderResolvedUpstream("hai_application_backend", primaryService, port)}${renderTrustedEdgeProtocol(options.trustedEdgeProxies)}${bucket ? renderStorageUpstream(bucket, options.storageBasePath) : ""}server {
11282
+ listen 8080;
11283
+ server_name _;
11284
+ ${trustedEdges} large_client_header_buffers 4 32k;
11285
+ ${bucket ? renderStorageLocation(bucket, reservedPaths, options.storageBasePath) : ""} location = /_hai/gateway-health {
11286
+ access_log off;
11287
+ return 200 'ok';
11288
+ }
11289
+ ${renderDownloadErrorLocation()} location / {
11290
+ recursive_error_pages on;
11291
+ error_page 418 = @hai_download_proxy;
11292
+ if ($hai_signed_download) { return 418; }
11293
+ ${proxySettings}
11294
+ }
11295
+ location @hai_download_proxy {
11296
+ access_log off;
11297
+ error_log /dev/null;
11298
+ proxy_intercept_errors on;
11299
+ error_page 404 = @hai_download_unavailable;
11300
+ ${proxySettings}
11199
11301
  }
11200
11302
  }
11201
11303
  `;
@@ -11234,24 +11336,35 @@ map "$hai_trusted_edge:$http_x_forwarded_proto" $hai_request_scheme {
11234
11336
  function renderWebLocation(webBasePath) {
11235
11337
  if (webBasePath && (!/^\/[\w.-]+(?:\/[\w.-]+)*$/.test(webBasePath) || webBasePath.split("/").some((segment) => segment === "." || segment === "..") || ["/health", "/_hai/web-health"].includes(webBasePath)))
11236
11338
  throw new Error("Invalid web base path");
11237
- return webBasePath ? ` absolute_redirect off;
11339
+ const fallback = `${renderDownloadErrorLocation()} location @hai_spa_fallback {
11340
+ error_page 418 = @hai_download_unavailable;
11341
+ if ($hai_signed_download) { return 418; }
11342
+ try_files /index.html =404;
11343
+ }
11344
+ `;
11345
+ return fallback + (webBasePath ? ` absolute_redirect off;
11238
11346
  location = ${webBasePath} {
11239
11347
  return 308 ${webBasePath}/$is_args$args;
11240
11348
  }
11241
11349
  location ${webBasePath}/ {
11242
11350
  rewrite ^${webBasePath.replaceAll(".", "\\.").replaceAll("-", "\\-")}/(.*)$ /$1 break;
11243
- try_files $uri/index.html $uri /index.html =404;
11351
+ try_files $uri/index.html $uri @hai_spa_fallback;
11352
+ }
11353
+ location / {
11354
+ error_page 418 = @hai_download_unavailable;
11355
+ if ($hai_signed_download) { return 418; }
11356
+ return 404;
11244
11357
  }
11245
- location / { return 404; }
11246
11358
  ` : ` location / {
11247
- try_files $uri $uri/ /index.html;
11359
+ try_files $uri $uri/ @hai_spa_fallback;
11248
11360
  }
11249
- `;
11361
+ `);
11250
11362
  }
11251
11363
  function generateStaticWebConfig(port, webBasePath) {
11252
11364
  if (!Number.isInteger(port) || port < 1 || port > 65535)
11253
11365
  throw new Error("Invalid web proxy target");
11254
- return `server {
11366
+ return `${renderDownloadErrorMaps()}
11367
+ server {
11255
11368
  listen ${port};
11256
11369
  server_name _;
11257
11370
  root /usr/share/nginx/html;
@@ -12042,7 +12155,7 @@ function createVercelProvider() {
12042
12155
  );
12043
12156
  token = apiToken;
12044
12157
  const username = user.user.username || user.user.email;
12045
- logger6.info("Vercel authenticated", { user: username });
12158
+ logger6.info("Vercel authenticated");
12046
12159
  return ok(username);
12047
12160
  } catch (error) {
12048
12161
  logger6.error("Vercel authentication failed", { error });
@@ -12252,7 +12365,7 @@ function createAliyunProvisioner() {
12252
12365
  accessKey = akId;
12253
12366
  secretKey = akSecret;
12254
12367
  signName = credentials.signName;
12255
- logger7.info("Aliyun authenticated", { accessKeyId: `${akId.slice(0, 6)}***` });
12368
+ logger7.info("Aliyun authenticated");
12256
12369
  return ok(akId);
12257
12370
  } catch (error) {
12258
12371
  logger7.error("Aliyun authentication failed", { error });
@@ -12493,7 +12606,7 @@ function createR2Provisioner() {
12493
12606
  token = apiTok;
12494
12607
  accessKey = credentials.accessKeyId;
12495
12608
  secretKey = credentials.secretAccessKey;
12496
- logger9.info("Cloudflare R2 authenticated", { accountId: acctId });
12609
+ logger9.info("Cloudflare R2 authenticated");
12497
12610
  return ok(acctId);
12498
12611
  } catch (error) {
12499
12612
  logger9.error("Cloudflare R2 authentication failed", { error });
@@ -12716,7 +12829,7 @@ function createUpstashProvisioner() {
12716
12829
  await listUpstashDatabases(userEmail, userKey);
12717
12830
  email = userEmail;
12718
12831
  apiKey = userKey;
12719
- logger11.info("Upstash authenticated", { email: userEmail });
12832
+ logger11.info("Upstash authenticated");
12720
12833
  return ok(userEmail);
12721
12834
  } catch (error) {
12722
12835
  logger11.error("Upstash authentication failed", { error });
@@ -12925,10 +13038,8 @@ var deploy = {
12925
13038
  } else {
12926
13039
  provider = createProviderByType(parsed.provider.type);
12927
13040
  const authResult = await provider.authenticate(parsed.provider.token);
12928
- if (!authResult.success) {
13041
+ if (!authResult.success)
12929
13042
  return err(authResult.error);
12930
- }
12931
- logger12.info("Provider authenticated", { provider: parsed.provider.type, user: authResult.data });
12932
13043
  }
12933
13044
  currentProvisioners.clear();
12934
13045
  if (parsed.services) {
@@ -12942,14 +13053,16 @@ var deploy = {
12942
13053
  if (!provAuthResult.success) {
12943
13054
  logger12.error("Provisioner authentication failed", {
12944
13055
  service: serviceType,
12945
- provisioner: serviceConfig.provisioner
13056
+ provisioner: serviceConfig.provisioner,
13057
+ errorCode: provAuthResult.error.code,
13058
+ traceId: provAuthResult.error.traceId
12946
13059
  });
12947
13060
  currentProvisioners.clear();
12948
13061
  return err(provAuthResult.error);
12949
13062
  }
12950
13063
  logger12.info("Provisioner authenticated", {
12951
13064
  service: serviceType,
12952
- user: provAuthResult.data
13065
+ provisioner: provisioner.name
12953
13066
  });
12954
13067
  currentProvisioners.set(serviceType, provisioner);
12955
13068
  }