mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-01 13:37:47 +00:00
Add add-on restore dialog
This commit is contained in:
parent
bdef9fd040
commit
c1df1d41f9
@ -50,6 +50,10 @@ import {
|
|||||||
fetchHassioStats,
|
fetchHassioStats,
|
||||||
HassioStats,
|
HassioStats,
|
||||||
} from "../../../../src/data/hassio/common";
|
} from "../../../../src/data/hassio/common";
|
||||||
|
import {
|
||||||
|
fetchHassioSnapshots,
|
||||||
|
HassioSnapshot,
|
||||||
|
} from "../../../../src/data/hassio/snapshot";
|
||||||
import { StoreAddon } from "../../../../src/data/supervisor/store";
|
import { StoreAddon } from "../../../../src/data/supervisor/store";
|
||||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
@ -61,6 +65,7 @@ import { HomeAssistant } from "../../../../src/types";
|
|||||||
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
||||||
import "../../components/hassio-card-content";
|
import "../../components/hassio-card-content";
|
||||||
import "../../components/supervisor-metric";
|
import "../../components/supervisor-metric";
|
||||||
|
import { showHassioAddonRestoreDialog } from "../../dialogs/addon/show-dialog-hassio-addon-restore";
|
||||||
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
||||||
import { showDialogSupervisorUpdate } from "../../dialogs/update/show-dialog-update";
|
import { showDialogSupervisorUpdate } from "../../dialogs/update/show-dialog-update";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
@ -82,6 +87,8 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public snapshots?: HassioSnapshot[];
|
||||||
|
|
||||||
@state() private _metrics?: HassioStats;
|
@state() private _metrics?: HassioStats;
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
@ -626,6 +633,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
${this.supervisor.localize("addon.dashboard.install")}
|
${this.supervisor.localize("addon.dashboard.install")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
`}
|
`}
|
||||||
|
${this.snapshots?.length
|
||||||
|
? html`<mwc-button @click=${this._restoreClicked}>
|
||||||
|
${this.supervisor.localize("addon.dashboard.restore")}
|
||||||
|
</mwc-button>`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
${this.addon.version
|
${this.addon.version
|
||||||
@ -698,6 +710,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _loadData(): Promise<void> {
|
private async _loadData(): Promise<void> {
|
||||||
|
const snapshots = await fetchHassioSnapshots(this.hass);
|
||||||
|
this.snapshots = snapshots.filter((snapshot) =>
|
||||||
|
snapshot.content.addons.includes(this.addon.slug)
|
||||||
|
);
|
||||||
|
|
||||||
if (this.addon.state === "started") {
|
if (this.addon.state === "started") {
|
||||||
this._metrics = await fetchHassioStats(
|
this._metrics = await fetchHassioStats(
|
||||||
this.hass,
|
this.hass,
|
||||||
@ -1000,6 +1017,22 @@ class HassioAddonInfo extends LitElement {
|
|||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _restoreClicked(): Promise<void> {
|
||||||
|
showHassioAddonRestoreDialog(this, {
|
||||||
|
supervisor: this.supervisor,
|
||||||
|
snapshots: this.snapshots || [],
|
||||||
|
addon: this.addon,
|
||||||
|
onRestore: () => {
|
||||||
|
const eventdata = {
|
||||||
|
success: true,
|
||||||
|
response: undefined,
|
||||||
|
path: "update",
|
||||||
|
};
|
||||||
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _startClicked(ev: CustomEvent): Promise<void> {
|
private async _startClicked(ev: CustomEvent): Promise<void> {
|
||||||
const button = ev.currentTarget as any;
|
const button = ev.currentTarget as any;
|
||||||
button.progress = true;
|
button.progress = true;
|
||||||
|
190
hassio/src/dialogs/addon/dialog-hassio-addon-restore.ts
Normal file
190
hassio/src/dialogs/addon/dialog-hassio-addon-restore.ts
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import relativeTime from "../../../../src/common/datetime/relative_time";
|
||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/common/search/search-input";
|
||||||
|
import { compare } from "../../../../src/common/string/compare";
|
||||||
|
import { nextRender } from "../../../../src/common/util/render-status";
|
||||||
|
import "../../../../src/components/ha-circular-progress";
|
||||||
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
|
import "../../../../src/components/ha-expansion-panel";
|
||||||
|
import "../../../../src/components/ha-settings-row";
|
||||||
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
|
import {
|
||||||
|
fetchHassioSnapshotInfo,
|
||||||
|
HassioPartialSnapshotCreateParams,
|
||||||
|
HassioSnapshotDetail,
|
||||||
|
supervisorRestorePartialSnapshot,
|
||||||
|
} from "../../../../src/data/hassio/snapshot";
|
||||||
|
import {
|
||||||
|
showAlertDialog,
|
||||||
|
showPromptDialog,
|
||||||
|
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { HassioAddonRestoreDialogParams } from "./show-dialog-hassio-addon-restore";
|
||||||
|
|
||||||
|
@customElement("dialog-hassio-addon-restore")
|
||||||
|
class HassioAddonRestoreDialog extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _dialogParams?: HassioAddonRestoreDialogParams;
|
||||||
|
|
||||||
|
@state() private _snapshots?: HassioSnapshotDetail[];
|
||||||
|
|
||||||
|
@state() private _restoring = false;
|
||||||
|
|
||||||
|
public showDialog(params: HassioAddonRestoreDialogParams) {
|
||||||
|
this._dialogParams = params;
|
||||||
|
this._restoring = false;
|
||||||
|
Promise.all(
|
||||||
|
params.snapshots.map((snapshot) =>
|
||||||
|
fetchHassioSnapshotInfo(this.hass, snapshot.slug)
|
||||||
|
)
|
||||||
|
).then((data) => {
|
||||||
|
this._snapshots = data.sort((a, b) => compare(b.date, a.date));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog() {
|
||||||
|
this._dialogParams = undefined;
|
||||||
|
this._snapshots = undefined;
|
||||||
|
this._restoring = false;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this._dialogParams || (!this._snapshots && !this._restoring)) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshotCount = this._snapshots?.length || 0;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-dialog
|
||||||
|
open
|
||||||
|
hideActions
|
||||||
|
@closed=${this.closeDialog}
|
||||||
|
.heading=${createCloseHeading(
|
||||||
|
this.hass,
|
||||||
|
this._dialogParams.supervisor.localize("dialog.addon_restore.title", {
|
||||||
|
name: this._dialogParams.addon.name,
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${this._restoring
|
||||||
|
? html`<div class="restore">
|
||||||
|
<ha-circular-progress size="large" active></ha-circular-progress>
|
||||||
|
<span
|
||||||
|
>${this._dialogParams.supervisor.localize(
|
||||||
|
"dialog.addon_restore.restore_in_progress"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>`
|
||||||
|
: html`${this._dialogParams.supervisor.localize(
|
||||||
|
"dialog.addon_restore.description",
|
||||||
|
{
|
||||||
|
name: this._dialogParams.addon.name,
|
||||||
|
count: snapshotCount,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
${this._snapshots?.map(
|
||||||
|
(snapshot) =>
|
||||||
|
html`<ha-settings-row three-lines>
|
||||||
|
<span slot="heading">
|
||||||
|
${snapshot.name || snapshot.slug}
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
<div>
|
||||||
|
${this._dialogParams!.supervisor.localize(
|
||||||
|
"dialog.addon_restore.version",
|
||||||
|
{
|
||||||
|
version:
|
||||||
|
snapshot.addons.find(
|
||||||
|
(addon) =>
|
||||||
|
addon.slug === this._dialogParams?.addon.slug
|
||||||
|
)?.version ||
|
||||||
|
this._dialogParams!.supervisor.localize(
|
||||||
|
"dialog.addon_restore.no_version"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
${relativeTime(new Date(snapshot.date), this.hass.localize)}
|
||||||
|
</span>
|
||||||
|
<mwc-button
|
||||||
|
.snapshot=${snapshot}
|
||||||
|
@click=${this._restoreClicked}
|
||||||
|
>
|
||||||
|
${this._dialogParams!.supervisor.localize(
|
||||||
|
"dialog.addon_restore.restore"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
</ha-settings-row>`
|
||||||
|
)}`}
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _restoreClicked(ev: CustomEvent) {
|
||||||
|
let password: string | null = null;
|
||||||
|
const snapshot: HassioSnapshotDetail = (ev.currentTarget as any).snapshot;
|
||||||
|
if (snapshot.protected) {
|
||||||
|
password = await showPromptDialog(this, {
|
||||||
|
text: this._dialogParams?.supervisor.localize(
|
||||||
|
"dialog.addon_restore.protected"
|
||||||
|
),
|
||||||
|
inputLabel: this._dialogParams?.supervisor.localize(
|
||||||
|
"dialog.addon_restore.password"
|
||||||
|
),
|
||||||
|
inputType: "password",
|
||||||
|
});
|
||||||
|
await nextRender();
|
||||||
|
if (!password) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._restoring = true;
|
||||||
|
|
||||||
|
const data: HassioPartialSnapshotCreateParams = {
|
||||||
|
addons: [this._dialogParams!.addon.slug],
|
||||||
|
};
|
||||||
|
if (password) {
|
||||||
|
data.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await supervisorRestorePartialSnapshot(this.hass, snapshot.slug, data);
|
||||||
|
} catch (err) {
|
||||||
|
await showAlertDialog(this, {
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
await nextRender();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._dialogParams?.onRestore();
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
.restore {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"dialog-hassio-addon-restore": HassioAddonRestoreDialog;
|
||||||
|
}
|
||||||
|
}
|
22
hassio/src/dialogs/addon/show-dialog-hassio-addon-restore.ts
Normal file
22
hassio/src/dialogs/addon/show-dialog-hassio-addon-restore.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
|
import { HassioSnapshot } from "../../../../src/data/hassio/snapshot";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
|
||||||
|
export interface HassioAddonRestoreDialogParams {
|
||||||
|
supervisor: Supervisor;
|
||||||
|
snapshots: HassioSnapshot[];
|
||||||
|
addon: HassioAddonDetails;
|
||||||
|
onRestore: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showHassioAddonRestoreDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
dialogParams: HassioAddonRestoreDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-hassio-addon-restore",
|
||||||
|
dialogImport: () => import("./dialog-hassio-addon-restore"),
|
||||||
|
dialogParams,
|
||||||
|
});
|
||||||
|
};
|
@ -39,7 +39,7 @@ export interface HassioSnapshotDetail extends HassioSnapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface HassioFullSnapshotCreateParams {
|
export interface HassioFullSnapshotCreateParams {
|
||||||
name: string;
|
name?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
export interface HassioPartialSnapshotCreateParams
|
export interface HassioPartialSnapshotCreateParams
|
||||||
@ -194,3 +194,26 @@ export const uploadSnapshot = async (
|
|||||||
}
|
}
|
||||||
return resp.json();
|
return resp.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const supervisorRestorePartialSnapshot = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
slug: string,
|
||||||
|
data: HassioPartialSnapshotCreateParams
|
||||||
|
) => {
|
||||||
|
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
|
||||||
|
await hass.callWS({
|
||||||
|
type: "supervisor/api",
|
||||||
|
endpoint: `/snapshots/${slug}/restore/partial`,
|
||||||
|
method: "post",
|
||||||
|
timeout: null,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await hass.callApi<HassioResponse<void>>(
|
||||||
|
"POST",
|
||||||
|
`hassio/snapshots/${slug}/restore/partial`,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -3630,6 +3630,7 @@
|
|||||||
"restart": "restart",
|
"restart": "restart",
|
||||||
"start": "start",
|
"start": "start",
|
||||||
"stop": "stop",
|
"stop": "stop",
|
||||||
|
"restore": "restore",
|
||||||
"install": "install",
|
"install": "install",
|
||||||
"uninstall": "uninstall",
|
"uninstall": "uninstall",
|
||||||
"rebuild": "rebuild",
|
"rebuild": "rebuild",
|
||||||
@ -3978,6 +3979,16 @@
|
|||||||
"create_snapshot": "Create a snapshot of {name} before updating",
|
"create_snapshot": "Create a snapshot of {name} before updating",
|
||||||
"updating": "Updating {name} to version {version}",
|
"updating": "Updating {name} to version {version}",
|
||||||
"snapshotting": "Creating snapshot of {name}"
|
"snapshotting": "Creating snapshot of {name}"
|
||||||
|
},
|
||||||
|
"addon_restore": {
|
||||||
|
"title": "Restore {name}",
|
||||||
|
"description": "You have {count, plural,\n one {one snapshot}\n other {{count} snapshots}\n} that includes restore points for {name}",
|
||||||
|
"version": "Version {version}",
|
||||||
|
"no_version": "No version",
|
||||||
|
"restore": "Restore",
|
||||||
|
"password": "Password",
|
||||||
|
"protected": "The snapshot is password protected, please provide the password for it.",
|
||||||
|
"restore_in_progress": "Restore in progress"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user