@wtfalch/foundry 0.33.1 → 0.35.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/dist/bootstrap/bootstrap.d.ts +1 -1
- package/dist/bootstrap/bootstrap.js +49 -11
- package/dist/bootstrap/declare.d.ts +12 -5
- package/dist/bootstrap/declare.js +33 -18
- package/dist/bootstrap/versions.d.ts +24 -0
- package/dist/bootstrap/versions.js +46 -0
- package/dist/calls-sfu.d.ts +10 -0
- package/dist/calls-sfu.js +44 -3
- package/dist/index.d.ts +21 -8
- package/dist/index.js +11 -4
- package/dist/migrations/0051_org_services.sql +13 -0
- package/dist/migrations/0052_platform_mail_relay.sql +8 -0
- package/dist/migrations/0053_heartbeats_provider.sql +9 -0
- package/dist/migrations/0053_org_version_pin.sql +6 -0
- package/dist/org.d.ts +76 -12
- package/dist/org.js +136 -20
- package/dist/plan/backup-gate.d.ts +93 -0
- package/dist/plan/backup-gate.js +261 -0
- package/dist/plan/broker-entries.d.ts +9 -1
- package/dist/plan/broker-entries.js +20 -3
- package/dist/plan/build-record.d.ts +56 -0
- package/dist/plan/build-record.js +109 -0
- package/dist/plan/calls-matrix.d.ts +9 -0
- package/dist/plan/calls-matrix.js +18 -1
- package/dist/plan/cloudflared-connector.d.ts +7 -1
- package/dist/plan/cloudflared-connector.js +15 -2
- package/dist/plan/company-backups.d.ts +31 -0
- package/dist/plan/company-backups.js +101 -0
- package/dist/plan/deploy-health.d.ts +12 -2
- package/dist/plan/deploy-health.js +3 -1
- package/dist/plan/destinations.d.ts +73 -0
- package/dist/plan/destinations.js +217 -0
- package/dist/plan/diff.d.ts +46 -8
- package/dist/plan/diff.js +367 -126
- package/dist/plan/edge-access.d.ts +15 -0
- package/dist/plan/edge-access.js +21 -2
- package/dist/plan/executor.d.ts +276 -5
- package/dist/plan/executor.js +1111 -109
- package/dist/plan/health-check-settings.d.ts +28 -0
- package/dist/plan/health-check-settings.js +36 -0
- package/dist/plan/health-gaps.d.ts +33 -0
- package/dist/plan/health-gaps.js +89 -0
- package/dist/plan/health-targets.d.ts +1 -3
- package/dist/plan/health-targets.js +36 -36
- package/dist/plan/held-key-refs.d.ts +15 -0
- package/dist/plan/held-key-refs.js +15 -0
- package/dist/plan/hostnames.d.ts +10 -0
- package/dist/plan/hostnames.js +14 -0
- package/dist/plan/index.d.ts +19 -6
- package/dist/plan/index.js +10 -3
- package/dist/plan/live.d.ts +11 -2
- package/dist/plan/live.js +16 -2
- package/dist/plan/locked-hostnames.d.ts +7 -3
- package/dist/plan/locked-hostnames.js +25 -4
- package/dist/plan/login-host.d.ts +61 -12
- package/dist/plan/login-host.js +99 -11
- package/dist/plan/mail-relay.d.ts +139 -0
- package/dist/plan/mail-relay.js +400 -0
- package/dist/plan/network-move.d.ts +20 -0
- package/dist/plan/network-move.js +41 -0
- package/dist/plan/oikos-ingress.d.ts +111 -0
- package/dist/plan/oikos-ingress.js +231 -0
- package/dist/plan/org-names.d.ts +30 -0
- package/dist/plan/org-names.js +108 -0
- package/dist/plan/own-diff.d.ts +170 -0
- package/dist/plan/own-diff.js +440 -0
- package/dist/plan/own-dns.d.ts +6 -0
- package/dist/plan/own-dns.js +15 -0
- package/dist/plan/own-hostnames.d.ts +27 -0
- package/dist/plan/own-hostnames.js +24 -0
- package/dist/plan/own-limits.d.ts +213 -0
- package/dist/plan/own-limits.js +232 -0
- package/dist/plan/own-services.d.ts +193 -0
- package/dist/plan/own-services.js +169 -0
- package/dist/plan/own-sfu.d.ts +69 -0
- package/dist/plan/own-sfu.js +256 -0
- package/dist/plan/plan-apply.d.ts +121 -10
- package/dist/plan/plan-apply.js +357 -18
- package/dist/plan/platform-diff.d.ts +155 -11
- package/dist/plan/platform-diff.js +271 -66
- package/dist/plan/platform-org.d.ts +107 -4
- package/dist/plan/platform-org.js +175 -3
- package/dist/plan/platform-providers.d.ts +474 -81
- package/dist/plan/platform-providers.js +1407 -560
- package/dist/plan/proxy-labels.d.ts +57 -11
- package/dist/plan/proxy-labels.js +94 -14
- package/dist/plan/run-options.d.ts +19 -0
- package/dist/plan/run-options.js +46 -0
- package/dist/plan/sign-in-org.d.ts +1 -1
- package/dist/plan/sign-in-org.js +20 -2
- package/dist/plan/zitadel-login-policy.d.ts +1 -1
- package/dist/plan/zitadel-login-policy.js +77 -9
- package/dist/plan/zitadel-system-api.d.ts +22 -1
- package/dist/plan/zitadel-system-api.js +49 -8
- package/dist/platform.d.ts +40 -0
- package/dist/platform.js +16 -0
- package/dist/provision.d.ts +2 -0
- package/dist/provision.js +3 -1
- package/dist/slug.d.ts +3 -1
- package/dist/slug.js +26 -3
- package/dist/store/postgres.js +93 -2
- package/dist/store/schema.d.ts +87 -0
- package/dist/store/schema.js +9 -0
- package/package.json +3 -3
- package/versions.digests.json +11 -0
- package/versions.json +3 -1
|
@@ -45,3 +45,31 @@ export declare const DEFAULT_HEALTH_CHECK_START_PERIOD_SECONDS = 30;
|
|
|
45
45
|
* Dockerfile's `EXPOSE`) names, not a public one.
|
|
46
46
|
*/
|
|
47
47
|
export declare function healthCheckSettings(path: string, port: number, startPeriodSeconds?: number): HealthCheckPatch;
|
|
48
|
+
/**
|
|
49
|
+
* For an app built from a Dockerfile that has its own HEALTHCHECK: Coolify's check off. Coolify then keeps the
|
|
50
|
+
* Dockerfile's check, and its rolling update waits on that check's state (with Coolify's check on it would replace
|
|
51
|
+
* the Dockerfile's, so two checks would never both be live). Coolify reads the Dockerfile only while its own check is
|
|
52
|
+
* off, so this goes on before the first deploy.
|
|
53
|
+
*/
|
|
54
|
+
export declare const IMAGE_HEALTHCHECK_SETTINGS: {
|
|
55
|
+
readonly health_check_enabled: false;
|
|
56
|
+
};
|
|
57
|
+
/** The four `updateApplication` fields that turn on a Coolify health check that runs a command inside the container. */
|
|
58
|
+
export interface CommandHealthCheckPatch {
|
|
59
|
+
health_check_enabled: true;
|
|
60
|
+
health_check_type: 'cmd';
|
|
61
|
+
health_check_command: string;
|
|
62
|
+
health_check_start_period: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The same fields as {@link healthCheckSettings}, for an image whose HTTP
|
|
66
|
+
* check cannot work: no `curl`, or an app that answers 401 on every route.
|
|
67
|
+
* Coolify's own HTTP check calls `localhost`, which `wget` in an Alpine image
|
|
68
|
+
* resolves to `::1` first, so a server that listens on IPv4 only is never
|
|
69
|
+
* reached. A command names `127.0.0.1` itself.
|
|
70
|
+
*/
|
|
71
|
+
export declare function commandHealthCheckSettings(command: string, startPeriodSeconds?: number): CommandHealthCheckPatch;
|
|
72
|
+
/** A GET on `127.0.0.1` that fails on any status of 400 or more; the `wget` of a busybox image. */
|
|
73
|
+
export declare const wgetProbe: (port: number | string, path: string) => string;
|
|
74
|
+
/** A TCP connect to `127.0.0.1`, for a server with no route that answers without a token. */
|
|
75
|
+
export declare const tcpProbe: (port: number | string) => string;
|
|
@@ -45,3 +45,39 @@ export function healthCheckSettings(path, port, startPeriodSeconds = DEFAULT_HEA
|
|
|
45
45
|
health_check_start_period: startPeriodSeconds,
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* For an app built from a Dockerfile that has its own HEALTHCHECK: Coolify's check off. Coolify then keeps the
|
|
50
|
+
* Dockerfile's check, and its rolling update waits on that check's state (with Coolify's check on it would replace
|
|
51
|
+
* the Dockerfile's, so two checks would never both be live). Coolify reads the Dockerfile only while its own check is
|
|
52
|
+
* off, so this goes on before the first deploy.
|
|
53
|
+
*/
|
|
54
|
+
export const IMAGE_HEALTHCHECK_SETTINGS = { health_check_enabled: false };
|
|
55
|
+
/**
|
|
56
|
+
* What Coolify accepts as `health_check_command`: letters, digits, space and
|
|
57
|
+
* `- _ . / : = @ , +`, at most 1000 characters (its `generate_healthcheck_commands`
|
|
58
|
+
* falls back to the HTTP check on anything else, without an error). It runs as
|
|
59
|
+
* `sh -c`, so the image needs a shell and the tool the command names.
|
|
60
|
+
*/
|
|
61
|
+
const COOLIFY_COMMAND = /^[a-zA-Z0-9 \-_./:=@,+]+$/;
|
|
62
|
+
/**
|
|
63
|
+
* The same fields as {@link healthCheckSettings}, for an image whose HTTP
|
|
64
|
+
* check cannot work: no `curl`, or an app that answers 401 on every route.
|
|
65
|
+
* Coolify's own HTTP check calls `localhost`, which `wget` in an Alpine image
|
|
66
|
+
* resolves to `::1` first, so a server that listens on IPv4 only is never
|
|
67
|
+
* reached. A command names `127.0.0.1` itself.
|
|
68
|
+
*/
|
|
69
|
+
export function commandHealthCheckSettings(command, startPeriodSeconds = DEFAULT_HEALTH_CHECK_START_PERIOD_SECONDS) {
|
|
70
|
+
if (!COOLIFY_COMMAND.test(command) || command.length > 1000) {
|
|
71
|
+
throw new Error(`health check command ${JSON.stringify(command)} is not one Coolify accepts`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
health_check_enabled: true,
|
|
75
|
+
health_check_type: 'cmd',
|
|
76
|
+
health_check_command: command,
|
|
77
|
+
health_check_start_period: startPeriodSeconds,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** A GET on `127.0.0.1` that fails on any status of 400 or more; the `wget` of a busybox image. */
|
|
81
|
+
export const wgetProbe = (port, path) => `wget -q -O /dev/null http://127.0.0.1:${port}${path}`;
|
|
82
|
+
/** A TCP connect to `127.0.0.1`, for a server with no route that answers without a token. */
|
|
83
|
+
export const tcpProbe = (port) => `nc -z 127.0.0.1 ${port}`;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** An app as Coolify keeps it: its name and everything create and update sent. */
|
|
2
|
+
export interface StoredApp {
|
|
3
|
+
name: string;
|
|
4
|
+
fields: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Apps that run once and then idle or exit, so Docker has no service to check. Matched by what the app is built
|
|
8
|
+
* from (repo and Dockerfile target), never by its name, so no look-alike name is exempt.
|
|
9
|
+
*/
|
|
10
|
+
export declare const EXIT_JOBS: ReadonlyArray<{
|
|
11
|
+
repo: string;
|
|
12
|
+
target: string;
|
|
13
|
+
reason: string;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Apps built from a Dockerfile in another repo that carries a HEALTHCHECK line. Foundry cannot read that file
|
|
17
|
+
* here, so each entry names the app by repo and Dockerfile and says where the line was seen. With the Coolify
|
|
18
|
+
* check off, Coolify keeps the image's check and its rolling update follows it.
|
|
19
|
+
*/
|
|
20
|
+
export declare const REPO_DOCKERFILE_CHECKED: ReadonlyArray<{
|
|
21
|
+
repo: string;
|
|
22
|
+
dockerfile?: string;
|
|
23
|
+
target?: string;
|
|
24
|
+
seen: string;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* A check that can fail. Either Coolify's own, on and with something to probe (a command that does something,
|
|
28
|
+
* or HTTP on a port; Coolify falls back to the exposed port and to `/`), or Coolify's off over a Dockerfile that
|
|
29
|
+
* carries its own HEALTHCHECK, which Coolify then keeps.
|
|
30
|
+
*/
|
|
31
|
+
export declare function hasRealCheck(fields: Record<string, unknown>): boolean;
|
|
32
|
+
/** The names of the long-running apps with no real check, not counting the exit jobs. */
|
|
33
|
+
export declare function healthGaps(apps: ReadonlyArray<StoredApp>): string[];
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apps that run once and then idle or exit, so Docker has no service to check. Matched by what the app is built
|
|
3
|
+
* from (repo and Dockerfile target), never by its name, so no look-alike name is exempt.
|
|
4
|
+
*/
|
|
5
|
+
export const EXIT_JOBS = [
|
|
6
|
+
{
|
|
7
|
+
repo: 'wtfalch/files',
|
|
8
|
+
target: 'migration',
|
|
9
|
+
reason: 'runs the migrations once; its image HEALTHCHECK reports done',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
repo: 'wtfalch/ai',
|
|
13
|
+
target: 'migration',
|
|
14
|
+
reason: 'runs the migrations once; its image HEALTHCHECK reports done',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
repo: 'wtfalch/integrations',
|
|
18
|
+
target: 'migration',
|
|
19
|
+
reason: 'runs the migrations once; its image HEALTHCHECK reports done',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
repo: 'wtfalch/integrations',
|
|
23
|
+
target: 'worker',
|
|
24
|
+
reason: 'a finite pass (work-once.ts) that exits 0; nothing waits on it',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Apps built from a Dockerfile in another repo that carries a HEALTHCHECK line. Foundry cannot read that file
|
|
29
|
+
* here, so each entry names the app by repo and Dockerfile and says where the line was seen. With the Coolify
|
|
30
|
+
* check off, Coolify keeps the image's check and its rolling update follows it.
|
|
31
|
+
*/
|
|
32
|
+
export const REPO_DOCKERFILE_CHECKED = [
|
|
33
|
+
{ repo: 'wtfalch/auth', dockerfile: '/login/Dockerfile', seen: 'login image, docker inspect' },
|
|
34
|
+
{ repo: 'wtfalch/auth', dockerfile: '/broker/Dockerfile', seen: 'broker image, docker inspect' },
|
|
35
|
+
{ repo: 'wtfalch/ai', target: 'worker', seen: 'ai-worker image, docker inspect' },
|
|
36
|
+
{ repo: 'wtfalch/integrations', target: 'runner', seen: 'integrations/Dockerfile line 49' },
|
|
37
|
+
{
|
|
38
|
+
repo: 'wtfalch/chat',
|
|
39
|
+
dockerfile: '/apps/chat-service/synapse-module/Dockerfile',
|
|
40
|
+
seen: 'chat Synapse image, docker inspect',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
/** Commands that succeed whatever the container is doing. */
|
|
44
|
+
const NO_OP = /^(?:true|:|exit 0|\/bin\/true|\/usr\/bin\/true|sh -c true)$/;
|
|
45
|
+
/** A `HEALTHCHECK` instruction that is not `HEALTHCHECK NONE`. */
|
|
46
|
+
const DOCKERFILE_HEALTHCHECK = /^\s*HEALTHCHECK\s+(?!NONE\b)/im;
|
|
47
|
+
const same = (a, b) => b === undefined || a === b;
|
|
48
|
+
function exitJob(f) {
|
|
49
|
+
return EXIT_JOBS.some((e) => f.git_repository === e.repo && f.dockerfile_target_build === e.target);
|
|
50
|
+
}
|
|
51
|
+
function repoDockerfileChecked(f) {
|
|
52
|
+
return REPO_DOCKERFILE_CHECKED.some((e) => f.git_repository === e.repo &&
|
|
53
|
+
same(f.dockerfile_location, e.dockerfile) &&
|
|
54
|
+
same(f.dockerfile_target_build, e.target));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A check that can fail. Either Coolify's own, on and with something to probe (a command that does something,
|
|
58
|
+
* or HTTP on a port; Coolify falls back to the exposed port and to `/`), or Coolify's off over a Dockerfile that
|
|
59
|
+
* carries its own HEALTHCHECK, which Coolify then keeps.
|
|
60
|
+
*/
|
|
61
|
+
export function hasRealCheck(fields) {
|
|
62
|
+
if (fields.health_check_enabled === true) {
|
|
63
|
+
if (fields.health_check_type === 'cmd') {
|
|
64
|
+
const command = fields.health_check_command;
|
|
65
|
+
return typeof command === 'string' && command.trim() !== '' && !NO_OP.test(command.trim());
|
|
66
|
+
}
|
|
67
|
+
const exposed = typeof fields.ports_exposes === 'string' && fields.ports_exposes.trim() !== '';
|
|
68
|
+
return typeof fields.health_check_port === 'number' || exposed;
|
|
69
|
+
}
|
|
70
|
+
if (fields.health_check_enabled === false) {
|
|
71
|
+
const dockerfile = fields.dockerfile;
|
|
72
|
+
if (typeof dockerfile === 'string') {
|
|
73
|
+
// An inline Dockerfile may arrive base64 (a PATCH), as plain text on create.
|
|
74
|
+
const text = DOCKERFILE_HEALTHCHECK.test(dockerfile)
|
|
75
|
+
? dockerfile
|
|
76
|
+
: Buffer.from(dockerfile, 'base64').toString('utf8');
|
|
77
|
+
return DOCKERFILE_HEALTHCHECK.test(text);
|
|
78
|
+
}
|
|
79
|
+
return repoDockerfileChecked(fields);
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
/** The names of the long-running apps with no real check, not counting the exit jobs. */
|
|
84
|
+
export function healthGaps(apps) {
|
|
85
|
+
return apps
|
|
86
|
+
.filter((a) => !exitJob(a.fields) && !hasRealCheck(a.fields))
|
|
87
|
+
.map((a) => a.name)
|
|
88
|
+
.sort();
|
|
89
|
+
}
|
|
@@ -71,9 +71,7 @@ export interface HealthTarget {
|
|
|
71
71
|
* the broker whenever the org has them; then whichever of hosting or site
|
|
72
72
|
* the org runs, if either.
|
|
73
73
|
*/
|
|
74
|
-
export declare function healthTargets(org: DeclaredOrg
|
|
75
|
-
platform?: DeclaredPlatform | null;
|
|
76
|
-
}): HealthTarget[];
|
|
74
|
+
export declare function healthTargets(org: DeclaredOrg): HealthTarget[];
|
|
77
75
|
/**
|
|
78
76
|
* The seven platform services #541 names (`platform:auth`, `platform:archon`,
|
|
79
77
|
* `platform:mail`, `platform:ai`, `platform:files`, `platform:integrations`,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { appEntry } from '../org.js';
|
|
2
2
|
import { agoraHostnameFor } from './agora.js';
|
|
3
3
|
import { bouleHostnameFor } from './boule.js';
|
|
4
|
-
import { isPlatformOwnOrg } from './diff.js';
|
|
5
4
|
import { callsHostnameFor, cmsHostnameFor } from './executor.js';
|
|
6
5
|
import { isBehindGate } from './gates.js';
|
|
7
|
-
import { entryHostnames } from './hostnames.js';
|
|
6
|
+
import { entryHostnames, siteHostnameFor } from './hostnames.js';
|
|
8
7
|
import { loginHostnameFor } from './login-host.js';
|
|
9
8
|
function okExpect() {
|
|
10
9
|
return { status: 200 };
|
|
@@ -25,12 +24,8 @@ const STALWART_READY_PATH = '/healthz/ready';
|
|
|
25
24
|
const CHAT_SERVICE_HEALTH_PATH = '/_chat/health';
|
|
26
25
|
const CALLS_HEALTH_PATH = '/healthz';
|
|
27
26
|
/** Boule, Agora and the company's own login host: every org gets these, with no flag to check (the "Why" this issue opens with). */
|
|
28
|
-
function alwaysOnTargets(org
|
|
29
|
-
|
|
30
|
-
// `loginHostnameFor` (#488; `login-host.ts`, `diff.ts`'s `planLoginHost`).
|
|
31
|
-
const loginHost = platform && isPlatformOwnOrg(org, platform.ownerOrgId)
|
|
32
|
-
? platform.auth.hostname
|
|
33
|
-
: loginHostnameFor(org);
|
|
27
|
+
function alwaysOnTargets(org) {
|
|
28
|
+
const loginHost = loginHostnameFor(org);
|
|
34
29
|
return [
|
|
35
30
|
{
|
|
36
31
|
service: 'boule',
|
|
@@ -62,14 +57,10 @@ function alwaysOnTargets(org, platform) {
|
|
|
62
57
|
];
|
|
63
58
|
}
|
|
64
59
|
/**
|
|
65
|
-
* The
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* `'auth-broker'` target, under `platform.auth.hostname`. This target still
|
|
70
|
-
* belongs in a company's own list (#541: "Include ... the broker when the
|
|
71
|
-
* company has them") so a status page scoped to one company shows every
|
|
72
|
-
* service that company's sign-in actually depends on, even a shared one.
|
|
60
|
+
* The company's own sign-in broker (`broker-entries.ts`), when it depends on
|
|
61
|
+
* it: any hosting entry declaring `uses.auth` signs in through it. Its
|
|
62
|
+
* `/healthy` is not routed to the outside, so no URL is built here (#541:
|
|
63
|
+
* "Include ... the broker when the company has them").
|
|
73
64
|
*/
|
|
74
65
|
function brokerTarget(org) {
|
|
75
66
|
const usesBroker = org.hosting.entries.some((entry) => Boolean(entry.uses?.auth));
|
|
@@ -82,14 +73,13 @@ function brokerTarget(org) {
|
|
|
82
73
|
depth: 'shallow',
|
|
83
74
|
restartSafe: false,
|
|
84
75
|
behindAccess: false,
|
|
85
|
-
reason: "the sign-in broker
|
|
76
|
+
reason: "the company's own sign-in broker answers on its internal network only (broker-entries.ts): its /healthy is not routed to the outside, so there is no URL to probe",
|
|
86
77
|
};
|
|
87
78
|
}
|
|
88
79
|
/**
|
|
89
|
-
* Mail, when this company's mail is not off.
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* `platformHealthTargets`'s own `'mail'` target is the real, reachable one.
|
|
80
|
+
* Mail, when this company's mail is not off. The company runs its own
|
|
81
|
+
* Stalwart (`own-mail`), reached only by the shared relay on the internal
|
|
82
|
+
* network, so there is no public URL to build here.
|
|
93
83
|
*/
|
|
94
84
|
function mailTarget(org) {
|
|
95
85
|
if (org.mail.mode === 'off')
|
|
@@ -101,7 +91,7 @@ function mailTarget(org) {
|
|
|
101
91
|
depth: 'deep',
|
|
102
92
|
restartSafe: false,
|
|
103
93
|
behindAccess: false,
|
|
104
|
-
reason: "
|
|
94
|
+
reason: "the company's own Stalwart is reached by the shared relay on the internal network only, so it has no public address to probe",
|
|
105
95
|
};
|
|
106
96
|
}
|
|
107
97
|
/**
|
|
@@ -200,11 +190,11 @@ function appTargets(org) {
|
|
|
200
190
|
if (org.hosting.enabled && appEntry(org.hosting)) {
|
|
201
191
|
return org.hosting.entries.map((entry) => hostingEntryTarget(org, entry));
|
|
202
192
|
}
|
|
203
|
-
if (org.site.enabled
|
|
193
|
+
if (org.site.enabled) {
|
|
204
194
|
return [
|
|
205
195
|
{
|
|
206
196
|
service: 'site',
|
|
207
|
-
url: `https://${org
|
|
197
|
+
url: `https://${siteHostnameFor(org)}${DEFAULT_API_HEALTH_PATH}`,
|
|
208
198
|
expect: okExpect(),
|
|
209
199
|
depth: 'deep',
|
|
210
200
|
restartSafe: true,
|
|
@@ -228,8 +218,8 @@ function appTargets(org) {
|
|
|
228
218
|
* the broker whenever the org has them; then whichever of hosting or site
|
|
229
219
|
* the org runs, if either.
|
|
230
220
|
*/
|
|
231
|
-
export function healthTargets(org
|
|
232
|
-
const targets = alwaysOnTargets(org
|
|
221
|
+
export function healthTargets(org) {
|
|
222
|
+
const targets = alwaysOnTargets(org);
|
|
233
223
|
const broker = brokerTarget(org);
|
|
234
224
|
if (broker)
|
|
235
225
|
targets.push(broker);
|
|
@@ -308,8 +298,10 @@ function internalOnlyTarget(service, reason) {
|
|
|
308
298
|
* one anyway.
|
|
309
299
|
*/
|
|
310
300
|
export function platformHealthTargets(platform) {
|
|
301
|
+
// The platform's own sign-in, mail and files are off (each company runs its own), so a target
|
|
302
|
+
// for a service that is not enabled would point at a host that does not exist.
|
|
311
303
|
return [
|
|
312
|
-
...authTargets(platform),
|
|
304
|
+
...(platform.auth.enabled ? authTargets(platform) : []),
|
|
313
305
|
{
|
|
314
306
|
service: 'archon',
|
|
315
307
|
// `provisionPlatformArchon` always puts a Cloudflare Access app in front
|
|
@@ -322,16 +314,24 @@ export function platformHealthTargets(platform) {
|
|
|
322
314
|
restartSafe: false,
|
|
323
315
|
behindAccess: true,
|
|
324
316
|
},
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
317
|
+
...(platform.mail.enabled
|
|
318
|
+
? [
|
|
319
|
+
{
|
|
320
|
+
service: 'mail',
|
|
321
|
+
url: `https://${platform.mail.hostname}${STALWART_READY_PATH}`,
|
|
322
|
+
expect: okExpect(),
|
|
323
|
+
depth: 'deep',
|
|
324
|
+
restartSafe: false,
|
|
325
|
+
behindAccess: false,
|
|
326
|
+
},
|
|
327
|
+
]
|
|
328
|
+
: []),
|
|
333
329
|
internalOnlyTarget('ai', "platform:ai's runner is created with no Coolify domains, tunnel, DNS record or Access app (platform-providers.ts) -- nothing public to probe yet"),
|
|
334
|
-
|
|
330
|
+
...(platform.files.enabled
|
|
331
|
+
? [
|
|
332
|
+
internalOnlyTarget('files', "platform:files' runner is created with no domains either, the same as platform:auth's apps (platform-providers.ts) -- nothing public to probe yet"),
|
|
333
|
+
]
|
|
334
|
+
: []),
|
|
335
335
|
{
|
|
336
336
|
service: 'integrations',
|
|
337
337
|
url: null,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The held-key refs the platform's steps deliver under and an org step reads back. A leaf module:
|
|
3
|
+
* platform-providers.ts and platform-org.ts import each other, so a constant read at load lives here.
|
|
4
|
+
*/
|
|
5
|
+
/** The ref the scheduler's admin token is delivered under: the host's real `JobsSchedulerPort` adapter reads it back. */
|
|
6
|
+
export declare const JOBS_ADMIN_TOKEN_REF = "jobs:scheduler:adminToken";
|
|
7
|
+
/** The ref the AI management key is delivered under; the host's `AiOrganisationPort` adapter reads it back. */
|
|
8
|
+
export declare const PLATFORM_AI_MANAGEMENT_KEY_REF = "ai:platform:managementKey";
|
|
9
|
+
/**
|
|
10
|
+
* The install's edge label token (`proxy-labels.ts` `EDGE_LABEL`): minted once, held, and written into the
|
|
11
|
+
* labels of every container foundry routes, so Traefik can be told to route only those.
|
|
12
|
+
*/
|
|
13
|
+
export declare const PROXY_LABEL_TOKEN_REF = "proxy:labels:token";
|
|
14
|
+
/** The ref the management private key (PKCS#8 PEM) is delivered under. */
|
|
15
|
+
export declare const FILES_MANAGEMENT_KEY_REF = "files:platform:managementKey";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The held-key refs the platform's steps deliver under and an org step reads back. A leaf module:
|
|
3
|
+
* platform-providers.ts and platform-org.ts import each other, so a constant read at load lives here.
|
|
4
|
+
*/
|
|
5
|
+
/** The ref the scheduler's admin token is delivered under: the host's real `JobsSchedulerPort` adapter reads it back. */
|
|
6
|
+
export const JOBS_ADMIN_TOKEN_REF = 'jobs:scheduler:adminToken';
|
|
7
|
+
/** The ref the AI management key is delivered under; the host's `AiOrganisationPort` adapter reads it back. */
|
|
8
|
+
export const PLATFORM_AI_MANAGEMENT_KEY_REF = 'ai:platform:managementKey';
|
|
9
|
+
/**
|
|
10
|
+
* The install's edge label token (`proxy-labels.ts` `EDGE_LABEL`): minted once, held, and written into the
|
|
11
|
+
* labels of every container foundry routes, so Traefik can be told to route only those.
|
|
12
|
+
*/
|
|
13
|
+
export const PROXY_LABEL_TOKEN_REF = 'proxy:labels:token';
|
|
14
|
+
/** The ref the management private key (PKCS#8 PEM) is delivered under. */
|
|
15
|
+
export const FILES_MANAGEMENT_KEY_REF = 'files:platform:managementKey';
|
package/dist/plan/hostnames.d.ts
CHANGED
|
@@ -38,6 +38,16 @@ export declare function tunnelDomainList(names: readonly string[]): string[];
|
|
|
38
38
|
export declare function coolifyTunnelDomains(names: readonly string[]): string;
|
|
39
39
|
/** What `domains` held on an `app` entry's Coolify app before entries had hostnames. */
|
|
40
40
|
export declare function legacyAppHostname(org: DeclaredOrg): string;
|
|
41
|
+
/**
|
|
42
|
+
* The public address of the org's cms site: its domain, or `<slug>.oikos.host`
|
|
43
|
+
* for a company without one (served by the shared `*.oikos.host` wildcard).
|
|
44
|
+
*/
|
|
45
|
+
export declare function siteHostnameFor(org: DeclaredOrg): string;
|
|
46
|
+
/**
|
|
47
|
+
* The cms studio's address: `cms.<domain>`, or `<slug>-cms.oikos.host` for a
|
|
48
|
+
* company without a domain (one label, so the free wildcard certificate covers it).
|
|
49
|
+
*/
|
|
50
|
+
export declare function cmsStudioHostnameFor(org: DeclaredOrg): string;
|
|
41
51
|
/**
|
|
42
52
|
* `share.<domain>` when hosting is on and an entry declares
|
|
43
53
|
* `uses.files.shareHost`, else `null`. The files service answers public
|
package/dist/plan/hostnames.js
CHANGED
|
@@ -63,6 +63,20 @@ export function coolifyTunnelDomains(names) {
|
|
|
63
63
|
export function legacyAppHostname(org) {
|
|
64
64
|
return org.hosting.usesDomain && org.domain ? org.domain : `${org.slug}.oikos.host`;
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* The public address of the org's cms site: its domain, or `<slug>.oikos.host`
|
|
68
|
+
* for a company without one (served by the shared `*.oikos.host` wildcard).
|
|
69
|
+
*/
|
|
70
|
+
export function siteHostnameFor(org) {
|
|
71
|
+
return org.site.usesDomain && org.domain ? org.domain : `${org.slug}.oikos.host`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The cms studio's address: `cms.<domain>`, or `<slug>-cms.oikos.host` for a
|
|
75
|
+
* company without a domain (one label, so the free wildcard certificate covers it).
|
|
76
|
+
*/
|
|
77
|
+
export function cmsStudioHostnameFor(org) {
|
|
78
|
+
return org.site.usesDomain && org.domain ? `cms.${org.domain}` : `${org.slug}-cms.oikos.host`;
|
|
79
|
+
}
|
|
66
80
|
/**
|
|
67
81
|
* `share.<domain>` when hosting is on and an entry declares
|
|
68
82
|
* `uses.files.shareHost`, else `null`. The files service answers public
|
package/dist/plan/index.d.ts
CHANGED
|
@@ -4,10 +4,18 @@ export type { StepAction, PlannedStep, PlanDetail, OrgPostgresPlan } from './dif
|
|
|
4
4
|
export { computePlanDetail, toPlan } from './diff.js';
|
|
5
5
|
export type { StepSystem } from './step-system.js';
|
|
6
6
|
export { stepSystemOf } from './step-system.js';
|
|
7
|
-
export type { PlatformPlanDetail } from './platform-diff.js';
|
|
8
|
-
export { planPlatform, toPlatformPlan, JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_AI_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_EDGE_KEY, PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_EDGE_KEY_REF, PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF, PLATFORM_SFU_APPLIED_SCOPE_KEY, } from './platform-diff.js';
|
|
7
|
+
export type { PlatformPlanDetail, MailRoute } from './platform-diff.js';
|
|
8
|
+
export { planPlatform, toPlatformPlan, JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_AI_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_RELAY_ROUTES_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_EDGE_KEY, PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_EDGE_KEY_REF, PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF, PLATFORM_SFU_APPLIED_SCOPE_KEY, } from './platform-diff.js';
|
|
9
9
|
export type { HeldKeyResolver, OrgPlatformReadiness } from './platform-org.js';
|
|
10
|
-
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesFromPlatform, heldPlatformKeyRefs, orgPlatformReadinessFrom, } from './platform-org.js';
|
|
10
|
+
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesForOwn, agoraHostValuesFromPlatform, heldOwnKeyRefs, heldPlatformKeyRefs, orgPlatformReadinessFor, orgPlatformReadinessFrom, } from './platform-org.js';
|
|
11
|
+
export type { PlanOwnAuthOptions, PlanOwnFilesOptions, PlanOwnFilesR2Options, PlanOwnJobsOptions, } from './own-diff.js';
|
|
12
|
+
export { OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY, OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, OWN_AUTH_BLOCKED_KEY, OWN_FILES_BLOCKED_KEY, OWN_FILES_R2_BLOCKED_KEY, OWN_JOBS_BLOCKED_KEY, OWN_MAIL_BLOCKED_KEY, planOwnAuth, planOwnFiles, planOwnFilesR2, planOwnJobs, planOwnMail, } from './own-diff.js';
|
|
13
|
+
export type { PlanOwnSfuOptions } from './own-diff.js';
|
|
14
|
+
export { OWN_SFU_BLOCKED_KEY, OWN_SFU_FIREWALL_KEY, planOwnSfu } from './own-diff.js';
|
|
15
|
+
export type { SfuAppDeps, SfuAppInput } from './own-sfu.js';
|
|
16
|
+
export { provisionSfuApp, sfuConfig, sfuPortsMappings } from './own-sfu.js';
|
|
17
|
+
export type { OwnAuthInstance, FilesKeysMint, FilesKeysMinted, OwnFilesDeps, OwnFilesR2Key, OwnFilesR2Keys, OwnServicePorts, OwnServicesPort, OwnServicesProvision, OwnSfuDeps, ProjectSource, ServiceNetwork, ServiceTarget, StepScope, } from './own-services.js';
|
|
18
|
+
export { OWN_FILES_R2_SCOPE_KEY, OWN_FILES_R2_UNDELIVERED_SCOPE_KEY, OWN_SERVICES_APPLIED_SCOPE_KEY, OwnServicesBlockedError, orgNetwork, orgTarget, platformNetwork, platformTarget, scopeFromOrg, scopeFromPlatform, } from './own-services.js';
|
|
11
19
|
export type { PlatformOrgClientFactories } from './platform-org-ports.js';
|
|
12
20
|
export { PlatformOrgBlockedError, PlatformOrgPorts } from './platform-org-ports.js';
|
|
13
21
|
export type { AdoptCandidate, AdoptInput, AdoptPlan, AdoptSnapshot } from './adopt.js';
|
|
@@ -15,8 +23,10 @@ export { AdoptRefusedError } from './adopt.js';
|
|
|
15
23
|
export type { AuditActor, AuditLog, AuthzGate, StepExecutor, StepDestroyer, DestroyTarget, StepExecutionContext, PlatformStepExecutor, PlatformStepExecutionContext, FoundryPlanApplyOptions, FoundryApplyOptions, } from './plan-apply.js';
|
|
16
24
|
export { FoundryPlanApply, DeclaredOrgNotFoundError, StalePlanError, ConfirmationRequiredError, NotAuthorizedError, NotATestOrgError, DeclaredPlatformNotFoundError, PlatformStalePlanError, PlatformConfirmationRequiredError, UpgradeConfirmationRequiredError, planNeedsConfirmation, } from './plan-apply.js';
|
|
17
25
|
export type { UnconfirmedUpgrade } from './confirm-upgrade.js';
|
|
18
|
-
export {
|
|
19
|
-
export type {
|
|
26
|
+
export { NetworkMoveBackupMissingError, UpgradeBackupMissingError } from './backup-gate.js';
|
|
27
|
+
export type { BackupGatePorts, MoveGatePorts } from './backup-gate.js';
|
|
28
|
+
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, filesPublicHostsValue, filesShareDomains, provisionFilesHost, provisionJobsHost, provisionMailServer, } from './platform-providers.js';
|
|
29
|
+
export type { FilesHostInput, JobsHostDeps, JobsHostInput, MailServerInput, PlatformAuthDeps, PlatformAuthSmtp, PlatformAuthRuntime, PlatformFilesDeps, PlatformFilesRuntime, PlatformEdgeDeps, PlatformDnsFactory, PlatformAccessFactory, PlatformAccessPort, PlatformTunnelsFactory, PlatformEdgeRuntime, } from './platform-providers.js';
|
|
20
30
|
export type { PlatformMailDeps, PlatformMailAdminPort, PlatformMailRuntime, } from './platform-providers.js';
|
|
21
31
|
export { FILES_INTROSPECTION_SECRET_REF, FILES_MANAGEMENT_ISSUER, FILES_MANAGEMENT_KEY_ID, FILES_MANAGEMENT_KEY_REF, PLATFORM_AUTH_INSTANCE_BEARER_REF, } from './platform-providers.js';
|
|
22
32
|
export { AI_MANAGEMENT_ISSUER, AI_MANAGEMENT_KEY_ID, AI_SIGNING_KEY_REF, AiManagementKeyExistsError, createAiManagementKeyMinter, PLATFORM_AI_MANAGEMENT_KEY_REF, PLATFORM_AI_MANAGEMENT_PERMISSIONS, } from './platform-providers.js';
|
|
@@ -33,8 +43,10 @@ export { ProvidersAiReader, ProvidersCloudflareReader, ProvidersCmsReader, Provi
|
|
|
33
43
|
export type { AgoraHostPort, AgoraHostValues } from './agora.js';
|
|
34
44
|
export type { BrokerPort } from './broker-entries.js';
|
|
35
45
|
export { PlatformBrokerPort } from './broker-entries.js';
|
|
36
|
-
export type { LoginHostBinding, LoginHostBindingsPort } from './login-host.js';
|
|
46
|
+
export type { LoginHostBinding, LoginHostBindingsPort, LoginHostDiscoveryPort, } from './login-host.js';
|
|
37
47
|
export { loginHostnameFor } from './login-host.js';
|
|
48
|
+
export { NO_DOMAIN_ZONE, ownHostnames } from './own-hostnames.js';
|
|
49
|
+
export type { OwnHostnames } from './own-hostnames.js';
|
|
38
50
|
export type { MailIdentityApp, MailIdentityAudienceCheck } from './mail-identity.js';
|
|
39
51
|
export { checkMailIdentityAudience } from './mail-identity.js';
|
|
40
52
|
export type { MailAllowlistApp } from './mail-identity-allowlist.js';
|
|
@@ -63,3 +75,4 @@ export { adaptZitadelListApps } from './executor.js';
|
|
|
63
75
|
export { HttpJobsSchedulerClient } from './jobs-scheduler-http.js';
|
|
64
76
|
export type { OrgAppVersionRef, OrgAppVersionStatus, WtfalchPackageDrift, PlatformVersionStatus, ReadPlatformVersionStatusOptions, StatusCoolifyPort, StatusGithubPort, GhExec as StatusGhExec, } from './version-status.js';
|
|
65
77
|
export { readPlatformVersionStatus, wtfalchVersionsFromPnpmLockfile, providersStatusCoolifyPort, ghCliStatusGithubPort, } from './version-status.js';
|
|
78
|
+
export { COMPANY_DESTINATION_NAME_PREFIX, COMPANY_NETWORKS, DEFAULT_MAX_DESTINATIONS, DestinationCapacityError, PLATFORM_NETWORK, companyNetworkName, ensureDestination, findDestination, placed, placedOnPlatform, validateNetworkName, } from './destinations.js';
|
package/dist/plan/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
export { readLiveResources, readLivePlatformResources } from './live.js';
|
|
2
2
|
export { computePlanDetail, toPlan } from './diff.js';
|
|
3
3
|
export { stepSystemOf } from './step-system.js';
|
|
4
|
-
export { planPlatform, toPlatformPlan, JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_AI_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_EDGE_KEY, PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_EDGE_KEY_REF, PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF, PLATFORM_SFU_APPLIED_SCOPE_KEY, } from './platform-diff.js';
|
|
5
|
-
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesFromPlatform, heldPlatformKeyRefs, orgPlatformReadinessFrom, } from './platform-org.js';
|
|
4
|
+
export { planPlatform, toPlatformPlan, JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_AI_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_RELAY_ROUTES_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_EDGE_KEY, PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_EDGE_KEY_REF, PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF, PLATFORM_SFU_APPLIED_SCOPE_KEY, } from './platform-diff.js';
|
|
5
|
+
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesForOwn, agoraHostValuesFromPlatform, heldOwnKeyRefs, heldPlatformKeyRefs, orgPlatformReadinessFor, orgPlatformReadinessFrom, } from './platform-org.js';
|
|
6
|
+
export { OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY, OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, OWN_AUTH_BLOCKED_KEY, OWN_FILES_BLOCKED_KEY, OWN_FILES_R2_BLOCKED_KEY, OWN_JOBS_BLOCKED_KEY, OWN_MAIL_BLOCKED_KEY, planOwnAuth, planOwnFiles, planOwnFilesR2, planOwnJobs, planOwnMail, } from './own-diff.js';
|
|
7
|
+
export { OWN_SFU_BLOCKED_KEY, OWN_SFU_FIREWALL_KEY, planOwnSfu } from './own-diff.js';
|
|
8
|
+
export { provisionSfuApp, sfuConfig, sfuPortsMappings } from './own-sfu.js';
|
|
9
|
+
export { OWN_FILES_R2_SCOPE_KEY, OWN_FILES_R2_UNDELIVERED_SCOPE_KEY, OWN_SERVICES_APPLIED_SCOPE_KEY, OwnServicesBlockedError, orgNetwork, orgTarget, platformNetwork, platformTarget, scopeFromOrg, scopeFromPlatform, } from './own-services.js';
|
|
6
10
|
export { PlatformOrgBlockedError, PlatformOrgPorts } from './platform-org-ports.js';
|
|
7
11
|
export { AdoptRefusedError } from './adopt.js';
|
|
8
12
|
export { FoundryPlanApply, DeclaredOrgNotFoundError, StalePlanError, ConfirmationRequiredError, NotAuthorizedError, NotATestOrgError, DeclaredPlatformNotFoundError, PlatformStalePlanError, PlatformConfirmationRequiredError, UpgradeConfirmationRequiredError, planNeedsConfirmation, } from './plan-apply.js';
|
|
9
|
-
export {
|
|
13
|
+
export { NetworkMoveBackupMissingError, UpgradeBackupMissingError } from './backup-gate.js';
|
|
14
|
+
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, filesPublicHostsValue, filesShareDomains, provisionFilesHost, provisionJobsHost, provisionMailServer, } from './platform-providers.js';
|
|
10
15
|
// #171: how a host signs files management requests with the delivered key.
|
|
11
16
|
export { FILES_INTROSPECTION_SECRET_REF, FILES_MANAGEMENT_ISSUER, FILES_MANAGEMENT_KEY_ID, FILES_MANAGEMENT_KEY_REF, PLATFORM_AUTH_INSTANCE_BEARER_REF, } from './platform-providers.js';
|
|
12
17
|
export { AI_MANAGEMENT_ISSUER, AI_MANAGEMENT_KEY_ID, AI_SIGNING_KEY_REF, AiManagementKeyExistsError, createAiManagementKeyMinter, PLATFORM_AI_MANAGEMENT_KEY_REF, PLATFORM_AI_MANAGEMENT_PERMISSIONS, } from './platform-providers.js';
|
|
@@ -18,6 +23,7 @@ export { MAIL_COPY_COMPLETED_SCOPE_KEY, MAIL_COPY_DONE_SCOPE_KEY, MissingMailCop
|
|
|
18
23
|
export { ProvidersAiReader, ProvidersCloudflareReader, ProvidersCmsReader, ProvidersCoolifyReader, ProvidersZitadelReader, storeLoginHostResolver, storeScopeResolver, storeRecordedIds, } from './providers-readers.js';
|
|
19
24
|
export { PlatformBrokerPort } from './broker-entries.js';
|
|
20
25
|
export { loginHostnameFor } from './login-host.js';
|
|
26
|
+
export { NO_DOMAIN_ZONE, ownHostnames } from './own-hostnames.js';
|
|
21
27
|
export { checkMailIdentityAudience } from './mail-identity.js';
|
|
22
28
|
export { checkMailAppsAllowlist } from './mail-identity-allowlist.js';
|
|
23
29
|
export { FoundryStepExecutor, NotBuiltError, healthUrls } from './executor.js';
|
|
@@ -32,3 +38,4 @@ export { createEdgeTunnels } from './edge-tunnels.js';
|
|
|
32
38
|
export { adaptZitadelListApps } from './executor.js';
|
|
33
39
|
export { HttpJobsSchedulerClient } from './jobs-scheduler-http.js';
|
|
34
40
|
export { readPlatformVersionStatus, wtfalchVersionsFromPnpmLockfile, providersStatusCoolifyPort, ghCliStatusGithubPort, } from './version-status.js';
|
|
41
|
+
export { COMPANY_DESTINATION_NAME_PREFIX, COMPANY_NETWORKS, DEFAULT_MAX_DESTINATIONS, DestinationCapacityError, PLATFORM_NETWORK, companyNetworkName, ensureDestination, findDestination, placed, placedOnPlatform, validateNetworkName, } from './destinations.js';
|
package/dist/plan/live.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type DeclaredOrg, type OrgId } from '../org.js';
|
|
2
|
+
import { type OwnAuthInstance } from './own-services.js';
|
|
2
3
|
/**
|
|
3
4
|
* One resource as seen live in one of foundry's seven systems.
|
|
4
5
|
*
|
|
@@ -87,6 +88,8 @@ export interface CmsReader {
|
|
|
87
88
|
}
|
|
88
89
|
export interface LiveReaders {
|
|
89
90
|
zitadel: ZitadelReader;
|
|
91
|
+
/** The reader for one own org's ZITADEL instance. Without it an own org's ZITADEL is not read. */
|
|
92
|
+
zitadelFor?: (instance: OwnAuthInstance) => ZitadelReader;
|
|
90
93
|
coolify: CoolifyReader;
|
|
91
94
|
cloudflare: CloudflareReader;
|
|
92
95
|
files: FilesReader;
|
|
@@ -94,7 +97,7 @@ export interface LiveReaders {
|
|
|
94
97
|
cms: CmsReader;
|
|
95
98
|
}
|
|
96
99
|
/** Every live resource foundry can see for `orgId`, across all six systems. */
|
|
97
|
-
export declare function readLiveResources(orgId: OrgId, readers: LiveReaders): Promise<LiveResource[]>;
|
|
100
|
+
export declare function readLiveResources(orgId: OrgId, readers: LiveReaders, org?: DeclaredOrg, orgRecordedIds?: Readonly<Record<string, string>>): Promise<LiveResource[]>;
|
|
98
101
|
/**
|
|
99
102
|
* #167: one narrow interface per system reading the shared platform's own
|
|
100
103
|
* resources (`platform.ts`'s `DeclaredPlatform`) -- the platform-scoped
|
|
@@ -118,6 +121,12 @@ export interface PlatformCloudflareReader {
|
|
|
118
121
|
/** #173: the platform's own Coolify-hosted jobs scheduler (`platform-diff.ts`'s `planJobs`). */
|
|
119
122
|
export interface PlatformCoolifyReader {
|
|
120
123
|
listResources(): Promise<LiveResource[]>;
|
|
124
|
+
/** Optional: with it the platform plan reads each recorded app's running commit and notes a build that differs (`build-record.ts`). */
|
|
125
|
+
listApplicationDeployments?(uuid: string, take?: number): Promise<Array<{
|
|
126
|
+
deployment_uuid: string;
|
|
127
|
+
commit?: string;
|
|
128
|
+
status: string;
|
|
129
|
+
}>>;
|
|
121
130
|
}
|
|
122
131
|
export interface PlatformLiveReaders {
|
|
123
132
|
cloudflare: PlatformCloudflareReader;
|
package/dist/plan/live.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { ownsServices } from '../org.js';
|
|
2
|
+
import { ownHostnames } from './own-hostnames.js';
|
|
3
|
+
import { OWN_SERVICES_APPLIED_SCOPE_KEY, orgTarget } from './own-services.js';
|
|
1
4
|
/** Every live resource foundry can see for `orgId`, across all six systems. */
|
|
2
|
-
export async function readLiveResources(orgId, readers) {
|
|
5
|
+
export async function readLiveResources(orgId, readers, org, orgRecordedIds) {
|
|
3
6
|
const [zitadel, coolify, cloudflare, files, ai, cms] = await Promise.all([
|
|
4
|
-
|
|
7
|
+
org && ownsServices(org)
|
|
8
|
+
? readOwnZitadel(orgId, readers, org, orgRecordedIds ?? {})
|
|
9
|
+
: readers.zitadel.listResources(orgId),
|
|
5
10
|
readers.coolify.listResources(orgId),
|
|
6
11
|
readers.cloudflare.listResources(orgId),
|
|
7
12
|
readers.files.listResources(orgId),
|
|
@@ -10,6 +15,15 @@ export async function readLiveResources(orgId, readers) {
|
|
|
10
15
|
]);
|
|
11
16
|
return [...zitadel, ...coolify, ...cloudflare, ...files, ...ai, ...cms];
|
|
12
17
|
}
|
|
18
|
+
/** An own org's ZITADEL is its own instance: nothing is read until it is applied, and never from the shared one. */
|
|
19
|
+
async function readOwnZitadel(orgId, readers, org, recordedIds) {
|
|
20
|
+
if (!readers.zitadelFor || !recordedIds[OWN_SERVICES_APPLIED_SCOPE_KEY])
|
|
21
|
+
return [];
|
|
22
|
+
const refs = orgTarget(org).refs;
|
|
23
|
+
return readers
|
|
24
|
+
.zitadelFor({ issuerUrl: ownHostnames(org).issuerUrl, bearerRef: refs.instanceBearer })
|
|
25
|
+
.listResources(orgId);
|
|
26
|
+
}
|
|
13
27
|
/** Every live resource foundry can see for the platform, across every wired reader (#167, #173). */
|
|
14
28
|
export async function readLivePlatformResources(readers) {
|
|
15
29
|
const [cloudflare, coolify] = await Promise.all([
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type DeclaredOrg } from '../org.js';
|
|
2
|
+
/** The labels an own org's services answer on, so no hosting entry may take them. */
|
|
3
|
+
export declare const OWN_SERVICE_LABELS: readonly ["auth", "mail", "files", "sfu"];
|
|
2
4
|
/**
|
|
3
5
|
* The names under the org's domain that foundry owns, so no hosting entry may
|
|
4
6
|
* take one: Agora, Boule, the cms studio when the site is on, calls when it uses the
|
|
5
|
-
* domain, the sign-in host when the issuer does,
|
|
6
|
-
*
|
|
7
|
+
* domain, the sign-in host when the issuer does, `auth.`, `mail.`, `files.` and
|
|
8
|
+
* `sfu.` for an org that runs its own services, and every platform service
|
|
9
|
+
* hostname that falls under the domain. An own org with no domain holds its
|
|
10
|
+
* oikos.host service names; any other org with no domain holds none.
|
|
7
11
|
*/
|
|
8
12
|
export declare function lockedHostnames(org: DeclaredOrg, platformHostnames: readonly string[]): Set<string>;
|