@postgres.ai/shared 4.0.0-pr-1028.8 → 4.0.0-pr-1028.10
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/package.json +1 -1
- package/pages/Branches/Branch/index.d.ts +1 -1
- package/pages/Branches/Branch/index.js +1 -1
- package/pages/Clone/index.d.ts +1 -1
- package/pages/Clone/index.js +3 -3
- package/pages/CreateBranch/index.d.ts +2 -2
- package/pages/CreateBranch/index.js +2 -2
- package/pages/CreateClone/index.d.ts +1 -1
- package/pages/CreateClone/index.js +1 -1
- package/pages/CreateSnapshot/index.d.ts +2 -2
- package/pages/CreateSnapshot/index.js +2 -2
- package/pages/Instance/Clones/ClonesList/index.d.ts +1 -0
- package/pages/Instance/Clones/ClonesList/index.js +2 -2
- package/pages/Instance/Clones/index.d.ts +1 -0
- package/pages/Instance/Clones/index.js +1 -1
- package/pages/Instance/Info/Retrieval/ConfirmFullRefreshModal/index.d.ts +8 -0
- package/pages/Instance/Info/Retrieval/ConfirmFullRefreshModal/index.js +66 -0
- package/pages/Instance/Info/Retrieval/index.js +8 -2
- package/pages/Instance/context.d.ts +1 -1
- package/pages/Instance/index.d.ts +1 -1
- package/pages/Instance/index.js +1 -1
- package/pages/Instance/stores/Main.d.ts +6 -0
- package/pages/Instance/stores/Main.js +18 -0
- package/pages/Snapshots/Snapshot/index.d.ts +1 -1
- package/pages/Snapshots/Snapshot/index.js +1 -1
- package/types/api/endpoints/fullRefresh.d.ts +6 -0
- package/types/api/endpoints/fullRefresh.js +1 -0
package/package.json
CHANGED
|
@@ -131,7 +131,7 @@ export const BranchesPage = observer((props) => {
|
|
|
131
131
|
return acc;
|
|
132
132
|
}
|
|
133
133
|
}, 0);
|
|
134
|
-
const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0;}' }), props.elements.breadcrumbs, _jsx(SectionTitle, { className: classes.title, tag: "h1", level: 1, text: `Branch ${props.branchId}`, children: _jsx(InstanceTabs, { tab: TABS_INDEX.BRANCHES, isPlatform: props.isPlatform, instanceId: props.instanceId, hasLogs: props.api.initWS !== undefined, hideInstanceTabs: props.
|
|
134
|
+
const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0;}' }), props.elements.breadcrumbs, _jsx(SectionTitle, { className: classes.title, tag: "h1", level: 1, text: `Branch ${props.branchId}`, children: _jsx(InstanceTabs, { tab: TABS_INDEX.BRANCHES, isPlatform: props.isPlatform, instanceId: props.instanceId, hasLogs: props.api.initWS !== undefined, hideInstanceTabs: props.hideBracnhingFeatures }) })] }));
|
|
135
135
|
useEffect(() => {
|
|
136
136
|
load(props.branchId, props.instanceId);
|
|
137
137
|
}, []);
|
package/pages/Clone/index.d.ts
CHANGED
package/pages/Clone/index.js
CHANGED
|
@@ -176,7 +176,7 @@ export const Clone = observer((props) => {
|
|
|
176
176
|
stores.main.load(props.instanceId, props.cloneId);
|
|
177
177
|
}, []);
|
|
178
178
|
const { instance, snapshots, clone, isResettingClone, isDestroyingClone, isReloading, isUpdatingClone, isCloneStable, } = stores.main;
|
|
179
|
-
const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0;}' }), props.elements.breadcrumbs, _jsx(SectionTitle, { className: classes.title, tag: "h1", level: 1, text: `Clone ${props.cloneId}`, children: _jsx(InstanceTabs, { tab: TABS_INDEX.CLONES, isPlatform: props.isPlatform, instanceId: props.instanceId, hasLogs: props.api.initWS !== undefined, hideInstanceTabs: props.
|
|
179
|
+
const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0;}' }), props.elements.breadcrumbs, _jsx(SectionTitle, { className: classes.title, tag: "h1", level: 1, text: `Clone ${props.cloneId}`, children: _jsx(InstanceTabs, { tab: TABS_INDEX.CLONES, isPlatform: props.isPlatform, instanceId: props.instanceId, hasLogs: props.api.initWS !== undefined, hideInstanceTabs: props.hideBracnhingFeatures }) })] }));
|
|
180
180
|
const cloneErrorMessage = ((_a = stores.main.instanceError) === null || _a === void 0 ? void 0 : _a.message) || ((_b = stores.main.cloneError) === null || _b === void 0 ? void 0 : _b.message);
|
|
181
181
|
const cloneErrorTitle = ((_c = stores.main.instanceError) === null || _c === void 0 ? void 0 : _c.title) || ((_d = stores.main.cloneError) === null || _d === void 0 ? void 0 : _d.title);
|
|
182
182
|
if (cloneErrorMessage && cloneErrorTitle)
|
|
@@ -220,8 +220,8 @@ export const Clone = observer((props) => {
|
|
|
220
220
|
isReloading ||
|
|
221
221
|
isUpdatingClone ||
|
|
222
222
|
!isCloneStable;
|
|
223
|
-
return (_jsxs(_Fragment, { children: [headRendered, _jsxs("div", { className: classes.wrapper, children: [_jsxs("div", { className: classes.summary, children: [_jsxs("div", { className: classes.actions, children: [_jsxs(Button, { variant: "contained", color: "primary", onClick: requestResetClone, disabled: isDisabledControls, title: 'Reset clone', className: classes.actionButton, children: ["Reset clone", isResettingClone && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "contained", color: "primary", onClick: requestDestroyClone, disabled: isDisabledControls, title: 'Delete this clone', className: classes.actionButton, children: ["Delete clone", isDestroyingClone && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "contained", color: "primary", onClick: createSnapshot, disabled: isDisabledControls, title: '
|
|
224
|
-
(stores.main.resetCloneError && (_jsx(ErrorStub, { title: 'Resetting error', message: stores.main.resetCloneError, className: classes.errorStub }))), _jsxs("div", { children: [_jsx("p", { children: _jsx("strong", { children: "Branch" }) }), _jsx("p", { className: classes.text, children: clone.branch })] }), _jsxs("div", { className: classes.title, children: [_jsx("p", { children: _jsx("strong", { children: "Created" }) }), _jsx("p", { className: classes.text, children: clone.createdAt })] }), _jsx("br", {}), _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: "Data state at" }), "\u00A0", _jsx(Tooltip, { content: _jsxs(_Fragment, { children: [_jsx("strong", { children: "Data state time" }), " is a time at which data is\u00A0 recovered for this clone."] }), children: icons.infoIcon })] }), _jsx("p", { className: classes.text, children: (_e = clone.snapshot) === null || _e === void 0 ? void 0 : _e.dataStateAt })] }), _jsx("br", {}), _jsxs("div", { children: [_jsx("p", { children: _jsx("strong", { children: "Status" }) }), _jsx(Status, { rawClone: clone, className: classes.status })] }), _jsx("br", {}), _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: "Summary" }), "\u00A0", _jsx(Tooltip, { content: _jsxs(_Fragment, { children: [_jsx("strong", { children: "Logical data size" }), " is a logical size of files in PGDATA which is being thin-cloned.", _jsx("br", {}), _jsx("br", {}), _jsx("strong", { children: "Physical data diff size" }), " is an actual size of a\u00A0 thin clone. On creation there is no diff between clone\u2019s\u00A0 and initial data state, all data blocks match. During work\u00A0 with the clone data diverges and data diff size increases.", _jsx("br", {}), _jsx("br", {}), _jsx("strong", { children: "Clone creation time" }), " is time which was\u00A0 spent to provision the clone."] }), children: icons.infoIcon })] }), _jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Logical data size:" }), ((_f = instance.state) === null || _f === void 0 ? void 0 : _f.dataSize)
|
|
223
|
+
return (_jsxs(_Fragment, { children: [headRendered, _jsxs("div", { className: classes.wrapper, children: [_jsxs("div", { className: classes.summary, children: [_jsxs("div", { className: classes.actions, children: [_jsxs(Button, { variant: "contained", color: "primary", onClick: requestResetClone, disabled: isDisabledControls, title: 'Reset clone', className: classes.actionButton, children: ["Reset clone", isResettingClone && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "contained", color: "primary", onClick: requestDestroyClone, disabled: isDisabledControls, title: 'Delete this clone', className: classes.actionButton, children: ["Delete clone", isDestroyingClone && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), !props.hideBracnhingFeatures && _jsxs(Button, { variant: "contained", color: "primary", onClick: createSnapshot, disabled: isDisabledControls, title: 'Create snapshot', className: classes.actionButton, children: ["Create snapshot", snapshots.snapshotDataLoading && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "outlined", color: "secondary", onClick: reloadClone, disabled: isDisabledControls, title: 'Refresh clone information', className: classes.actionButton, children: ["Reload info", isReloading && _jsx(Spinner, { size: "sm", className: classes.spinner })] })] }), stores.main.destroyCloneError ||
|
|
224
|
+
(stores.main.resetCloneError && (_jsx(ErrorStub, { title: 'Resetting error', message: stores.main.resetCloneError, className: classes.errorStub }))), !props.hideBracnhingFeatures && _jsxs("div", { children: [_jsx("p", { children: _jsx("strong", { children: "Branch" }) }), _jsx("p", { className: classes.text, children: clone.branch })] }), _jsxs("div", { className: classes.title, children: [_jsx("p", { children: _jsx("strong", { children: "Created" }) }), _jsx("p", { className: classes.text, children: clone.createdAt })] }), _jsx("br", {}), _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: "Data state at" }), "\u00A0", _jsx(Tooltip, { content: _jsxs(_Fragment, { children: [_jsx("strong", { children: "Data state time" }), " is a time at which data is\u00A0 recovered for this clone."] }), children: icons.infoIcon })] }), _jsx("p", { className: classes.text, children: (_e = clone.snapshot) === null || _e === void 0 ? void 0 : _e.dataStateAt })] }), _jsx("br", {}), _jsxs("div", { children: [_jsx("p", { children: _jsx("strong", { children: "Status" }) }), _jsx(Status, { rawClone: clone, className: classes.status })] }), _jsx("br", {}), _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: "Summary" }), "\u00A0", _jsx(Tooltip, { content: _jsxs(_Fragment, { children: [_jsx("strong", { children: "Logical data size" }), " is a logical size of files in PGDATA which is being thin-cloned.", _jsx("br", {}), _jsx("br", {}), _jsx("strong", { children: "Physical data diff size" }), " is an actual size of a\u00A0 thin clone. On creation there is no diff between clone\u2019s\u00A0 and initial data state, all data blocks match. During work\u00A0 with the clone data diverges and data diff size increases.", _jsx("br", {}), _jsx("br", {}), _jsx("strong", { children: "Clone creation time" }), " is time which was\u00A0 spent to provision the clone."] }), children: icons.infoIcon })] }), _jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Logical data size:" }), ((_f = instance.state) === null || _f === void 0 ? void 0 : _f.dataSize)
|
|
225
225
|
? formatBytesIEC(instance.state.dataSize)
|
|
226
226
|
: '-'] }), _jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Physical data diff size:" }), clone.metadata.cloneDiffSize
|
|
227
227
|
? formatBytesIEC(clone.metadata.cloneDiffSize)
|
|
@@ -10,9 +10,9 @@ interface CreateBranchProps {
|
|
|
10
10
|
breadcrumbs: React.ReactNode;
|
|
11
11
|
};
|
|
12
12
|
isPlatform?: boolean;
|
|
13
|
-
|
|
13
|
+
hideBracnhingFeatures?: boolean;
|
|
14
14
|
}
|
|
15
|
-
export declare const CreateBranchPage: (({ instanceId, api, elements, routes, isPlatform,
|
|
15
|
+
export declare const CreateBranchPage: (({ instanceId, api, elements, routes, isPlatform, hideBracnhingFeatures }: CreateBranchProps) => JSX.Element) & {
|
|
16
16
|
displayName: string;
|
|
17
17
|
};
|
|
18
18
|
export {};
|
|
@@ -76,7 +76,7 @@ const useStyles = makeStyles({
|
|
|
76
76
|
whiteSpace: 'initial',
|
|
77
77
|
},
|
|
78
78
|
}, { index: 1 });
|
|
79
|
-
export const CreateBranchPage = observer(({ instanceId, api, elements, routes, isPlatform,
|
|
79
|
+
export const CreateBranchPage = observer(({ instanceId, api, elements, routes, isPlatform, hideBracnhingFeatures }) => {
|
|
80
80
|
const stores = useCreatedStores(api);
|
|
81
81
|
const classes = useStyles();
|
|
82
82
|
const history = useHistory();
|
|
@@ -114,7 +114,7 @@ export const CreateBranchPage = observer(({ instanceId, api, elements, routes, i
|
|
|
114
114
|
if (isBranchesLoading) {
|
|
115
115
|
return _jsx(StubSpinner, {});
|
|
116
116
|
}
|
|
117
|
-
return (_jsxs(_Fragment, { children: [elements.breadcrumbs, _jsx(SectionTitle, { tag: "h1", level: 1, text: "Create branch", className: classes.title, children: _jsx(InstanceTabs, { tab: TABS_INDEX.BRANCHES, isPlatform: isPlatform, instanceId: instanceId, hasLogs: api.initWS !== undefined, hideInstanceTabs:
|
|
117
|
+
return (_jsxs(_Fragment, { children: [elements.breadcrumbs, _jsx(SectionTitle, { tag: "h1", level: 1, text: "Create branch", className: classes.title, children: _jsx(InstanceTabs, { tab: TABS_INDEX.BRANCHES, isPlatform: isPlatform, instanceId: instanceId, hasLogs: api.initWS !== undefined, hideInstanceTabs: hideBracnhingFeatures }) }), _jsxs("div", { className: classes.wrapper, children: [_jsxs("div", { className: classes.container, children: [(snapshotsError || getBranchesError) && (_jsx("div", { className: classes.marginTop, children: _jsx(ErrorStub, { message: (snapshotsError === null || snapshotsError === void 0 ? void 0 : snapshotsError.message) || (getBranchesError === null || getBranchesError === void 0 ? void 0 : getBranchesError.message) }) })), _jsxs("div", { className: classes.form, children: [_jsx(TextField, { label: "Branch name", variant: "outlined", required: true, fullWidth: true, size: "small", InputLabelProps: {
|
|
118
118
|
shrink: true,
|
|
119
119
|
}, value: formik.values.branchName, error: Boolean(formik.errors.branchName), className: classes.marginBottom, onChange: (e) => formik.setFieldValue('branchName', e.target.value) }), _jsx("p", { className: cn(classes.marginTop, classes.marginBottom), children: "Choose an existing branch. The new branch will initially point at the same snapshot as the parent branch but going further, their evolution paths will be independent - new snapshots can be created for both branches." }), _jsx(Select, { fullWidth: true, label: "Parent branch", value: formik.values.baseBranch, disabled: !branchesList || formik.isSubmitting, onChange: handleParentBranchChange, error: Boolean(formik.errors.baseBranch), items: branchesList
|
|
120
120
|
? branchesList.map((branch) => {
|
|
@@ -105,7 +105,7 @@ export const CreateClone = observer((props) => {
|
|
|
105
105
|
return;
|
|
106
106
|
history.push(props.routes.clone(stores.main.clone.id));
|
|
107
107
|
}, [stores.main.clone, stores.main.isCloneStable]);
|
|
108
|
-
const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0; }' }), props.elements.breadcrumbs, _jsx(SectionTitle, { tag: "h1", level: 1, text: "Create clone", className: styles.pageTitle, children: _jsx(InstanceTabs, { tab: TABS_INDEX.CLONES, isPlatform: props.isPlatform, instanceId: props.instanceId, hasLogs: props.api.initWS !== undefined, hideInstanceTabs: props.
|
|
108
|
+
const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0; }' }), props.elements.breadcrumbs, _jsx(SectionTitle, { tag: "h1", level: 1, text: "Create clone", className: styles.pageTitle, children: _jsx(InstanceTabs, { tab: TABS_INDEX.CLONES, isPlatform: props.isPlatform, instanceId: props.instanceId, hasLogs: props.api.initWS !== undefined, hideInstanceTabs: props.hideBracnhingFeatures }) })] }));
|
|
109
109
|
// Initial loading spinner.
|
|
110
110
|
if (!stores.main.instance || isLoadingSnapshots)
|
|
111
111
|
return (_jsxs(_Fragment, { children: [headRendered, _jsx(StubSpinner, {})] }));
|
|
@@ -10,9 +10,9 @@ interface CreateSnapshotProps {
|
|
|
10
10
|
breadcrumbs: React.ReactNode;
|
|
11
11
|
};
|
|
12
12
|
isPlatform?: boolean;
|
|
13
|
-
|
|
13
|
+
hideBracnhingFeatures?: boolean;
|
|
14
14
|
}
|
|
15
|
-
export declare const CreateSnapshotPage: (({ instanceId, api, elements, routes, isPlatform,
|
|
15
|
+
export declare const CreateSnapshotPage: (({ instanceId, api, elements, routes, isPlatform, hideBracnhingFeatures }: CreateSnapshotProps) => JSX.Element) & {
|
|
16
16
|
displayName: string;
|
|
17
17
|
};
|
|
18
18
|
export {};
|
|
@@ -71,7 +71,7 @@ const useStyles = makeStyles({
|
|
|
71
71
|
lineHeight: '26px'
|
|
72
72
|
},
|
|
73
73
|
}, { index: 1 });
|
|
74
|
-
export const CreateSnapshotPage = observer(({ instanceId, api, elements, routes, isPlatform,
|
|
74
|
+
export const CreateSnapshotPage = observer(({ instanceId, api, elements, routes, isPlatform, hideBracnhingFeatures }) => {
|
|
75
75
|
var _a, _b;
|
|
76
76
|
const stores = useCreatedStores(api);
|
|
77
77
|
const classes = useStyles();
|
|
@@ -104,7 +104,7 @@ export const CreateSnapshotPage = observer(({ instanceId, api, elements, routes,
|
|
|
104
104
|
return;
|
|
105
105
|
formik.setFieldValue('cloneID', cloneID);
|
|
106
106
|
}, [history.location.search, formik.initialValues]);
|
|
107
|
-
return (_jsxs(_Fragment, { children: [elements.breadcrumbs, _jsx(SectionTitle, { tag: "h1", level: 1, text: "Create snapshot", className: classes.title, children: _jsx(InstanceTabs, { tab: TABS_INDEX.SNAPSHOTS, isPlatform: isPlatform, instanceId: instanceId, hasLogs: api.initWS !== undefined, hideInstanceTabs:
|
|
107
|
+
return (_jsxs(_Fragment, { children: [elements.breadcrumbs, _jsx(SectionTitle, { tag: "h1", level: 1, text: "Create snapshot", className: classes.title, children: _jsx(InstanceTabs, { tab: TABS_INDEX.SNAPSHOTS, isPlatform: isPlatform, instanceId: instanceId, hasLogs: api.initWS !== undefined, hideInstanceTabs: hideBracnhingFeatures }) }), _jsxs("div", { className: classes.wrapper, children: [_jsx("div", { className: classes.container, children: _jsxs("div", { className: classes.marginTop2x, children: [_jsx("strong", { children: "Clone ID" }), _jsx("p", { className: classes.marginTop, children: "Choose a clone ID from the dropdown below. This will be the starting point for your new snapshot." }), _jsx(Select, { fullWidth: true, label: "Clone ID *", value: formik.values.cloneID, disabled: formik.isSubmitting, className: classes.marginBottom2x, onChange: (e) => formik.setFieldValue('cloneID', e.target.value), error: Boolean(formik.errors.cloneID), items: clonesList
|
|
108
108
|
? clonesList.map((clone, i) => {
|
|
109
109
|
var _a, _b;
|
|
110
110
|
const isLatest = i === 0;
|
|
@@ -78,12 +78,12 @@ export const ClonesList = (props) => {
|
|
|
78
78
|
};
|
|
79
79
|
if (!((_b = state.clones) === null || _b === void 0 ? void 0 : _b.length))
|
|
80
80
|
return _jsx("p", { className: styles.emptyStub, children: props.emptyStubText });
|
|
81
|
-
return (_jsxs(_Fragment, { children: [_jsx(HorizontalScrollContainer, { children: _jsxs(Table, { children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableHeaderCell, {}), _jsx(TableHeaderCell, { children: "Status" }), _jsx(TableHeaderCell, { children: "ID" }), _jsx(TableHeaderCell, { children: _jsxs("div", { onClick: handleSortByBranch, className: cn(styles.interactiveRow, styles.verticalCentered), children: ["Branch", _jsx(ArrowDropDownIcon, { className: cn(state.sortByCreated === 'asc' && styles.hideSortIcon, state.sortByBranch === 'asc' && styles.sortIconUp, styles.sortIcon) })] }) }), _jsx(TableHeaderCell, { children: _jsx(Tooltip, { content: "When enabled, neither manual nor automated deletion of this clone is possible. Note that abandoned protected clones may lead to out-of-disk-space events because they hold old data, blocking cleanup and refresh processes.", children: _jsxs("div", { className: styles.verticalCentered, children: ["Protected", _jsx(InfoIcon, { className: styles.infoIcon })] }) }) }), _jsx(TableHeaderCell, { children: _jsxs("div", { onClick: handleSortByCreated, className: cn(styles.interactiveRow, styles.verticalCentered), children: ["Created", _jsx(ArrowDropDownIcon, { className: cn(state.sortByBranch === 'asc' && styles.hideSortIcon, state.sortByCreated === 'asc' && styles.sortIconUp, styles.sortIcon) })] }) }), _jsx(TableHeaderCell, { children: "Port" }), _jsx(TableHeaderCell, { children: "DB user" }), _jsx(TableHeaderCell, { children: _jsx(Tooltip, { content: "Clone's own size \u2013 how much data was added or modified.", children: _jsxs("div", { className: styles.verticalCentered, children: ["Diff size", _jsx(InfoIcon, { className: styles.infoIcon })] }) }) }), _jsx(TableHeaderCell, { children: "Disk" }), _jsx(TableHeaderCell, { children: "Data state time" })] }) }), _jsx(TableBody, { children: state.clones.map((clone) => {
|
|
81
|
+
return (_jsxs(_Fragment, { children: [_jsx(HorizontalScrollContainer, { children: _jsxs(Table, { children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableHeaderCell, {}), _jsx(TableHeaderCell, { children: "Status" }), _jsx(TableHeaderCell, { children: "ID" }), !props.hideBracnhingFeatures && _jsx(TableHeaderCell, { children: _jsxs("div", { onClick: handleSortByBranch, className: cn(styles.interactiveRow, styles.verticalCentered), children: ["Branch", _jsx(ArrowDropDownIcon, { className: cn(state.sortByCreated === 'asc' && styles.hideSortIcon, state.sortByBranch === 'asc' && styles.sortIconUp, styles.sortIcon) })] }) }), _jsx(TableHeaderCell, { children: _jsx(Tooltip, { content: "When enabled, neither manual nor automated deletion of this clone is possible. Note that abandoned protected clones may lead to out-of-disk-space events because they hold old data, blocking cleanup and refresh processes.", children: _jsxs("div", { className: styles.verticalCentered, children: ["Protected", _jsx(InfoIcon, { className: styles.infoIcon })] }) }) }), _jsx(TableHeaderCell, { children: _jsxs("div", { onClick: handleSortByCreated, className: cn(styles.interactiveRow, styles.verticalCentered), children: ["Created", _jsx(ArrowDropDownIcon, { className: cn(state.sortByBranch === 'asc' && styles.hideSortIcon, state.sortByCreated === 'asc' && styles.sortIconUp, styles.sortIcon) })] }) }), _jsx(TableHeaderCell, { children: "Port" }), _jsx(TableHeaderCell, { children: "DB user" }), _jsx(TableHeaderCell, { children: _jsx(Tooltip, { content: "Clone's own size \u2013 how much data was added or modified.", children: _jsxs("div", { className: styles.verticalCentered, children: ["Diff size", _jsx(InfoIcon, { className: styles.infoIcon })] }) }) }), _jsx(TableHeaderCell, { children: "Disk" }), _jsx(TableHeaderCell, { children: "Data state time" })] }) }), _jsx(TableBody, { children: state.clones.map((clone) => {
|
|
82
82
|
var _a, _b;
|
|
83
83
|
const clonePagePath = host.routes.clone(clone.id);
|
|
84
84
|
return (_jsxs(TableRow, { hover: !props.isDisabled, onClick: props.isDisabled
|
|
85
85
|
? undefined
|
|
86
|
-
: () => history.push(clonePagePath), className: cn(!props.isDisabled && styles.interactiveRow), children: [_jsx(MenuCell, { clone: clone, onConnect: openConnectionModal, clonePagePath: clonePagePath }), _jsx(TableBodyCell, { children: _jsx(Tooltip, { content: clone.status.message, children: _jsx(Status, { type: getCloneStatusType(clone.status.code), children: getCloneStatusText(clone.status.code) }) }) }), _jsx(TableBodyCell, { children: clone.id }), _jsx(TableBodyCell, { children: clone.branch }), _jsx(TableBodyCell, { children: clone.protected ? (_jsx(Tooltip, { content: "Clone is protected from manual and automated deletion. Note that abandoned protected clones may lead to out-of-disk-space events because they hold old data, blocking cleanup and refresh processes.", children: _jsx(ShieldIcon, { className: styles.protectionIcon }) })) : (_jsx(Tooltip, { content: "Clone is not protected from deletion. To save disk space it will be automatically deleted if there is no activity for a long time.", children: _jsx(RenewableIcon, { className: styles.protectionIcon }) })) }), _jsxs(TableBodyCell, { children: [clone.createdAt, " (", isValidDate(clone.createdAtDate)
|
|
86
|
+
: () => history.push(clonePagePath), className: cn(!props.isDisabled && styles.interactiveRow), children: [_jsx(MenuCell, { clone: clone, onConnect: openConnectionModal, clonePagePath: clonePagePath }), _jsx(TableBodyCell, { children: _jsx(Tooltip, { content: clone.status.message, children: _jsx(Status, { type: getCloneStatusType(clone.status.code), children: getCloneStatusText(clone.status.code) }) }) }), _jsx(TableBodyCell, { children: clone.id }), !props.hideBracnhingFeatures && _jsx(TableBodyCell, { children: clone.branch }), _jsx(TableBodyCell, { children: clone.protected ? (_jsx(Tooltip, { content: "Clone is protected from manual and automated deletion. Note that abandoned protected clones may lead to out-of-disk-space events because they hold old data, blocking cleanup and refresh processes.", children: _jsx(ShieldIcon, { className: styles.protectionIcon }) })) : (_jsx(Tooltip, { content: "Clone is not protected from deletion. To save disk space it will be automatically deleted if there is no activity for a long time.", children: _jsx(RenewableIcon, { className: styles.protectionIcon }) })) }), _jsxs(TableBodyCell, { children: [clone.createdAt, " (", isValidDate(clone.createdAtDate)
|
|
87
87
|
? formatDistanceToNowStrict(clone.createdAtDate, {
|
|
88
88
|
addSuffix: true,
|
|
89
89
|
})
|
|
@@ -60,5 +60,5 @@ export const Clones = observer((props) => {
|
|
|
60
60
|
const canCreateClone = hasSnapshots && !stores.main.isDisabledInstance;
|
|
61
61
|
return (_jsxs("div", { className: classes.root, children: [!onlyRenderList && (_jsxs(_Fragment, { children: [_jsx(SectionTitle, { level: 2, tag: "h2", text: "Cloning summary" }), _jsx(Header, { expectedCloningTimeS: round(instance.state.cloning.expectedCloningTime, 2), logicalSize: instance.state.dataSize, clonesCount: instance.state.cloning.clones.length })] })), _jsx(SectionTitle, { level: 2, tag: "h3", text: `Clones (${instance.state.cloning.clones.length})`, rightContent: _jsxs(_Fragment, { children: [_jsx(Button, { theme: "primary", onClick: goToCloneAddPage, isDisabled: !canCreateClone, isLoading: isLoadingSnapshots, children: "Create clone" }), !hasSnapshots && (_jsx(Tooltip, { content: "No snapshots", children: _jsx("div", { style: { display: 'flex' }, children: _jsx(InfoIcon, { className: classes.infoIcon }) }) }))] }) }), _jsx(ClonesList, { clones: isShortList
|
|
62
62
|
? instance.state.cloning.clones.slice(0, SHORT_LIST_SIZE)
|
|
63
|
-
: instance.state.cloning.clones, isDisabled: stores.main.isDisabledInstance, emptyStubText: "This instance has no active clones." }), showListSizeButton && !onlyRenderList && (_jsx(Button, { className: classes.listSizeButton, onClick: toggleListSize, children: isShortList ? 'Show more' : 'Show less' }))] }));
|
|
63
|
+
: instance.state.cloning.clones, isDisabled: stores.main.isDisabledInstance, emptyStubText: "This instance has no active clones.", hideBracnhingFeatures: props.hideBracnhingFeatures }), showListSizeButton && !onlyRenderList && (_jsx(Button, { className: classes.listSizeButton, onClick: toggleListSize, children: isShortList ? 'Show more' : 'Show less' }))] }));
|
|
64
64
|
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { makeStyles } from '@material-ui/core';
|
|
4
|
+
import { Modal } from '@postgres.ai/shared/components/Modal';
|
|
5
|
+
import { Text } from '@postgres.ai/shared/components/Text';
|
|
6
|
+
import { SimpleModalControls } from '@postgres.ai/shared/components/SimpleModalControls';
|
|
7
|
+
import { useStores } from '@postgres.ai/shared/pages/Instance/context';
|
|
8
|
+
const useStyles = makeStyles({
|
|
9
|
+
errorMessage: {
|
|
10
|
+
color: 'red',
|
|
11
|
+
marginTop: '10px',
|
|
12
|
+
wordBreak: 'break-all',
|
|
13
|
+
},
|
|
14
|
+
checkboxRoot: {
|
|
15
|
+
padding: '9px 10px',
|
|
16
|
+
},
|
|
17
|
+
grayText: {
|
|
18
|
+
color: '#8a8a8a',
|
|
19
|
+
fontSize: '12px',
|
|
20
|
+
wordBreak: 'break-word',
|
|
21
|
+
},
|
|
22
|
+
marginTop: {
|
|
23
|
+
marginTop: '6px',
|
|
24
|
+
},
|
|
25
|
+
}, { index: 1 });
|
|
26
|
+
export const ConfirmFullRefreshModal = ({ isOpen, onClose, instanceId, }) => {
|
|
27
|
+
var _a;
|
|
28
|
+
const classes = useStyles();
|
|
29
|
+
const stores = useStores();
|
|
30
|
+
const { fullRefresh } = stores.main;
|
|
31
|
+
const [fullRefreshError, setFullRefreshError] = useState(null);
|
|
32
|
+
const handleClose = () => {
|
|
33
|
+
onClose();
|
|
34
|
+
};
|
|
35
|
+
const handleConfirm = async () => {
|
|
36
|
+
const result = await fullRefresh(instanceId);
|
|
37
|
+
if (!result) {
|
|
38
|
+
setFullRefreshError({ error: { message: 'Unexpected error occurred.' } });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const { response, error } = result;
|
|
42
|
+
if (error) {
|
|
43
|
+
setFullRefreshError({
|
|
44
|
+
error: {
|
|
45
|
+
message: error.message,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (response) {
|
|
51
|
+
onClose();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
return (_jsxs(Modal, { title: 'Confirmation', onClose: handleClose, isOpen: isOpen, size: "sm", children: [_jsx(Text, { children: "Are you sure you want to perform a full refresh of the instance? This action cannot be undone." }), fullRefreshError && _jsx("p", { className: classes.errorMessage, children: (_a = fullRefreshError.error) === null || _a === void 0 ? void 0 : _a.message }), _jsx(SimpleModalControls, { items: [
|
|
55
|
+
{
|
|
56
|
+
text: 'Cancel',
|
|
57
|
+
onClick: handleClose,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
text: 'Confirm',
|
|
61
|
+
variant: 'primary',
|
|
62
|
+
onClick: handleConfirm,
|
|
63
|
+
isDisabled: fullRefreshError !== null,
|
|
64
|
+
},
|
|
65
|
+
] })] }));
|
|
66
|
+
};
|
|
@@ -12,6 +12,7 @@ import { Property } from '../components/Property';
|
|
|
12
12
|
import { RefreshFailedAlert } from './RefreshFailedAlert';
|
|
13
13
|
import { getTypeByStatus, isRetrievalUnknown } from './utils';
|
|
14
14
|
import { RetrievalModal } from './RetrievalModal';
|
|
15
|
+
import { ConfirmFullRefreshModal } from './ConfirmFullRefreshModal';
|
|
15
16
|
const useStyles = makeStyles(() => ({
|
|
16
17
|
infoIcon: {
|
|
17
18
|
height: '12px',
|
|
@@ -21,6 +22,9 @@ const useStyles = makeStyles(() => ({
|
|
|
21
22
|
},
|
|
22
23
|
detailsButton: {
|
|
23
24
|
marginLeft: '8px',
|
|
25
|
+
'@media (max-width: 600px)': {
|
|
26
|
+
marginTop: '4px',
|
|
27
|
+
},
|
|
24
28
|
},
|
|
25
29
|
}), { index: 1 });
|
|
26
30
|
export const Retrieval = observer(() => {
|
|
@@ -28,6 +32,7 @@ export const Retrieval = observer(() => {
|
|
|
28
32
|
const stores = useStores();
|
|
29
33
|
const classes = useStyles();
|
|
30
34
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
35
|
+
const [isFullRefreshModalOpen, setIsFullRefreshModalOpen] = useState(false);
|
|
31
36
|
const { instance, instanceRetrieval } = stores.main;
|
|
32
37
|
if (!instance)
|
|
33
38
|
return null;
|
|
@@ -39,9 +44,10 @@ export const Retrieval = observer(() => {
|
|
|
39
44
|
const { mode, status, activity } = instanceRetrieval;
|
|
40
45
|
const isVisible = mode !== 'physical' && !isRetrievalUnknown(mode);
|
|
41
46
|
const isActive = mode === 'logical' && status === 'refreshing';
|
|
42
|
-
|
|
47
|
+
const canCallFullRefresh = retrieving.status === 'finished' || retrieving.status === 'failed';
|
|
48
|
+
return (_jsxs(Section, { title: "Retrieval", children: [_jsx(Property, { name: "Status", children: _jsxs(Status, { type: getTypeByStatus(retrieving.status), children: [capitalize(retrieving.status), isVisible && (_jsx(_Fragment, { children: _jsx(Button, { theme: "primary", onClick: () => setIsModalOpen(true), isDisabled: !isActive, className: classes.detailsButton, children: "Show details" }) })), _jsx(Button, { theme: "secondary", onClick: () => setIsFullRefreshModalOpen(true), isDisabled: !canCallFullRefresh, className: classes.detailsButton, children: "Full refresh" })] }) }), _jsx(Property, { name: "Mode", children: retrieving.mode }), _jsx(Property, { name: "Last refresh", children: retrieving.lastRefresh
|
|
43
49
|
? formatDateStd(retrieving.lastRefresh, { withDistance: true })
|
|
44
50
|
: '-' }), _jsx(Property, { name: "Next refresh", children: retrieving.nextRefresh
|
|
45
51
|
? formatDateStd(retrieving.nextRefresh, { withDistance: true })
|
|
46
|
-
: 'Not scheduled' }), _jsx(RefreshFailedAlert, {}), _jsx(RetrievalModal, { data: activity, isOpen: isModalOpen, onClose: () => setIsModalOpen(false) })] }));
|
|
52
|
+
: 'Not scheduled' }), _jsx(RefreshFailedAlert, {}), _jsx(RetrievalModal, { data: activity, isOpen: isModalOpen, onClose: () => setIsModalOpen(false) }), _jsx(ConfirmFullRefreshModal, { isOpen: isFullRefreshModalOpen, onClose: () => setIsFullRefreshModalOpen(false), instanceId: instance.id })] }));
|
|
47
53
|
});
|
package/pages/Instance/index.js
CHANGED
|
@@ -85,7 +85,7 @@ export const Instance = observer((props) => {
|
|
|
85
85
|
setHasBeenRedirected(true);
|
|
86
86
|
}
|
|
87
87
|
}, [instance, hasBeenRedirected]);
|
|
88
|
-
return (_jsx(HostProvider, { value: props, children: _jsxs(StoresProvider, { value: stores, children: [props.elements.breadcrumbs, _jsx(SectionTitle, { text: props.title, level: 1, tag: "h1", className: classes.title, rightContent: _jsx(Button, { onClick: () => load(props.instanceId, isPlatform), isDisabled: !instance && !instanceError, className: classes.reloadButton, children: "Reload info" }), children: isInstanceIntegrated && (_jsx(InstanceTabs, { instanceId: props.instanceId, tab: activeTab, onTabChange: (tabID) => setActiveTab(tabID), isPlatform: isPlatform, hasLogs: api.initWS !== undefined, hideInstanceTabs: props.
|
|
88
|
+
return (_jsx(HostProvider, { value: props, children: _jsxs(StoresProvider, { value: stores, children: [props.elements.breadcrumbs, _jsx(SectionTitle, { text: props.title, level: 1, tag: "h1", className: classes.title, rightContent: _jsx(Button, { onClick: () => load(props.instanceId, isPlatform), isDisabled: !instance && !instanceError, className: classes.reloadButton, children: "Reload info" }), children: isInstanceIntegrated && (_jsx(InstanceTabs, { instanceId: props.instanceId, tab: activeTab, onTabChange: (tabID) => setActiveTab(tabID), isPlatform: isPlatform, hasLogs: api.initWS !== undefined, hideInstanceTabs: props.hideBracnhingFeatures })) }), instanceError && (_jsx(ErrorStub, { ...instanceError, className: classes.errorStub })), isInstanceIntegrated ? (_jsxs(_Fragment, { children: [_jsxs(TabPanel, { value: activeTab, index: TABS_INDEX.OVERVIEW, children: [!instanceError && (_jsx("div", { className: classes.content, children: instance && ((_b = (_a = instance.state) === null || _a === void 0 ? void 0 : _a.retrieving) === null || _b === void 0 ? void 0 : _b.status) ? (_jsxs(_Fragment, { children: [_jsx(Clones, {}), _jsx(Info, {})] })) : (_jsx(StubSpinner, {})) })), _jsx(ClonesModal, {}), _jsx(SnapshotsModal, {})] }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.CLONES, children: activeTab === TABS_INDEX.CLONES && (_jsx("div", { className: classes.content, children: !instanceError &&
|
|
89
89
|
(instance ? _jsx(Clones, { onlyRenderList: true }) : _jsx(StubSpinner, {})) })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.LOGS, children: activeTab === TABS_INDEX.LOGS && (_jsx(Logs, { api: api, instanceId: props.instanceId })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.CONFIGURATION, children: activeTab === TABS_INDEX.CONFIGURATION && (_jsx(Configuration, { instanceId: instanceId, switchActiveTab: switchTab, isConfigurationActive: isConfigurationActive, reload: () => load(props.instanceId), disableConfigModification: (_c = instance === null || instance === void 0 ? void 0 : instance.state) === null || _c === void 0 ? void 0 : _c.engine.disableConfigModification })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.SNAPSHOTS, children: activeTab === TABS_INDEX.SNAPSHOTS && (_jsx(Snapshots, { instanceId: instanceId })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.BRANCHES, children: activeTab === TABS_INDEX.BRANCHES && (_jsx(Branches, { instanceId: instanceId })) })] })) : !isLoadingInstance && !instanceError ? (_jsx(TabPanel, { value: activeTab, index: activeTab, children: _jsx(InactiveInstance, { instance: instance, org: (_d = props.elements.breadcrumbs) === null || _d === void 0 ? void 0 : _d.props.org }) })) : (!instanceError && (_jsx(TabPanel, { value: activeTab, index: activeTab, children: _jsx("div", { className: classes.content, children: _jsx(StubSpinner, {}) }) })))] }) }));
|
|
90
90
|
});
|
|
91
91
|
function TabPanel(props) {
|
|
@@ -22,6 +22,7 @@ import { GetBranches } from '@postgres.ai/shared/types/api/endpoints/getBranches
|
|
|
22
22
|
import { DeleteBranch } from '@postgres.ai/shared/types/api/endpoints/deleteBranch';
|
|
23
23
|
import { GetSeImages } from '@postgres.ai/shared/types/api/endpoints/getSeImages';
|
|
24
24
|
import { DestroySnapshot } from '@postgres.ai/shared/types/api/endpoints/destroySnapshot';
|
|
25
|
+
import { FullRefresh } from "../../../types/api/endpoints/fullRefresh";
|
|
25
26
|
export declare type Api = {
|
|
26
27
|
getInstance: GetInstance;
|
|
27
28
|
getSnapshots?: GetSnapshots;
|
|
@@ -42,6 +43,7 @@ export declare type Api = {
|
|
|
42
43
|
getSnapshotList?: GetSnapshotList;
|
|
43
44
|
deleteBranch?: DeleteBranch;
|
|
44
45
|
destroySnapshot?: DestroySnapshot;
|
|
46
|
+
fullRefresh?: FullRefresh;
|
|
45
47
|
};
|
|
46
48
|
declare type Error = {
|
|
47
49
|
title?: string;
|
|
@@ -137,5 +139,9 @@ export declare class MainStore {
|
|
|
137
139
|
response: boolean | null;
|
|
138
140
|
error: any;
|
|
139
141
|
} | undefined>;
|
|
142
|
+
fullRefresh: (instanceId: string) => Promise<{
|
|
143
|
+
response: string | null;
|
|
144
|
+
error: Error | null;
|
|
145
|
+
} | undefined>;
|
|
140
146
|
}
|
|
141
147
|
export {};
|
|
@@ -289,6 +289,24 @@ export class MainStore {
|
|
|
289
289
|
error: error ? await error.json().then((err) => err) : null,
|
|
290
290
|
};
|
|
291
291
|
};
|
|
292
|
+
this.fullRefresh = async (instanceId) => {
|
|
293
|
+
var _a, _b;
|
|
294
|
+
if (!this.api.fullRefresh)
|
|
295
|
+
return;
|
|
296
|
+
const { response, error } = await this.api.fullRefresh({
|
|
297
|
+
instanceId,
|
|
298
|
+
});
|
|
299
|
+
if (error) {
|
|
300
|
+
const parsedError = await error.json().then((err) => ({
|
|
301
|
+
message: err.message || 'An unknown error occurred',
|
|
302
|
+
}));
|
|
303
|
+
return { response: null, error: parsedError };
|
|
304
|
+
}
|
|
305
|
+
else if ((_b = (_a = this.instance) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.retrieving) {
|
|
306
|
+
this.instance.state.retrieving.status = 'refreshing';
|
|
307
|
+
}
|
|
308
|
+
return { response: response ? String(response) : null, error: null };
|
|
309
|
+
};
|
|
292
310
|
makeAutoObservable(this);
|
|
293
311
|
this.api = api;
|
|
294
312
|
this.snapshots = new SnapshotsStore(api);
|
|
@@ -134,7 +134,7 @@ export const SnapshotPage = observer((props) => {
|
|
|
134
134
|
history.push(props.routes.snapshots());
|
|
135
135
|
load(props.snapshotId, props.instanceId);
|
|
136
136
|
};
|
|
137
|
-
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.
|
|
137
|
+
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.hideBracnhingFeatures }) })] }));
|
|
138
138
|
useEffect(() => {
|
|
139
139
|
load(props.snapshotId, props.instanceId);
|
|
140
140
|
}, []);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|