@postgres.ai/shared 4.0.2-pr-1148.1 → 4.0.2-pr-1200
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/components/UpgradeCloneModal/index.d.ts +11 -0
- package/components/UpgradeCloneModal/index.js +75 -0
- package/package.json +1 -1
- package/pages/Branches/Branch/index.js +27 -2
- package/pages/Branches/Branch/stores/Main.d.ts +5 -0
- package/pages/Branches/Branch/stores/Main.js +29 -0
- package/pages/Branches/components/BranchesTable/index.js +2 -2
- package/pages/Branches/index.js +10 -1
- package/pages/Clone/Status/index.js +3 -0
- package/pages/Clone/index.js +12 -4
- package/pages/Clone/stores/Main.d.ts +9 -1
- package/pages/Clone/stores/Main.js +40 -3
- package/pages/CreateBranch/stores/Main.d.ts +3 -0
- package/pages/CreateClone/index.js +2 -0
- package/pages/CreateClone/stores/Main.d.ts +5 -1
- package/pages/CreateClone/stores/Main.js +8 -3
- package/pages/Instance/Configuration/PhysicalMode/EnvsEditor/index.d.ts +2 -0
- package/pages/Instance/Configuration/PhysicalMode/EnvsEditor/index.js +8 -1
- package/pages/Instance/Configuration/SimpleMode/PreviewCard.d.ts +3 -1
- package/pages/Instance/Configuration/SimpleMode/PreviewCard.js +7 -7
- package/pages/Instance/Configuration/SimpleMode/index.d.ts +6 -1
- package/pages/Instance/Configuration/SimpleMode/index.js +81 -22
- package/pages/Instance/Configuration/configOptions.d.ts +10 -0
- package/pages/Instance/Configuration/configOptions.js +24 -1
- package/pages/Instance/Info/Snapshots/Calendar/Day/index.js +3 -3
- package/pages/Instance/Info/Snapshots/Calendar/utils.d.ts +3 -0
- package/pages/Instance/Info/Snapshots/utils.d.ts +6 -0
- package/pages/Instance/Snapshots/components/SnapshotsList/index.js +11 -1
- package/pages/Instance/Snapshots/index.js +11 -3
- package/pages/Instance/Snapshots/utils/index.d.ts +3 -0
- package/pages/Instance/Tabs/styles.js +6 -6
- package/pages/Instance/stores/Main.d.ts +2 -2
- package/pages/Instance/stores/Main.js +17 -9
- package/pages/Snapshots/Snapshot/index.js +28 -2
- package/pages/Snapshots/Snapshot/stores/Main.d.ts +5 -0
- package/pages/Snapshots/Snapshot/stores/Main.js +29 -0
- package/stores/Snapshots.d.ts +1 -1
- package/stores/Snapshots.js +12 -6
- package/types/api/endpoints/getBranches.d.ts +6 -0
- package/types/api/endpoints/testDbSource.d.ts +3 -1
- package/types/api/endpoints/testDbSource.js +6 -0
- package/types/api/endpoints/updateBranch.d.ts +11 -0
- package/types/api/endpoints/updateBranch.js +1 -0
- package/types/api/endpoints/updateSnapshot.d.ts +11 -0
- package/types/api/endpoints/updateSnapshot.js +1 -0
- package/types/api/endpoints/upgradeClone.d.ts +8 -0
- package/types/api/endpoints/upgradeClone.js +1 -0
- package/types/api/entities/branchSnapshots.d.ts +3 -0
- package/types/api/entities/clone.d.ts +9 -2
- package/types/api/entities/instance.d.ts +11 -1
- package/types/api/entities/instanceState.d.ts +16 -1
- package/types/api/entities/snapshot.d.ts +6 -0
- package/utils/api.d.ts +1 -1
- package/utils/api.js +6 -0
- package/utils/clone.d.ts +3 -1
- package/utils/clone.js +7 -1
- package/utils/clonePoller.d.ts +12 -0
- package/utils/clonePoller.js +41 -0
- package/utils/date.js +12 -12
- package/utils/protection.d.ts +5 -0
- package/utils/protection.js +23 -0
|
@@ -13,11 +13,15 @@ import { makeStyles, Button, TextField, IconButton, Table, TableHead, TableRow,
|
|
|
13
13
|
import { ErrorStub } from '@postgres.ai/shared/components/ErrorStub';
|
|
14
14
|
import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner';
|
|
15
15
|
import { SectionTitle } from '@postgres.ai/shared/components/SectionTitle';
|
|
16
|
+
import { Select } from '@postgres.ai/shared/components/Select';
|
|
17
|
+
import { Spinner } from '@postgres.ai/shared/components/Spinner';
|
|
16
18
|
import { DestroySnapshotModal } from '@postgres.ai/shared/pages/Snapshots/Snapshot/DestorySnapshotModal';
|
|
17
19
|
import { HorizontalScrollContainer } from '@postgres.ai/shared/components/HorizontalScrollContainer';
|
|
18
20
|
import { Tooltip } from '@postgres.ai/shared/components/Tooltip';
|
|
19
21
|
import { icons } from '@postgres.ai/shared/styles/icons';
|
|
20
22
|
import { formatBytesIEC } from '@postgres.ai/shared/utils/units';
|
|
23
|
+
import { parseDate } from '@postgres.ai/shared/utils/date';
|
|
24
|
+
import { protectionOptions, getProtectionSelectValue, } from '@postgres.ai/shared/utils/protection';
|
|
21
25
|
import { styles } from '@postgres.ai/shared/styles/styles';
|
|
22
26
|
import { SyntaxHighlight } from '@postgres.ai/shared/components/SyntaxHighlight';
|
|
23
27
|
import { TableBodyCell, TableBodyCellMenu, TableHeaderCell, } from '@postgres.ai/shared/components/Table';
|
|
@@ -71,6 +75,13 @@ const useStyles = makeStyles(() => ({
|
|
|
71
75
|
summary: {
|
|
72
76
|
marginTop: 20,
|
|
73
77
|
},
|
|
78
|
+
protectionControl: {
|
|
79
|
+
display: 'flex',
|
|
80
|
+
alignItems: 'center',
|
|
81
|
+
marginTop: '8px',
|
|
82
|
+
marginBottom: '8px',
|
|
83
|
+
maxWidth: 525,
|
|
84
|
+
},
|
|
74
85
|
text: {
|
|
75
86
|
marginTop: '4px',
|
|
76
87
|
},
|
|
@@ -129,11 +140,18 @@ export const SnapshotPage = observer((props) => {
|
|
|
129
140
|
const history = useHistory();
|
|
130
141
|
const stores = useCreatedStores(props.api);
|
|
131
142
|
const [isOpenDestroyModal, setIsOpenDestroyModal] = useState(false);
|
|
132
|
-
const { snapshot, branchSnapshot, isSnapshotsLoading, snapshotError, branchSnapshotError, load, } = stores.main;
|
|
143
|
+
const { snapshot, branchSnapshot, isSnapshotsLoading, isUpdatingSnapshot, updateSnapshotProtection, updateSnapshotError, snapshotError, branchSnapshotError, load, } = stores.main;
|
|
133
144
|
const afterSubmitClick = () => {
|
|
134
145
|
history.push(props.routes.snapshots());
|
|
135
146
|
load(props.snapshotId, props.instanceId);
|
|
136
147
|
};
|
|
148
|
+
const protectedTillDate = (snapshot === null || snapshot === void 0 ? void 0 : snapshot.protectedTill)
|
|
149
|
+
? parseDate(snapshot.protectedTill)
|
|
150
|
+
: null;
|
|
151
|
+
const handleProtectionChange = (e) => {
|
|
152
|
+
const value = e.target.value;
|
|
153
|
+
updateSnapshotProtection(props.snapshotId, props.instanceId, value === 'none' ? null : parseInt(value, 10));
|
|
154
|
+
};
|
|
137
155
|
const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0;}' }), props.elements.breadcrumbs, _jsx(SectionTitle, { className: classes.title, tag: "h1", level: 1, text: `Snapshot ${props.snapshotId}`, children: _jsx(InstanceTabs, { tab: TABS_INDEX.SNAPSHOTS, isPlatform: props.isPlatform, instanceId: props.instanceId, hasLogs: props.api.initWS !== undefined, hideInstanceTabs: props.hideBranchingFeatures }) })] }));
|
|
138
156
|
useEffect(() => {
|
|
139
157
|
load(props.snapshotId, props.instanceId);
|
|
@@ -147,7 +165,15 @@ export const SnapshotPage = observer((props) => {
|
|
|
147
165
|
? formatBytesIEC(snapshot.logicalSize)
|
|
148
166
|
: '-'] }), _jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Physical data diff size:" }), (snapshot === null || snapshot === void 0 ? void 0 : snapshot.physicalSize)
|
|
149
167
|
? formatBytesIEC(snapshot.physicalSize)
|
|
150
|
-
: '-'] }), (branchSnapshot === null || branchSnapshot === void 0 ? void 0 : branchSnapshot.message) && (_jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Message:" }), branchSnapshot.message] }))] }),
|
|
168
|
+
: '-'] }), (branchSnapshot === null || branchSnapshot === void 0 ? void 0 : branchSnapshot.message) && (_jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Message:" }), branchSnapshot.message] }))] }), _jsxs("div", { className: classes.summary, children: [_jsx("p", { children: _jsx("strong", { children: "Deletion protection" }) }), _jsxs("div", { className: classes.protectionControl, children: [_jsx(Select, { label: "Deletion protection", items: (snapshot === null || snapshot === void 0 ? void 0 : snapshot.protected) && protectedTillDate
|
|
169
|
+
? [
|
|
170
|
+
{
|
|
171
|
+
value: 'current',
|
|
172
|
+
children: `Protected until ${protectedTillDate.toLocaleString()}`,
|
|
173
|
+
},
|
|
174
|
+
...protectionOptions,
|
|
175
|
+
]
|
|
176
|
+
: protectionOptions, value: getProtectionSelectValue(Boolean(snapshot === null || snapshot === void 0 ? void 0 : snapshot.protected), protectedTillDate), onChange: handleProtectionChange, disabled: isUpdatingSnapshot, fullWidth: true }), isUpdatingSnapshot && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsx("p", { className: classes.text, children: "Protected snapshots cannot be deleted manually or automatically." }), updateSnapshotError && (_jsx(ErrorStub, { title: "Update error", message: updateSnapshotError, className: classes.marginTop }))] }), _jsx("br", {}), _jsx("p", { children: _jsx("strong", { children: "Snapshot info" }) }), (snapshot === null || snapshot === void 0 ? void 0 : snapshot.pool) && (_jsxs("div", { className: classes.copyFieldContainer, children: [_jsx(TextField, { variant: "outlined", label: "snapshot pool", value: snapshot.pool, className: classes.textField, margin: "normal", fullWidth: true, InputLabelProps: {
|
|
151
177
|
shrink: true,
|
|
152
178
|
style: styles.inputFieldLabel,
|
|
153
179
|
}, FormHelperTextProps: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SnapshotsStore, SnapshotsApi } from '@postgres.ai/shared/stores/Snapshots';
|
|
2
2
|
import { DestroySnapshot } from '@postgres.ai/shared/types/api/endpoints/destroySnapshot';
|
|
3
|
+
import { UpdateSnapshot } from '@postgres.ai/shared/types/api/endpoints/updateSnapshot';
|
|
3
4
|
import { SnapshotDto } from '@postgres.ai/shared/types/api/entities/snapshot';
|
|
4
5
|
import { GetBranchSnapshot } from '@postgres.ai/shared/types/api/endpoints/getBranchSnapshot';
|
|
5
6
|
import { BranchSnapshotDto } from '@postgres.ai/shared/types/api/entities/branchSnapshot';
|
|
@@ -10,6 +11,7 @@ declare type Error = {
|
|
|
10
11
|
};
|
|
11
12
|
export declare type Api = SnapshotsApi & {
|
|
12
13
|
destroySnapshot: DestroySnapshot;
|
|
14
|
+
updateSnapshot?: UpdateSnapshot;
|
|
13
15
|
getBranchSnapshot?: GetBranchSnapshot;
|
|
14
16
|
initWS?: InitWS;
|
|
15
17
|
};
|
|
@@ -19,12 +21,15 @@ export declare class MainStore {
|
|
|
19
21
|
snapshotError: Error | null;
|
|
20
22
|
branchSnapshotError: Error | null;
|
|
21
23
|
isSnapshotsLoading: boolean;
|
|
24
|
+
isUpdatingSnapshot: boolean;
|
|
25
|
+
updateSnapshotError: string | null;
|
|
22
26
|
private readonly api;
|
|
23
27
|
readonly snapshots: SnapshotsStore;
|
|
24
28
|
constructor(api: Api);
|
|
25
29
|
load: (snapshotId: string, instanceId: string) => Promise<void>;
|
|
26
30
|
getSnapshot: (snapshotId: string, instanceId: string) => Promise<boolean | undefined>;
|
|
27
31
|
getBranchSnapshot: (snapshotId: string, instanceId: string) => Promise<import("@postgres.ai/shared/types/api/entities/branchSnapshot").BranchSnapshotDTO | null | undefined>;
|
|
32
|
+
updateSnapshotProtection: (snapshotId: string, instanceId: string, durationMinutes: number | null) => Promise<void>;
|
|
28
33
|
destroySnapshot: (snapshotId: string, forceDelete: boolean, instanceId: string) => Promise<{
|
|
29
34
|
response: boolean | null;
|
|
30
35
|
error: any;
|
|
@@ -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;
|
package/stores/Snapshots.d.ts
CHANGED
|
@@ -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/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;
|
|
@@ -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: {
|
|
@@ -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/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/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/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/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
|
+
}
|
package/utils/date.js
CHANGED
|
@@ -5,23 +5,23 @@
|
|
|
5
5
|
*--------------------------------------------------------------------------
|
|
6
6
|
*/
|
|
7
7
|
import { parse, startOfDay, isSameDay, startOfMonth, startOfWeek, endOfMonth, endOfWeek, differenceInDays, addDays, addMonths, isSameMonth, format, formatDistanceToNowStrict, } from 'date-fns';
|
|
8
|
+
const compactDatePattern = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/;
|
|
9
|
+
// formatDateToISO converts a compact UTC timestamp ('20060102150405') to RFC3339 ('2006-01-02T15:04:05Z').
|
|
10
|
+
// The value is a UTC wall-clock, so it must never be interpreted in the browser timezone.
|
|
8
11
|
export const formatDateToISO = (dateString) => {
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
var _a;
|
|
13
|
+
const parts = compactDatePattern.exec((_a = dateString === null || dateString === void 0 ? void 0 : dateString.trim()) !== null && _a !== void 0 ? _a : '');
|
|
14
|
+
if (!parts) {
|
|
11
15
|
return '';
|
|
12
16
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (!isValidDate(parsedDate) || isNaN(parsedDate.getTime())) {
|
|
17
|
-
return '';
|
|
18
|
-
}
|
|
19
|
-
return format(parsedDate, "yyyy-MM-dd'T'HH:mm:ssXXX");
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
// Return empty string for invalid date formats
|
|
17
|
+
const [compact, year, month, day, hours, minutes, seconds] = parts;
|
|
18
|
+
const parsedDate = new Date(Date.UTC(+year, +month - 1, +day, +hours, +minutes, +seconds));
|
|
19
|
+
if (!isValidDate(parsedDate)) {
|
|
23
20
|
return '';
|
|
24
21
|
}
|
|
22
|
+
const isoDate = parsedDate.toISOString().replace(/\.\d+Z$/, 'Z');
|
|
23
|
+
// reject values rolled over by Date.UTC, e.g. month 13 or hour 25.
|
|
24
|
+
return isoDate.replace(/[-:TZ]/g, '') === compact ? isoDate : '';
|
|
25
25
|
};
|
|
26
26
|
// parseDate parses date of both format: '2006-01-02 15:04:05 UTC' and `2006-01-02T15:04:05Z` (RFC3339).
|
|
27
27
|
export const parseDate = (dateStr) => parse(dateStr.replace(' UTC', 'Z').replace('T', ' '), 'yyyy-MM-dd HH:mm:ssX', new Date());
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// protectionOptions lists the deletion-protection choices shared by the branch and snapshot
|
|
2
|
+
// detail pages: no protection, a set of bounded durations, and forever. The values are the
|
|
3
|
+
// protectionDurationMinutes passed to the update endpoints ('0' means forever, 'none' clears
|
|
4
|
+
// protection).
|
|
5
|
+
export const protectionOptions = [
|
|
6
|
+
{ value: 'none', children: 'No protection' },
|
|
7
|
+
{ value: '60', children: '1 hour' },
|
|
8
|
+
{ value: '720', children: '12 hours' },
|
|
9
|
+
{ value: '1440', children: '1 day' },
|
|
10
|
+
{ value: '4320', children: '3 days' },
|
|
11
|
+
{ value: '10080', children: '7 days' },
|
|
12
|
+
{ value: '43200', children: '30 days' },
|
|
13
|
+
{ value: '0', children: 'Forever' },
|
|
14
|
+
];
|
|
15
|
+
// getProtectionSelectValue maps an entity's protection state to the Select value: 'none' when
|
|
16
|
+
// unprotected, 'current' when protected until a specific time, '0' when protected forever.
|
|
17
|
+
export const getProtectionSelectValue = (isProtected, protectedTillDate) => {
|
|
18
|
+
if (!isProtected)
|
|
19
|
+
return 'none';
|
|
20
|
+
if (!protectedTillDate)
|
|
21
|
+
return '0';
|
|
22
|
+
return 'current';
|
|
23
|
+
};
|