@wtfalch/foundry 0.16.2 → 0.16.3

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.
@@ -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
@@ -1357,6 +1358,25 @@ export declare const ACCESS_POLICY_NAME = "foundry: allowed emails";
1357
1358
  * uses for the org's own app.
1358
1359
  */
1359
1360
  export declare function isRunningOrStarting(status: string | undefined): boolean;
1361
+ /**
1362
+ * Reads a Postgres database and starts it when Coolify reports it as not
1363
+ * running. `createPostgresDatabase` does not start what it creates (no
1364
+ * `instant_deploy`), and a database found by name or by recorded id may have
1365
+ * been left stopped, so every site that points an app at a database calls this
1366
+ * instead of `getDatabase`. Returns what `getDatabase` returned.
1367
+ */
1368
+ export declare function getDatabaseStarted(coolify: {
1369
+ getDatabase(uuid: string): Promise<{
1370
+ uuid: string;
1371
+ internal_db_url?: string;
1372
+ status?: string;
1373
+ }>;
1374
+ raw(method: string, path: string, body?: unknown): Promise<unknown>;
1375
+ }, dbId: string): Promise<{
1376
+ uuid: string;
1377
+ internal_db_url?: string;
1378
+ status?: string;
1379
+ }>;
1360
1380
  export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer {
1361
1381
  private readonly ports;
1362
1382
  constructor(ports: FoundryStepExecutorPorts);
@@ -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.getDatabase(dbId);
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.getDatabase(dbId);
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.getDatabase(synapseDbId),
2228
- this.ports.coolify.getDatabase(masDbId),
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.getDatabase(synapseDbId),
2521
- this.ports.coolify.getDatabase(masDbId),
2522
- this.ports.coolify.getDatabase(serviceDbId),
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.getDatabase(dbId);
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
  }
@@ -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'>;
@@ -4,7 +4,7 @@ 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
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';
10
10
  import { applyCallsSfuPlatformStep } from './platform-sfu.js';
@@ -429,7 +429,7 @@ export class FoundryPlatformStepExecutor {
429
429
  });
430
430
  appId = created.uuid;
431
431
  }
432
- const db = await coolify.getDatabase(dbId);
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.getDatabase(dbId);
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
@@ -2108,7 +2108,7 @@ async function provisionPlatformArchon(context, deps) {
2108
2108
  }
2109
2109
  await record(PLATFORM_ARCHON_DB_SCOPE_KEY, dbId);
2110
2110
  }
2111
- const db = await deps.coolify.getDatabase(dbId);
2111
+ const db = await getDatabaseStarted(deps.coolify, dbId);
2112
2112
  if (!db.internal_db_url) {
2113
2113
  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
2114
  }
@@ -3264,7 +3264,7 @@ async function provisionPlatformAi(context, deps) {
3264
3264
  }
3265
3265
  await record(PLATFORM_AI_DB_SCOPE_KEY, dbId);
3266
3266
  }
3267
- const db = await coolify.getDatabase(dbId);
3267
+ const db = await getDatabaseStarted(coolify, dbId);
3268
3268
  if (!db.internal_db_url) {
3269
3269
  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
3270
  }
@@ -3987,7 +3987,7 @@ async function provisionPlatformFiles(context, deps) {
3987
3987
  }
3988
3988
  await record(PLATFORM_FILES_DB_SCOPE_KEY, dbId);
3989
3989
  }
3990
- const db = await deps.coolify.getDatabase(dbId);
3990
+ const db = await getDatabaseStarted(deps.coolify, dbId);
3991
3991
  if (!db.internal_db_url) {
3992
3992
  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
3993
  }
@@ -4346,7 +4346,7 @@ async function provisionPlatformIntegrations(action, context, deps) {
4346
4346
  }
4347
4347
  await record(INTEGRATIONS_DATABASE_SCOPE_KEY, dbId);
4348
4348
  }
4349
- const db = await coolify.getDatabase(dbId);
4349
+ const db = await getDatabaseStarted(coolify, dbId);
4350
4350
  if (!db.internal_db_url) {
4351
4351
  throw new Error(`platform:integrations: database ${dbId} has no internal_db_url yet; not deploying the integrations host without a database`);
4352
4352
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/foundry",
3
- "version": "0.16.2",
3
+ "version": "0.16.3",
4
4
  "description": "Foundry's SDK: the declared org's types, the plan and apply contracts, and the state store's interface.",
5
5
  "repository": {
6
6
  "type": "git",