@wtfalch/foundry 0.39.2 → 0.39.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.
@@ -74,22 +74,34 @@ export declare const SIGNIN_DB_BACKUP_ENV: {
74
74
  readonly keyId: "BACKUP_S3_KEY_ID";
75
75
  readonly secret: "BACKUP_S3_SECRET";
76
76
  readonly prefix: "BACKUP_S3_PREFIX";
77
+ readonly script: "BACKUP_S3_SCRIPT";
77
78
  };
78
79
  /** Where in the bucket a company's dumps go: `signin-db/<slug>/signin-db-<UTC time>.dump`. */
79
80
  export declare function signinDbBackupPrefix(slug: string): string;
80
- /** The env entries the backup command needs, for the database app. */
81
- export declare function signinDbBackupEnvs(target: SigninDbBackupTarget, slug: string): Array<{
81
+ /** The env entries the backup command needs, for the database app, the script included. */
82
+ export declare function signinDbBackupEnvs(target: SigninDbBackupTarget, slug: string, db: {
83
+ user: string;
84
+ database: string;
85
+ }): Array<{
82
86
  key: string;
83
87
  value: string;
84
88
  }>;
85
89
  /**
86
- * The command Coolify runs in the container. A custom-format dump goes to a temporary file (an upload
90
+ * The script the backup command runs. A custom-format dump goes to a temporary file (an upload
87
91
  * needs a length), is signed with SigV4 and put with curl, and the file is removed on every exit. The
88
92
  * credentials reach curl on its standard input as a config, never in an argument. Every failure ends the
89
- * run non-zero, and curl's error text names the HTTP status only. No single quote: Coolify wraps the
90
- * command in single quotes.
93
+ * run non-zero, and curl's error text names the HTTP status only. It lives in the app's env, base64,
94
+ * because Coolify stores a task's command in 255 characters and this script is longer.
95
+ */
96
+ export declare function signinDbBackupScript(user: string, database: string): string;
97
+ /** The most Coolify stores in a scheduled task's command (`varchar(255)`); a longer one answers 500. */
98
+ export declare const COOLIFY_TASK_COMMAND_MAX = 255;
99
+ /**
100
+ * The command Coolify runs in the container: it decodes the script from the env and runs it. An
101
+ * empty env fails the run instead of passing it. No single quote: Coolify wraps the command in single
102
+ * quotes.
91
103
  */
92
- export declare function signinDbBackupCommand(user: string, database: string): string;
104
+ export declare function signinDbBackupCommand(): string;
93
105
  /**
94
106
  * Makes sure the database app has its backup task, and returns its uuid. The task is found by name
95
107
  * before it is created, and updated to the full wanted body every time (Coolify hides the stored
@@ -97,9 +109,6 @@ export declare function signinDbBackupCommand(user: string, database: string): s
97
109
  * read back and must be listed enabled with the wanted frequency and timeout, or this throws: a
98
110
  * schedule that cannot be seen is not a backup. The caller records the uuid. Errors name the label only.
99
111
  */
100
- export declare function ensureSigninDbBackupTask(coolify: Pick<CoolifyPort, 'raw'>, appId: string, label: string, db: {
101
- user: string;
102
- database: string;
103
- }): Promise<string>;
112
+ export declare function ensureSigninDbBackupTask(coolify: Pick<CoolifyPort, 'raw'>, appId: string, label: string): Promise<string>;
104
113
  /** A company on the TLS database whose nightly dump is not scheduled yet: the sign-in step plans again to schedule it. */
105
114
  export declare function signinDbBackupMissing(recordedIds: Readonly<Record<string, string>>): boolean;
@@ -113,13 +113,14 @@ export const SIGNIN_DB_BACKUP_ENV = {
113
113
  keyId: 'BACKUP_S3_KEY_ID',
114
114
  secret: 'BACKUP_S3_SECRET',
115
115
  prefix: 'BACKUP_S3_PREFIX',
116
+ script: 'BACKUP_S3_SCRIPT',
116
117
  };
117
118
  /** Where in the bucket a company's dumps go: `signin-db/<slug>/signin-db-<UTC time>.dump`. */
118
119
  export function signinDbBackupPrefix(slug) {
119
120
  return `signin-db/${slug}`;
120
121
  }
121
- /** The env entries the backup command needs, for the database app. */
122
- export function signinDbBackupEnvs(target, slug) {
122
+ /** The env entries the backup command needs, for the database app, the script included. */
123
+ export function signinDbBackupEnvs(target, slug, db) {
123
124
  return [
124
125
  { key: SIGNIN_DB_BACKUP_ENV.endpoint, value: target.endpoint.replace(/\/+$/, '') },
125
126
  { key: SIGNIN_DB_BACKUP_ENV.bucket, value: target.bucket },
@@ -127,16 +128,20 @@ export function signinDbBackupEnvs(target, slug) {
127
128
  { key: SIGNIN_DB_BACKUP_ENV.keyId, value: target.accessKeyId },
128
129
  { key: SIGNIN_DB_BACKUP_ENV.secret, value: target.secretAccessKey },
129
130
  { key: SIGNIN_DB_BACKUP_ENV.prefix, value: signinDbBackupPrefix(slug) },
131
+ {
132
+ key: SIGNIN_DB_BACKUP_ENV.script,
133
+ value: Buffer.from(signinDbBackupScript(db.user, db.database), 'utf8').toString('base64'),
134
+ },
130
135
  ];
131
136
  }
132
137
  /**
133
- * The command Coolify runs in the container. A custom-format dump goes to a temporary file (an upload
138
+ * The script the backup command runs. A custom-format dump goes to a temporary file (an upload
134
139
  * needs a length), is signed with SigV4 and put with curl, and the file is removed on every exit. The
135
140
  * credentials reach curl on its standard input as a config, never in an argument. Every failure ends the
136
- * run non-zero, and curl's error text names the HTTP status only. No single quote: Coolify wraps the
137
- * command in single quotes.
141
+ * run non-zero, and curl's error text names the HTTP status only. It lives in the app's env, base64,
142
+ * because Coolify stores a task's command in 255 characters and this script is longer.
138
143
  */
139
- export function signinDbBackupCommand(user, database) {
144
+ export function signinDbBackupScript(user, database) {
140
145
  const e = SIGNIN_DB_BACKUP_ENV;
141
146
  return [
142
147
  'set -eu',
@@ -146,6 +151,17 @@ export function signinDbBackupCommand(user, database) {
146
151
  `printf "user = \\"%s:%s\\"\\n" "$${e.keyId}" "$${e.secret}" | curl -sS --fail --max-time ${UPLOAD_MAX_SECONDS} --aws-sigv4 "aws:amz:$${e.region}:s3" -K - -T "$f" "$${e.endpoint}/$${e.bucket}/$${e.prefix}/\${f##*/}"`,
147
152
  ].join('; ');
148
153
  }
154
+ /** The most Coolify stores in a scheduled task's command (`varchar(255)`); a longer one answers 500. */
155
+ export const COOLIFY_TASK_COMMAND_MAX = 255;
156
+ /**
157
+ * The command Coolify runs in the container: it decodes the script from the env and runs it. An
158
+ * empty env fails the run instead of passing it. No single quote: Coolify wraps the command in single
159
+ * quotes.
160
+ */
161
+ export function signinDbBackupCommand() {
162
+ const v = `$${SIGNIN_DB_BACKUP_ENV.script}`;
163
+ return `[ -n "${v}" ] && printf %s "${v}" | base64 -d > /tmp/signin-db-backup.sh && sh /tmp/signin-db-backup.sh`;
164
+ }
149
165
  function rows(body) {
150
166
  if (!Array.isArray(body))
151
167
  throw new Error('scheduled tasks list has an unexpected shape');
@@ -158,11 +174,11 @@ function rows(body) {
158
174
  * read back and must be listed enabled with the wanted frequency and timeout, or this throws: a
159
175
  * schedule that cannot be seen is not a backup. The caller records the uuid. Errors name the label only.
160
176
  */
161
- export async function ensureSigninDbBackupTask(coolify, appId, label, db) {
177
+ export async function ensureSigninDbBackupTask(coolify, appId, label) {
162
178
  const base = `/applications/${appId}/scheduled-tasks`;
163
179
  const body = {
164
180
  name: SIGNIN_DB_BACKUP_TASK_NAME,
165
- command: signinDbBackupCommand(db.user, db.database),
181
+ command: signinDbBackupCommand(),
166
182
  frequency: SIGNIN_DB_BACKUP_FREQUENCY,
167
183
  timeout: SIGNIN_DB_BACKUP_TIMEOUT_SECONDS,
168
184
  enabled: true,
@@ -223,7 +223,10 @@ export async function provisionSigninDb(input) {
223
223
  { key: SIGNIN_DB_ENV.database, value: SIGNIN_DB_NAME },
224
224
  { key: SIGNIN_DB_ENV.cert, value: b64(server.certPem) },
225
225
  { key: SIGNIN_DB_ENV.key, value: b64(server.keyPem) },
226
- ...signinDbBackupEnvs(backup.target, input.slug),
226
+ ...signinDbBackupEnvs(backup.target, input.slug, {
227
+ user: SIGNIN_DB_USER,
228
+ database: SIGNIN_DB_NAME,
229
+ }),
227
230
  ];
228
231
  const changed = await envsDiffer(coolify, app.appId, envs);
229
232
  await coolify.bulkUpsertEnvs(app.appId, envs);
@@ -237,10 +240,7 @@ export async function provisionSigninDb(input) {
237
240
  }
238
241
  await input.record(SIGNIN_DB_CERT_NOT_AFTER_SCOPE_KEY, String(server.notAfter));
239
242
  // The database is not used, and ZITADEL is not stopped for a move, until its nightly dump is scheduled.
240
- await input.record(SIGNIN_DB_BACKUP_TASK_SCOPE_KEY, await ensureSigninDbBackupTask(coolify, app.appId, label, {
241
- user: SIGNIN_DB_USER,
242
- database: SIGNIN_DB_NAME,
243
- }));
243
+ await input.record(SIGNIN_DB_BACKUP_TASK_SCOPE_KEY, await ensureSigninDbBackupTask(coolify, app.appId, label));
244
244
  const dsn = signinDbDsn(input.internalHost, password);
245
245
  runtime.onSecret?.(dsn);
246
246
  return { appId: app.appId, dsn, ca: ca.certPem, password };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/foundry",
3
- "version": "0.39.2",
3
+ "version": "0.39.4",
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",