@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 ·
|
|
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 ·
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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">
|
|
2265
|
-
<div class="o-cf">{{ d.engineName }} runs one node
|
|
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
|
|
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
|
|
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">
|
|
2588
|
-
<div class="o-cf">{{ d.engineName }} runs one node
|
|
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') {
|