@supacloud/cli 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/dist/index.js +49 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,18 +111,32 @@ loopback development origins, with the default `:80` likewise omitted. Use
|
|
|
111
111
|
`release` is an official CLI entry point for the existing Management API
|
|
112
112
|
logical-backup and PostgREST lifecycle capabilities. It requires the Management
|
|
113
113
|
API context above; it does not promote an application `service_role` key to
|
|
114
|
-
Management authority.
|
|
115
|
-
exposed by this command group.
|
|
114
|
+
Management authority.
|
|
116
115
|
|
|
117
116
|
```bash
|
|
118
117
|
supacloud-cli release logical_backup_list --ref abc123
|
|
119
118
|
supacloud-cli release logical_backup_create --ref abc123
|
|
119
|
+
supacloud-cli release logical_backup_restore --ref abc123 \
|
|
120
|
+
--backup_id logical-full_abc123_<backup-id-suffix> \
|
|
121
|
+
--expected_sha256 <64-lowercase-hex> \
|
|
122
|
+
--restore_confirmation RESTORE_PROJECT:abc123:logical-full_abc123_<backup-id-suffix>:<64-lowercase-hex>
|
|
120
123
|
supacloud-cli release postgrest_status --ref abc123
|
|
121
124
|
supacloud-cli release postgrest_restart --ref abc123
|
|
122
125
|
```
|
|
123
126
|
|
|
124
127
|
Backup creation reports success only after the CLI verifies exactly one new
|
|
125
128
|
logical-backup receipt against the inventory before and after the mutation.
|
|
129
|
+
Logical restore is project-scoped: pause the selected project first, obtain the
|
|
130
|
+
backup ID and SHA-256 from `logical_backup_list`, and supply both the normal
|
|
131
|
+
production `--confirm-production <ref>` value (for production profiles) and
|
|
132
|
+
the exact `--restore_confirmation
|
|
133
|
+
RESTORE_PROJECT:<ref>:<backup_id>:<sha256>`. Before POST, the CLI re-reads
|
|
134
|
+
that same project's inventory and binds the request to the complete verified
|
|
135
|
+
backup identity; it then verifies both the server receipt and a fresh inventory
|
|
136
|
+
read. It never retries a restore. A transport, server, or unreadable-response
|
|
137
|
+
failure is reported as `OUTCOME_UNKNOWN`; read the inventory and investigate
|
|
138
|
+
before any new restore decision.
|
|
139
|
+
|
|
126
140
|
PostgREST restart reports success only after it receives a matching restart
|
|
127
141
|
receipt and reads back `desired=running`, `actual=running`, and
|
|
128
142
|
`health=healthy`. Both mutating controls follow the normal production
|
package/dist/index.js
CHANGED
|
@@ -6486,7 +6486,7 @@ var ACTION_POLICY = {
|
|
|
6486
6486
|
mutations: { read: ["status"] },
|
|
6487
6487
|
release: {
|
|
6488
6488
|
read: ["logical_backup_list", "postgrest_status"],
|
|
6489
|
-
write: ["logical_backup_create", "postgrest_restart"]
|
|
6489
|
+
write: ["logical_backup_create", "logical_backup_restore", "postgrest_restart"]
|
|
6490
6490
|
},
|
|
6491
6491
|
secrets: { read: ["list"], write: ["upsert", "delete"] },
|
|
6492
6492
|
frontend: {
|
|
@@ -12277,6 +12277,19 @@ function newlyCreatedBackup(before, after) {
|
|
|
12277
12277
|
const additions = after.filter((backup) => !known.has(backup.backup_id));
|
|
12278
12278
|
return additions.length === 1 ? additions[0] : null;
|
|
12279
12279
|
}
|
|
12280
|
+
function restoreRequest(projectRef, backupId, expectedSha256, restoreConfirmation) {
|
|
12281
|
+
if (typeof backupId !== "string" || !backupBelongsToProject(backupId, projectRef)) {
|
|
12282
|
+
throw new Error("'backup_id' must identify a logical-full backup for 'ref'");
|
|
12283
|
+
}
|
|
12284
|
+
if (typeof expectedSha256 !== "string" || !SHA256.test(expectedSha256)) {
|
|
12285
|
+
throw new Error("'expected_sha256' must be a lowercase SHA-256 digest");
|
|
12286
|
+
}
|
|
12287
|
+
const confirmation = `RESTORE_PROJECT:${projectRef}:${backupId}:${expectedSha256}`;
|
|
12288
|
+
if (restoreConfirmation !== confirmation) {
|
|
12289
|
+
throw new Error("'restore_confirmation' must exactly confirm the selected logical backup restore");
|
|
12290
|
+
}
|
|
12291
|
+
return { backup_id: backupId, expected_sha256: expectedSha256, confirmation };
|
|
12292
|
+
}
|
|
12280
12293
|
function endpoint(projectRef) {
|
|
12281
12294
|
if (!validProjectRef(projectRef))
|
|
12282
12295
|
throw new Error("'ref' is invalid for release controls");
|
|
@@ -12334,15 +12347,19 @@ function isRestartReceipt(value) {
|
|
|
12334
12347
|
return isRecord(value) && value.service === "postgrest" && value.action === "restart" && value.success === true;
|
|
12335
12348
|
}
|
|
12336
12349
|
function registerReleaseTools(server, http, options = {}) {
|
|
12337
|
-
server.tool("release", "Verified release controls using a Management API credential. Actions: logical_backup_list, logical_backup_create, postgrest_status, postgrest_restart", {
|
|
12350
|
+
server.tool("release", "Verified release controls using a Management API credential. Actions: logical_backup_list, logical_backup_create, logical_backup_restore, postgrest_status, postgrest_restart", {
|
|
12338
12351
|
action: withDescription(stringEnum([
|
|
12339
12352
|
"logical_backup_list",
|
|
12340
12353
|
"logical_backup_create",
|
|
12354
|
+
"logical_backup_restore",
|
|
12341
12355
|
"postgrest_status",
|
|
12342
12356
|
"postgrest_restart"
|
|
12343
12357
|
]), "Release control action"),
|
|
12344
|
-
ref: optional(Type.String(), options.projectRef ? "Optional override when not auto-linked" : "Project ref")
|
|
12345
|
-
|
|
12358
|
+
ref: optional(Type.String(), options.projectRef ? "Optional override when not auto-linked" : "Project ref"),
|
|
12359
|
+
backup_id: optional(Type.String(), "[logical_backup_restore] Exact verified logical-full backup ID from the selected project inventory"),
|
|
12360
|
+
expected_sha256: optional(Type.String(), "[logical_backup_restore] Exact lowercase SHA-256 from the selected project inventory"),
|
|
12361
|
+
restore_confirmation: optional(Type.String(), "[logical_backup_restore] Exact RESTORE_PROJECT:<ref>:<backup_id>:<sha256> confirmation")
|
|
12362
|
+
}, async ({ action, ref, backup_id, expected_sha256, restore_confirmation }) => {
|
|
12346
12363
|
const projectRef = typeof ref === "string" && ref || options.projectRef;
|
|
12347
12364
|
if (!projectRef)
|
|
12348
12365
|
throw new Error("'ref' is required for release controls");
|
|
@@ -12379,6 +12396,32 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
12379
12396
|
backup: publicBackup(addedBackup)
|
|
12380
12397
|
});
|
|
12381
12398
|
}
|
|
12399
|
+
if (action === "logical_backup_restore") {
|
|
12400
|
+
const request = restoreRequest(projectRef, backup_id, expected_sha256, restore_confirmation);
|
|
12401
|
+
const before = await readInventory(http, projectRef);
|
|
12402
|
+
const beforeFailure = readInventoryFailure("release.logical_backup.restore", before);
|
|
12403
|
+
if (beforeFailure)
|
|
12404
|
+
return beforeFailure;
|
|
12405
|
+
const selectedBackup = before.inventory.find((backup) => backup.backup_id === request.backup_id && backup.sha256 === request.expected_sha256);
|
|
12406
|
+
if (!selectedBackup) {
|
|
12407
|
+
return releaseControlFailure("release.logical_backup.restore", "MUTATION_NOT_SUCCEEDED", null);
|
|
12408
|
+
}
|
|
12409
|
+
const mutation2 = await http.postReleaseMutation(`${endpoint(projectRef)}/database/backups/logical/restore`, request, { timeoutMs: BACKUP_TIMEOUT_MS });
|
|
12410
|
+
if (!mutation2.ok || mutation2.status !== 200) {
|
|
12411
|
+
return mutationFailure("release.logical_backup.restore", mutation2);
|
|
12412
|
+
}
|
|
12413
|
+
const responseBackup = isRecord(mutation2.data) ? verifiedBackup(mutation2.data.restored_backup, projectRef) : null;
|
|
12414
|
+
const after = await readInventory(http, projectRef);
|
|
12415
|
+
const afterFailure = readInventoryFailure("release.logical_backup.restore", after);
|
|
12416
|
+
const restoredInventoryBackup = after.inventory?.find((backup) => backup.backup_id === request.backup_id);
|
|
12417
|
+
if (!responseBackup || !equalBackup(responseBackup, selectedBackup) || afterFailure || !restoredInventoryBackup || !equalBackup(restoredInventoryBackup, selectedBackup)) {
|
|
12418
|
+
return releaseControlFailure("release.logical_backup.restore", "OUTCOME_UNKNOWN", mutation2.status);
|
|
12419
|
+
}
|
|
12420
|
+
return releaseControlSuccess("release.logical_backup.restore", {
|
|
12421
|
+
project_ref: projectRef,
|
|
12422
|
+
backup: publicBackup(selectedBackup)
|
|
12423
|
+
});
|
|
12424
|
+
}
|
|
12382
12425
|
if (action === "postgrest_status") {
|
|
12383
12426
|
const read2 = await readPostgrestStatus(http, projectRef);
|
|
12384
12427
|
const failure = readPostgrestFailure("release.postgrest.status", read2);
|
|
@@ -12407,7 +12450,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
12407
12450
|
// package.json
|
|
12408
12451
|
var package_default = {
|
|
12409
12452
|
name: "@supacloud/cli",
|
|
12410
|
-
version: "0.
|
|
12453
|
+
version: "0.24.0",
|
|
12411
12454
|
description: "Project-scoped CLI for SupaCloud users",
|
|
12412
12455
|
type: "module",
|
|
12413
12456
|
main: "./dist/index.js",
|
|
@@ -12652,6 +12695,7 @@ EXAMPLES
|
|
|
12652
12695
|
${preferredCommand} project logs --log_type database
|
|
12653
12696
|
${preferredCommand} project task_stats
|
|
12654
12697
|
${preferredCommand} release logical_backup_create --ref abc123
|
|
12698
|
+
${preferredCommand} release logical_backup_restore --ref abc123 --backup_id <backup_id> --expected_sha256 <sha256> --restore_confirmation RESTORE_PROJECT:abc123:<backup_id>:<sha256>
|
|
12655
12699
|
${preferredCommand} release postgrest_status --ref abc123
|
|
12656
12700
|
${preferredCommand} release postgrest_restart --ref abc123
|
|
12657
12701
|
${preferredCommand} queue stats --queue emails
|