@podosoft/podokit 1.5.0 → 1.6.1
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.md +2 -1
- package/dist/app-proxy-timeouts.d.ts +27 -0
- package/dist/app-proxy-timeouts.js +180 -0
- package/dist/assemble.d.ts +5 -2
- package/dist/assemble.js +14 -4
- package/dist/deploy-checks.d.ts +44 -0
- package/dist/deploy-checks.js +267 -0
- package/dist/deploy-compose-profile.d.ts +6 -2
- package/dist/deploy-compose-profile.js +15 -6
- package/dist/deploy-compose-render.d.ts +12 -4
- package/dist/deploy-compose-render.js +47 -18
- package/dist/deploy-compose.d.ts +2 -5
- package/dist/deploy-compose.js +7 -2
- package/dist/deploy-profile.d.ts +6 -2
- package/dist/deploy-profile.js +11 -7
- package/dist/deploy-render.d.ts +4 -1
- package/dist/deploy-render.js +27 -21
- package/dist/deploy-schema.d.ts +60 -2
- package/dist/deploy-schema.js +141 -14
- package/dist/deploy.d.ts +2 -5
- package/dist/deploy.js +5 -2
- package/dist/dev.d.ts +21 -0
- package/dist/dev.js +78 -3
- package/dist/eject.js +2 -0
- package/dist/index.js +85 -5
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +5 -2
- package/dist/lockfile.d.ts +24 -0
- package/dist/lockfile.js +74 -4
- package/dist/provider.js +10 -5
- package/dist/remove.js +1 -1
- package/dist/template-files.d.ts +42 -0
- package/dist/template-files.js +102 -0
- package/dist/templates/fullstack/AGENTS.md +30 -4
- package/dist/templates/fullstack/README.md +8 -7
- package/dist/templates/fullstack/apps/api/package.json +4 -4
- package/dist/templates/fullstack/apps/api/scripts/build.extensions.mjs +6 -0
- package/dist/templates/fullstack/apps/api/scripts/build.mjs +65 -16
- package/dist/templates/fullstack/apps/api/scripts/run-migrations.mjs +7 -3
- package/dist/templates/fullstack/apps/api/scripts/verify-api-contract.ts +7 -2
- package/dist/templates/fullstack/apps/api/src/app.spec.ts +22 -0
- package/dist/templates/fullstack/apps/api/src/app.ts +41 -25
- package/dist/templates/fullstack/apps/api/src/build.spec.ts +121 -0
- package/dist/templates/fullstack/apps/api/src/config/env.extensions.ts +20 -0
- package/dist/templates/fullstack/apps/api/src/config/env.validation.spec.ts +76 -0
- package/dist/templates/fullstack/apps/api/src/config/env.validation.ts +18 -3
- package/dist/templates/fullstack/apps/api/src/config/provider-selection.spec.ts +53 -0
- package/dist/templates/fullstack/apps/api/src/config/provider-selection.ts +79 -0
- package/dist/templates/fullstack/apps/api/src/config/providers.ts +7 -3
- package/dist/templates/fullstack/apps/api/src/core/api-contract.extensions.ts +6 -0
- package/dist/templates/fullstack/apps/api/src/core/api-contract.spec.ts +72 -4
- package/dist/templates/fullstack/apps/api/src/core/api-contract.ts +78 -6
- package/dist/templates/fullstack/apps/api/src/core/services.ts +13 -0
- package/dist/templates/fullstack/apps/api/src/database/data-source.spec.ts +27 -0
- package/dist/templates/fullstack/apps/api/src/database/data-source.ts +7 -10
- package/dist/templates/fullstack/apps/api/src/database/database.spec.ts +12 -0
- package/dist/templates/fullstack/apps/api/src/database/database.ts +2 -3
- package/dist/templates/fullstack/apps/api/src/database/entities.ts +12 -0
- package/dist/templates/fullstack/apps/api/src/database/migrate.ts +12 -2
- package/dist/templates/fullstack/apps/api/src/database/migration-files.spec.ts +116 -0
- package/dist/templates/fullstack/apps/api/src/database/migration-files.ts +134 -0
- package/dist/templates/fullstack/apps/api/src/database/migration-readiness.spec.ts +36 -0
- package/dist/templates/fullstack/apps/api/src/database/migration-readiness.ts +25 -0
- package/dist/templates/fullstack/apps/api/src/database/run-migrations.spec.ts +12 -1
- package/dist/templates/fullstack/apps/api/src/database/sqlite-migrator.ts +2 -36
- package/dist/templates/fullstack/apps/api/src/main.ts +2 -2
- package/dist/templates/fullstack/apps/api/tsconfig.json +2 -0
- package/dist/templates/fullstack/apps/web/package.json +1 -1
- package/dist/templates/fullstack/apps/web/src/app.css +3 -0
- package/dist/templates/fullstack/apps/web/src/app.extensions.css +11 -0
- package/dist/templates/fullstack/apps/web/src/lib/components/site-runtime.svelte +3 -1
- package/dist/templates/fullstack/apps/web/src/lib/server/api.test.ts +19 -0
- package/dist/templates/fullstack/apps/web/src/lib/server/api.ts +4 -1
- package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.test.ts +79 -0
- package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.ts +55 -1
- package/dist/templates/fullstack/apps/web/src/lib/server/proxy-timeouts.ts +2 -1
- package/dist/templates/fullstack/apps/web/tsconfig.json +1 -1
- package/dist/templates/fullstack/apps/web/vite.config.test.ts +33 -0
- package/dist/templates/fullstack/apps/web/vite.config.ts +55 -13
- package/dist/templates/fullstack/apps/web/vite.extensions.ts +23 -0
- package/dist/templates/fullstack/compose.dev.yaml +7 -4
- package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/SKILL.md +2 -2
- package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/references/profile.md +13 -3
- package/dist/templates/fullstack/dot-claude/skills/podokit-update/SKILL.md +5 -2
- package/dist/templates/fullstack/dot-env.docker.example +5 -2
- package/dist/templates/fullstack/dot-env.example +4 -0
- package/dist/templates/fullstack/dot-podokit/dev.json +1 -1
- package/dist/templates/fullstack/infra/k3s/configmap.yaml +1 -0
- package/dist/templates/fullstack/tests/README.md +50 -1
- package/dist/templates/fullstack/tests/helpers/app-paths.ts +18 -0
- package/dist/templates/fullstack/tests/helpers/paths.ts +33 -0
- package/dist/templates/fullstack/tests/helpers/seeded-admin.ts +15 -0
- package/dist/templates/fullstack/tests/package.json +3 -3
- package/dist/templates/fullstack/tests/playwright.config.ts +10 -4
- package/dist/templates/fullstack/tests/playwright.extensions.ts +149 -1
- package/dist/templates/fullstack/tests/run-e2e.ts +28 -0
- package/dist/templates/fullstack/tests/ui/smoke.ui.spec.ts +5 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.test.ts +37 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +15 -6
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flag-settings.test.ts +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flag-settings.ts +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flags.ts +12 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-nav.ts +11 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/nav.test.ts +53 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/nav.ts +35 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/registry.svelte.ts +15 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/account-page.svelte +11 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/admin-sidebar.svelte +2 -23
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.svelte +7 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.test.ts +30 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/impersonation-banner.svelte +3 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/settings/app-flags-settings.svelte +65 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +32 -23
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.test.ts +27 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/site-footer.svelte +4 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/site-runtime.svelte +3 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/theme-toggle.svelte +1 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/en.json +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/ko.json +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/app-hooks.ts +11 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/non-indexable-paths.ts +8 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/search-indexing.test.ts +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/search-indexing.ts +5 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(admin)/admin/settings/+page.svelte +1 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/account/+page.server.ts +2 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/account/+page.svelte +1 -5
- package/dist/templates/modules/admin-dashboard/files/dot-claude/skills/podokit-data-table/SKILL.md +2 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +159 -170
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +72 -81
- package/dist/templates/modules/admin-dashboard/files/tests/api/email.api.spec.ts +1 -6
- package/dist/templates/modules/admin-dashboard/files/tests/api/profile-image.api.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/api/site-settings.api.spec.ts +43 -53
- package/dist/templates/modules/admin-dashboard/files/tests/api/users-admin.api.spec.ts +3 -1
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +24 -6
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/app-accounts.ts +24 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/devices.ts +41 -11
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/disposable-users.ts +5 -3
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/hydration.ts +13 -4
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/mailpit.ts +3 -9
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/sms.ts +2 -4
- package/dist/templates/modules/admin-dashboard/files/tests/playwright.config.ts +14 -6
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account-flows.ui.spec.ts +4 -5
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +55 -55
- package/dist/templates/modules/admin-dashboard/files/tests/ui/app-flags.ui.spec.ts +63 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/appearance.ui.spec.ts +12 -16
- package/dist/templates/modules/admin-dashboard/files/tests/ui/audit.ui.spec.ts +3 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +32 -35
- package/dist/templates/modules/admin-dashboard/files/tests/ui/dashboard.ui.spec.ts +4 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email-admin.ui.spec.ts +1 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +1 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/general-effects.ui.spec.ts +82 -91
- package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +6 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/i18n.ui.spec.ts +16 -20
- package/dist/templates/modules/admin-dashboard/files/tests/ui/impersonation.ui.spec.ts +6 -3
- package/dist/templates/modules/admin-dashboard/files/tests/ui/login-methods.ui.spec.ts +2 -3
- package/dist/templates/modules/admin-dashboard/files/tests/ui/oidc.ui.spec.ts +2 -4
- package/dist/templates/modules/admin-dashboard/files/tests/ui/organizations.ui.spec.ts +5 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +7 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/same-origin-requests.ui.spec.ts +89 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +5 -4
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings-dialogs.ui.spec.ts +67 -73
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +84 -93
- package/dist/templates/modules/admin-dashboard/files/tests/ui/table-toolbar.ui.spec.ts +8 -7
- package/dist/templates/modules/admin-dashboard/files/tests/ui/theme-mode.ui.spec.ts +35 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/two-factor.ui.spec.ts +51 -54
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +10 -9
- package/dist/templates/modules/admin-dashboard/module.manifest.json +7 -2
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.module.ts +5 -1
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.spec.ts +17 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.ts +18 -0
- package/dist/templates/modules/audit-log/module.manifest.json +15 -3
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-app-name.spec.ts +21 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-config-store.ts +13 -1
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-environment.ts +9 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +19 -3
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.module.ts +2 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.runtime.spec.ts +10 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +7 -5
- package/dist/templates/modules/auth/files/apps/api/src/auth/schema-recheck.spec.ts +86 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/schema-recheck.ts +44 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.service.ts +2 -1
- package/dist/templates/modules/auth/files/apps/api/src/migrate.ts +3 -1
- package/dist/templates/modules/auth/files/apps/api/src/settings/app-flags.ts +9 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.spec.ts +43 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.ts +30 -5
- package/dist/templates/modules/auth/module.manifest.json +8 -2
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.spec.ts +107 -1
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.ts +113 -9
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.spec.ts +40 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.ts +52 -5
- package/dist/templates/modules/bullmq/files/apps/api/src/main-worker.ts +9 -7
- package/dist/templates/modules/bullmq/files/apps/api/src/worker.extensions.ts +13 -0
- package/dist/templates/modules/bullmq/module.manifest.json +5 -1
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.worker.spec.ts +2 -0
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.spec.ts +174 -1
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.ts +150 -3
- package/dist/templates/modules/jobs-local/module.manifest.json +3 -0
- package/dist/templates/modules/logging/files/apps/api/src/logging/logging.module.spec.ts +16 -0
- package/dist/templates/modules/logging/files/apps/api/src/logging/logging.module.ts +15 -10
- package/dist/templates/modules/mailer/module.manifest.json +1 -1
- package/dist/templates/modules/object-storage-local/files/tests/api/storage-local.api.spec.ts +3 -6
- package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +5 -4
- package/dist/templates/modules/object-storage-s3/files/tests/api/storage.api.spec.ts +3 -6
- package/dist/templates/modules/rate-limit/files/tests/api/rate-limit.api.spec.ts +45 -20
- package/dist/templates/modules/sse/files/tests/api/events.api.spec.ts +3 -6
- package/dist/templates/todo/AGENTS.md +5 -1
- package/dist/templates/todo/README.md +6 -4
- package/dist/text-diff.d.ts +12 -0
- package/dist/text-diff.js +111 -0
- package/dist/update.d.ts +61 -3
- package/dist/update.js +152 -27
- package/package.json +2 -2
- package/dist/templates/modules/admin-dashboard/files/tests/api/exclusive.api.spec.ts +0 -75
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/exclusive.ts +0 -80
package/dist/deploy-schema.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AppProxyTimeout, AppProxyTimeouts } from "./app-proxy-timeouts";
|
|
1
2
|
/**
|
|
2
3
|
* Validation primitives shared by every deployment driver.
|
|
3
4
|
*
|
|
@@ -30,6 +31,23 @@ export declare const SENSITIVE_KEY: RegExp;
|
|
|
30
31
|
export declare const STABLE_SEMVER_TAG_PATTERN = "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$";
|
|
31
32
|
/** Keys PodoKit derives itself, so a profile may not set them. */
|
|
32
33
|
export declare const MANAGED_RUNTIME_CONFIG_KEYS: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Keys that decide which forwarded address web and API record. They follow from
|
|
36
|
+
* the proxies in front of the deployment, so a profile states that one number and
|
|
37
|
+
* PodoKit renders the four values that agree with it.
|
|
38
|
+
*/
|
|
39
|
+
export declare const CLIENT_ADDRESS_KEYS: string[];
|
|
40
|
+
export declare function parseEdgeProxies(exposure: Record<string, unknown>, fallback: number): number;
|
|
41
|
+
/**
|
|
42
|
+
* What the web server and the API trust, given how many proxies each request to
|
|
43
|
+
* the web server has passed. Behind at least one, the web server takes its origin
|
|
44
|
+
* from X-Forwarded-Proto and X-Forwarded-Host and the client from the
|
|
45
|
+
* X-Forwarded-For entry the outermost trusted proxy wrote (XFF_DEPTH); with none
|
|
46
|
+
* it trusts no forwarded header. It forwards that one address to the API, so the
|
|
47
|
+
* API always trusts one hop: the web proxy, the Compose gateway, or an Ingress
|
|
48
|
+
* that trusts no proxy in front of it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function proxyTrustConfig(webProxyHops: number, trustForwardedOrigin?: boolean): Record<string, string>;
|
|
33
51
|
export declare function containsUrlCredentials(value: string): boolean;
|
|
34
52
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
35
53
|
export declare function assertOnlyKeys(value: Record<string, unknown>, allowed: string[], field: string): void;
|
|
@@ -50,13 +68,53 @@ export declare function validateImage(value: string, field: string): void;
|
|
|
50
68
|
export declare function validateImageRepository(value: string, field: string): void;
|
|
51
69
|
export declare function parseReleaseProfile(release: Record<string, unknown>): ReleaseProfile;
|
|
52
70
|
export declare function parseRuntimeConfig(runtimeConfig: Record<string, unknown>): Record<string, string>;
|
|
71
|
+
/**
|
|
72
|
+
* An application-owned PostgreSQL pool each API or worker process opens beside
|
|
73
|
+
* PodoKit's own. Either `sizeKey` names the runtimeConfig key that caps it, or
|
|
74
|
+
* `size` is the cap the application's code sets, such as a driver's default.
|
|
75
|
+
* `extraConnections` counts connections it opens beyond that cap, such as one to
|
|
76
|
+
* check a server that stopped answering.
|
|
77
|
+
*/
|
|
78
|
+
export type AppConnectionPool = {
|
|
79
|
+
sizeKey: string;
|
|
80
|
+
extraConnections: number;
|
|
81
|
+
} | {
|
|
82
|
+
size: number;
|
|
83
|
+
extraConnections: number;
|
|
84
|
+
};
|
|
85
|
+
/** What the connection budget needs to know about the database, beyond its mode. */
|
|
86
|
+
export interface PostgresBudgetProfile {
|
|
87
|
+
/** The server's max_connections; PodoKit sets it on a database it runs. */
|
|
88
|
+
maxConnections?: number;
|
|
89
|
+
appPools?: AppConnectionPool[];
|
|
90
|
+
}
|
|
91
|
+
export declare function parsePostgresBudget(value: Record<string, unknown>, field: string, runtimeConfig: Record<string, string>): PostgresBudgetProfile;
|
|
92
|
+
/** Bun closes silent connections on a 4-second tick, up to 4 seconds before their idle timeout. */
|
|
93
|
+
export declare const IDLE_TICK_SECONDS = 4;
|
|
94
|
+
export declare const MAX_IDLE_TIMEOUT_SECONDS = 255;
|
|
95
|
+
export declare const IDLE_TIMEOUT_KEYS: readonly ["CONNECTION_IDLE_TIMEOUT", "API_IDLE_TIMEOUT"];
|
|
96
|
+
/** The longest the /api proxy waits for response headers, and what sets it. */
|
|
97
|
+
export interface LongestProxyWait {
|
|
98
|
+
/** Milliseconds, or null for a route without a deadline. */
|
|
99
|
+
timeoutMs: number | null;
|
|
100
|
+
source: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* BACKEND_PROXY_TIMEOUT_MS, or the application's longest APP_PROXY_TIMEOUTS
|
|
104
|
+
* entry when that waits longer; an entry without a deadline wins outright.
|
|
105
|
+
*/
|
|
106
|
+
export declare function longestProxyWait(runtimeConfig: Record<string, string>, entries: AppProxyTimeout[]): LongestProxyWait;
|
|
107
|
+
export declare function describeProxyWait(wait: LongestProxyWait): string;
|
|
108
|
+
/** Whether an idle timeout keeps a silent connection open for the whole wait. */
|
|
109
|
+
export declare function idleTimeoutCovers(seconds: number, wait: LongestProxyWait): boolean;
|
|
53
110
|
/**
|
|
54
111
|
* The idle timeouts of the web server (CONNECTION_IDLE_TIMEOUT) and the API
|
|
55
112
|
* (API_IDLE_TIMEOUT), both run by Bun. Each is the profile's own value, or else
|
|
56
113
|
* the fewest seconds that keep a silent connection open past the /api proxy's
|
|
57
|
-
*
|
|
114
|
+
* longest wait: 35 for the default 30000 ms, and 0 (off) when an application
|
|
115
|
+
* route has no deadline.
|
|
58
116
|
*/
|
|
59
|
-
export declare function idleTimeoutConfig(runtimeConfig: Record<string, string
|
|
117
|
+
export declare function idleTimeoutConfig(runtimeConfig: Record<string, string>, proxyTimeouts: AppProxyTimeouts): Record<string, string>;
|
|
60
118
|
export declare function validateHttpsOrigin(value: string): void;
|
|
61
119
|
export declare function parseVerification(verification: Record<string, unknown>): VerificationProfile;
|
|
62
120
|
export declare function profileDirectory(projectRoot: string): string;
|
package/dist/deploy-schema.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MANAGED_RUNTIME_CONFIG_KEYS = exports.STABLE_SEMVER_TAG_PATTERN = exports.SENSITIVE_KEY = exports.DNS_LABEL = exports.PROFILE_NAME = void 0;
|
|
3
|
+
exports.IDLE_TIMEOUT_KEYS = exports.MAX_IDLE_TIMEOUT_SECONDS = exports.IDLE_TICK_SECONDS = exports.CLIENT_ADDRESS_KEYS = exports.MANAGED_RUNTIME_CONFIG_KEYS = exports.STABLE_SEMVER_TAG_PATTERN = exports.SENSITIVE_KEY = exports.DNS_LABEL = exports.PROFILE_NAME = void 0;
|
|
4
|
+
exports.parseEdgeProxies = parseEdgeProxies;
|
|
5
|
+
exports.proxyTrustConfig = proxyTrustConfig;
|
|
4
6
|
exports.containsUrlCredentials = containsUrlCredentials;
|
|
5
7
|
exports.isRecord = isRecord;
|
|
6
8
|
exports.assertOnlyKeys = assertOnlyKeys;
|
|
@@ -17,6 +19,10 @@ exports.validateImage = validateImage;
|
|
|
17
19
|
exports.validateImageRepository = validateImageRepository;
|
|
18
20
|
exports.parseReleaseProfile = parseReleaseProfile;
|
|
19
21
|
exports.parseRuntimeConfig = parseRuntimeConfig;
|
|
22
|
+
exports.parsePostgresBudget = parsePostgresBudget;
|
|
23
|
+
exports.longestProxyWait = longestProxyWait;
|
|
24
|
+
exports.describeProxyWait = describeProxyWait;
|
|
25
|
+
exports.idleTimeoutCovers = idleTimeoutCovers;
|
|
20
26
|
exports.idleTimeoutConfig = idleTimeoutConfig;
|
|
21
27
|
exports.validateHttpsOrigin = validateHttpsOrigin;
|
|
22
28
|
exports.parseVerification = parseVerification;
|
|
@@ -36,6 +42,38 @@ exports.MANAGED_RUNTIME_CONFIG_KEYS = [
|
|
|
36
42
|
"PROTOCOL_HEADER",
|
|
37
43
|
"HOST_HEADER",
|
|
38
44
|
];
|
|
45
|
+
/**
|
|
46
|
+
* Keys that decide which forwarded address web and API record. They follow from
|
|
47
|
+
* the proxies in front of the deployment, so a profile states that one number and
|
|
48
|
+
* PodoKit renders the four values that agree with it.
|
|
49
|
+
*/
|
|
50
|
+
exports.CLIENT_ADDRESS_KEYS = ["ADDRESS_HEADER", "XFF_DEPTH", "TRUSTED_PROXY_HOPS", "TRUSTED_PROXY_HEADER"];
|
|
51
|
+
/** Edge proxies are counted, not configured; more than this is a typo. */
|
|
52
|
+
const MAX_EDGE_PROXIES = 8;
|
|
53
|
+
function parseEdgeProxies(exposure, fallback) {
|
|
54
|
+
const value = exposure.edgeProxies ?? fallback;
|
|
55
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > MAX_EDGE_PROXIES) {
|
|
56
|
+
throw new Error(`Deployment profile field "exposure.edgeProxies" must be an integer from 0 to ${MAX_EDGE_PROXIES}.`);
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* What the web server and the API trust, given how many proxies each request to
|
|
62
|
+
* the web server has passed. Behind at least one, the web server takes its origin
|
|
63
|
+
* from X-Forwarded-Proto and X-Forwarded-Host and the client from the
|
|
64
|
+
* X-Forwarded-For entry the outermost trusted proxy wrote (XFF_DEPTH); with none
|
|
65
|
+
* it trusts no forwarded header. It forwards that one address to the API, so the
|
|
66
|
+
* API always trusts one hop: the web proxy, the Compose gateway, or an Ingress
|
|
67
|
+
* that trusts no proxy in front of it.
|
|
68
|
+
*/
|
|
69
|
+
function proxyTrustConfig(webProxyHops, trustForwardedOrigin = webProxyHops > 0) {
|
|
70
|
+
return {
|
|
71
|
+
...(trustForwardedOrigin ? { PROTOCOL_HEADER: "x-forwarded-proto", HOST_HEADER: "x-forwarded-host" } : {}),
|
|
72
|
+
...(webProxyHops > 0 ? { ADDRESS_HEADER: "x-forwarded-for", XFF_DEPTH: String(webProxyHops) } : {}),
|
|
73
|
+
TRUSTED_PROXY_HOPS: "1",
|
|
74
|
+
TRUSTED_PROXY_HEADER: "x-forwarded-for",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
39
77
|
/** Keys the generated API no longer reads; it refuses to start while they are set. */
|
|
40
78
|
const RENAMED_RUNTIME_CONFIG_KEYS = {
|
|
41
79
|
RATE_LIMIT_TRUSTED_PROXY_HOPS: "TRUSTED_PROXY_HOPS",
|
|
@@ -189,6 +227,10 @@ function parseRuntimeConfig(runtimeConfig) {
|
|
|
189
227
|
if (exports.MANAGED_RUNTIME_CONFIG_KEYS.includes(key)) {
|
|
190
228
|
throw new Error(`Deployment runtimeConfig key "${key}" is managed by PodoKit.`);
|
|
191
229
|
}
|
|
230
|
+
if (exports.CLIENT_ADDRESS_KEYS.includes(key)) {
|
|
231
|
+
throw new Error(`Deployment runtimeConfig key "${key}" is derived from exposure.edgeProxies, the number of reverse ` +
|
|
232
|
+
"proxies in front of the deployment; remove it and set exposure.edgeProxies instead.");
|
|
233
|
+
}
|
|
192
234
|
const renamed = RENAMED_RUNTIME_CONFIG_KEYS[key];
|
|
193
235
|
if (renamed) {
|
|
194
236
|
throw new Error(`Deployment runtimeConfig key "${key}" was renamed to "${renamed}".`);
|
|
@@ -203,33 +245,118 @@ function parseRuntimeConfig(runtimeConfig) {
|
|
|
203
245
|
}
|
|
204
246
|
return parsed;
|
|
205
247
|
}
|
|
248
|
+
/** PostgreSQL's own ceiling for max_connections. */
|
|
249
|
+
const MAX_POSTGRES_CONNECTIONS = 262_143;
|
|
250
|
+
/** Pools whose sizes the connection budget already reads. */
|
|
251
|
+
const PODOKIT_POOL_KEYS = ["DB_POOL_MAX", "DB_AUTH_POOL_MAX"];
|
|
252
|
+
function parsePostgresBudget(value, field, runtimeConfig) {
|
|
253
|
+
const parsed = {};
|
|
254
|
+
if (value.maxConnections !== undefined) {
|
|
255
|
+
const maxConnections = value.maxConnections;
|
|
256
|
+
if (typeof maxConnections !== "number" ||
|
|
257
|
+
!Number.isInteger(maxConnections) ||
|
|
258
|
+
maxConnections < 4 ||
|
|
259
|
+
maxConnections > MAX_POSTGRES_CONNECTIONS) {
|
|
260
|
+
throw new Error(`Deployment profile field "${field}.maxConnections" must be an integer from 4 to ${MAX_POSTGRES_CONNECTIONS}.`);
|
|
261
|
+
}
|
|
262
|
+
parsed.maxConnections = maxConnections;
|
|
263
|
+
}
|
|
264
|
+
if (value.appPools !== undefined) {
|
|
265
|
+
if (!Array.isArray(value.appPools)) {
|
|
266
|
+
throw new Error(`Deployment profile field "${field}.appPools" must be an array.`);
|
|
267
|
+
}
|
|
268
|
+
const seen = new Set();
|
|
269
|
+
parsed.appPools = value.appPools.map((entry, index) => {
|
|
270
|
+
const entryField = `${field}.appPools[${index}]`;
|
|
271
|
+
if (!isRecord(entry))
|
|
272
|
+
throw new Error(`Deployment profile field "${entryField}" must be an object.`);
|
|
273
|
+
assertOnlyKeys(entry, ["sizeKey", "size", "extraConnections"], entryField);
|
|
274
|
+
if ((entry.sizeKey === undefined) === (entry.size === undefined)) {
|
|
275
|
+
throw new Error(`Deployment profile field "${entryField}" must set either sizeKey, the runtimeConfig key that caps the pool, ` +
|
|
276
|
+
"or size, the cap the application sets itself.");
|
|
277
|
+
}
|
|
278
|
+
const extraConnections = entry.extraConnections ?? 0;
|
|
279
|
+
if (typeof extraConnections !== "number" || !Number.isInteger(extraConnections) || extraConnections < 0) {
|
|
280
|
+
throw new Error(`Deployment profile field "${entryField}.extraConnections" must be a non-negative integer.`);
|
|
281
|
+
}
|
|
282
|
+
if (entry.size !== undefined) {
|
|
283
|
+
const size = entry.size;
|
|
284
|
+
if (typeof size !== "number" || !Number.isInteger(size) || size < 1 || size > MAX_POSTGRES_CONNECTIONS) {
|
|
285
|
+
throw new Error(`Deployment profile field "${entryField}.size" must be an integer from 1 to ${MAX_POSTGRES_CONNECTIONS}.`);
|
|
286
|
+
}
|
|
287
|
+
return { size, extraConnections };
|
|
288
|
+
}
|
|
289
|
+
const sizeKey = requiredString(entry, "sizeKey");
|
|
290
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(sizeKey) || PODOKIT_POOL_KEYS.includes(sizeKey) || seen.has(sizeKey)) {
|
|
291
|
+
throw new Error(`Deployment profile field "${entryField}.sizeKey" must name another pool's runtimeConfig key, once; ` +
|
|
292
|
+
`${PODOKIT_POOL_KEYS.join(" and ")} are counted already.`);
|
|
293
|
+
}
|
|
294
|
+
seen.add(sizeKey);
|
|
295
|
+
if (!/^[1-9][0-9]*$/.test(runtimeConfig[sizeKey] ?? "")) {
|
|
296
|
+
throw new Error(`Deployment profile field "${entryField}.sizeKey" names ${sizeKey}, which runtimeConfig must set to a positive integer.`);
|
|
297
|
+
}
|
|
298
|
+
return { sizeKey, extraConnections };
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
return parsed;
|
|
302
|
+
}
|
|
206
303
|
/** Bun closes silent connections on a 4-second tick, up to 4 seconds before their idle timeout. */
|
|
207
|
-
|
|
208
|
-
|
|
304
|
+
exports.IDLE_TICK_SECONDS = 4;
|
|
305
|
+
exports.MAX_IDLE_TIMEOUT_SECONDS = 255;
|
|
209
306
|
const DEFAULT_BACKEND_PROXY_TIMEOUT_MS = 30_000;
|
|
307
|
+
exports.IDLE_TIMEOUT_KEYS = ["CONNECTION_IDLE_TIMEOUT", "API_IDLE_TIMEOUT"];
|
|
308
|
+
/**
|
|
309
|
+
* BACKEND_PROXY_TIMEOUT_MS, or the application's longest APP_PROXY_TIMEOUTS
|
|
310
|
+
* entry when that waits longer; an entry without a deadline wins outright.
|
|
311
|
+
*/
|
|
312
|
+
function longestProxyWait(runtimeConfig, entries) {
|
|
313
|
+
const proxyTimeout = runtimeConfig.BACKEND_PROXY_TIMEOUT_MS?.trim();
|
|
314
|
+
let longest = {
|
|
315
|
+
timeoutMs: proxyTimeout && /^\d+$/.test(proxyTimeout) ? Number(proxyTimeout) : DEFAULT_BACKEND_PROXY_TIMEOUT_MS,
|
|
316
|
+
source: "BACKEND_PROXY_TIMEOUT_MS",
|
|
317
|
+
};
|
|
318
|
+
for (const entry of entries) {
|
|
319
|
+
if (longest.timeoutMs !== null && (entry.timeoutMs === null || entry.timeoutMs > longest.timeoutMs)) {
|
|
320
|
+
longest = { timeoutMs: entry.timeoutMs, source: `APP_PROXY_TIMEOUTS entry ${entry.pathPrefix}` };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return longest;
|
|
324
|
+
}
|
|
325
|
+
function describeProxyWait(wait) {
|
|
326
|
+
return `${wait.timeoutMs === null ? "no deadline" : `${wait.timeoutMs} ms`}, ${wait.source}`;
|
|
327
|
+
}
|
|
328
|
+
/** Whether an idle timeout keeps a silent connection open for the whole wait. */
|
|
329
|
+
function idleTimeoutCovers(seconds, wait) {
|
|
330
|
+
return seconds === 0 || (wait.timeoutMs !== null && (seconds - exports.IDLE_TICK_SECONDS) * 1000 > wait.timeoutMs);
|
|
331
|
+
}
|
|
210
332
|
/**
|
|
211
333
|
* The idle timeouts of the web server (CONNECTION_IDLE_TIMEOUT) and the API
|
|
212
334
|
* (API_IDLE_TIMEOUT), both run by Bun. Each is the profile's own value, or else
|
|
213
335
|
* the fewest seconds that keep a silent connection open past the /api proxy's
|
|
214
|
-
*
|
|
336
|
+
* longest wait: 35 for the default 30000 ms, and 0 (off) when an application
|
|
337
|
+
* route has no deadline.
|
|
215
338
|
*/
|
|
216
|
-
function idleTimeoutConfig(runtimeConfig) {
|
|
217
|
-
const proxyTimeout = runtimeConfig.BACKEND_PROXY_TIMEOUT_MS?.trim();
|
|
218
|
-
const proxyMs = proxyTimeout && /^\d+$/.test(proxyTimeout) ? Number(proxyTimeout) : DEFAULT_BACKEND_PROXY_TIMEOUT_MS;
|
|
219
|
-
const derived = Math.floor(proxyMs / 1000) + IDLE_TICK_SECONDS + 1;
|
|
339
|
+
function idleTimeoutConfig(runtimeConfig, proxyTimeouts) {
|
|
220
340
|
const config = {};
|
|
221
|
-
for (const name of
|
|
341
|
+
for (const name of exports.IDLE_TIMEOUT_KEYS) {
|
|
222
342
|
const configured = runtimeConfig[name];
|
|
223
343
|
if (configured !== undefined) {
|
|
224
344
|
config[name] = configured;
|
|
345
|
+
continue;
|
|
225
346
|
}
|
|
226
|
-
|
|
227
|
-
throw new Error(
|
|
228
|
-
|
|
347
|
+
if ("error" in proxyTimeouts)
|
|
348
|
+
throw new Error(proxyTimeouts.error);
|
|
349
|
+
const wait = longestProxyWait(runtimeConfig, proxyTimeouts.entries);
|
|
350
|
+
if (wait.timeoutMs === null) {
|
|
351
|
+
config[name] = "0";
|
|
352
|
+
continue;
|
|
229
353
|
}
|
|
230
|
-
|
|
231
|
-
|
|
354
|
+
const derived = Math.floor(wait.timeoutMs / 1000) + exports.IDLE_TICK_SECONDS + 1;
|
|
355
|
+
if (derived > exports.MAX_IDLE_TIMEOUT_SECONDS) {
|
|
356
|
+
throw new Error(`The /api proxy waits up to ${wait.timeoutMs} ms (${wait.source}), longer than the longest idle timeout ` +
|
|
357
|
+
`(${exports.MAX_IDLE_TIMEOUT_SECONDS} seconds); set ${name} to "0" to turn it off.`);
|
|
232
358
|
}
|
|
359
|
+
config[name] = String(derived);
|
|
233
360
|
}
|
|
234
361
|
return config;
|
|
235
362
|
}
|
package/dist/deploy.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
+
import { type DeployFinding } from "./deploy-checks";
|
|
1
2
|
import type { CommandRunner } from "./dev";
|
|
2
3
|
import { type DeploymentImages } from "./deploy-render";
|
|
3
|
-
export
|
|
4
|
-
code: string;
|
|
5
|
-
ok: boolean;
|
|
6
|
-
message: string;
|
|
7
|
-
}
|
|
4
|
+
export type DeploymentDoctorFinding = DeployFinding;
|
|
8
5
|
export interface DeploymentPlanAction {
|
|
9
6
|
order: number;
|
|
10
7
|
kind: "dependencies" | "migration" | "application" | "verification" | "rollback";
|
package/dist/deploy.js
CHANGED
|
@@ -14,6 +14,8 @@ const node_fs_1 = require("node:fs");
|
|
|
14
14
|
const node_os_1 = require("node:os");
|
|
15
15
|
const node_path_1 = require("node:path");
|
|
16
16
|
const api_paths_1 = require("./api-paths");
|
|
17
|
+
const app_proxy_timeouts_1 = require("./app-proxy-timeouts");
|
|
18
|
+
const deploy_checks_1 = require("./deploy-checks");
|
|
17
19
|
const deploy_profile_1 = require("./deploy-profile");
|
|
18
20
|
const deploy_render_1 = require("./deploy-render");
|
|
19
21
|
function defaultRunner(command, args, options) {
|
|
@@ -143,7 +145,7 @@ function apiSecretRequirements(profile) {
|
|
|
143
145
|
}
|
|
144
146
|
function doctorDeployment(projectRoot, profileName, runner = defaultRunner) {
|
|
145
147
|
const profile = (0, deploy_profile_1.loadDeploymentProfile)(projectRoot, profileName);
|
|
146
|
-
const findings =
|
|
148
|
+
const findings = (0, deploy_checks_1.profileFindings)(projectRoot, profile);
|
|
147
149
|
const fingerprint = inspectClusterFingerprint(profile.target.context, runner);
|
|
148
150
|
findings.push({
|
|
149
151
|
code: "cluster-fingerprint",
|
|
@@ -496,11 +498,12 @@ function planDeployment(projectRoot, profileName, release, runner = defaultRunne
|
|
|
496
498
|
warnings: [
|
|
497
499
|
"Application rollback does not reverse database migrations.",
|
|
498
500
|
"PersistentVolumeClaims and Kubernetes Secrets are never deleted by apply or rollback.",
|
|
501
|
+
...[(0, deploy_checks_1.connectionBudgetWarning)(profile, (0, deploy_checks_1.installedModules)(projectRoot))].filter((warning) => warning !== null),
|
|
499
502
|
],
|
|
500
503
|
};
|
|
501
504
|
const temporaryRoot = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "podokit-deploy-plan-"));
|
|
502
505
|
try {
|
|
503
|
-
const runtime = (0, deploy_render_1.renderDeployment)(temporaryRoot, profileName, profile, release, images, state.rolloutStateDigest);
|
|
506
|
+
const runtime = (0, deploy_render_1.renderDeployment)(temporaryRoot, profileName, profile, release, images, state.rolloutStateDigest, (0, app_proxy_timeouts_1.readAppProxyTimeouts)(projectRoot));
|
|
504
507
|
return { ...base, planHash: planDigest(base, runtime) };
|
|
505
508
|
}
|
|
506
509
|
finally {
|
package/dist/dev.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ export interface DevConfig {
|
|
|
9
9
|
export interface DevRuntime {
|
|
10
10
|
alias: string;
|
|
11
11
|
config: DevConfig;
|
|
12
|
+
/** Browser origins of the development stack, for CORS_ORIGIN. */
|
|
13
|
+
origins: string[];
|
|
14
|
+
/** BETTER_AUTH_URL for the containers. */
|
|
15
|
+
authUrl: string;
|
|
12
16
|
projectRoot: string;
|
|
13
17
|
routeId: string;
|
|
14
18
|
runtimeComposePath: string;
|
|
@@ -22,8 +26,25 @@ export type CommandRunner = (command: string, args: string[], options: {
|
|
|
22
26
|
capture: boolean;
|
|
23
27
|
}) => CommandResult;
|
|
24
28
|
export declare function readDevConfig(projectRoot: string): DevConfig;
|
|
29
|
+
/**
|
|
30
|
+
* The origins a browser reaches the development stack on: the gateway
|
|
31
|
+
* hostname, the HTTPS tunnel origin when one is declared, and plain localhost
|
|
32
|
+
* for the compose file's own proxy.
|
|
33
|
+
*/
|
|
34
|
+
export declare function devOrigins(config: Pick<DevConfig, "hostname" | "publicUrl">): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Template variables for files that name the development host. They follow
|
|
37
|
+
* `.podokit/dev.json` when it exists and is valid, so a renamed host renders
|
|
38
|
+
* the same way it is routed; otherwise the project name's default.
|
|
39
|
+
*/
|
|
40
|
+
export declare function devTemplateVars(projectName: string, projectRoot?: string): Record<string, string>;
|
|
25
41
|
export declare function resolveDevRuntime(projectRoot: string): DevRuntime;
|
|
26
42
|
export declare function installedDevProfiles(projectRoot: string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* The compose overlay `podo dev` adds. Besides the gateway network it sets
|
|
45
|
+
* CORS_ORIGIN and BETTER_AUTH_URL for web and api from `.podokit/dev.json`, so
|
|
46
|
+
* the browser origins follow the routed hostname whatever `.env.docker` says.
|
|
47
|
+
*/
|
|
27
48
|
export declare function renderRuntimeCompose(runtime: DevRuntime): string;
|
|
28
49
|
export declare function renderRoute(runtime: DevRuntime): string;
|
|
29
50
|
/**
|
package/dist/dev.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.readDevConfig = readDevConfig;
|
|
4
|
+
exports.devOrigins = devOrigins;
|
|
5
|
+
exports.devTemplateVars = devTemplateVars;
|
|
4
6
|
exports.resolveDevRuntime = resolveDevRuntime;
|
|
5
7
|
exports.installedDevProfiles = installedDevProfiles;
|
|
6
8
|
exports.renderRuntimeCompose = renderRuntimeCompose;
|
|
@@ -106,6 +108,68 @@ function readDevConfig(projectRoot) {
|
|
|
106
108
|
apiPaths: (0, api_paths_1.parseApiPaths)(config.apiPaths ?? [], `${DEV_CONFIG}.apiPaths`, webSocketPaths),
|
|
107
109
|
};
|
|
108
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The origins a browser reaches the development stack on: the gateway
|
|
113
|
+
* hostname, the HTTPS tunnel origin when one is declared, and plain localhost
|
|
114
|
+
* for the compose file's own proxy.
|
|
115
|
+
*/
|
|
116
|
+
function devOrigins(config) {
|
|
117
|
+
return [
|
|
118
|
+
...new Set([
|
|
119
|
+
`http://${config.hostname}`,
|
|
120
|
+
...(config.publicUrl ? [new URL(config.publicUrl).origin] : []),
|
|
121
|
+
"http://localhost",
|
|
122
|
+
]),
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Template variables for files that name the development host. They follow
|
|
127
|
+
* `.podokit/dev.json` when it exists and is valid, so a renamed host renders
|
|
128
|
+
* the same way it is routed; otherwise the project name's default.
|
|
129
|
+
*/
|
|
130
|
+
function devTemplateVars(projectName, projectRoot) {
|
|
131
|
+
let config = { hostname: `${projectName}.localhost` };
|
|
132
|
+
const path = projectRoot ? (0, node_path_1.join)(projectRoot, DEV_CONFIG) : undefined;
|
|
133
|
+
if (path && (0, node_fs_1.existsSync)(path)) {
|
|
134
|
+
try {
|
|
135
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
|
|
136
|
+
if (typeof parsed.hostname === "string") {
|
|
137
|
+
validateHostname(parsed.hostname);
|
|
138
|
+
if (parsed.publicUrl)
|
|
139
|
+
validatePublicUrl(parsed.publicUrl);
|
|
140
|
+
config = { hostname: parsed.hostname, publicUrl: parsed.publicUrl };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// podo dev reports an unreadable dev.json; rendering keeps the default.
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
devHostname: config.hostname,
|
|
149
|
+
devCorsOrigin: devOrigins(config).join(","),
|
|
150
|
+
devAuthUrl: `http://${config.hostname}`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* BETTER_AUTH_URL for the containers: the gateway origin, or the origin named
|
|
155
|
+
* by PODOKIT_DEV_AUTH_URL for an OAuth round trip through `publicUrl`.
|
|
156
|
+
*/
|
|
157
|
+
function devAuthUrl(config, origins) {
|
|
158
|
+
const requested = process.env.PODOKIT_DEV_AUTH_URL?.trim();
|
|
159
|
+
if (!requested)
|
|
160
|
+
return `http://${config.hostname}`;
|
|
161
|
+
let origin;
|
|
162
|
+
try {
|
|
163
|
+
origin = new URL(requested).origin;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
origin = "";
|
|
167
|
+
}
|
|
168
|
+
if (!origins.includes(origin) || origin === "http://localhost") {
|
|
169
|
+
throw new Error(`PODOKIT_DEV_AUTH_URL must be http://${config.hostname}${config.publicUrl ? ` or ${new URL(config.publicUrl).origin}` : ""} (from ${DEV_CONFIG}).`);
|
|
170
|
+
}
|
|
171
|
+
return origin;
|
|
172
|
+
}
|
|
109
173
|
function stableId(projectRoot) {
|
|
110
174
|
return (0, node_crypto_1.createHash)("sha256").update(projectRoot).digest("hex").slice(0, 10);
|
|
111
175
|
}
|
|
@@ -118,9 +182,12 @@ function resolveDevRuntime(projectRoot) {
|
|
|
118
182
|
const config = readDevConfig(root);
|
|
119
183
|
const id = stableId(root);
|
|
120
184
|
const projectSlug = slug(config.hostname.replace(/\.localhost$/, ""));
|
|
185
|
+
const origins = devOrigins(config);
|
|
121
186
|
return {
|
|
122
187
|
alias: `podokit-${projectSlug}-${id}`,
|
|
123
188
|
config,
|
|
189
|
+
origins,
|
|
190
|
+
authUrl: devAuthUrl(config, origins),
|
|
124
191
|
projectRoot: root,
|
|
125
192
|
routeId: `route-${projectSlug}-${id}`,
|
|
126
193
|
runtimeComposePath: (0, node_path_1.join)(root, RUNTIME_COMPOSE),
|
|
@@ -143,11 +210,19 @@ function routesToApi(config) {
|
|
|
143
210
|
function anyOf(matchers) {
|
|
144
211
|
return matchers.length > 1 ? `(${matchers.join(" || ")})` : (matchers[0] ?? "");
|
|
145
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* The compose overlay `podo dev` adds. Besides the gateway network it sets
|
|
215
|
+
* CORS_ORIGIN and BETTER_AUTH_URL for web and api from `.podokit/dev.json`, so
|
|
216
|
+
* the browser origins follow the routed hostname whatever `.env.docker` says.
|
|
217
|
+
*/
|
|
146
218
|
function renderRuntimeCompose(runtime) {
|
|
147
|
-
const
|
|
148
|
-
|
|
219
|
+
const environment = ` environment:\n` +
|
|
220
|
+
` CORS_ORIGIN: ${JSON.stringify(runtime.origins.join(","))}\n` +
|
|
221
|
+
` BETTER_AUTH_URL: ${JSON.stringify(runtime.authUrl)}\n`;
|
|
222
|
+
const apiNetworks = routesToApi(runtime.config)
|
|
223
|
+
? ` networks:\n ${GATEWAY_NETWORK}:\n aliases: [${runtime.alias}-api]\n`
|
|
149
224
|
: "";
|
|
150
|
-
return `# Generated by podo dev. Do not edit.\nservices:\n traefik:\n profiles: [podokit-legacy-proxy]\n web:\n networks:\n ${GATEWAY_NETWORK}:\n aliases: [${runtime.alias}]\n${
|
|
225
|
+
return `# Generated by podo dev. Do not edit.\nservices:\n traefik:\n profiles: [podokit-legacy-proxy]\n web:\n${environment} networks:\n ${GATEWAY_NETWORK}:\n aliases: [${runtime.alias}]\n api:\n${environment}${apiNetworks}networks:\n ${GATEWAY_NETWORK}:\n external: true\n name: ${GATEWAY_NETWORK}\n`;
|
|
151
226
|
}
|
|
152
227
|
function renderRoute(runtime) {
|
|
153
228
|
const { alias, config, routeId } = runtime;
|
package/dist/eject.js
CHANGED