@wtfalch/foundry 0.16.2 → 0.16.4
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/plan/executor.d.ts
CHANGED
|
@@ -142,6 +142,7 @@ export interface CoolifyPort {
|
|
|
142
142
|
getDatabase(uuid: string): Promise<{
|
|
143
143
|
uuid: string;
|
|
144
144
|
internal_db_url?: string;
|
|
145
|
+
status?: string;
|
|
145
146
|
}>;
|
|
146
147
|
/**
|
|
147
148
|
* Every database Coolify holds. `coolifyDatabase` finds its own by name and
|
|
@@ -236,6 +237,8 @@ export interface CoolifyPort {
|
|
|
236
237
|
name?: string;
|
|
237
238
|
description?: string;
|
|
238
239
|
instant_deploy?: boolean;
|
|
240
|
+
/** Extra `docker run` options (`--entrypoint "..."` becomes the compose entrypoint). */
|
|
241
|
+
custom_docker_run_options?: string;
|
|
239
242
|
/**
|
|
240
243
|
* `host:container`, comma separated -- publishes host ports directly
|
|
241
244
|
* (`@wtfalch/providers`' `CoolifyClient.createDockerImageApplication`
|
|
@@ -1357,6 +1360,25 @@ export declare const ACCESS_POLICY_NAME = "foundry: allowed emails";
|
|
|
1357
1360
|
* uses for the org's own app.
|
|
1358
1361
|
*/
|
|
1359
1362
|
export declare function isRunningOrStarting(status: string | undefined): boolean;
|
|
1363
|
+
/**
|
|
1364
|
+
* Reads a Postgres database and starts it when Coolify reports it as not
|
|
1365
|
+
* running. `createPostgresDatabase` does not start what it creates (no
|
|
1366
|
+
* `instant_deploy`), and a database found by name or by recorded id may have
|
|
1367
|
+
* been left stopped, so every site that points an app at a database calls this
|
|
1368
|
+
* instead of `getDatabase`. Returns what `getDatabase` returned.
|
|
1369
|
+
*/
|
|
1370
|
+
export declare function getDatabaseStarted(coolify: {
|
|
1371
|
+
getDatabase(uuid: string): Promise<{
|
|
1372
|
+
uuid: string;
|
|
1373
|
+
internal_db_url?: string;
|
|
1374
|
+
status?: string;
|
|
1375
|
+
}>;
|
|
1376
|
+
raw(method: string, path: string, body?: unknown): Promise<unknown>;
|
|
1377
|
+
}, dbId: string): Promise<{
|
|
1378
|
+
uuid: string;
|
|
1379
|
+
internal_db_url?: string;
|
|
1380
|
+
status?: string;
|
|
1381
|
+
}>;
|
|
1360
1382
|
export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer {
|
|
1361
1383
|
private readonly ports;
|
|
1362
1384
|
constructor(ports: FoundryStepExecutorPorts);
|
package/dist/plan/executor.js
CHANGED
|
@@ -551,6 +551,20 @@ export function isRunningOrStarting(status) {
|
|
|
551
551
|
return true;
|
|
552
552
|
return !status.startsWith('exited') && !status.startsWith('stopped');
|
|
553
553
|
}
|
|
554
|
+
/**
|
|
555
|
+
* Reads a Postgres database and starts it when Coolify reports it as not
|
|
556
|
+
* running. `createPostgresDatabase` does not start what it creates (no
|
|
557
|
+
* `instant_deploy`), and a database found by name or by recorded id may have
|
|
558
|
+
* been left stopped, so every site that points an app at a database calls this
|
|
559
|
+
* instead of `getDatabase`. Returns what `getDatabase` returned.
|
|
560
|
+
*/
|
|
561
|
+
export async function getDatabaseStarted(coolify, dbId) {
|
|
562
|
+
const db = await coolify.getDatabase(dbId);
|
|
563
|
+
if (db.status !== undefined && !db.status.startsWith('running')) {
|
|
564
|
+
await coolify.raw('POST', `/databases/${dbId}/start`);
|
|
565
|
+
}
|
|
566
|
+
return db;
|
|
567
|
+
}
|
|
554
568
|
// ---------------------------------------------------------------------------
|
|
555
569
|
// Executor
|
|
556
570
|
// ---------------------------------------------------------------------------
|
|
@@ -1442,7 +1456,7 @@ export class FoundryStepExecutor {
|
|
|
1442
1456
|
* never logged, never interpolated into a thrown error.
|
|
1443
1457
|
*/
|
|
1444
1458
|
async pushDatabaseUrl(appId, dbId, dbName) {
|
|
1445
|
-
const db = await this.ports.coolify
|
|
1459
|
+
const db = await getDatabaseStarted(this.ports.coolify, dbId);
|
|
1446
1460
|
// Nothing to push; `planApp` reports this as blocked from the live reader.
|
|
1447
1461
|
if (!db.internal_db_url)
|
|
1448
1462
|
return { pushed: false, changed: false };
|
|
@@ -2027,7 +2041,7 @@ export class FoundryStepExecutor {
|
|
|
2027
2041
|
// recorded, logged, or interpolated into a thrown error. Its own call,
|
|
2028
2042
|
// separate from the two app-env calls above, since it has nothing to
|
|
2029
2043
|
// push once `internal_db_url` is absent.
|
|
2030
|
-
const db = await this.ports.coolify
|
|
2044
|
+
const db = await getDatabaseStarted(this.ports.coolify, dbId);
|
|
2031
2045
|
if (db.internal_db_url) {
|
|
2032
2046
|
const dbEnvs = [
|
|
2033
2047
|
{ key: 'DATABASE_URL_OWNER', value: db.internal_db_url },
|
|
@@ -2224,8 +2238,8 @@ export class FoundryStepExecutor {
|
|
|
2224
2238
|
const masDbId = await this.ensureCallsDatabase(context, projectUuid, environment, names.masDb, 'mas', CALLS_MAS_DB_SCOPE_KEY);
|
|
2225
2239
|
context.recordScopeId(CALLS_MAS_DB_SCOPE_KEY, masDbId);
|
|
2226
2240
|
const [synapseDbRow, masDbRow] = await Promise.all([
|
|
2227
|
-
this.ports.coolify
|
|
2228
|
-
this.ports.coolify
|
|
2241
|
+
getDatabaseStarted(this.ports.coolify, synapseDbId),
|
|
2242
|
+
getDatabaseStarted(this.ports.coolify, masDbId),
|
|
2229
2243
|
]);
|
|
2230
2244
|
const synapseDb = synapseDbRow.internal_db_url
|
|
2231
2245
|
? parseCallsDbUrl(synapseDbRow.internal_db_url)
|
|
@@ -2517,9 +2531,9 @@ export class FoundryStepExecutor {
|
|
|
2517
2531
|
const serviceDbId = await this.ensureCallsDatabase(context, projectUuid, environment, names.serviceDb, 'chat', CHAT_SERVICE_DB_SCOPE_KEY);
|
|
2518
2532
|
context.recordScopeId(CHAT_SERVICE_DB_SCOPE_KEY, serviceDbId);
|
|
2519
2533
|
const [synapseDbRow, masDbRow, serviceDbRow] = await Promise.all([
|
|
2520
|
-
this.ports.coolify
|
|
2521
|
-
this.ports.coolify
|
|
2522
|
-
this.ports.coolify
|
|
2534
|
+
getDatabaseStarted(this.ports.coolify, synapseDbId),
|
|
2535
|
+
getDatabaseStarted(this.ports.coolify, masDbId),
|
|
2536
|
+
getDatabaseStarted(this.ports.coolify, serviceDbId),
|
|
2523
2537
|
]);
|
|
2524
2538
|
const synapseDb = synapseDbRow.internal_db_url
|
|
2525
2539
|
? parseCallsDbUrl(synapseDbRow.internal_db_url)
|
|
@@ -2692,7 +2706,7 @@ export class FoundryStepExecutor {
|
|
|
2692
2706
|
const dbName = `${name}-db`;
|
|
2693
2707
|
const dbId = await this.ensureCallsDatabase(context, projectUuid, environment, dbName, dbName.replace(/-/g, '_'), CALLS_SERVICE_DB_SCOPE_KEY);
|
|
2694
2708
|
context.recordScopeId(CALLS_SERVICE_DB_SCOPE_KEY, dbId);
|
|
2695
|
-
const db = await this.ports.coolify
|
|
2709
|
+
const db = await getDatabaseStarted(this.ports.coolify, dbId);
|
|
2696
2710
|
if (!db.internal_db_url) {
|
|
2697
2711
|
throw new Error(`coolify:calls-service for ${org.slug}: its database has no internal_db_url yet -- retry this apply once Coolify reports one`);
|
|
2698
2712
|
}
|
|
@@ -46,6 +46,15 @@ export declare const PLATFORM_AUTH_KEY = "platform:auth";
|
|
|
46
46
|
* that was never actually applied.
|
|
47
47
|
*/
|
|
48
48
|
export declare const PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY = "auth_zitadel_applied_image_tag";
|
|
49
|
+
/**
|
|
50
|
+
* The image has `Entrypoint /app/zitadel` and no `Cmd`, so without this it
|
|
51
|
+
* runs zitadel with no subcommand, exits, and restarts forever. Coolify turns
|
|
52
|
+
* `--entrypoint` in `custom_docker_run_options` into the compose `entrypoint`
|
|
53
|
+
* (its PATCH takes this field; it refuses `dockerfile`).
|
|
54
|
+
*/
|
|
55
|
+
export declare const AUTH_ZITADEL_RUN_OPTIONS = "--entrypoint \"/app/zitadel start-from-init --masterkeyFromEnv --tlsMode external\"";
|
|
56
|
+
/** The run options last applied to the ZITADEL app (value: the options string), so a changed value forces one redeploy. */
|
|
57
|
+
export declare const PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY = "zitadel_platform_auth_run_options_applied";
|
|
49
58
|
/**
|
|
50
59
|
* #251: the Synapse/MAS image tags an org's calls Matrix stack last
|
|
51
60
|
* successful apply actually set live. `coolifyCalls` (`executor.ts`)
|
|
@@ -41,6 +41,15 @@ export const PLATFORM_AUTH_KEY = 'platform:auth';
|
|
|
41
41
|
* that was never actually applied.
|
|
42
42
|
*/
|
|
43
43
|
export const PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY = 'auth_zitadel_applied_image_tag';
|
|
44
|
+
/**
|
|
45
|
+
* The image has `Entrypoint /app/zitadel` and no `Cmd`, so without this it
|
|
46
|
+
* runs zitadel with no subcommand, exits, and restarts forever. Coolify turns
|
|
47
|
+
* `--entrypoint` in `custom_docker_run_options` into the compose `entrypoint`
|
|
48
|
+
* (its PATCH takes this field; it refuses `dockerfile`).
|
|
49
|
+
*/
|
|
50
|
+
export const AUTH_ZITADEL_RUN_OPTIONS = '--entrypoint "/app/zitadel start-from-init --masterkeyFromEnv --tlsMode external"';
|
|
51
|
+
/** The run options last applied to the ZITADEL app (value: the options string), so a changed value forces one redeploy. */
|
|
52
|
+
export const PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY = 'zitadel_platform_auth_run_options_applied';
|
|
44
53
|
/**
|
|
45
54
|
* #251: the Synapse/MAS image tags an org's calls Matrix stack last
|
|
46
55
|
* successful apply actually set live. `coolifyCalls` (`executor.ts`)
|
|
@@ -753,8 +762,15 @@ function planJobs(platform, byKey, claimed, observable) {
|
|
|
753
762
|
claimed.add(key);
|
|
754
763
|
if (!platform.jobs.enabled)
|
|
755
764
|
return { step: null, blocked: null };
|
|
756
|
-
if (live)
|
|
757
|
-
|
|
765
|
+
if (live) {
|
|
766
|
+
// The step also starts the scheduler's database (0.16.3), so a live app
|
|
767
|
+
// whose database the reader saw stopped plans it again. A status the
|
|
768
|
+
// reader could not read is absent, and plans nothing.
|
|
769
|
+
const databaseStatus = live.config.databaseStatus;
|
|
770
|
+
if (typeof databaseStatus !== 'string' || databaseStatus.startsWith('running')) {
|
|
771
|
+
return { step: null, blocked: null };
|
|
772
|
+
}
|
|
773
|
+
}
|
|
758
774
|
// No Coolify reader means the host wired no Coolify at all for the
|
|
759
775
|
// platform: the step's executor would throw every apply, and the scheduler
|
|
760
776
|
// could never be seen live, so it would re-plan forever. A fixed text: it
|
|
@@ -1103,7 +1119,10 @@ function planAuth(platform, recordedIds, executorBlocked, executorMissing, authD
|
|
|
1103
1119
|
connectorApplied &&
|
|
1104
1120
|
brokerApplied &&
|
|
1105
1121
|
brokerDeployed &&
|
|
1106
|
-
loginClientApplied
|
|
1122
|
+
loginClientApplied &&
|
|
1123
|
+
// The ZITADEL run options reach the container only through the auth
|
|
1124
|
+
// step's redeploy, so a platform recorded before them still plans it.
|
|
1125
|
+
recordedIds[PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY] === AUTH_ZITADEL_RUN_OPTIONS) {
|
|
1107
1126
|
return { step: null, blocked: null };
|
|
1108
1127
|
}
|
|
1109
1128
|
// The admin key was minted but never reached the host. ZITADEL shows a
|
|
@@ -51,7 +51,7 @@ export declare const JOBS_ADMIN_TOKEN_REF = "jobs:scheduler:adminToken";
|
|
|
51
51
|
* org's own app, reused here for the platform's one scheduler instead of once
|
|
52
52
|
* per org.
|
|
53
53
|
*/
|
|
54
|
-
type JobsCoolifyPort = Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'requireGithubApp' | 'findApplicationByName' | 'createGithubAppApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'bulkUpsertEnvs' | 'getApplication' | 'deploy'>;
|
|
54
|
+
type JobsCoolifyPort = Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'requireGithubApp' | 'findApplicationByName' | 'createGithubAppApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'getApplication' | 'deploy'>;
|
|
55
55
|
/**
|
|
56
56
|
* Options for {@link FoundryPlatformStepExecutor}'s constructor, replacing
|
|
57
57
|
* four positional arguments that shifted on every merge that added a
|
|
@@ -340,7 +340,7 @@ export interface PlatformMailRuntime {
|
|
|
340
340
|
* {@link PlatformStepExecutionContext}, bundled like {@link PlatformAuthDeps}.
|
|
341
341
|
*/
|
|
342
342
|
export interface PlatformArchonDeps {
|
|
343
|
-
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'bulkUpsertEnvs' | 'listEnvs' | 'updateApplication' | 'deploy'>;
|
|
343
|
+
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'listEnvs' | 'updateApplication' | 'deploy'>;
|
|
344
344
|
cloudflare: Pick<CloudflarePort, 'findZoneByApex' | 'findTunnelByName' | 'createTunnel' | 'putTunnelConfig' | 'getTunnelToken' | 'findDnsRecord' | 'createDnsRecord' | 'updateDnsRecord' | 'listAccessApps' | 'createAccessApp' | 'listAccessPolicies' | 'createAccessPolicy' | 'deleteAccessApp'>;
|
|
345
345
|
/** Must target the platform's own ZITADEL instance, or `zitadelForInstance` must be set. */
|
|
346
346
|
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'findOrCreateProject' | 'createOidcApp' | 'listApps' | 'deleteApp'>;
|
|
@@ -770,7 +770,7 @@ export declare class ProvidersPlatformCloudflareReader implements PlatformCloudf
|
|
|
770
770
|
export declare class ProvidersPlatformCoolifyReader implements PlatformCoolifyReader {
|
|
771
771
|
private readonly client;
|
|
772
772
|
private readonly platformStore;
|
|
773
|
-
constructor(client: Pick<CoolifyPort, 'getApplication' | 'listDatabaseBackups'>, platformStore: Pick<PlatformStore, 'getServiceIds'>);
|
|
773
|
+
constructor(client: Pick<CoolifyPort, 'getApplication' | 'getDatabase' | 'listDatabaseBackups'>, platformStore: Pick<PlatformStore, 'getServiceIds'>);
|
|
774
774
|
listResources(): Promise<LiveResource[]>;
|
|
775
775
|
}
|
|
776
776
|
export {};
|
|
@@ -4,9 +4,9 @@ import { applyObjects, openInstance, platformMailSettingsObjects } from '@wtfalc
|
|
|
4
4
|
import { VERSIONS, floatingTagBlocked } from '../bootstrap/versions.js';
|
|
5
5
|
import { redactSecrets } from '../calls-sfu-deploy-support.js';
|
|
6
6
|
import { ensureCloudflaredConnector } from './cloudflared-connector.js';
|
|
7
|
-
import { ACCESS_POLICY_NAME, isNotFoundError, isRunningOrStarting } from './executor.js';
|
|
7
|
+
import { ACCESS_POLICY_NAME, getDatabaseStarted, isNotFoundError, isRunningOrStarting, } from './executor.js';
|
|
8
8
|
import { coolifyDomains } from './hostnames.js';
|
|
9
|
-
import { JOBS_SCHEDULER_KEY, PLATFORM_AI_APPLIED_GITREF_SCOPE_KEY, PLATFORM_AI_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_AI_DEPLOY_PENDING_SCOPE_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AI_MANAGEMENT_KEY_CONFLICT_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_AI_MIGRATE_BLOCKED_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_SET_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_BROKER_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_ARCHON_CONNECTOR_APP_SCOPE_KEY, PLATFORM_ARCHON_GATE_CLOSED_SCOPE_KEY, PLATFORM_ARCHON_KEY, PLATFORM_ARCHON_MASTER_CLOUDFLARE_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_URL_REF, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY, PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY, PLATFORM_AUTH_BROKER_SCOPE_KEY, PLATFORM_AUTH_CONNECTOR_APP_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY, PLATFORM_BACKUP_DATABASE_SCOPE_KEYS, PLATFORM_BACKUP_SERVICE_KEYS, PLATFORM_FILES_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_GITREF_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY, PLATFORM_INTEGRATIONS_KEY_SET_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_SCOPE_KEY, PLATFORM_MAIL_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_MAIL_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_MAIL_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_MAIL_CONNECTOR_APP_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, parseBrokerDeployRecord, parseLoginClientRecord, platformMailMxHost, } from './platform-diff.js';
|
|
9
|
+
import { AUTH_ZITADEL_RUN_OPTIONS, JOBS_SCHEDULER_KEY, PLATFORM_AI_APPLIED_GITREF_SCOPE_KEY, PLATFORM_AI_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_AI_DEPLOY_PENDING_SCOPE_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AI_MANAGEMENT_KEY_CONFLICT_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_AI_MIGRATE_BLOCKED_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_SET_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_BROKER_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_ARCHON_CONNECTOR_APP_SCOPE_KEY, PLATFORM_ARCHON_GATE_CLOSED_SCOPE_KEY, PLATFORM_ARCHON_KEY, PLATFORM_ARCHON_MASTER_CLOUDFLARE_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_URL_REF, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY, PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY, PLATFORM_AUTH_BROKER_SCOPE_KEY, PLATFORM_AUTH_CONNECTOR_APP_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY, PLATFORM_BACKUP_DATABASE_SCOPE_KEYS, PLATFORM_BACKUP_SERVICE_KEYS, PLATFORM_FILES_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_GITREF_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY, PLATFORM_INTEGRATIONS_KEY_SET_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_SCOPE_KEY, PLATFORM_MAIL_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_MAIL_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_MAIL_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_MAIL_CONNECTOR_APP_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, parseBrokerDeployRecord, parseLoginClientRecord, platformMailMxHost, } from './platform-diff.js';
|
|
10
10
|
import { applyCallsSfuPlatformStep } from './platform-sfu.js';
|
|
11
11
|
import { CreateInstanceError, createZitadelInstance, generateSystemApiKeypair, instanceWithoutBearerMessage, systemApiUsersEnv, } from './zitadel-system-api.js';
|
|
12
12
|
/** The key `runPlatformSteps` records the apex zone's id under (`system:kind:key`). */
|
|
@@ -429,7 +429,7 @@ export class FoundryPlatformStepExecutor {
|
|
|
429
429
|
});
|
|
430
430
|
appId = created.uuid;
|
|
431
431
|
}
|
|
432
|
-
const db = await coolify
|
|
432
|
+
const db = await getDatabaseStarted(coolify, dbId);
|
|
433
433
|
if (!db.internal_db_url) {
|
|
434
434
|
throw new Error(`platform step coolify:jobs: database ${dbId} has no internal_db_url yet; not deploying the scheduler without a database`);
|
|
435
435
|
}
|
|
@@ -852,7 +852,7 @@ async function provisionPlatformAuth(context, deps) {
|
|
|
852
852
|
dbId = created.uuid;
|
|
853
853
|
await record(PLATFORM_AUTH_DB_SCOPE_KEY, dbId);
|
|
854
854
|
}
|
|
855
|
-
const db = await deps.coolify
|
|
855
|
+
const db = await getDatabaseStarted(deps.coolify, dbId);
|
|
856
856
|
if (!db.internal_db_url) {
|
|
857
857
|
// Static config check before either app is touched (`diff.ts`'s
|
|
858
858
|
// `coolifyCalls` follows the same rule for its own databases): Coolify
|
|
@@ -871,6 +871,7 @@ async function provisionPlatformAuth(context, deps) {
|
|
|
871
871
|
docker_registry_image_name: AUTH_ZITADEL_IMAGE,
|
|
872
872
|
docker_registry_image_tag: AUTH_ZITADEL_IMAGE_TAG,
|
|
873
873
|
ports_exposes: AUTH_ZITADEL_PORT,
|
|
874
|
+
custom_docker_run_options: AUTH_ZITADEL_RUN_OPTIONS,
|
|
874
875
|
name: PLATFORM_AUTH_ZITADEL_APP_NAME,
|
|
875
876
|
description: `ZITADEL for the platform's own sign-in (#168, ${AUTH_ZITADEL_IMAGE}:${AUTH_ZITADEL_IMAGE_TAG})`,
|
|
876
877
|
instant_deploy: false,
|
|
@@ -882,9 +883,14 @@ async function provisionPlatformAuth(context, deps) {
|
|
|
882
883
|
// create must not leave the app unnamed for good, and the tunnel addresses
|
|
883
884
|
// the app by this name. (The port cannot read the current name back, and
|
|
884
885
|
// setting the same value again is harmless.)
|
|
886
|
+
// The run options go in the same patch, so an app made before they existed
|
|
887
|
+
// gets them too.
|
|
885
888
|
await deps.coolify.updateApplication(zitadelAppId, {
|
|
886
889
|
custom_internal_name: PLATFORM_AUTH_ZITADEL_INTERNAL_NAME,
|
|
890
|
+
custom_docker_run_options: AUTH_ZITADEL_RUN_OPTIONS,
|
|
887
891
|
});
|
|
892
|
+
// The container only takes new run options at its next deploy.
|
|
893
|
+
const zitadelRunOptionsNew = recorded[PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY] !== AUTH_ZITADEL_RUN_OPTIONS;
|
|
888
894
|
// #251: only on create, never unconditionally -- nothing here patches
|
|
889
895
|
// `docker_registry_image_tag` on an app `findOrCreatePlatformApp` found
|
|
890
896
|
// already running, so that is the only moment this tag is actually live.
|
|
@@ -1012,9 +1018,12 @@ async function provisionPlatformAuth(context, deps) {
|
|
|
1012
1018
|
await deps.coolify.bulkUpsertEnvs(brokerAppId, brokerEnvs);
|
|
1013
1019
|
// Deploy an app that was just created, is not running (a throw after the
|
|
1014
1020
|
// create left it never deployed, or it stopped), or whose envs changed.
|
|
1015
|
-
if (zitadelApp.justCreated || !zitadelApp.running || zitadelEnvsChanged) {
|
|
1021
|
+
if (zitadelApp.justCreated || !zitadelApp.running || zitadelEnvsChanged || zitadelRunOptionsNew) {
|
|
1016
1022
|
await deps.coolify.deploy(zitadelAppId);
|
|
1017
1023
|
await record(PLATFORM_AUTH_ZITADEL_DEPLOYED_SCOPE_KEY, zitadelAppId);
|
|
1024
|
+
// Only now: the options are live once the deploy went out. A throw between
|
|
1025
|
+
// the PATCH and here leaves the marker unset, so the next run deploys.
|
|
1026
|
+
await record(PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY, AUTH_ZITADEL_RUN_OPTIONS);
|
|
1018
1027
|
}
|
|
1019
1028
|
if (loginApp.justCreated || !loginApp.running || loginEnvsChanged) {
|
|
1020
1029
|
await deps.coolify.deploy(loginAppId);
|
|
@@ -2108,7 +2117,7 @@ async function provisionPlatformArchon(context, deps) {
|
|
|
2108
2117
|
}
|
|
2109
2118
|
await record(PLATFORM_ARCHON_DB_SCOPE_KEY, dbId);
|
|
2110
2119
|
}
|
|
2111
|
-
const db = await deps.coolify
|
|
2120
|
+
const db = await getDatabaseStarted(deps.coolify, dbId);
|
|
2112
2121
|
if (!db.internal_db_url) {
|
|
2113
2122
|
throw new Error('coolify:app for the platform (archon): the database has no internal_db_url yet -- retry this apply once Coolify reports one');
|
|
2114
2123
|
}
|
|
@@ -3264,7 +3273,7 @@ async function provisionPlatformAi(context, deps) {
|
|
|
3264
3273
|
}
|
|
3265
3274
|
await record(PLATFORM_AI_DB_SCOPE_KEY, dbId);
|
|
3266
3275
|
}
|
|
3267
|
-
const db = await coolify
|
|
3276
|
+
const db = await getDatabaseStarted(coolify, dbId);
|
|
3268
3277
|
if (!db.internal_db_url) {
|
|
3269
3278
|
throw new Error('coolify:app for the platform (ai): the database has no internal_db_url yet -- retry this apply once Coolify reports one');
|
|
3270
3279
|
}
|
|
@@ -3987,7 +3996,7 @@ async function provisionPlatformFiles(context, deps) {
|
|
|
3987
3996
|
}
|
|
3988
3997
|
await record(PLATFORM_FILES_DB_SCOPE_KEY, dbId);
|
|
3989
3998
|
}
|
|
3990
|
-
const db = await deps.coolify
|
|
3999
|
+
const db = await getDatabaseStarted(deps.coolify, dbId);
|
|
3991
4000
|
if (!db.internal_db_url) {
|
|
3992
4001
|
throw new Error('coolify:app for the platform (files): the database has no internal_db_url yet -- retry this apply once Coolify reports one');
|
|
3993
4002
|
}
|
|
@@ -4346,7 +4355,7 @@ async function provisionPlatformIntegrations(action, context, deps) {
|
|
|
4346
4355
|
}
|
|
4347
4356
|
await record(INTEGRATIONS_DATABASE_SCOPE_KEY, dbId);
|
|
4348
4357
|
}
|
|
4349
|
-
const db = await coolify
|
|
4358
|
+
const db = await getDatabaseStarted(coolify, dbId);
|
|
4350
4359
|
if (!db.internal_db_url) {
|
|
4351
4360
|
throw new Error(`platform:integrations: database ${dbId} has no internal_db_url yet; not deploying the integrations host without a database`);
|
|
4352
4361
|
}
|
|
@@ -5029,11 +5038,27 @@ export class ProvidersPlatformCoolifyReader {
|
|
|
5029
5038
|
if (recordedAppId) {
|
|
5030
5039
|
try {
|
|
5031
5040
|
const app = await this.client.getApplication(recordedAppId);
|
|
5041
|
+
// The scheduler's database status, so `planJobs` can see a stopped
|
|
5042
|
+
// one. Left out when it cannot be read: unknown is not "stopped".
|
|
5043
|
+
let databaseStatus;
|
|
5044
|
+
const databaseId = recorded[JOBS_DATABASE_SCOPE_KEY];
|
|
5045
|
+
if (databaseId) {
|
|
5046
|
+
try {
|
|
5047
|
+
databaseStatus = (await this.client.getDatabase(databaseId)).status;
|
|
5048
|
+
}
|
|
5049
|
+
catch {
|
|
5050
|
+
databaseStatus = undefined;
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5032
5053
|
resources.push({
|
|
5033
5054
|
system: 'coolify',
|
|
5034
5055
|
kind: 'jobs',
|
|
5035
5056
|
key: JOBS_SCHEDULER_KEY,
|
|
5036
|
-
config: {
|
|
5057
|
+
config: {
|
|
5058
|
+
uuid: app.uuid,
|
|
5059
|
+
status: app.status,
|
|
5060
|
+
...(databaseStatus !== undefined && { databaseStatus }),
|
|
5061
|
+
},
|
|
5037
5062
|
});
|
|
5038
5063
|
}
|
|
5039
5064
|
catch {
|