@camstack/system 1.2.19 → 1.2.20
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.
|
@@ -3,6 +3,15 @@ export declare class BackupOrchestratorAddon extends BaseAddon<Record<string, ne
|
|
|
3
3
|
private systemBackup;
|
|
4
4
|
private policies;
|
|
5
5
|
private dataDir;
|
|
6
|
+
/**
|
|
7
|
+
* Transient build buffer for archive fan-out. The tar is built here
|
|
8
|
+
* ONCE, streamed to every destination via the storage cap's chunked
|
|
9
|
+
* upload, then removed. Lives in the addon's private `ctx.dataDir`
|
|
10
|
+
* (never `api.storage.resolve`, per the AddonContext contract) — a
|
|
11
|
+
* `cache` storage location is never declared, so resolving one threw
|
|
12
|
+
* and aborted every run before a byte was written.
|
|
13
|
+
*/
|
|
14
|
+
private stagingDir;
|
|
6
15
|
private scheduleTimer;
|
|
7
16
|
/**
|
|
8
17
|
* Baseline used as the "after" anchor when a policy has a cron but
|
|
@@ -47,12 +56,13 @@ export declare class BackupOrchestratorAddon extends BaseAddon<Record<string, ne
|
|
|
47
56
|
* unreachable destination doesn't blank out the whole UI.
|
|
48
57
|
*/
|
|
49
58
|
private aggregatedList;
|
|
59
|
+
private stagingPath;
|
|
50
60
|
/**
|
|
51
|
-
* Build a single archive in the
|
|
61
|
+
* Build a single archive in the private staging dir, then push
|
|
52
62
|
* it to every selected destination via the storage cap's chunked
|
|
53
63
|
* upload protocol. Per-destination errors are logged and counted
|
|
54
64
|
* via allSettled semantics — one bad sink doesn't kill the run. The
|
|
55
|
-
*
|
|
65
|
+
* staging file is deleted in `finally` so a crash mid-fanout doesn't
|
|
56
66
|
* leak.
|
|
57
67
|
*/
|
|
58
68
|
private triggerBackup;
|
|
@@ -99,8 +109,8 @@ export declare class BackupOrchestratorAddon extends BaseAddon<Record<string, ne
|
|
|
99
109
|
/**
|
|
100
110
|
* Schedule a restore for the next boot. Pulls the archive bytes
|
|
101
111
|
* down from the destination's storage location via the chunked-
|
|
102
|
-
* download protocol into the
|
|
103
|
-
* usual restore marker against the
|
|
112
|
+
* download protocol into the private staging dir, then writes the
|
|
113
|
+
* usual restore marker against the staged path so the boot-time
|
|
104
114
|
* apply hook (in server-backend launcher) extracts before any
|
|
105
115
|
* addon starts.
|
|
106
116
|
*
|
|
@@ -4587,8 +4587,8 @@ function previewNextRuns(cron, count, after) {
|
|
|
4587
4587
|
* that list with the `backup_destination_policies` table to surface
|
|
4588
4588
|
* `enabled` / `retentionCount` / `label`. The actual archive bytes
|
|
4589
4589
|
* travel through `api.storage.beginUpload` / `writeChunk` /
|
|
4590
|
-
* `finalizeUpload`, with the staging archive built into the
|
|
4591
|
-
*
|
|
4590
|
+
* `finalizeUpload`, with the staging archive built into the addon's
|
|
4591
|
+
* private `ctx.dataDir/staging` dir.
|
|
4592
4592
|
*/
|
|
4593
4593
|
/**
|
|
4594
4594
|
* Hardcoded seed for newly-discovered `backups` locations. Operators
|
|
@@ -4636,6 +4636,15 @@ var BackupOrchestratorAddon = class extends require_dist.BaseAddon {
|
|
|
4636
4636
|
systemBackup = null;
|
|
4637
4637
|
policies = null;
|
|
4638
4638
|
dataDir = "";
|
|
4639
|
+
/**
|
|
4640
|
+
* Transient build buffer for archive fan-out. The tar is built here
|
|
4641
|
+
* ONCE, streamed to every destination via the storage cap's chunked
|
|
4642
|
+
* upload, then removed. Lives in the addon's private `ctx.dataDir`
|
|
4643
|
+
* (never `api.storage.resolve`, per the AddonContext contract) — a
|
|
4644
|
+
* `cache` storage location is never declared, so resolving one threw
|
|
4645
|
+
* and aborted every run before a byte was written.
|
|
4646
|
+
*/
|
|
4647
|
+
stagingDir = "";
|
|
4639
4648
|
scheduleTimer = null;
|
|
4640
4649
|
/**
|
|
4641
4650
|
* Baseline used as the "after" anchor when a policy has a cron but
|
|
@@ -4650,6 +4659,7 @@ var BackupOrchestratorAddon = class extends require_dist.BaseAddon {
|
|
|
4650
4659
|
}
|
|
4651
4660
|
async onInitialize() {
|
|
4652
4661
|
this.dataDir = this.ctx.nodeDataDir;
|
|
4662
|
+
this.stagingDir = node_path.join(this.ctx.dataDir, "staging");
|
|
4653
4663
|
this.systemBackup = new SystemBackupService(this.dataDir, this.ctx.logger);
|
|
4654
4664
|
this.policies = this.resolvePolicyService();
|
|
4655
4665
|
if (this.policies) try {
|
|
@@ -4826,12 +4836,16 @@ var BackupOrchestratorAddon = class extends require_dist.BaseAddon {
|
|
|
4826
4836
|
}
|
|
4827
4837
|
return out;
|
|
4828
4838
|
}
|
|
4839
|
+
async stagingPath(filename) {
|
|
4840
|
+
await node_fs_promises.mkdir(this.stagingDir, { recursive: true });
|
|
4841
|
+
return node_path.join(this.stagingDir, filename);
|
|
4842
|
+
}
|
|
4829
4843
|
/**
|
|
4830
|
-
* Build a single archive in the
|
|
4844
|
+
* Build a single archive in the private staging dir, then push
|
|
4831
4845
|
* it to every selected destination via the storage cap's chunked
|
|
4832
4846
|
* upload protocol. Per-destination errors are logged and counted
|
|
4833
4847
|
* via allSettled semantics — one bad sink doesn't kill the run. The
|
|
4834
|
-
*
|
|
4848
|
+
* staging file is deleted in `finally` so a crash mid-fanout doesn't
|
|
4835
4849
|
* leak.
|
|
4836
4850
|
*/
|
|
4837
4851
|
async triggerBackup(input) {
|
|
@@ -4844,11 +4858,7 @@ var BackupOrchestratorAddon = class extends require_dist.BaseAddon {
|
|
|
4844
4858
|
const policies = await this.loadPoliciesIndexed(targets.map((t) => t.id));
|
|
4845
4859
|
const archiveId = (0, node_crypto.randomUUID)();
|
|
4846
4860
|
const archiveFilename = `${archiveId}.tar.gz`;
|
|
4847
|
-
const
|
|
4848
|
-
const cachePath = await this.ctx.api.storage.resolve.query({
|
|
4849
|
-
location: "cache",
|
|
4850
|
-
relativePath: cacheRelativePath
|
|
4851
|
-
});
|
|
4861
|
+
const cachePath = await this.stagingPath(archiveFilename);
|
|
4852
4862
|
const result = await this.systemBackup.createArchive({
|
|
4853
4863
|
archivePath: cachePath,
|
|
4854
4864
|
...input?.locations ? { locations: input.locations } : {}
|
|
@@ -5092,8 +5102,8 @@ var BackupOrchestratorAddon = class extends require_dist.BaseAddon {
|
|
|
5092
5102
|
/**
|
|
5093
5103
|
* Schedule a restore for the next boot. Pulls the archive bytes
|
|
5094
5104
|
* down from the destination's storage location via the chunked-
|
|
5095
|
-
* download protocol into the
|
|
5096
|
-
* usual restore marker against the
|
|
5105
|
+
* download protocol into the private staging dir, then writes the
|
|
5106
|
+
* usual restore marker against the staged path so the boot-time
|
|
5097
5107
|
* apply hook (in server-backend launcher) extracts before any
|
|
5098
5108
|
* addon starts.
|
|
5099
5109
|
*
|
|
@@ -5104,10 +5114,7 @@ var BackupOrchestratorAddon = class extends require_dist.BaseAddon {
|
|
|
5104
5114
|
if (!this.systemBackup) throw new Error("orchestrator not initialized");
|
|
5105
5115
|
const entry = (await readLocationManifest(this.storageApi(), input.destinationId)).archives.find((a) => a.id === input.backupId);
|
|
5106
5116
|
if (!entry) throw new Error(`backup restore: backup "${input.backupId}" not found at location "${input.destinationId}"`);
|
|
5107
|
-
const cachePath = await this.
|
|
5108
|
-
location: "cache",
|
|
5109
|
-
relativePath: `restore-${input.backupId}.tar.gz`
|
|
5110
|
-
});
|
|
5117
|
+
const cachePath = await this.stagingPath(`restore-${input.backupId}.tar.gz`);
|
|
5111
5118
|
await downloadArchiveToCache({
|
|
5112
5119
|
api: this.ctx.api.storage,
|
|
5113
5120
|
locationId: input.destinationId,
|
|
@@ -4572,8 +4572,8 @@ function previewNextRuns(cron, count, after) {
|
|
|
4572
4572
|
* that list with the `backup_destination_policies` table to surface
|
|
4573
4573
|
* `enabled` / `retentionCount` / `label`. The actual archive bytes
|
|
4574
4574
|
* travel through `api.storage.beginUpload` / `writeChunk` /
|
|
4575
|
-
* `finalizeUpload`, with the staging archive built into the
|
|
4576
|
-
*
|
|
4575
|
+
* `finalizeUpload`, with the staging archive built into the addon's
|
|
4576
|
+
* private `ctx.dataDir/staging` dir.
|
|
4577
4577
|
*/
|
|
4578
4578
|
/**
|
|
4579
4579
|
* Hardcoded seed for newly-discovered `backups` locations. Operators
|
|
@@ -4621,6 +4621,15 @@ var BackupOrchestratorAddon = class extends BaseAddon {
|
|
|
4621
4621
|
systemBackup = null;
|
|
4622
4622
|
policies = null;
|
|
4623
4623
|
dataDir = "";
|
|
4624
|
+
/**
|
|
4625
|
+
* Transient build buffer for archive fan-out. The tar is built here
|
|
4626
|
+
* ONCE, streamed to every destination via the storage cap's chunked
|
|
4627
|
+
* upload, then removed. Lives in the addon's private `ctx.dataDir`
|
|
4628
|
+
* (never `api.storage.resolve`, per the AddonContext contract) — a
|
|
4629
|
+
* `cache` storage location is never declared, so resolving one threw
|
|
4630
|
+
* and aborted every run before a byte was written.
|
|
4631
|
+
*/
|
|
4632
|
+
stagingDir = "";
|
|
4624
4633
|
scheduleTimer = null;
|
|
4625
4634
|
/**
|
|
4626
4635
|
* Baseline used as the "after" anchor when a policy has a cron but
|
|
@@ -4635,6 +4644,7 @@ var BackupOrchestratorAddon = class extends BaseAddon {
|
|
|
4635
4644
|
}
|
|
4636
4645
|
async onInitialize() {
|
|
4637
4646
|
this.dataDir = this.ctx.nodeDataDir;
|
|
4647
|
+
this.stagingDir = path$1.join(this.ctx.dataDir, "staging");
|
|
4638
4648
|
this.systemBackup = new SystemBackupService(this.dataDir, this.ctx.logger);
|
|
4639
4649
|
this.policies = this.resolvePolicyService();
|
|
4640
4650
|
if (this.policies) try {
|
|
@@ -4811,12 +4821,16 @@ var BackupOrchestratorAddon = class extends BaseAddon {
|
|
|
4811
4821
|
}
|
|
4812
4822
|
return out;
|
|
4813
4823
|
}
|
|
4824
|
+
async stagingPath(filename) {
|
|
4825
|
+
await fsp.mkdir(this.stagingDir, { recursive: true });
|
|
4826
|
+
return path$1.join(this.stagingDir, filename);
|
|
4827
|
+
}
|
|
4814
4828
|
/**
|
|
4815
|
-
* Build a single archive in the
|
|
4829
|
+
* Build a single archive in the private staging dir, then push
|
|
4816
4830
|
* it to every selected destination via the storage cap's chunked
|
|
4817
4831
|
* upload protocol. Per-destination errors are logged and counted
|
|
4818
4832
|
* via allSettled semantics — one bad sink doesn't kill the run. The
|
|
4819
|
-
*
|
|
4833
|
+
* staging file is deleted in `finally` so a crash mid-fanout doesn't
|
|
4820
4834
|
* leak.
|
|
4821
4835
|
*/
|
|
4822
4836
|
async triggerBackup(input) {
|
|
@@ -4829,11 +4843,7 @@ var BackupOrchestratorAddon = class extends BaseAddon {
|
|
|
4829
4843
|
const policies = await this.loadPoliciesIndexed(targets.map((t) => t.id));
|
|
4830
4844
|
const archiveId = randomUUID();
|
|
4831
4845
|
const archiveFilename = `${archiveId}.tar.gz`;
|
|
4832
|
-
const
|
|
4833
|
-
const cachePath = await this.ctx.api.storage.resolve.query({
|
|
4834
|
-
location: "cache",
|
|
4835
|
-
relativePath: cacheRelativePath
|
|
4836
|
-
});
|
|
4846
|
+
const cachePath = await this.stagingPath(archiveFilename);
|
|
4837
4847
|
const result = await this.systemBackup.createArchive({
|
|
4838
4848
|
archivePath: cachePath,
|
|
4839
4849
|
...input?.locations ? { locations: input.locations } : {}
|
|
@@ -5077,8 +5087,8 @@ var BackupOrchestratorAddon = class extends BaseAddon {
|
|
|
5077
5087
|
/**
|
|
5078
5088
|
* Schedule a restore for the next boot. Pulls the archive bytes
|
|
5079
5089
|
* down from the destination's storage location via the chunked-
|
|
5080
|
-
* download protocol into the
|
|
5081
|
-
* usual restore marker against the
|
|
5090
|
+
* download protocol into the private staging dir, then writes the
|
|
5091
|
+
* usual restore marker against the staged path so the boot-time
|
|
5082
5092
|
* apply hook (in server-backend launcher) extracts before any
|
|
5083
5093
|
* addon starts.
|
|
5084
5094
|
*
|
|
@@ -5089,10 +5099,7 @@ var BackupOrchestratorAddon = class extends BaseAddon {
|
|
|
5089
5099
|
if (!this.systemBackup) throw new Error("orchestrator not initialized");
|
|
5090
5100
|
const entry = (await readLocationManifest(this.storageApi(), input.destinationId)).archives.find((a) => a.id === input.backupId);
|
|
5091
5101
|
if (!entry) throw new Error(`backup restore: backup "${input.backupId}" not found at location "${input.destinationId}"`);
|
|
5092
|
-
const cachePath = await this.
|
|
5093
|
-
location: "cache",
|
|
5094
|
-
relativePath: `restore-${input.backupId}.tar.gz`
|
|
5095
|
-
});
|
|
5102
|
+
const cachePath = await this.stagingPath(`restore-${input.backupId}.tar.gz`);
|
|
5096
5103
|
await downloadArchiveToCache({
|
|
5097
5104
|
api: this.ctx.api.storage,
|
|
5098
5105
|
locationId: input.destinationId,
|