@postgres.ai/shared 4.1.2 → 4.2.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/.gitlab-ci.yml +7 -3
- package/components/UpgradeCloneModal/index.tsx +145 -0
- package/dist/components/UpgradeCloneModal/index.d.ts +11 -0
- package/dist/components/UpgradeCloneModal/index.js +75 -0
- package/dist/package.json +2 -2
- package/dist/pages/Branches/Branch/index.js +27 -2
- package/dist/pages/Branches/Branch/stores/Main.d.ts +5 -0
- package/dist/pages/Branches/Branch/stores/Main.js +29 -0
- package/dist/pages/Branches/components/BranchesTable/index.js +2 -2
- package/dist/pages/Branches/index.js +10 -1
- package/dist/pages/Clone/Status/index.js +3 -0
- package/dist/pages/Clone/index.js +12 -4
- package/dist/pages/Clone/stores/Main.d.ts +9 -1
- package/dist/pages/Clone/stores/Main.js +40 -3
- package/dist/pages/CreateBranch/stores/Main.d.ts +3 -0
- package/dist/pages/CreateClone/index.js +2 -0
- package/dist/pages/CreateClone/stores/Main.d.ts +5 -1
- package/dist/pages/CreateClone/stores/Main.js +8 -3
- package/dist/pages/Instance/Configuration/PhysicalMode/EnvsEditor/index.d.ts +11 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/EnvsEditor/index.js +24 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/PgBackRest/index.d.ts +10 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/PgBackRest/index.js +29 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/Sync/index.d.ts +9 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/Sync/index.js +14 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/Walg/index.d.ts +10 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/Walg/index.js +21 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/index.d.ts +10 -0
- package/dist/pages/Instance/Configuration/PhysicalMode/index.js +17 -0
- package/dist/pages/Instance/Configuration/SimpleMode/PreviewCard.d.ts +13 -0
- package/dist/pages/Instance/Configuration/SimpleMode/PreviewCard.js +14 -0
- package/dist/pages/Instance/Configuration/SimpleMode/index.d.ts +19 -0
- package/dist/pages/Instance/Configuration/SimpleMode/index.js +166 -0
- package/dist/pages/Instance/Configuration/configMode.d.ts +2 -0
- package/dist/pages/Instance/Configuration/configMode.js +7 -0
- package/dist/pages/Instance/Configuration/configOptions.d.ts +16 -0
- package/dist/pages/Instance/Configuration/configOptions.js +71 -0
- package/dist/pages/Instance/Configuration/connectionString.d.ts +20 -0
- package/dist/pages/Instance/Configuration/connectionString.js +129 -0
- package/dist/pages/Instance/Configuration/dockerCatalog.d.ts +2 -0
- package/dist/pages/Instance/Configuration/dockerCatalog.js +19 -0
- package/dist/pages/Instance/Configuration/index.d.ts +1 -2
- package/dist/pages/Instance/Configuration/index.js +115 -86
- package/dist/pages/Instance/Configuration/useForm.d.ts +20 -0
- package/dist/pages/Instance/Configuration/useForm.js +126 -5
- package/dist/pages/Instance/Configuration/utils/index.js +1 -17
- package/dist/pages/Instance/Info/Snapshots/Calendar/Day/index.js +3 -3
- package/dist/pages/Instance/Info/Snapshots/Calendar/utils.d.ts +3 -0
- package/dist/pages/Instance/Info/Snapshots/utils.d.ts +6 -0
- package/dist/pages/Instance/Snapshots/components/SnapshotsList/index.js +11 -1
- package/dist/pages/Instance/Snapshots/index.js +11 -3
- package/dist/pages/Instance/Snapshots/utils/index.d.ts +3 -0
- package/dist/pages/Instance/Tabs/styles.js +6 -6
- package/dist/pages/Instance/index.js +1 -3
- package/dist/pages/Instance/stores/Main.d.ts +22 -2
- package/dist/pages/Instance/stores/Main.js +26 -9
- package/dist/pages/Snapshots/Snapshot/index.js +28 -2
- package/dist/pages/Snapshots/Snapshot/stores/Main.d.ts +5 -0
- package/dist/pages/Snapshots/Snapshot/stores/Main.js +29 -0
- package/dist/stores/Snapshots.d.ts +1 -1
- package/dist/stores/Snapshots.js +12 -6
- package/dist/types/api/endpoints/getBranches.d.ts +6 -0
- package/dist/types/api/endpoints/probeSource.d.ts +32 -0
- package/dist/types/api/endpoints/probeSource.js +1 -0
- package/dist/types/api/endpoints/testDbSource.d.ts +3 -1
- package/dist/types/api/endpoints/testDbSource.js +6 -0
- package/dist/types/api/endpoints/updateBranch.d.ts +11 -0
- package/dist/types/api/endpoints/updateBranch.js +1 -0
- package/dist/types/api/endpoints/updateSnapshot.d.ts +11 -0
- package/dist/types/api/endpoints/updateSnapshot.js +1 -0
- package/dist/types/api/endpoints/upgradeClone.d.ts +8 -0
- package/dist/types/api/endpoints/upgradeClone.js +1 -0
- package/dist/types/api/entities/branchSnapshots.d.ts +3 -0
- package/dist/types/api/entities/clone.d.ts +9 -2
- package/dist/types/api/entities/config.d.ts +32 -0
- package/dist/types/api/entities/config.js +45 -18
- package/dist/types/api/entities/instance.d.ts +11 -1
- package/dist/types/api/entities/instanceState.d.ts +16 -1
- package/dist/types/api/entities/snapshot.d.ts +6 -0
- package/dist/utils/api.d.ts +1 -1
- package/dist/utils/api.js +6 -0
- package/dist/utils/clone.d.ts +3 -1
- package/dist/utils/clone.js +7 -1
- package/dist/utils/clonePoller.d.ts +12 -0
- package/dist/utils/clonePoller.js +41 -0
- package/dist/utils/date.js +12 -12
- package/dist/utils/protection.d.ts +5 -0
- package/dist/utils/protection.js +23 -0
- package/package.json +2 -2
- package/pages/Branches/Branch/index.tsx +70 -0
- package/pages/Branches/Branch/stores/Main.ts +40 -0
- package/pages/Branches/components/BranchesTable/index.tsx +2 -0
- package/pages/Branches/index.tsx +13 -1
- package/pages/Clone/Status/index.tsx +3 -0
- package/pages/Clone/index.tsx +58 -0
- package/pages/Clone/stores/Main.ts +51 -8
- package/pages/CreateClone/index.tsx +3 -0
- package/pages/CreateClone/stores/Main.ts +10 -8
- package/pages/Instance/Configuration/PhysicalMode/EnvsEditor/index.tsx +129 -0
- package/pages/Instance/Configuration/PhysicalMode/PgBackRest/index.tsx +89 -0
- package/pages/Instance/Configuration/PhysicalMode/Sync/index.tsx +64 -0
- package/pages/Instance/Configuration/PhysicalMode/Walg/index.tsx +63 -0
- package/pages/Instance/Configuration/PhysicalMode/index.tsx +93 -0
- package/pages/Instance/Configuration/SimpleMode/PreviewCard.tsx +176 -0
- package/pages/Instance/Configuration/SimpleMode/index.tsx +303 -0
- package/pages/Instance/Configuration/configMode.ts +12 -0
- package/pages/Instance/Configuration/configOptions.ts +111 -0
- package/pages/Instance/Configuration/connectionString.ts +157 -0
- package/pages/Instance/Configuration/dockerCatalog.ts +21 -0
- package/pages/Instance/Configuration/index.tsx +151 -49
- package/pages/Instance/Configuration/useForm.ts +155 -5
- package/pages/Instance/Configuration/utils/index.ts +1 -17
- package/pages/Instance/Info/Snapshots/Calendar/Day/index.tsx +3 -3
- package/pages/Instance/Snapshots/components/SnapshotsList/index.tsx +21 -1
- package/pages/Instance/Snapshots/index.tsx +15 -5
- package/pages/Instance/Tabs/styles.ts +7 -7
- package/pages/Instance/index.tsx +0 -4
- package/pages/Instance/stores/Main.ts +29 -7
- package/pages/Snapshots/Snapshot/index.tsx +71 -0
- package/pages/Snapshots/Snapshot/stores/Main.ts +40 -0
- package/postgres.ai-shared-4.2.0.tgz +0 -0
- package/stores/Snapshots.ts +16 -7
- package/types/api/endpoints/getBranches.ts +3 -0
- package/types/api/endpoints/probeSource.ts +34 -0
- package/types/api/endpoints/testDbSource.ts +10 -1
- package/types/api/endpoints/updateBranch.ts +11 -0
- package/types/api/endpoints/updateSnapshot.ts +11 -0
- package/types/api/endpoints/upgradeClone.ts +8 -0
- package/types/api/entities/clone.ts +13 -1
- package/types/api/entities/config.ts +46 -0
- package/types/api/entities/instanceState.ts +6 -0
- package/types/api/entities/snapshot.ts +3 -0
- package/utils/api.ts +8 -0
- package/utils/clone.ts +7 -1
- package/utils/clonePoller.ts +47 -0
- package/utils/date.ts +19 -14
- package/utils/protection.ts +25 -0
- package/postgres.ai-shared-4.1.2.tgz +0 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { makeAutoObservable } from 'mobx';
|
|
8
8
|
import { SnapshotsStore, } from '@postgres.ai/shared/stores/Snapshots';
|
|
9
9
|
import { generateSnapshotPageId } from '@postgres.ai/shared/pages/Instance/Snapshots/utils';
|
|
10
|
+
import { getTextFromUnknownApiError } from '@postgres.ai/shared/utils/api';
|
|
10
11
|
export class MainStore {
|
|
11
12
|
constructor(api) {
|
|
12
13
|
this.snapshot = null;
|
|
@@ -14,6 +15,8 @@ export class MainStore {
|
|
|
14
15
|
this.snapshotError = null;
|
|
15
16
|
this.branchSnapshotError = null;
|
|
16
17
|
this.isSnapshotsLoading = false;
|
|
18
|
+
this.isUpdatingSnapshot = false;
|
|
19
|
+
this.updateSnapshotError = null;
|
|
17
20
|
this.load = async (snapshotId, instanceId) => {
|
|
18
21
|
if (!snapshotId)
|
|
19
22
|
return;
|
|
@@ -55,6 +58,32 @@ export class MainStore {
|
|
|
55
58
|
}
|
|
56
59
|
return response;
|
|
57
60
|
};
|
|
61
|
+
this.updateSnapshotProtection = async (snapshotId, instanceId, durationMinutes) => {
|
|
62
|
+
if (!this.api.updateSnapshot || !this.snapshot)
|
|
63
|
+
return;
|
|
64
|
+
this.isUpdatingSnapshot = true;
|
|
65
|
+
this.updateSnapshotError = null;
|
|
66
|
+
const prevProtected = this.snapshot.protected;
|
|
67
|
+
const isProtected = durationMinutes !== null;
|
|
68
|
+
this.snapshot.protected = isProtected;
|
|
69
|
+
const { response, error } = await this.api.updateSnapshot({
|
|
70
|
+
instanceId,
|
|
71
|
+
snapshotId: this.snapshot.id,
|
|
72
|
+
snapshot: {
|
|
73
|
+
isProtected,
|
|
74
|
+
protectionDurationMinutes: durationMinutes !== null && durationMinutes !== void 0 ? durationMinutes : undefined,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
if (response) {
|
|
78
|
+
await this.load(snapshotId, instanceId);
|
|
79
|
+
}
|
|
80
|
+
else if (this.snapshot) {
|
|
81
|
+
this.snapshot.protected = prevProtected;
|
|
82
|
+
}
|
|
83
|
+
if (error)
|
|
84
|
+
this.updateSnapshotError = await getTextFromUnknownApiError(error);
|
|
85
|
+
this.isUpdatingSnapshot = false;
|
|
86
|
+
};
|
|
58
87
|
this.destroySnapshot = async (snapshotId, forceDelete, instanceId) => {
|
|
59
88
|
if (!this.api.destroySnapshot || !snapshotId)
|
|
60
89
|
return;
|
|
@@ -18,7 +18,7 @@ export declare class SnapshotsStore {
|
|
|
18
18
|
private readonly api;
|
|
19
19
|
constructor(api: SnapshotsApi);
|
|
20
20
|
load: (instanceId: string) => Promise<boolean | undefined>;
|
|
21
|
-
reload: (instanceId: string, branchName?: string) => Promise<boolean | undefined>;
|
|
21
|
+
reload: (instanceId: string, branchName?: string, silent?: boolean) => Promise<boolean | undefined>;
|
|
22
22
|
createSnapshot: (cloneId: string, message: string, instanceId: string) => Promise<import("../types/api/entities/createSnapshot").CreateSnapshotDTO | null | undefined>;
|
|
23
23
|
private loadData;
|
|
24
24
|
}
|
package/dist/stores/Snapshots.js
CHANGED
|
@@ -18,7 +18,7 @@ export class SnapshotsStore {
|
|
|
18
18
|
this.error = null;
|
|
19
19
|
return this.loadData(instanceId);
|
|
20
20
|
};
|
|
21
|
-
this.reload = (instanceId, branchName) => this.loadData(instanceId, branchName);
|
|
21
|
+
this.reload = (instanceId, branchName, silent) => this.loadData(instanceId, branchName, silent);
|
|
22
22
|
this.createSnapshot = async (cloneId, message, instanceId) => {
|
|
23
23
|
if (!this.api.createSnapshot || !cloneId)
|
|
24
24
|
return;
|
|
@@ -35,18 +35,24 @@ export class SnapshotsStore {
|
|
|
35
35
|
}
|
|
36
36
|
return response;
|
|
37
37
|
};
|
|
38
|
-
this.loadData = async (instanceId, branchName) => {
|
|
38
|
+
this.loadData = async (instanceId, branchName, silent) => {
|
|
39
39
|
if (!this.api.getSnapshots)
|
|
40
40
|
return;
|
|
41
|
-
|
|
41
|
+
if (!silent)
|
|
42
|
+
this.isLoading = true;
|
|
42
43
|
const { response, error } = await this.api.getSnapshots({
|
|
43
44
|
instanceId,
|
|
44
45
|
branchName,
|
|
45
46
|
});
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
if (!silent)
|
|
48
|
+
this.isLoading = false;
|
|
49
|
+
if (response) {
|
|
48
50
|
this.data = response;
|
|
49
|
-
|
|
51
|
+
this.error = null;
|
|
52
|
+
}
|
|
53
|
+
// a silent background refresh keeps the existing list on a transient failure; errors
|
|
54
|
+
// surface only on a foreground load.
|
|
55
|
+
if (error && !silent) {
|
|
50
56
|
this.error = await error
|
|
51
57
|
.json()
|
|
52
58
|
.then((error) => { var _a, _b; return (_b = (_a = error.details) === null || _a === void 0 ? void 0 : _a.split('"message": "')[1]) === null || _b === void 0 ? void 0 : _b.split('"')[0]; });
|
|
@@ -6,6 +6,9 @@ export interface Branch {
|
|
|
6
6
|
snapshotID: string;
|
|
7
7
|
dataset: string;
|
|
8
8
|
numSnapshots: number;
|
|
9
|
+
protected: boolean;
|
|
10
|
+
protectedTill?: string;
|
|
11
|
+
deleteAt?: string;
|
|
9
12
|
}
|
|
10
13
|
export declare const formatBranchesDto: (dto: Branch[]) => {
|
|
11
14
|
dataStateAt: string;
|
|
@@ -15,6 +18,9 @@ export declare const formatBranchesDto: (dto: Branch[]) => {
|
|
|
15
18
|
snapshotID: string;
|
|
16
19
|
dataset: string;
|
|
17
20
|
numSnapshots: number;
|
|
21
|
+
protected: boolean;
|
|
22
|
+
protectedTill?: string | undefined;
|
|
23
|
+
deleteAt?: string | undefined;
|
|
18
24
|
}[];
|
|
19
25
|
export declare type GetBranches = (instanceId: string) => Promise<{
|
|
20
26
|
response: Branch[] | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare type ProbeSourceRequest = {
|
|
2
|
+
url: string;
|
|
3
|
+
password: string;
|
|
4
|
+
};
|
|
5
|
+
export declare type SourceConnection = {
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
username: string;
|
|
9
|
+
dbname: string;
|
|
10
|
+
};
|
|
11
|
+
export declare type ProposedConfig = {
|
|
12
|
+
source: SourceConnection;
|
|
13
|
+
detectedProvider: string;
|
|
14
|
+
dockerImage: string;
|
|
15
|
+
dockerTag: string;
|
|
16
|
+
pgMajorVersion: number;
|
|
17
|
+
databases: string[];
|
|
18
|
+
sharedBuffers: string;
|
|
19
|
+
memoryProbed: boolean;
|
|
20
|
+
sharedPreloadLibraries: string;
|
|
21
|
+
queryTuning: {
|
|
22
|
+
[key: string]: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export declare type ProbeSourceError = {
|
|
26
|
+
status: number;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
export declare type ProbeSource = (req: ProbeSourceRequest) => Promise<{
|
|
30
|
+
response: ProposedConfig | null;
|
|
31
|
+
error: ProbeSourceError | null;
|
|
32
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -17,6 +17,8 @@ export declare type TestDbSource = (values: dbSource) => Promise<{
|
|
|
17
17
|
export declare const formatTuningParams: (tuningParams: {
|
|
18
18
|
[key: string]: string;
|
|
19
19
|
} | undefined) => string;
|
|
20
|
-
export declare const formatTuningParamsToObj: (tuningParams: string |
|
|
20
|
+
export declare const formatTuningParamsToObj: (tuningParams: string | {
|
|
21
|
+
[key: string]: string;
|
|
22
|
+
} | undefined) => {
|
|
21
23
|
[key: string]: string;
|
|
22
24
|
};
|
|
@@ -11,6 +11,12 @@ export const formatTuningParams = (tuningParams) => {
|
|
|
11
11
|
return formattedTuningParams;
|
|
12
12
|
};
|
|
13
13
|
export const formatTuningParamsToObj = (tuningParams) => {
|
|
14
|
+
// Simple mode seeds tuningParams as an object (the proposed queryTuning map);
|
|
15
|
+
// Expert mode keeps it as the textarea string. Pass an already-parsed object
|
|
16
|
+
// through unchanged so callers never run .split on a non-string.
|
|
17
|
+
if (tuningParams && typeof tuningParams === 'object') {
|
|
18
|
+
return tuningParams;
|
|
19
|
+
}
|
|
14
20
|
let formattedTuningParams = {};
|
|
15
21
|
if (tuningParams) {
|
|
16
22
|
const tuningParamsArr = tuningParams.split('\n');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,9 +4,11 @@ export declare type CloneDto = {
|
|
|
4
4
|
id: string;
|
|
5
5
|
branch: string;
|
|
6
6
|
status: {
|
|
7
|
-
code: 'OK' | 'CREATING' | 'DELETING' | 'RESETTING' | 'FATAL';
|
|
7
|
+
code: 'OK' | 'CREATING' | 'DELETING' | 'RESETTING' | 'UPGRADING' | 'WARNING' | 'FATAL';
|
|
8
8
|
message: string;
|
|
9
9
|
};
|
|
10
|
+
dockerImage?: string;
|
|
11
|
+
dbVersion?: string;
|
|
10
12
|
protected: boolean;
|
|
11
13
|
protectedTill?: string;
|
|
12
14
|
metadata: {
|
|
@@ -39,13 +41,18 @@ export declare const formatCloneDto: (dto: CloneDto) => {
|
|
|
39
41
|
logicalSize: number;
|
|
40
42
|
message: string;
|
|
41
43
|
branch: string;
|
|
44
|
+
protected: boolean;
|
|
45
|
+
protectedTill?: string | undefined;
|
|
46
|
+
deleteAt?: string | undefined;
|
|
42
47
|
} | null;
|
|
43
48
|
id: string;
|
|
44
49
|
branch: string;
|
|
45
50
|
status: {
|
|
46
|
-
code: 'OK' | 'CREATING' | 'DELETING' | 'RESETTING' | 'FATAL';
|
|
51
|
+
code: 'OK' | 'CREATING' | 'DELETING' | 'RESETTING' | 'UPGRADING' | 'WARNING' | 'FATAL';
|
|
47
52
|
message: string;
|
|
48
53
|
};
|
|
54
|
+
dockerImage?: string | undefined;
|
|
55
|
+
dbVersion?: string | undefined;
|
|
49
56
|
protected: boolean;
|
|
50
57
|
protectedTill?: string | undefined;
|
|
51
58
|
metadata: {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export interface DatabaseType {
|
|
2
2
|
[name: string]: string | Object;
|
|
3
3
|
}
|
|
4
|
+
export declare type RetrievalMode = 'logical' | 'physical' | 'unknown' | '';
|
|
4
5
|
export declare type configTypes = {
|
|
5
6
|
global?: {
|
|
6
7
|
debug?: boolean;
|
|
7
8
|
};
|
|
9
|
+
retrievalMode?: RetrievalMode;
|
|
8
10
|
databaseContainer?: {
|
|
9
11
|
dockerImage?: string;
|
|
10
12
|
dockerPath?: string;
|
|
@@ -46,6 +48,25 @@ export declare type configTypes = {
|
|
|
46
48
|
};
|
|
47
49
|
};
|
|
48
50
|
};
|
|
51
|
+
physicalRestore?: {
|
|
52
|
+
options?: {
|
|
53
|
+
tool?: string;
|
|
54
|
+
dockerImage?: string;
|
|
55
|
+
sync?: {
|
|
56
|
+
enabled?: boolean;
|
|
57
|
+
};
|
|
58
|
+
walg?: {
|
|
59
|
+
backupName?: string;
|
|
60
|
+
};
|
|
61
|
+
pgbackrest?: {
|
|
62
|
+
stanza?: string;
|
|
63
|
+
delta?: boolean;
|
|
64
|
+
};
|
|
65
|
+
envs?: {
|
|
66
|
+
[key: string]: string | number | boolean;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
};
|
|
49
70
|
};
|
|
50
71
|
};
|
|
51
72
|
};
|
|
@@ -71,6 +92,17 @@ export declare const formatConfig: (config: configTypes) => {
|
|
|
71
92
|
dockerTag?: string | undefined;
|
|
72
93
|
dockerImageType?: string | undefined;
|
|
73
94
|
debug: boolean | undefined;
|
|
95
|
+
retrievalMode: RetrievalMode;
|
|
96
|
+
physicalTool: string;
|
|
97
|
+
physicalDockerImage: string;
|
|
98
|
+
physicalSyncEnabled: boolean;
|
|
99
|
+
physicalWalgBackupName: string;
|
|
100
|
+
physicalPgbackrestStanza: string;
|
|
101
|
+
physicalPgbackrestDelta: boolean;
|
|
102
|
+
physicalEnvs: {
|
|
103
|
+
key: string;
|
|
104
|
+
value: string;
|
|
105
|
+
}[];
|
|
74
106
|
dockerImage: string | undefined;
|
|
75
107
|
};
|
|
76
108
|
export declare type Config = ReturnType<typeof formatConfig>;
|
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import { formatDatabases, formatDumpCustomOptions, getImageMajorVersion, getImageType, isSeDockerImage, } from '@postgres.ai/shared/pages/Instance/Configuration/utils';
|
|
2
2
|
import { formatTuningParams } from '../endpoints/testDbSource';
|
|
3
|
+
const formatPhysicalEnvs = (envs) => {
|
|
4
|
+
if (!envs)
|
|
5
|
+
return [];
|
|
6
|
+
return Object.entries(envs).map(([key, value]) => ({
|
|
7
|
+
key,
|
|
8
|
+
value: typeof value === 'string' ? value : String(value),
|
|
9
|
+
}));
|
|
10
|
+
};
|
|
11
|
+
const inferRetrievalMode = (config) => {
|
|
12
|
+
var _a, _b, _c, _d;
|
|
13
|
+
if (config.retrievalMode)
|
|
14
|
+
return config.retrievalMode;
|
|
15
|
+
if ((_b = (_a = config.retrieval) === null || _a === void 0 ? void 0 : _a.spec) === null || _b === void 0 ? void 0 : _b.physicalRestore)
|
|
16
|
+
return 'physical';
|
|
17
|
+
if ((_d = (_c = config.retrieval) === null || _c === void 0 ? void 0 : _c.spec) === null || _d === void 0 ? void 0 : _d.logicalDump)
|
|
18
|
+
return 'logical';
|
|
19
|
+
return '';
|
|
20
|
+
};
|
|
3
21
|
export const formatConfig = (config) => {
|
|
4
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45;
|
|
22
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58;
|
|
5
23
|
const dockerImage = (_a = config.databaseContainer) === null || _a === void 0 ? void 0 : _a.dockerImage;
|
|
24
|
+
const physical = (_d = (_c = (_b = config.retrieval) === null || _b === void 0 ? void 0 : _b.spec) === null || _c === void 0 ? void 0 : _c.physicalRestore) === null || _d === void 0 ? void 0 : _d.options;
|
|
6
25
|
return {
|
|
7
|
-
debug: (
|
|
26
|
+
debug: (_e = config.global) === null || _e === void 0 ? void 0 : _e.debug,
|
|
27
|
+
retrievalMode: inferRetrievalMode(config),
|
|
28
|
+
physicalTool: (_f = physical === null || physical === void 0 ? void 0 : physical.tool) !== null && _f !== void 0 ? _f : '',
|
|
29
|
+
physicalDockerImage: (_g = physical === null || physical === void 0 ? void 0 : physical.dockerImage) !== null && _g !== void 0 ? _g : '',
|
|
30
|
+
physicalSyncEnabled: (_j = (_h = physical === null || physical === void 0 ? void 0 : physical.sync) === null || _h === void 0 ? void 0 : _h.enabled) !== null && _j !== void 0 ? _j : false,
|
|
31
|
+
physicalWalgBackupName: (_l = (_k = physical === null || physical === void 0 ? void 0 : physical.walg) === null || _k === void 0 ? void 0 : _k.backupName) !== null && _l !== void 0 ? _l : '',
|
|
32
|
+
physicalPgbackrestStanza: (_o = (_m = physical === null || physical === void 0 ? void 0 : physical.pgbackrest) === null || _m === void 0 ? void 0 : _m.stanza) !== null && _o !== void 0 ? _o : '',
|
|
33
|
+
physicalPgbackrestDelta: (_q = (_p = physical === null || physical === void 0 ? void 0 : physical.pgbackrest) === null || _p === void 0 ? void 0 : _p.delta) !== null && _q !== void 0 ? _q : false,
|
|
34
|
+
physicalEnvs: formatPhysicalEnvs(physical === null || physical === void 0 ? void 0 : physical.envs),
|
|
8
35
|
dockerImage: isSeDockerImage(dockerImage)
|
|
9
36
|
? getImageMajorVersion(dockerImage)
|
|
10
37
|
: dockerImage && getImageType(dockerImage) === 'Generic Postgres'
|
|
@@ -18,20 +45,20 @@ export const formatConfig = (config) => {
|
|
|
18
45
|
dockerTag: dockerImage.split(':')[1],
|
|
19
46
|
}),
|
|
20
47
|
dockerPath: dockerImage,
|
|
21
|
-
tuningParams: formatTuningParams((
|
|
22
|
-
sharedBuffers: (
|
|
23
|
-
sharedPreloadLibraries: (
|
|
24
|
-
timetable: (
|
|
25
|
-
dbname: (
|
|
26
|
-
host: (
|
|
27
|
-
port: (
|
|
28
|
-
username: (
|
|
29
|
-
password: (
|
|
30
|
-
databases: formatDatabases((
|
|
31
|
-
dumpParallelJobs: (
|
|
32
|
-
dumpIgnoreErrors: (
|
|
33
|
-
restoreParallelJobs: (
|
|
34
|
-
restoreIgnoreErrors: (
|
|
48
|
+
tuningParams: formatTuningParams((_r = config.databaseConfigs) === null || _r === void 0 ? void 0 : _r.configs),
|
|
49
|
+
sharedBuffers: (_t = (_s = config.databaseConfigs) === null || _s === void 0 ? void 0 : _s.configs) === null || _t === void 0 ? void 0 : _t.shared_buffers,
|
|
50
|
+
sharedPreloadLibraries: (_v = (_u = config.databaseConfigs) === null || _u === void 0 ? void 0 : _u.configs) === null || _v === void 0 ? void 0 : _v.shared_preload_libraries,
|
|
51
|
+
timetable: (_x = (_w = config.retrieval) === null || _w === void 0 ? void 0 : _w.refresh) === null || _x === void 0 ? void 0 : _x.timetable,
|
|
52
|
+
dbname: (_3 = (_2 = (_1 = (_0 = (_z = (_y = config.retrieval) === null || _y === void 0 ? void 0 : _y.spec) === null || _z === void 0 ? void 0 : _z.logicalDump) === null || _0 === void 0 ? void 0 : _0.options) === null || _1 === void 0 ? void 0 : _1.source) === null || _2 === void 0 ? void 0 : _2.connection) === null || _3 === void 0 ? void 0 : _3.dbname,
|
|
53
|
+
host: (_9 = (_8 = (_7 = (_6 = (_5 = (_4 = config.retrieval) === null || _4 === void 0 ? void 0 : _4.spec) === null || _5 === void 0 ? void 0 : _5.logicalDump) === null || _6 === void 0 ? void 0 : _6.options) === null || _7 === void 0 ? void 0 : _7.source) === null || _8 === void 0 ? void 0 : _8.connection) === null || _9 === void 0 ? void 0 : _9.host,
|
|
54
|
+
port: (_15 = (_14 = (_13 = (_12 = (_11 = (_10 = config.retrieval) === null || _10 === void 0 ? void 0 : _10.spec) === null || _11 === void 0 ? void 0 : _11.logicalDump) === null || _12 === void 0 ? void 0 : _12.options) === null || _13 === void 0 ? void 0 : _13.source) === null || _14 === void 0 ? void 0 : _14.connection) === null || _15 === void 0 ? void 0 : _15.port,
|
|
55
|
+
username: (_21 = (_20 = (_19 = (_18 = (_17 = (_16 = config.retrieval) === null || _16 === void 0 ? void 0 : _16.spec) === null || _17 === void 0 ? void 0 : _17.logicalDump) === null || _18 === void 0 ? void 0 : _18.options) === null || _19 === void 0 ? void 0 : _19.source) === null || _20 === void 0 ? void 0 : _20.connection) === null || _21 === void 0 ? void 0 : _21.username,
|
|
56
|
+
password: (_27 = (_26 = (_25 = (_24 = (_23 = (_22 = config.retrieval) === null || _22 === void 0 ? void 0 : _22.spec) === null || _23 === void 0 ? void 0 : _23.logicalDump) === null || _24 === void 0 ? void 0 : _24.options) === null || _25 === void 0 ? void 0 : _25.source) === null || _26 === void 0 ? void 0 : _26.connection) === null || _27 === void 0 ? void 0 : _27.password,
|
|
57
|
+
databases: formatDatabases((_32 = (_31 = (_30 = (_29 = (_28 = config.retrieval) === null || _28 === void 0 ? void 0 : _28.spec) === null || _29 === void 0 ? void 0 : _29.logicalDump) === null || _30 === void 0 ? void 0 : _30.options) === null || _31 === void 0 ? void 0 : _31.databases) !== null && _32 !== void 0 ? _32 : null),
|
|
58
|
+
dumpParallelJobs: (_36 = (_35 = (_34 = (_33 = config.retrieval) === null || _33 === void 0 ? void 0 : _33.spec) === null || _34 === void 0 ? void 0 : _34.logicalDump) === null || _35 === void 0 ? void 0 : _35.options) === null || _36 === void 0 ? void 0 : _36.parallelJobs,
|
|
59
|
+
dumpIgnoreErrors: (_40 = (_39 = (_38 = (_37 = config.retrieval) === null || _37 === void 0 ? void 0 : _37.spec) === null || _38 === void 0 ? void 0 : _38.logicalDump) === null || _39 === void 0 ? void 0 : _39.options) === null || _40 === void 0 ? void 0 : _40.ignoreErrors,
|
|
60
|
+
restoreParallelJobs: (_44 = (_43 = (_42 = (_41 = config.retrieval) === null || _41 === void 0 ? void 0 : _41.spec) === null || _42 === void 0 ? void 0 : _42.logicalRestore) === null || _43 === void 0 ? void 0 : _43.options) === null || _44 === void 0 ? void 0 : _44.parallelJobs,
|
|
61
|
+
restoreIgnoreErrors: (_48 = (_47 = (_46 = (_45 = config.retrieval) === null || _45 === void 0 ? void 0 : _45.spec) === null || _46 === void 0 ? void 0 : _46.logicalRestore) === null || _47 === void 0 ? void 0 : _47.options) === null || _48 === void 0 ? void 0 : _48.ignoreErrors,
|
|
35
62
|
restoreConfigs: (() => {
|
|
36
63
|
var _a, _b, _c, _d;
|
|
37
64
|
const configs = (_d = (_c = (_b = (_a = config.retrieval) === null || _a === void 0 ? void 0 : _a.spec) === null || _b === void 0 ? void 0 : _b.logicalRestore) === null || _c === void 0 ? void 0 : _c.options) === null || _d === void 0 ? void 0 : _d.configs;
|
|
@@ -41,7 +68,7 @@ export const formatConfig = (config) => {
|
|
|
41
68
|
.map(([k, v]) => `${k}=${v}`)
|
|
42
69
|
.join('\n');
|
|
43
70
|
})(),
|
|
44
|
-
pgDumpCustomOptions: formatDumpCustomOptions((
|
|
45
|
-
pgRestoreCustomOptions: formatDumpCustomOptions((
|
|
71
|
+
pgDumpCustomOptions: formatDumpCustomOptions((_53 = (_52 = (_51 = (_50 = (_49 = config.retrieval) === null || _49 === void 0 ? void 0 : _49.spec) === null || _50 === void 0 ? void 0 : _50.logicalDump) === null || _51 === void 0 ? void 0 : _51.options) === null || _52 === void 0 ? void 0 : _52.customOptions) !== null && _53 !== void 0 ? _53 : null),
|
|
72
|
+
pgRestoreCustomOptions: formatDumpCustomOptions((_58 = (_57 = (_56 = (_55 = (_54 = config.retrieval) === null || _54 === void 0 ? void 0 : _54.spec) === null || _55 === void 0 ? void 0 : _55.logicalRestore) === null || _56 === void 0 ? void 0 : _56.options) === null || _57 === void 0 ? void 0 : _57.customOptions) !== null && _58 !== void 0 ? _58 : null),
|
|
46
73
|
};
|
|
47
74
|
};
|
|
@@ -75,13 +75,18 @@ export declare const formatInstanceDto: (dto: InstanceDto) => {
|
|
|
75
75
|
logicalSize: number;
|
|
76
76
|
message: string;
|
|
77
77
|
branch: string;
|
|
78
|
+
protected: boolean;
|
|
79
|
+
protectedTill?: string | undefined;
|
|
80
|
+
deleteAt?: string | undefined;
|
|
78
81
|
} | null;
|
|
79
82
|
id: string;
|
|
80
83
|
branch: string;
|
|
81
84
|
status: {
|
|
82
|
-
code: "OK" | "CREATING" | "DELETING" | "RESETTING" | "FATAL";
|
|
85
|
+
code: "OK" | "CREATING" | "DELETING" | "RESETTING" | "UPGRADING" | "WARNING" | "FATAL";
|
|
83
86
|
message: string;
|
|
84
87
|
};
|
|
88
|
+
dockerImage?: string | undefined;
|
|
89
|
+
dbVersion?: string | undefined;
|
|
85
90
|
protected: boolean;
|
|
86
91
|
protectedTill?: string | undefined;
|
|
87
92
|
metadata: {
|
|
@@ -106,6 +111,11 @@ export declare const formatInstanceDto: (dto: InstanceDto) => {
|
|
|
106
111
|
code: "OK" | "WARNING" | "NO_RESPONSE";
|
|
107
112
|
message: string;
|
|
108
113
|
};
|
|
114
|
+
cloneUpgrade?: {
|
|
115
|
+
available: boolean;
|
|
116
|
+
targetVersion?: number | undefined;
|
|
117
|
+
reason?: string | undefined;
|
|
118
|
+
} | undefined;
|
|
109
119
|
clones?: import("./clone").CloneDto[] | undefined;
|
|
110
120
|
expectedCloningTime?: number | undefined;
|
|
111
121
|
fileSystem?: {
|
|
@@ -12,6 +12,11 @@ export declare type InstanceStateDto = {
|
|
|
12
12
|
code: 'OK' | 'WARNING' | 'NO_RESPONSE';
|
|
13
13
|
message: string;
|
|
14
14
|
};
|
|
15
|
+
cloneUpgrade?: {
|
|
16
|
+
available: boolean;
|
|
17
|
+
targetVersion?: number;
|
|
18
|
+
reason?: string;
|
|
19
|
+
};
|
|
15
20
|
clones?: CloneDto[];
|
|
16
21
|
expectedCloningTime?: number;
|
|
17
22
|
pools?: PoolDto[];
|
|
@@ -80,13 +85,18 @@ export declare const formatInstanceStateDto: (dto: InstanceStateDto) => {
|
|
|
80
85
|
logicalSize: number;
|
|
81
86
|
message: string;
|
|
82
87
|
branch: string;
|
|
88
|
+
protected: boolean;
|
|
89
|
+
protectedTill?: string | undefined;
|
|
90
|
+
deleteAt?: string | undefined;
|
|
83
91
|
} | null;
|
|
84
92
|
id: string;
|
|
85
93
|
branch: string;
|
|
86
94
|
status: {
|
|
87
|
-
code: "OK" | "CREATING" | "DELETING" | "RESETTING" | "FATAL";
|
|
95
|
+
code: "OK" | "CREATING" | "DELETING" | "RESETTING" | "UPGRADING" | "WARNING" | "FATAL";
|
|
88
96
|
message: string;
|
|
89
97
|
};
|
|
98
|
+
dockerImage?: string | undefined;
|
|
99
|
+
dbVersion?: string | undefined;
|
|
90
100
|
protected: boolean;
|
|
91
101
|
protectedTill?: string | undefined;
|
|
92
102
|
metadata: {
|
|
@@ -111,6 +121,11 @@ export declare const formatInstanceStateDto: (dto: InstanceStateDto) => {
|
|
|
111
121
|
code: 'OK' | 'WARNING' | 'NO_RESPONSE';
|
|
112
122
|
message: string;
|
|
113
123
|
};
|
|
124
|
+
cloneUpgrade?: {
|
|
125
|
+
available: boolean;
|
|
126
|
+
targetVersion?: number | undefined;
|
|
127
|
+
reason?: string | undefined;
|
|
128
|
+
} | undefined;
|
|
114
129
|
clones?: CloneDto[] | undefined;
|
|
115
130
|
expectedCloningTime?: number | undefined;
|
|
116
131
|
fileSystem?: {
|
|
@@ -9,6 +9,9 @@ export declare type SnapshotDto = {
|
|
|
9
9
|
logicalSize: number;
|
|
10
10
|
message: string;
|
|
11
11
|
branch: string;
|
|
12
|
+
protected: boolean;
|
|
13
|
+
protectedTill?: string;
|
|
14
|
+
deleteAt?: string;
|
|
12
15
|
};
|
|
13
16
|
export declare const formatSnapshotDto: (dto: SnapshotDto) => {
|
|
14
17
|
createdAtDate: Date;
|
|
@@ -23,5 +26,8 @@ export declare const formatSnapshotDto: (dto: SnapshotDto) => {
|
|
|
23
26
|
logicalSize: number;
|
|
24
27
|
message: string;
|
|
25
28
|
branch: string;
|
|
29
|
+
protected: boolean;
|
|
30
|
+
protectedTill?: string | undefined;
|
|
31
|
+
deleteAt?: string | undefined;
|
|
26
32
|
};
|
|
27
33
|
export declare type Snapshot = ReturnType<typeof formatSnapshotDto>;
|
package/dist/utils/api.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const getTextFromUnknownApiError: (error: Response) => Promise<
|
|
1
|
+
export declare const getTextFromUnknownApiError: (error: Response) => Promise<any>;
|
package/dist/utils/api.js
CHANGED
|
@@ -9,6 +9,12 @@ export const getTextFromUnknownApiError = async (error) => {
|
|
|
9
9
|
try {
|
|
10
10
|
const result = await error.json();
|
|
11
11
|
log(result);
|
|
12
|
+
// The engine answers with models.Error{code, message}. Showing the serialized object puts
|
|
13
|
+
// `{"code":"BAD_REQUEST","message":"..."}` in front of the user when the message alone is
|
|
14
|
+
// what they need; anything without a usable message still falls back to the raw body.
|
|
15
|
+
if (typeof (result === null || result === void 0 ? void 0 : result.message) === 'string' && result.message !== '') {
|
|
16
|
+
return result.message;
|
|
17
|
+
}
|
|
12
18
|
return JSON.stringify(result);
|
|
13
19
|
}
|
|
14
20
|
catch (e) {
|
package/dist/utils/clone.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ declare const STATUS_CODE_TO_TYPE: {
|
|
|
4
4
|
CREATING: "waiting";
|
|
5
5
|
DELETING: "waiting";
|
|
6
6
|
RESETTING: "waiting";
|
|
7
|
+
UPGRADING: "waiting";
|
|
8
|
+
WARNING: "warning";
|
|
7
9
|
FATAL: "error";
|
|
8
10
|
};
|
|
9
11
|
declare type StatusCode = keyof typeof STATUS_CODE_TO_TYPE;
|
|
10
|
-
export declare const getCloneStatusType: (statusCode: StatusCode) => "error" | "waiting" | "ok";
|
|
12
|
+
export declare const getCloneStatusType: (statusCode: StatusCode) => "error" | "warning" | "waiting" | "ok";
|
|
11
13
|
export declare const getCloneStatusText: (statusCode: StatusCode) => string;
|
|
12
14
|
export declare const checkIsCloneStable: (clone: Clone) => boolean;
|
|
13
15
|
export {};
|
package/dist/utils/clone.js
CHANGED
|
@@ -10,6 +10,8 @@ const STATUS_CODE_TO_TYPE = {
|
|
|
10
10
|
CREATING: 'waiting',
|
|
11
11
|
DELETING: 'waiting',
|
|
12
12
|
RESETTING: 'waiting',
|
|
13
|
+
UPGRADING: 'waiting',
|
|
14
|
+
WARNING: 'warning',
|
|
13
15
|
FATAL: 'error',
|
|
14
16
|
};
|
|
15
17
|
export const getCloneStatusType = (statusCode) => { var _a; return (_a = STATUS_CODE_TO_TYPE[statusCode]) !== null && _a !== void 0 ? _a : 'unknown'; };
|
|
@@ -18,4 +20,8 @@ export const getCloneStatusText = (statusCode) => {
|
|
|
18
20
|
return statusCode;
|
|
19
21
|
return capitalize(statusCode);
|
|
20
22
|
};
|
|
21
|
-
|
|
23
|
+
// WARNING is terminal too: it means the clone is running, just not on the version that was
|
|
24
|
+
// requested. Leaving it out would make the clone page poll forever after a failed upgrade.
|
|
25
|
+
export const checkIsCloneStable = (clone) => clone.status.code === 'OK' ||
|
|
26
|
+
clone.status.code === 'WARNING' ||
|
|
27
|
+
clone.status.code === 'FATAL';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const UNSTABLE_CLONE_UPDATE_TIMEOUT = 1000;
|
|
2
|
+
export declare class ClonePoller {
|
|
3
|
+
private readonly intervalMs;
|
|
4
|
+
private timeout?;
|
|
5
|
+
private stopped;
|
|
6
|
+
constructor(intervalMs?: number);
|
|
7
|
+
get isStopped(): boolean;
|
|
8
|
+
start: () => void;
|
|
9
|
+
stop: () => void;
|
|
10
|
+
cancel: () => void;
|
|
11
|
+
scheduleNext: (tick: () => void) => void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*--------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai
|
|
3
|
+
* All Rights Reserved. Proprietary and confidential.
|
|
4
|
+
* Unauthorized copying of this file, via any medium is strictly prohibited
|
|
5
|
+
*--------------------------------------------------------------------------
|
|
6
|
+
*/
|
|
7
|
+
// interval between clone status polls while a clone is still unstable.
|
|
8
|
+
export const UNSTABLE_CLONE_UPDATE_TIMEOUT = 1000;
|
|
9
|
+
// ClonePoller owns a single self-rescheduling timeout used to poll clone status
|
|
10
|
+
// until it becomes stable. It is reusable: start() re-arms a poller that was
|
|
11
|
+
// previously stopped, and stop() cancels any pending tick and blocks further
|
|
12
|
+
// scheduling — including a tick that an in-flight request is about to queue.
|
|
13
|
+
export class ClonePoller {
|
|
14
|
+
constructor(intervalMs = UNSTABLE_CLONE_UPDATE_TIMEOUT) {
|
|
15
|
+
this.intervalMs = intervalMs;
|
|
16
|
+
this.stopped = false;
|
|
17
|
+
// start re-arms the poller so a reused store polls again.
|
|
18
|
+
this.start = () => {
|
|
19
|
+
this.stopped = false;
|
|
20
|
+
};
|
|
21
|
+
// stop halts polling and cancels any pending tick.
|
|
22
|
+
this.stop = () => {
|
|
23
|
+
this.stopped = true;
|
|
24
|
+
clearTimeout(this.timeout);
|
|
25
|
+
};
|
|
26
|
+
// cancel drops a pending tick without blocking future scheduling.
|
|
27
|
+
this.cancel = () => {
|
|
28
|
+
clearTimeout(this.timeout);
|
|
29
|
+
};
|
|
30
|
+
// scheduleNext queues the next tick, unless the poller has been stopped.
|
|
31
|
+
this.scheduleNext = (tick) => {
|
|
32
|
+
clearTimeout(this.timeout);
|
|
33
|
+
if (this.stopped)
|
|
34
|
+
return;
|
|
35
|
+
this.timeout = setTimeout(tick, this.intervalMs);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
get isStopped() {
|
|
39
|
+
return this.stopped;
|
|
40
|
+
}
|
|
41
|
+
}
|