@wtfalch/foundry 0.8.0 → 0.10.0
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/dist/bootstrap/declare.js +1 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/migrations/0029_hosting_entries.sql +109 -0
- package/dist/migrations/0030_hosting_entry_hostnames.sql +24 -0
- package/dist/org.d.ts +87 -29
- package/dist/org.js +180 -27
- package/dist/plan/agora.d.ts +7 -0
- package/dist/plan/agora.js +9 -0
- package/dist/plan/broker-entries.d.ts +104 -0
- package/dist/plan/broker-entries.js +214 -0
- package/dist/plan/diff.d.ts +2 -0
- package/dist/plan/diff.js +274 -50
- package/dist/plan/executor.d.ts +89 -29
- package/dist/plan/executor.js +495 -233
- package/dist/plan/hostnames.d.ts +24 -0
- package/dist/plan/hostnames.js +56 -0
- package/dist/plan/index.d.ts +3 -1
- package/dist/plan/index.js +2 -1
- package/dist/plan/locked-hostnames.d.ts +8 -0
- package/dist/plan/locked-hostnames.js +26 -0
- package/dist/plan/login-host.d.ts +1 -1
- package/dist/plan/login-host.js +5 -2
- package/dist/plan/plan-apply.d.ts +16 -0
- package/dist/plan/plan-apply.js +48 -11
- package/dist/plan/platform-diff.d.ts +69 -10
- package/dist/plan/platform-diff.js +133 -14
- package/dist/plan/platform-providers.d.ts +102 -2
- package/dist/plan/platform-providers.js +486 -40
- package/dist/plan/zitadel-system-api.d.ts +64 -0
- package/dist/plan/zitadel-system-api.js +125 -0
- package/dist/platform.d.ts +1 -1
- package/dist/store/postgres.js +12 -12
- package/dist/store/schema.d.ts +20 -1
- package/dist/store/schema.js +7 -1
- package/package.json +1 -1
package/dist/plan/executor.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OrgMailOptions, OrgMailOrg, OrgMailResult } from '@wtfalch/email/postmaster';
|
|
2
2
|
import type { DeclaredOrg, HostingBuildPack } from '../org.js';
|
|
3
3
|
import type { DeclaredOrgStore } from '../store.js';
|
|
4
|
+
import { type BrokerPort } from './broker-entries.js';
|
|
4
5
|
import type { StepAction } from './diff.js';
|
|
5
6
|
import { type LoginHostBindingsPort } from './login-host.js';
|
|
6
7
|
import { type MailCopyPort } from './mail-copy.js';
|
|
@@ -97,6 +98,19 @@ export interface CoolifyPort {
|
|
|
97
98
|
domains?: string;
|
|
98
99
|
/** Selects which of two Dockerfiles in one repo to build (`cmsSite`'s own site/cms apps). */
|
|
99
100
|
dockerfile_location?: string;
|
|
101
|
+
/** The port the app listens on (`HostingEntry.port`). */
|
|
102
|
+
ports_exposes?: string;
|
|
103
|
+
/** `false` for an internal entry: Coolify makes no domain for it. */
|
|
104
|
+
autogenerate_domain?: boolean;
|
|
105
|
+
/** The folder a `static` entry serves (`HostingEntry.folder`). */
|
|
106
|
+
publish_directory?: string;
|
|
107
|
+
/** Whether a push to `git_branch` deploys (`HostingEntry.autoDeploy`). */
|
|
108
|
+
is_auto_deploy_enabled?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* The app's folder in the repo (`HostingEntry.folder`). Coolify's create
|
|
111
|
+
* endpoint takes it; `@wtfalch/providers` forwards the body as it is.
|
|
112
|
+
*/
|
|
113
|
+
base_directory?: string;
|
|
100
114
|
}): Promise<{
|
|
101
115
|
uuid: string;
|
|
102
116
|
}>;
|
|
@@ -316,12 +330,20 @@ export interface CoolifyPort {
|
|
|
316
330
|
* `CoolifyClient.listDatabaseBackups` method-for-method.
|
|
317
331
|
* `provisionPlatformBackup` reads this to decide whether a database
|
|
318
332
|
* already has an enabled, `save_s3` schedule before creating a second one.
|
|
333
|
+
* `s3_storage_uuid` (#257 fix round 3) is the field a replaced storage's
|
|
334
|
+
* repoint compares against -- Coolify's "Update database backup" operation
|
|
335
|
+
* (https://coolify.io/docs/api-reference/api/operations/update-database-backup)
|
|
336
|
+
* documents it as a field of the same scheduled-backup resource this list
|
|
337
|
+
* returns, but the list endpoint's own response has not been checked
|
|
338
|
+
* against a live instance, so it is optional here rather than assumed
|
|
339
|
+
* present.
|
|
319
340
|
*/
|
|
320
341
|
listDatabaseBackups(databaseUuid: string): Promise<Array<{
|
|
321
342
|
uuid: string;
|
|
322
343
|
frequency: string;
|
|
323
344
|
enabled: boolean;
|
|
324
345
|
save_s3: boolean;
|
|
346
|
+
s3_storage_uuid?: string;
|
|
325
347
|
}>>;
|
|
326
348
|
/**
|
|
327
349
|
* #257: schedules dumps of a database to the registered S3 storage --
|
|
@@ -1134,6 +1156,13 @@ export interface FoundryStepExecutorPorts {
|
|
|
1134
1156
|
* or repointed without the binding.
|
|
1135
1157
|
*/
|
|
1136
1158
|
loginHostBindings?: LoginHostBindingsPort;
|
|
1159
|
+
/**
|
|
1160
|
+
* F5: the broker's `AUTH_APPS`, where an entry with `uses.auth` gets its own
|
|
1161
|
+
* entry. Optional the same way `loginHostBindings` is: without it the plan
|
|
1162
|
+
* reports each broker entry blocked (the host also sets
|
|
1163
|
+
* `FoundryPlanApplyOptions.broker`).
|
|
1164
|
+
*/
|
|
1165
|
+
broker?: BrokerPort;
|
|
1137
1166
|
/**
|
|
1138
1167
|
* Hands a just-minted, one-time secret (today: an API app's
|
|
1139
1168
|
* `clientSecret`) to the host, immediately after the provider returns it
|
|
@@ -1198,7 +1227,7 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1198
1227
|
} | undefined>;
|
|
1199
1228
|
/**
|
|
1200
1229
|
* `plan/diff.ts`'s `planAccess` (#84): puts the org's app hostname behind a
|
|
1201
|
-
* Cloudflare Access app that lets only `
|
|
1230
|
+
* Cloudflare Access app that lets only the `app` entry's `accessEmails` through.
|
|
1202
1231
|
* foundry names its Access app `foundry:<orgId>` and reuses only an app with
|
|
1203
1232
|
* that name, so a re-run after a crash does not duplicate it. It refuses,
|
|
1204
1233
|
* rather than adopt, when another Access app already holds the hostname:
|
|
@@ -1384,7 +1413,7 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1384
1413
|
* P2, `.plans/2026-09-26-level-1-org-map.md`) -- a second `bulkUpsertEnvs`
|
|
1385
1414
|
* call rather than folding into `pushDatabaseUrl`'s, since this one runs
|
|
1386
1415
|
* unconditionally (`coolifyApp`) where that one runs only once
|
|
1387
|
-
* `
|
|
1416
|
+
* the `app` entry's `database` is declared and provisioned.
|
|
1388
1417
|
*
|
|
1389
1418
|
* `APP_URL` and `LOG_DIR` (#75) are pushed unconditionally; every other
|
|
1390
1419
|
* value is pushed only once its own source step has actually recorded it
|
|
@@ -1397,10 +1426,25 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1397
1426
|
* is its own design question, left out of #204's own "How" rather than
|
|
1398
1427
|
* guessed) is out of scope here (task brief). `DATABASE_URL_OWNER`/
|
|
1399
1428
|
* `DATABASE_URL` are pushed by `pushDatabaseUrl` instead, not here --
|
|
1400
|
-
* that call runs only once `
|
|
1429
|
+
* that call runs only once the `app` entry's `database` is declared and
|
|
1401
1430
|
* provisioned, where this one is unconditional (#239).
|
|
1402
1431
|
*/
|
|
1403
1432
|
private pushAppEnv;
|
|
1433
|
+
/**
|
|
1434
|
+
* F5: the app's `AUTH_APP_KEY`, minted once and only when the app has none
|
|
1435
|
+
* of at least 32 characters (the broker refuses a shorter one). `null` once
|
|
1436
|
+
* it has one: the app's env is where the key lives, and
|
|
1437
|
+
* `coolifyBrokerEntry` reads it back from there. It goes to this env and
|
|
1438
|
+
* the broker's, nowhere else.
|
|
1439
|
+
*/
|
|
1440
|
+
private ensureAuthAppKey;
|
|
1441
|
+
/**
|
|
1442
|
+
* `plan/diff.ts`'s `planBrokerEntries` (F5): the entry's `AUTH_APPS` entry,
|
|
1443
|
+
* written by `writeBrokerEntry` with the key `pushAppEnv` put in the app's
|
|
1444
|
+
* env. Records what it wrote (never the key) and any foreign entry the
|
|
1445
|
+
* broker would drop.
|
|
1446
|
+
*/
|
|
1447
|
+
private coolifyBrokerEntry;
|
|
1404
1448
|
/**
|
|
1405
1449
|
* `null` once the app already has one -- `AUTH_COOKIE_SECRET` is generated
|
|
1406
1450
|
* only the first time (task brief), never regenerated or overwritten, so a
|
|
@@ -1793,15 +1837,27 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1793
1837
|
* existed falls back to the old zone-wide pass, the one case where no
|
|
1794
1838
|
* hostname list was ever written to look up instead.
|
|
1795
1839
|
*
|
|
1796
|
-
* `
|
|
1797
|
-
* published under whatever tunnel id was live the *last*
|
|
1798
|
-
* recorded that domain, which
|
|
1799
|
-
* `tunnelId` -- a tunnel recreated under
|
|
1800
|
-
* (`findTunnelByName` returning a new id)
|
|
1801
|
-
*
|
|
1802
|
-
*
|
|
1803
|
-
*
|
|
1804
|
-
*
|
|
1840
|
+
* `previousIds` (round 4 finding 1, round 6 finding 2): the old domain's
|
|
1841
|
+
* CNAMEs were published under whatever tunnel id was live the *last*
|
|
1842
|
+
* time this step recorded that domain, which may be any id this step has
|
|
1843
|
+
* ever replaced, not necessarily `tunnelId` -- a tunnel recreated under
|
|
1844
|
+
* the same name (`findTunnelByName` returning a new id), possibly more
|
|
1845
|
+
* than once with no successful publish in between, in the same apply
|
|
1846
|
+
* that also changes the domain leaves the old domain's records pointed
|
|
1847
|
+
* at an old id. Matching only `tunnelId`'s target left them live
|
|
1848
|
+
* forever; matching only the single most recent previous id (round 4)
|
|
1849
|
+
* still missed an older one two unresolved recreates back. Every target
|
|
1850
|
+
* is checked so this stays a no-op once a retry has already moved the
|
|
1851
|
+
* old domain onto the current id.
|
|
1852
|
+
*
|
|
1853
|
+
* Round 6 finding 1: once `previousHostnames` is non-empty, every name in
|
|
1854
|
+
* it has now been looked up -- deleted (its CNAME matched one of
|
|
1855
|
+
* `dnsTargets`), left alone (found but foreign), or never found at all
|
|
1856
|
+
* (already gone) -- so none of them needs tracking under `previousDomain`
|
|
1857
|
+
* in {@link CLOUDFLARE_TUNNEL_DNS_TRACKED_SCOPE_KEY} any longer; dropped
|
|
1858
|
+
* from that set accordingly. The zone-wide fallback path (no recorded
|
|
1859
|
+
* hostnames) never added entries of its own to begin with, so there is
|
|
1860
|
+
* nothing of this domain's left in that set either way.
|
|
1805
1861
|
*/
|
|
1806
1862
|
private deleteStaleTunnelDnsRecords;
|
|
1807
1863
|
/**
|
|
@@ -1828,13 +1884,15 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1828
1884
|
* (round 4 finding 3), so a crash mid-write is recovered by re-running
|
|
1829
1885
|
* this same idempotent step, never silently skipped.
|
|
1830
1886
|
*
|
|
1831
|
-
* Round 3: `
|
|
1832
|
-
*
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1887
|
+
* Round 3, round 6 finding 2: `previousIds` is every tunnel id this same
|
|
1888
|
+
* step has ever replaced (`cloudflareTunnel`'s own accumulated set,
|
|
1889
|
+
* captured before `tunnel` above is computed for this run) -- a tunnel
|
|
1890
|
+
* Cloudflare shows as recreated under the same name (`findTunnelByName`
|
|
1891
|
+
* returning a new id for `<slug>-tunnel`), possibly more than once with
|
|
1892
|
+
* no successful publish in between, leaves every CNAME still pointed at
|
|
1893
|
+
* one of those old ids. That is this org's own stale record, not a
|
|
1894
|
+
* foreign one, so it is repointed rather than refused, whichever of
|
|
1895
|
+
* `previousIds` it still names.
|
|
1838
1896
|
*
|
|
1839
1897
|
* Round 4 finding 2: `previousHostnames` is this domain's own previously
|
|
1840
1898
|
* recorded hostname list (empty when the domain just changed --
|
|
@@ -1844,17 +1902,19 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1844
1902
|
* `hostnames` below but was never cleaned up before this round: its CNAME
|
|
1845
1903
|
* stayed live and its name stayed in the recorded list forever, because
|
|
1846
1904
|
* only a domain *change* ever ran cleanup. Each dropped name's CNAME is
|
|
1847
|
-
* deleted here when it still points at this tunnel or
|
|
1848
|
-
*
|
|
1849
|
-
* simply left out of the returned list -- nothing here is
|
|
1850
|
-
* reclaim or report as a conflict.
|
|
1905
|
+
* deleted here when it still points at this tunnel or any of
|
|
1906
|
+
* `previousIds`; a name already gone, or now pointed at something
|
|
1907
|
+
* foreign, is simply left out of the returned list -- nothing here is
|
|
1908
|
+
* this method's to reclaim or report as a conflict.
|
|
1851
1909
|
*
|
|
1852
|
-
* Round 5 finding 1: `context` is used
|
|
1853
|
-
*
|
|
1854
|
-
* {@link
|
|
1855
|
-
*
|
|
1856
|
-
*
|
|
1857
|
-
*
|
|
1910
|
+
* Round 5 finding 1, round 6 finding 1: `context` is used to add every
|
|
1911
|
+
* hostname this call confirms correct (settled, created or repointed) to
|
|
1912
|
+
* {@link CLOUDFLARE_TUNNEL_DNS_TRACKED_SCOPE_KEY} as soon as it is
|
|
1913
|
+
* confirmed, and to drop a dropped hostname from it once that hostname's
|
|
1914
|
+
* own CNAME is resolved one way or another -- additively, alongside
|
|
1915
|
+
* whatever other domains that shared set already names, never
|
|
1916
|
+
* overwriting them. `destroy` and {@link deleteStaleTunnelDnsRecords}
|
|
1917
|
+
* both read this set.
|
|
1858
1918
|
*/
|
|
1859
1919
|
private publishTunnelDnsRecords;
|
|
1860
1920
|
/**
|