@chisquare-tech/origine-ngx-apps-services 0.2.2 → 0.2.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.
@@ -78,6 +78,10 @@ class ServicesApi {
78
78
  deleteBackup(project, id, name) {
79
79
  return this.api.delete(this.i(project, id, 'backup/' + enc(name)));
80
80
  }
81
+ /** A link to a RabbitMQ instance's management UI, for the caller only (read-only for Developers). */
82
+ management(project, id) {
83
+ return this.api.post(this.i(project, id, 'management'), {}).pipe(map((r) => data(r)));
84
+ }
81
85
  restore(project, id, body) {
82
86
  return this.api.post(this.i(project, id, 'restore'), body).pipe(map((r) => data(r)));
83
87
  }
@@ -402,7 +406,7 @@ class ServicesListPage {
402
406
  </div>
403
407
  <div class="o-g2">
404
408
  <a class="o-card o-cb o-flex" [routerLink]="host.link('services/new')" [queryParams]="{ engine: 'nats' }" data-testid="svc-new-nats"><span class="o-sq" style="background:var(--o-ok)"></span><div class="o-col" style="gap:2px"><b>NATS with JetStream</b><span class="o-faint o-sm">Streams, consumers, KV buckets · 3-node cluster</span></div><span class="o-r o-faint o-sm">Create →</span></a>
405
- <a class="o-card o-cb o-flex" [routerLink]="host.link('services/new')" [queryParams]="{ engine: 'rabbitmq' }" data-testid="svc-new-rabbitmq"><span class="o-sq" style="background:var(--o-run)"></span><div class="o-col" style="gap:2px"><b>RabbitMQ</b><span class="o-faint o-sm">Quorum queues · inspected in the console</span></div><span class="o-r o-faint o-sm">Create →</span></a>
409
+ <a class="o-card o-cb o-flex" [routerLink]="host.link('services/new')" [queryParams]="{ engine: 'rabbitmq' }" data-testid="svc-new-rabbitmq"><span class="o-sq" style="background:var(--o-run)"></span><div class="o-col" style="gap:2px"><b>RabbitMQ</b><span class="o-faint o-sm">Quorum queues · management UI through the platform</span></div><span class="o-r o-faint o-sm">Create →</span></a>
406
410
  </div>
407
411
  <span class="o-faint o-sm">Lifecycle states: {{ lifecycle }}. Open-source engines only.</span>
408
412
  </div>
@@ -452,7 +456,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
452
456
  </div>
453
457
  <div class="o-g2">
454
458
  <a class="o-card o-cb o-flex" [routerLink]="host.link('services/new')" [queryParams]="{ engine: 'nats' }" data-testid="svc-new-nats"><span class="o-sq" style="background:var(--o-ok)"></span><div class="o-col" style="gap:2px"><b>NATS with JetStream</b><span class="o-faint o-sm">Streams, consumers, KV buckets · 3-node cluster</span></div><span class="o-r o-faint o-sm">Create →</span></a>
455
- <a class="o-card o-cb o-flex" [routerLink]="host.link('services/new')" [queryParams]="{ engine: 'rabbitmq' }" data-testid="svc-new-rabbitmq"><span class="o-sq" style="background:var(--o-run)"></span><div class="o-col" style="gap:2px"><b>RabbitMQ</b><span class="o-faint o-sm">Quorum queues · inspected in the console</span></div><span class="o-r o-faint o-sm">Create →</span></a>
459
+ <a class="o-card o-cb o-flex" [routerLink]="host.link('services/new')" [queryParams]="{ engine: 'rabbitmq' }" data-testid="svc-new-rabbitmq"><span class="o-sq" style="background:var(--o-run)"></span><div class="o-col" style="gap:2px"><b>RabbitMQ</b><span class="o-faint o-sm">Quorum queues · management UI through the platform</span></div><span class="o-r o-faint o-sm">Create →</span></a>
456
460
  </div>
457
461
  <span class="o-faint o-sm">Lifecycle states: {{ lifecycle }}. Open-source engines only.</span>
458
462
  </div>
@@ -496,7 +500,9 @@ class ServicesNewPage {
496
500
  return '3-node cluster';
497
501
  if (e?.ha === 'standby')
498
502
  return e.id === 'mysql' ? 'Standby with automatic failover (3 nodes: MOCO runs 1, 3 or 5)' : 'Standby node with automatic failover';
499
- return `${e?.name ?? 'This engine'} runs one node for now`;
503
+ if (e?.ha === 'replica')
504
+ return 'Replica with automatic failover (three Sentinels watch the primary)';
505
+ return `${e?.name ?? 'This engine'} runs one node`;
500
506
  }, ...(ngDevMode ? [{ debugName: "haLabel" }] : /* istanbul ignore next */ []));
501
507
  this.keep = signal(7, ...(ngDevMode ? [{ debugName: "keep" }] : /* istanbul ignore next */ []));
502
508
  this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
@@ -1601,7 +1607,7 @@ class ServicePage {
1601
1607
  this.conn = computed(() => this.revealed()?.connection ?? this.d()?.connection ?? null, ...(ngDevMode ? [{ debugName: "conn" }] : /* istanbul ignore next */ []));
1602
1608
  this.snippetKeys = computed(() => {
1603
1609
  const s = this.conn()?.snippets ?? {};
1604
- return ['uri', 'psql', 'mysql', 'valkey-cli', 'nats', 'node', 'cli'].filter((k) => k in s);
1610
+ return ['uri', 'psql', 'mysql', 'valkey-cli', 'sentinel', 'nats', 'node', 'cli'].filter((k) => k in s);
1605
1611
  }, ...(ngDevMode ? [{ debugName: "snippetKeys" }] : /* istanbul ignore next */ []));
1606
1612
  this.backups = computed(() => [...(this.d()?.backups ?? [])].sort((a, b) => (b.startedAt ?? '9').localeCompare(a.startedAt ?? '9')), ...(ngDevMode ? [{ debugName: "backups" }] : /* istanbul ignore next */ []));
1607
1613
  this.attEnvs = computed(() => {
@@ -1774,7 +1780,7 @@ class ServicePage {
1774
1780
  return `${cpu} / ${ram} / ${d.diskGb} GB`;
1775
1781
  }
1776
1782
  snippetLabel(k) {
1777
- return { uri: 'URI', node: 'Node', cli: 'origine CLI', nats: 'nats' }[k] ?? k;
1783
+ return { uri: 'URI', node: 'Node', cli: 'origine CLI', nats: 'nats', sentinel: 'Sentinel' }[k] ?? k;
1778
1784
  }
1779
1785
  attState(a) {
1780
1786
  return a.state === 'live' ? 'Injected' : a.state === 'failed' ? 'Failed' : 'Redeploying…';
@@ -1903,13 +1909,14 @@ class ServicePage {
1903
1909
  return `${d.name}-restore-1`.slice(0, 40);
1904
1910
  }
1905
1911
  askRestore(d, b) {
1906
- this.dialog.set({ kind: 'restore', name: this.restoreName(d), point: '', backup: b.name });
1912
+ const vols = d.engine === 'rabbitmq' && b.volumes?.phase === 'completed';
1913
+ this.dialog.set({ kind: 'restore', name: this.restoreName(d), point: '', backup: b.name, volumes: vols, hasVolumes: vols });
1907
1914
  }
1908
1915
  askPitr(d) {
1909
1916
  this.dialog.set({ kind: 'restore', name: this.restoreName(d), point: this.pitr(), backup: '' });
1910
1917
  }
1911
1918
  restore(d, dl) {
1912
- const body = dl.point ? { pointInTime: fromLocalInput(dl.point), name: dl.name } : { backup: dl.backup, name: dl.name };
1919
+ const body = dl.point ? { pointInTime: fromLocalInput(dl.point), name: dl.name } : { backup: dl.backup, name: dl.name, volumes: !!dl.volumes };
1913
1920
  this.busy.set(true);
1914
1921
  this.error.set('');
1915
1922
  this.api.restore(this.key(), d.id, body).subscribe({
@@ -1964,6 +1971,35 @@ class ServicePage {
1964
1971
  refresh() {
1965
1972
  this.load();
1966
1973
  }
1974
+ /** Opens the instance's management UI in a new tab: a link for this person that expires. */
1975
+ openManagement(d) {
1976
+ const tab = window.open('', '_blank');
1977
+ this.busy.set(true);
1978
+ this.error.set('');
1979
+ this.api.management(this.key(), d.id).subscribe({
1980
+ next: (l) => {
1981
+ this.busy.set(false);
1982
+ if (tab) {
1983
+ tab.opener = null;
1984
+ tab.location.href = l.url;
1985
+ }
1986
+ else
1987
+ window.open(l.url, '_blank', 'noopener');
1988
+ this.notice.set(`The management UI of ${d.name} opened in a new tab${l.mode === 'read' ? ', read-only for your role' : ''}. Its link works for you for 30 minutes.`);
1989
+ },
1990
+ error: (e) => {
1991
+ this.busy.set(false);
1992
+ tab?.close();
1993
+ this.error.set(apiMessage(e, 'The management UI could not be opened.'));
1994
+ },
1995
+ });
1996
+ }
1997
+ snapshotLine(d) {
1998
+ const v = d.volumeSnapshots;
1999
+ if (v?.class)
2000
+ return `Each backup also snapshots every node's volume (${v.class}): restores can bring the messages back.`;
2001
+ return `Backups hold the definitions only: ${v?.message || 'this cell has no CSI snapshot class'}.`;
2002
+ }
1967
2003
  snapshotName(d) {
1968
2004
  return d.engine === 'nats' ? 'stream snapshot' : d.engine === 'rabbitmq' ? 'definitions export' : 'RDB snapshot';
1969
2005
  }
@@ -2034,6 +2070,7 @@ class ServicePage {
2034
2070
  <div class="o-flex"><h1 class="o-t">{{ d.name }}</h1><span [class]="chip(d.state)" data-testid="svc-state">{{ d.state }}</span></div>
2035
2071
  </div>
2036
2072
  <div class="o-acts">
2073
+ @if (d.management && !d.deletedAt) {<button type="button" class="o-btn" (click)="openManagement(d)" [disabled]="busy() || d.state !== 'Running' && d.state !== 'Updating' && d.state !== 'Degraded'" [title]="d.management === 'read' ? 'Read-only for your role' : 'As the instance administrator'" data-testid="svc-management">Open management UI@if (d.management === 'read') {<span class="o-faint o-xs"> · read-only</span>}</button>}
2037
2074
  @if (d.canManage && !d.deletedAt) {<button type="button" class="o-btn" (click)="askRotate(d)" [disabled]="busy() || !d.users?.length" data-testid="svc-rotate">Rotate credentials</button>}
2038
2075
  @if (d.canManage && !d.deletedAt) {<button type="button" class="o-btn" (click)="setTab('settings')">Resize</button>}
2039
2076
  <a class="o-btn" [routerLink]="host.link('dashboards')">Dashboard</a>
@@ -2189,7 +2226,7 @@ class ServicePage {
2189
2226
  @for (b of backups(); track b.name) {
2190
2227
  <div class="o-row" [attr.data-testid]="'svc-backup-' + b.name">
2191
2228
  <span [class]="'o-dot ' + backupDot(b)"></span><span style="width:150px">{{ when(b.startedAt) || 'Pending' }}</span><span class="o-faint o-sm" style="width:80px">{{ backupNote(b) }}</span>
2192
- <span class="o-chip">{{ kind(b) }}</span><span class="o-faint o-sm">{{ keepLine(b, d.retentionDays) }}</span>
2229
+ <span class="o-chip">{{ kind(b) }}</span>@if (b.volumes) {<span [class]="'o-chip ' + (b.volumes.phase === 'completed' ? 'o-grn' : b.volumes.phase === 'failed' ? 'o-red' : 'o-blu')" [title]="b.volumes.error || b.volumes.snapshots + ' volume snapshot(s)'" data-testid="svc-backup-volumes">{{ b.volumes.phase === 'completed' ? '+ queue data' : b.volumes.phase === 'failed' ? 'queue data failed' : 'snapshotting' }}</span>}<span class="o-faint o-sm">{{ keepLine(b, d.retentionDays) }}</span>
2193
2230
  @if (b.phase === 'completed' && d.canManage) {<button type="button" class="o-lnk o-r" (click)="askRestore(d, b)">Restore</button>}
2194
2231
  @if (b.kind === 'on-demand' && d.canManage && b.phase !== 'running') {<button type="button" class="o-lnk o-faint" [class.o-r]="b.phase !== 'completed'" (click)="deleteBackup(d, b)" [attr.aria-label]="'Delete ' + b.name" [disabled]="busy()">×</button>}
2195
2232
  </div>
@@ -2205,7 +2242,8 @@ class ServicePage {
2205
2242
  <button type="button" class="o-btn o-dark" (click)="askPitr(d)" [disabled]="!d.canManage || !pitr() || !d.window?.from" data-testid="svc-pitr-go">Restore…</button>
2206
2243
  </div>
2207
2244
  } @else {
2208
- <div class="o-card o-cb o-col" style="gap:6px"><b>Restore a snapshot</b><span class="o-hint">{{ d.engineName }} restores from a daily {{ snapshotName(d) }}: pick one on the left. It always creates a new instance.</span></div>
2245
+ <div class="o-card o-cb o-col" style="gap:6px"><b>Restore a snapshot</b><span class="o-hint">{{ d.engineName }} restores from a daily {{ snapshotName(d) }}: pick one on the left. It always creates a new instance.</span>
2246
+ @if (d.engine === 'rabbitmq') {<span class="o-hint" data-testid="svc-snapshot-support"><span [class]="'o-chip ' + (d.volumeSnapshots?.class ? 'o-grn' : '')">{{ d.volumeSnapshots?.class ? 'Queue data snapshots' : 'Definitions only' }}</span> {{ snapshotLine(d) }}</span>}</div>
2209
2247
  }
2210
2248
  <div class="o-card o-cb o-form">
2211
2249
  <b>Schedule</b>
@@ -2260,9 +2298,18 @@ class ServicePage {
2260
2298
  <div class="o-row"><span style="flex:1">3-node cluster<span class="o-faint o-sm"> · {{ d.engine === 'nats' ? 'streams replicated on three servers' : 'quorum queues on three nodes' }}</span></span><button type="button" class="o-tog" [class.o-on]="d.standby" [disabled]="!d.canManage || busy()" (click)="topology(d, !d.standby, 0)" aria-label="3-node cluster" data-testid="svc-standby"></button></div>
2261
2299
  <div class="o-cf">Each node is billed at the plan's price · {{ nodesPrice(d) }}/mo now</div>
2262
2300
  }
2301
+ @case ('replica') {
2302
+ <div class="o-row"><span style="flex:1">Replica with automatic failover<span class="o-faint o-sm"> · three Sentinels promote it if the primary fails</span></span><button type="button" class="o-tog" [class.o-on]="d.standby" [disabled]="!d.canManage || busy()" (click)="topology(d, !d.standby, 0)" aria-label="Replica with failover" data-testid="svc-standby"></button></div>
2303
+ <div class="o-row"><span style="flex:1">Second replica@if (d.sentinel; as sn) {<span class="o-faint o-sm o-mono"> · Sentinels {{ sn.hosts.length }} × :{{ sn.port }}, master {{ sn.master }}</span>}</span><span class="o-faint o-sm" data-testid="svc-replicas">{{ d.replicas }}</span>
2304
+ @if (d.canManage) {
2305
+ <button type="button" class="o-btn o-xs" (click)="topology(d, d.standby, d.replicas - 1)" [disabled]="busy() || d.replicas === 0">−</button>
2306
+ <button type="button" class="o-btn o-xs" (click)="topology(d, true, d.replicas + 1)" [disabled]="busy() || d.replicas + 1 > (engine()?.maxReplicas ?? 0)" data-testid="svc-replica-add">+ Replica</button>
2307
+ }</div>
2308
+ <div class="o-cf">Each node is billed at the plan's price · {{ nodesPrice(d) }}/mo now · apps get REDIS_SENTINEL_URL beside REDIS_URL</div>
2309
+ }
2263
2310
  @default {
2264
- <div class="o-row"><span style="flex:1">Replica with failover</span><b class="o-ph-tag o-later">Later</b></div>
2265
- <div class="o-cf">{{ d.engineName }} runs one node for now; daily snapshots keep its data.</div>
2311
+ <div class="o-row"><span style="flex:1">One node</span></div>
2312
+ <div class="o-cf">{{ d.engineName }} runs one node; daily snapshots keep its data.</div>
2266
2313
  }
2267
2314
  }
2268
2315
  </div>
@@ -2317,6 +2364,7 @@ class ServicePage {
2317
2364
  }
2318
2365
  @case ('restore') {
2319
2366
  <span>{{ dl.point ? 'Restores ' + d.name + ' as it was at ' + whenLocal(dl.point) : 'Restores backup ' + dl.backup }} into a new instance with the same users and passwords. {{ d.name }} keeps running untouched; move its apps over afterwards if you want.</span>
2367
+ @if (dl.hasVolumes) {<label class="o-flex o-sm"><input type="checkbox" [(ngModel)]="dl.volumes" data-testid="svc-restore-volumes" /> With the queue data (the backup's volume snapshots): messages, not only the definitions</label>}
2320
2368
  <label class="o-fld"><span class="o-sm">New instance</span><input class="o-in o-mono" [(ngModel)]="dl.name" data-testid="svc-restore-name" autocomplete="off" /></label>
2321
2369
  }
2322
2370
  @case ('rotate') {
@@ -2357,6 +2405,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
2357
2405
  <div class="o-flex"><h1 class="o-t">{{ d.name }}</h1><span [class]="chip(d.state)" data-testid="svc-state">{{ d.state }}</span></div>
2358
2406
  </div>
2359
2407
  <div class="o-acts">
2408
+ @if (d.management && !d.deletedAt) {<button type="button" class="o-btn" (click)="openManagement(d)" [disabled]="busy() || d.state !== 'Running' && d.state !== 'Updating' && d.state !== 'Degraded'" [title]="d.management === 'read' ? 'Read-only for your role' : 'As the instance administrator'" data-testid="svc-management">Open management UI@if (d.management === 'read') {<span class="o-faint o-xs"> · read-only</span>}</button>}
2360
2409
  @if (d.canManage && !d.deletedAt) {<button type="button" class="o-btn" (click)="askRotate(d)" [disabled]="busy() || !d.users?.length" data-testid="svc-rotate">Rotate credentials</button>}
2361
2410
  @if (d.canManage && !d.deletedAt) {<button type="button" class="o-btn" (click)="setTab('settings')">Resize</button>}
2362
2411
  <a class="o-btn" [routerLink]="host.link('dashboards')">Dashboard</a>
@@ -2512,7 +2561,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
2512
2561
  @for (b of backups(); track b.name) {
2513
2562
  <div class="o-row" [attr.data-testid]="'svc-backup-' + b.name">
2514
2563
  <span [class]="'o-dot ' + backupDot(b)"></span><span style="width:150px">{{ when(b.startedAt) || 'Pending' }}</span><span class="o-faint o-sm" style="width:80px">{{ backupNote(b) }}</span>
2515
- <span class="o-chip">{{ kind(b) }}</span><span class="o-faint o-sm">{{ keepLine(b, d.retentionDays) }}</span>
2564
+ <span class="o-chip">{{ kind(b) }}</span>@if (b.volumes) {<span [class]="'o-chip ' + (b.volumes.phase === 'completed' ? 'o-grn' : b.volumes.phase === 'failed' ? 'o-red' : 'o-blu')" [title]="b.volumes.error || b.volumes.snapshots + ' volume snapshot(s)'" data-testid="svc-backup-volumes">{{ b.volumes.phase === 'completed' ? '+ queue data' : b.volumes.phase === 'failed' ? 'queue data failed' : 'snapshotting' }}</span>}<span class="o-faint o-sm">{{ keepLine(b, d.retentionDays) }}</span>
2516
2565
  @if (b.phase === 'completed' && d.canManage) {<button type="button" class="o-lnk o-r" (click)="askRestore(d, b)">Restore</button>}
2517
2566
  @if (b.kind === 'on-demand' && d.canManage && b.phase !== 'running') {<button type="button" class="o-lnk o-faint" [class.o-r]="b.phase !== 'completed'" (click)="deleteBackup(d, b)" [attr.aria-label]="'Delete ' + b.name" [disabled]="busy()">×</button>}
2518
2567
  </div>
@@ -2528,7 +2577,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
2528
2577
  <button type="button" class="o-btn o-dark" (click)="askPitr(d)" [disabled]="!d.canManage || !pitr() || !d.window?.from" data-testid="svc-pitr-go">Restore…</button>
2529
2578
  </div>
2530
2579
  } @else {
2531
- <div class="o-card o-cb o-col" style="gap:6px"><b>Restore a snapshot</b><span class="o-hint">{{ d.engineName }} restores from a daily {{ snapshotName(d) }}: pick one on the left. It always creates a new instance.</span></div>
2580
+ <div class="o-card o-cb o-col" style="gap:6px"><b>Restore a snapshot</b><span class="o-hint">{{ d.engineName }} restores from a daily {{ snapshotName(d) }}: pick one on the left. It always creates a new instance.</span>
2581
+ @if (d.engine === 'rabbitmq') {<span class="o-hint" data-testid="svc-snapshot-support"><span [class]="'o-chip ' + (d.volumeSnapshots?.class ? 'o-grn' : '')">{{ d.volumeSnapshots?.class ? 'Queue data snapshots' : 'Definitions only' }}</span> {{ snapshotLine(d) }}</span>}</div>
2532
2582
  }
2533
2583
  <div class="o-card o-cb o-form">
2534
2584
  <b>Schedule</b>
@@ -2583,9 +2633,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
2583
2633
  <div class="o-row"><span style="flex:1">3-node cluster<span class="o-faint o-sm"> · {{ d.engine === 'nats' ? 'streams replicated on three servers' : 'quorum queues on three nodes' }}</span></span><button type="button" class="o-tog" [class.o-on]="d.standby" [disabled]="!d.canManage || busy()" (click)="topology(d, !d.standby, 0)" aria-label="3-node cluster" data-testid="svc-standby"></button></div>
2584
2634
  <div class="o-cf">Each node is billed at the plan's price · {{ nodesPrice(d) }}/mo now</div>
2585
2635
  }
2636
+ @case ('replica') {
2637
+ <div class="o-row"><span style="flex:1">Replica with automatic failover<span class="o-faint o-sm"> · three Sentinels promote it if the primary fails</span></span><button type="button" class="o-tog" [class.o-on]="d.standby" [disabled]="!d.canManage || busy()" (click)="topology(d, !d.standby, 0)" aria-label="Replica with failover" data-testid="svc-standby"></button></div>
2638
+ <div class="o-row"><span style="flex:1">Second replica@if (d.sentinel; as sn) {<span class="o-faint o-sm o-mono"> · Sentinels {{ sn.hosts.length }} × :{{ sn.port }}, master {{ sn.master }}</span>}</span><span class="o-faint o-sm" data-testid="svc-replicas">{{ d.replicas }}</span>
2639
+ @if (d.canManage) {
2640
+ <button type="button" class="o-btn o-xs" (click)="topology(d, d.standby, d.replicas - 1)" [disabled]="busy() || d.replicas === 0">−</button>
2641
+ <button type="button" class="o-btn o-xs" (click)="topology(d, true, d.replicas + 1)" [disabled]="busy() || d.replicas + 1 > (engine()?.maxReplicas ?? 0)" data-testid="svc-replica-add">+ Replica</button>
2642
+ }</div>
2643
+ <div class="o-cf">Each node is billed at the plan's price · {{ nodesPrice(d) }}/mo now · apps get REDIS_SENTINEL_URL beside REDIS_URL</div>
2644
+ }
2586
2645
  @default {
2587
- <div class="o-row"><span style="flex:1">Replica with failover</span><b class="o-ph-tag o-later">Later</b></div>
2588
- <div class="o-cf">{{ d.engineName }} runs one node for now; daily snapshots keep its data.</div>
2646
+ <div class="o-row"><span style="flex:1">One node</span></div>
2647
+ <div class="o-cf">{{ d.engineName }} runs one node; daily snapshots keep its data.</div>
2589
2648
  }
2590
2649
  }
2591
2650
  </div>
@@ -2640,6 +2699,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
2640
2699
  }
2641
2700
  @case ('restore') {
2642
2701
  <span>{{ dl.point ? 'Restores ' + d.name + ' as it was at ' + whenLocal(dl.point) : 'Restores backup ' + dl.backup }} into a new instance with the same users and passwords. {{ d.name }} keeps running untouched; move its apps over afterwards if you want.</span>
2702
+ @if (dl.hasVolumes) {<label class="o-flex o-sm"><input type="checkbox" [(ngModel)]="dl.volumes" data-testid="svc-restore-volumes" /> With the queue data (the backup's volume snapshots): messages, not only the definitions</label>}
2643
2703
  <label class="o-fld"><span class="o-sm">New instance</span><input class="o-in o-mono" [(ngModel)]="dl.name" data-testid="svc-restore-name" autocomplete="off" /></label>
2644
2704
  }
2645
2705
  @case ('rotate') {