mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-10 19:06:36 +00:00
Improve onboarding backup restore (#23340)
* Improve onboarding backup restore * Fix onboarding backup restore * Fix restoring value in onboarding-restore-backup
This commit is contained in:
parent
f1f53b9f24
commit
37aa2bd869
@ -1,7 +1,7 @@
|
|||||||
import { mdiFolderUpload } from "@mdi/js";
|
import { mdiFolderUpload } from "@mdi/js";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/ha-circular-progress";
|
import "../../../src/components/ha-circular-progress";
|
||||||
import "../../../src/components/ha-file-upload";
|
import "../../../src/components/ha-file-upload";
|
||||||
@ -10,10 +10,12 @@ import { uploadBackup } from "../../../src/data/hassio/backup";
|
|||||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
import type { HomeAssistant } from "../../../src/types";
|
import type { HomeAssistant } from "../../../src/types";
|
||||||
|
import type { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"backup-uploaded": { backup: HassioBackup };
|
"backup-uploaded": { backup: HassioBackup };
|
||||||
|
"backup-cleared": void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,6 +23,8 @@ declare global {
|
|||||||
export class HassioUploadBackup extends LitElement {
|
export class HassioUploadBackup extends LitElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public localize?: LocalizeFunc;
|
||||||
|
|
||||||
@state() public value: string | null = null;
|
@state() public value: string | null = null;
|
||||||
|
|
||||||
@state() private _uploading = false;
|
@state() private _uploading = false;
|
||||||
@ -32,13 +36,26 @@ export class HassioUploadBackup extends LitElement {
|
|||||||
.uploading=${this._uploading}
|
.uploading=${this._uploading}
|
||||||
.icon=${mdiFolderUpload}
|
.icon=${mdiFolderUpload}
|
||||||
accept="application/x-tar"
|
accept="application/x-tar"
|
||||||
label="Upload backup"
|
.label=${this.localize?.(
|
||||||
supports="Supports .TAR files"
|
"ui.panel.page-onboarding.restore.upload_backup"
|
||||||
|
) || "Upload backup"}
|
||||||
|
.supports=${this.localize?.(
|
||||||
|
"ui.panel.page-onboarding.restore.upload_supports"
|
||||||
|
) || "Supports .TAR files"}
|
||||||
|
.secondary=${this.localize?.(
|
||||||
|
"ui.panel.page-onboarding.restore.upload_drop"
|
||||||
|
) || "Or drop your file here"}
|
||||||
@file-picked=${this._uploadFile}
|
@file-picked=${this._uploadFile}
|
||||||
|
@files-cleared=${this._clear}
|
||||||
></ha-file-upload>
|
></ha-file-upload>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _clear() {
|
||||||
|
this.value = null;
|
||||||
|
fireEvent(this, "backup-cleared");
|
||||||
|
}
|
||||||
|
|
||||||
private async _uploadFile(ev) {
|
private async _uploadFile(ev) {
|
||||||
const file = ev.detail.files[0];
|
const file = ev.detail.files[0];
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ const _computeAddons = (addons): AddonCheckboxItem[] =>
|
|||||||
|
|
||||||
@customElement("supervisor-backup-content")
|
@customElement("supervisor-backup-content")
|
||||||
export class SupervisorBackupContent extends LitElement {
|
export class SupervisorBackupContent extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public localize?: LocalizeFunc;
|
@property({ attribute: false }) public localize?: LocalizeFunc;
|
||||||
|
|
||||||
@ -186,12 +186,13 @@ export class SupervisorBackupContent extends LitElement {
|
|||||||
.iconPath=${mdiHomeAssistant}
|
.iconPath=${mdiHomeAssistant}
|
||||||
.version=${this.backup
|
.version=${this.backup
|
||||||
? this.backup.homeassistant
|
? this.backup.homeassistant
|
||||||
: this.hass.config.version}
|
: this.hass?.config.version}
|
||||||
>
|
>
|
||||||
</supervisor-formfield-label>`}
|
</supervisor-formfield-label>`}
|
||||||
>
|
>
|
||||||
<ha-checkbox
|
<ha-checkbox
|
||||||
.checked=${this.homeAssistant}
|
.checked=${this.onboarding || this.homeAssistant}
|
||||||
|
.disabled=${this.onboarding}
|
||||||
@change=${this._toggleHomeAssistant}
|
@change=${this._toggleHomeAssistant}
|
||||||
>
|
>
|
||||||
</ha-checkbox>
|
</ha-checkbox>
|
||||||
@ -334,7 +335,7 @@ export class SupervisorBackupContent extends LitElement {
|
|||||||
| HassioFullBackupCreateParams {
|
| HassioFullBackupCreateParams {
|
||||||
const data: any = {};
|
const data: any = {};
|
||||||
|
|
||||||
if (!this.backup) {
|
if (!this.backup && this.hass) {
|
||||||
data.name =
|
data.name =
|
||||||
this.backupName ||
|
this.backupName ||
|
||||||
formatDate(new Date(), this.hass.locale, this.hass.config);
|
formatDate(new Date(), this.hass.locale, this.hass.config);
|
||||||
@ -364,7 +365,9 @@ export class SupervisorBackupContent extends LitElement {
|
|||||||
if (folders?.length) {
|
if (folders?.length) {
|
||||||
data.folders = folders;
|
data.folders = folders;
|
||||||
}
|
}
|
||||||
data.homeassistant = this.homeAssistant;
|
|
||||||
|
// onboarding needs at least homeassistant to restore
|
||||||
|
data.homeassistant = this.onboarding || this.homeAssistant;
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -386,6 +389,7 @@ export class SupervisorBackupContent extends LitElement {
|
|||||||
.iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
|
.iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
|
||||||
.imageUrl=${section === "addons" &&
|
.imageUrl=${section === "addons" &&
|
||||||
!this.onboarding &&
|
!this.onboarding &&
|
||||||
|
this.hass &&
|
||||||
atLeastVersion(this.hass.config.version, 0, 105) &&
|
atLeastVersion(this.hass.config.version, 0, 105) &&
|
||||||
addons?.get(item.slug)?.icon
|
addons?.get(item.slug)?.icon
|
||||||
? `/api/hassio/addons/${item.slug}/icon`
|
? `/api/hassio/addons/${item.slug}/icon`
|
||||||
|
@ -8,9 +8,11 @@ import { atLeastVersion } from "../../../../src/common/config/version";
|
|||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
|
||||||
import { slugify } from "../../../../src/common/string/slugify";
|
import { slugify } from "../../../../src/common/string/slugify";
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-md-dialog";
|
||||||
|
import "../../../../src/components/ha-dialog-header";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
|
import "../../../../src/components/ha-button";
|
||||||
import "../../../../src/components/ha-button-menu";
|
import "../../../../src/components/ha-button-menu";
|
||||||
import "../../../../src/components/ha-header-bar";
|
import "../../../../src/components/ha-header-bar";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-icon-button";
|
||||||
@ -19,6 +21,7 @@ import type { HassioBackupDetail } from "../../../../src/data/hassio/backup";
|
|||||||
import {
|
import {
|
||||||
fetchHassioBackupInfo,
|
fetchHassioBackupInfo,
|
||||||
removeBackup,
|
removeBackup,
|
||||||
|
restoreBackup,
|
||||||
} from "../../../../src/data/hassio/backup";
|
} from "../../../../src/data/hassio/backup";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
@ -33,6 +36,7 @@ import "../../components/supervisor-backup-content";
|
|||||||
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
||||||
import type { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
import type { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
||||||
import type { BackupOrRestoreKey } from "../../util/translations";
|
import type { BackupOrRestoreKey } from "../../util/translations";
|
||||||
|
import type { HaMdDialog } from "../../../../src/components/ha-md-dialog";
|
||||||
|
|
||||||
@customElement("dialog-hassio-backup")
|
@customElement("dialog-hassio-backup")
|
||||||
class HassioBackupDialog
|
class HassioBackupDialog
|
||||||
@ -52,13 +56,20 @@ class HassioBackupDialog
|
|||||||
@query("supervisor-backup-content")
|
@query("supervisor-backup-content")
|
||||||
private _backupContent!: SupervisorBackupContent;
|
private _backupContent!: SupervisorBackupContent;
|
||||||
|
|
||||||
|
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||||
|
|
||||||
public async showDialog(dialogParams: HassioBackupDialogParams) {
|
public async showDialog(dialogParams: HassioBackupDialogParams) {
|
||||||
this._backup = await fetchHassioBackupInfo(this.hass, dialogParams.slug);
|
|
||||||
this._dialogParams = dialogParams;
|
this._dialogParams = dialogParams;
|
||||||
|
this._backup = await fetchHassioBackupInfo(this.hass, dialogParams.slug);
|
||||||
|
if (!this._backup) {
|
||||||
|
this._error = this._localize("no_backup_found");
|
||||||
|
} else if (this._dialogParams.onboarding && !this._backup.homeassistant) {
|
||||||
|
this._error = this._localize("restore_no_home_assistant");
|
||||||
|
}
|
||||||
this._restoringBackup = false;
|
this._restoringBackup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog() {
|
private _dialogClosed(): void {
|
||||||
this._backup = undefined;
|
this._backup = undefined;
|
||||||
this._dialogParams = undefined;
|
this._dialogParams = undefined;
|
||||||
this._restoringBackup = false;
|
this._restoringBackup = false;
|
||||||
@ -66,6 +77,10 @@ class HassioBackupDialog
|
|||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this._dialog?.close();
|
||||||
|
}
|
||||||
|
|
||||||
private _localize(key: BackupOrRestoreKey) {
|
private _localize(key: BackupOrRestoreKey) {
|
||||||
return (
|
return (
|
||||||
this._dialogParams!.supervisor?.localize(`backup.${key}`) ||
|
this._dialogParams!.supervisor?.localize(`backup.${key}`) ||
|
||||||
@ -78,100 +93,80 @@ class HassioBackupDialog
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-md-dialog
|
||||||
open
|
open
|
||||||
scrimClickAction
|
.disableCancelAction=${!this._error}
|
||||||
@closed=${this.closeDialog}
|
@closed=${this._dialogClosed}
|
||||||
.heading=${this._backup.name}
|
|
||||||
>
|
>
|
||||||
<div slot="heading">
|
<ha-dialog-header slot="headline">
|
||||||
<ha-header-bar>
|
<ha-icon-button
|
||||||
<span slot="title">${this._backup.name}</span>
|
slot="navigationIcon"
|
||||||
<ha-icon-button
|
.label=${this._localize("close")}
|
||||||
.label=${this._localize("close")}
|
.path=${mdiClose}
|
||||||
.path=${mdiClose}
|
@click=${this.closeDialog}
|
||||||
slot="actionItems"
|
.disabled=${this._restoringBackup}
|
||||||
dialogAction="cancel"
|
></ha-icon-button>
|
||||||
></ha-icon-button>
|
<span slot="title" .title=${this._backup.name}
|
||||||
</ha-header-bar>
|
>${this._backup.name}</span
|
||||||
|
>
|
||||||
|
${!this._dialogParams.onboarding && this._dialogParams.supervisor
|
||||||
|
? html`<ha-button-menu
|
||||||
|
slot="actionItems"
|
||||||
|
fixed
|
||||||
|
@action=${this._handleMenuAction}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
.label=${this._dialogParams.supervisor.localize(
|
||||||
|
"backup.more_actions"
|
||||||
|
)}
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
slot="trigger"
|
||||||
|
></ha-icon-button>
|
||||||
|
<mwc-list-item
|
||||||
|
>${this._dialogParams.supervisor.localize(
|
||||||
|
"backup.download_backup"
|
||||||
|
)}</mwc-list-item
|
||||||
|
>
|
||||||
|
<mwc-list-item class="error"
|
||||||
|
>${this._dialogParams.supervisor.localize(
|
||||||
|
"backup.delete_backup_title"
|
||||||
|
)}</mwc-list-item
|
||||||
|
>
|
||||||
|
</ha-button-menu>`
|
||||||
|
: nothing}
|
||||||
|
</ha-dialog-header>
|
||||||
|
<div slot="content">
|
||||||
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: this._restoringBackup
|
||||||
|
? html`<div class="loading">
|
||||||
|
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||||
|
</div>`
|
||||||
|
: html`
|
||||||
|
<supervisor-backup-content
|
||||||
|
.hass=${this.hass}
|
||||||
|
.supervisor=${this._dialogParams.supervisor}
|
||||||
|
.backup=${this._backup}
|
||||||
|
.onboarding=${this._dialogParams.onboarding || false}
|
||||||
|
.localize=${this._dialogParams.localize}
|
||||||
|
dialogInitialFocus
|
||||||
|
>
|
||||||
|
</supervisor-backup-content>
|
||||||
|
`}
|
||||||
</div>
|
</div>
|
||||||
${this._restoringBackup
|
<div slot="actions">
|
||||||
? html`<ha-circular-progress indeterminate></ha-circular-progress>`
|
<ha-button
|
||||||
: html`
|
.disabled=${this._restoringBackup || !!this._error}
|
||||||
<supervisor-backup-content
|
@click=${this._restoreClicked}
|
||||||
.hass=${this.hass}
|
>
|
||||||
.supervisor=${this._dialogParams.supervisor}
|
${this._localize("restore")}
|
||||||
.backup=${this._backup}
|
</ha-button>
|
||||||
.onboarding=${this._dialogParams.onboarding || false}
|
</div>
|
||||||
.localize=${this._dialogParams.localize}
|
</ha-md-dialog>
|
||||||
dialogInitialFocus
|
|
||||||
>
|
|
||||||
</supervisor-backup-content>
|
|
||||||
`}
|
|
||||||
${this._error
|
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
|
||||||
: nothing}
|
|
||||||
|
|
||||||
<mwc-button
|
|
||||||
.disabled=${this._restoringBackup}
|
|
||||||
slot="secondaryAction"
|
|
||||||
@click=${this._restoreClicked}
|
|
||||||
>
|
|
||||||
${this._localize("restore")}
|
|
||||||
</mwc-button>
|
|
||||||
|
|
||||||
${!this._dialogParams.onboarding && this._dialogParams.supervisor
|
|
||||||
? html`<ha-button-menu
|
|
||||||
fixed
|
|
||||||
slot="primaryAction"
|
|
||||||
@action=${this._handleMenuAction}
|
|
||||||
@closed=${stopPropagation}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
.label=${this._dialogParams.supervisor.localize(
|
|
||||||
"backup.more_actions"
|
|
||||||
)}
|
|
||||||
.path=${mdiDotsVertical}
|
|
||||||
slot="trigger"
|
|
||||||
></ha-icon-button>
|
|
||||||
<mwc-list-item
|
|
||||||
>${this._dialogParams.supervisor.localize(
|
|
||||||
"backup.download_backup"
|
|
||||||
)}</mwc-list-item
|
|
||||||
>
|
|
||||||
<mwc-list-item class="error"
|
|
||||||
>${this._dialogParams.supervisor.localize(
|
|
||||||
"backup.delete_backup_title"
|
|
||||||
)}</mwc-list-item
|
|
||||||
>
|
|
||||||
</ha-button-menu>`
|
|
||||||
: nothing}
|
|
||||||
</ha-dialog>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return [
|
|
||||||
haStyle,
|
|
||||||
haStyleDialog,
|
|
||||||
css`
|
|
||||||
ha-circular-progress {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
ha-header-bar {
|
|
||||||
--mdc-theme-on-primary: var(--primary-text-color);
|
|
||||||
--mdc-theme-primary: var(--mdc-theme-surface);
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
ha-icon-button {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||||
switch (ev.detail.index) {
|
switch (ev.detail.index) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -184,18 +179,9 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _restoreClicked() {
|
private async _restoreClicked() {
|
||||||
const backupDetails = this._backupContent.backupDetails();
|
|
||||||
this._restoringBackup = true;
|
this._restoringBackup = true;
|
||||||
this._dialogParams?.onRestoring?.();
|
const backupDetails = this._backupContent.backupDetails();
|
||||||
if (this._backupContent.backupType === "full") {
|
|
||||||
await this._fullRestoreClicked(backupDetails);
|
|
||||||
} else {
|
|
||||||
await this._partialRestoreClicked(backupDetails);
|
|
||||||
}
|
|
||||||
this._restoringBackup = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _partialRestoreClicked(backupDetails) {
|
|
||||||
const supervisor = this._dialogParams?.supervisor;
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
@ -204,91 +190,45 @@ class HassioBackupDialog
|
|||||||
state: supervisor.info.state,
|
state: supervisor.info.state,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
this._restoringBackup = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title: this._localize("confirm_restore_partial_backup_title"),
|
title: this._localize(
|
||||||
text: this._localize("confirm_restore_partial_backup_text"),
|
this._backupContent.backupType === "full"
|
||||||
|
? "confirm_restore_full_backup_title"
|
||||||
|
: "confirm_restore_partial_backup_title"
|
||||||
|
),
|
||||||
|
text: this._localize(
|
||||||
|
this._backupContent.backupType === "full"
|
||||||
|
? "confirm_restore_full_backup_text"
|
||||||
|
: "confirm_restore_partial_backup_text"
|
||||||
|
),
|
||||||
confirmText: this._localize("restore"),
|
confirmText: this._localize("restore"),
|
||||||
dismissText: this._localize("cancel"),
|
dismissText: this._localize("cancel"),
|
||||||
}))
|
}))
|
||||||
) {
|
) {
|
||||||
|
this._restoringBackup = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._dialogParams?.onboarding) {
|
try {
|
||||||
try {
|
await restoreBackup(
|
||||||
await this.hass!.callApi(
|
this.hass,
|
||||||
"POST",
|
this._backupContent.backupType,
|
||||||
|
this._backup!.slug,
|
||||||
`hassio/${
|
backupDetails,
|
||||||
atLeastVersion(this.hass!.config.version, 2021, 9)
|
!!this.hass && atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
? "backups"
|
|
||||||
: "snapshots"
|
|
||||||
}/${this._backup!.slug}/restore/partial`,
|
|
||||||
backupDetails
|
|
||||||
);
|
|
||||||
this.closeDialog();
|
|
||||||
} catch (error: any) {
|
|
||||||
this._error = error.body.message;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._dialogParams?.onRestoring?.();
|
|
||||||
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(backupDetails),
|
|
||||||
});
|
|
||||||
this.closeDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _fullRestoreClicked(backupDetails) {
|
|
||||||
const supervisor = this._dialogParams?.supervisor;
|
|
||||||
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
|
||||||
await showAlertDialog(this, {
|
|
||||||
title: supervisor.localize("backup.could_not_restore"),
|
|
||||||
text: supervisor.localize("backup.restore_blocked_not_running", {
|
|
||||||
state: supervisor.info.state,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!(await showConfirmationDialog(this, {
|
|
||||||
title: this._localize("confirm_restore_full_backup_title"),
|
|
||||||
text: this._localize("confirm_restore_full_backup_text"),
|
|
||||||
confirmText: this._localize("restore"),
|
|
||||||
dismissText: this._localize("cancel"),
|
|
||||||
}))
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._dialogParams?.onboarding) {
|
|
||||||
this.hass!.callApi(
|
|
||||||
"POST",
|
|
||||||
`hassio/${
|
|
||||||
atLeastVersion(this.hass!.config.version, 2021, 9)
|
|
||||||
? "backups"
|
|
||||||
: "snapshots"
|
|
||||||
}/${this._backup!.slug}/restore/full`,
|
|
||||||
backupDetails
|
|
||||||
).then(
|
|
||||||
() => {
|
|
||||||
this.closeDialog();
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
this._error = error.body.message;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
this._dialogParams?.onRestoring?.();
|
this._dialogParams?.onRestoring?.();
|
||||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(backupDetails),
|
|
||||||
});
|
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
|
} catch (error: any) {
|
||||||
|
this._error =
|
||||||
|
error?.body?.message || this._localize("restore_start_failed");
|
||||||
|
} finally {
|
||||||
|
this._restoringBackup = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,7 +301,36 @@ class HassioBackupDialog
|
|||||||
private get _computeName() {
|
private get _computeName() {
|
||||||
return this._backup
|
return this._backup
|
||||||
? this._backup.name || this._backup.slug
|
? this._backup.name || this._backup.slug
|
||||||
: "Unnamed backup";
|
: this._localize("unnamed_backup");
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
ha-circular-progress {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
ha-header-bar {
|
||||||
|
--mdc-theme-on-primary: var(--primary-text-color);
|
||||||
|
--mdc-theme-primary: var(--mdc-theme-surface);
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
ha-icon-button {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.loading {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import { bytesToString } from "../util/bytes-to-string";
|
|||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"file-picked": { files: File[] };
|
"file-picked": { files: File[] };
|
||||||
|
"files-cleared": void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +217,7 @@ export class HaFileUpload extends LitElement {
|
|||||||
this._input!.value = "";
|
this._input!.value = "";
|
||||||
this.value = undefined;
|
this.value = undefined;
|
||||||
fireEvent(this, "change");
|
fireEvent(this, "change");
|
||||||
|
fireEvent(this, "files-cleared");
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { atLeastVersion } from "../../common/config/version";
|
import { atLeastVersion } from "../../common/config/version";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
|
import { handleFetchPromise } from "../../util/hass-call-api";
|
||||||
import type { HassioResponse } from "./common";
|
import type { HassioResponse } from "./common";
|
||||||
import { hassioApiResultExtractor } from "./common";
|
import { hassioApiResultExtractor } from "./common";
|
||||||
|
|
||||||
@ -105,11 +106,13 @@ export const fetchHassioBackupInfo = async (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// When called from onboarding we don't have hass
|
// When called from onboarding we don't have hass
|
||||||
const resp = await fetch(`/api/hassio/backups/${backup}/info`, {
|
return hassioApiResultExtractor(
|
||||||
method: "GET",
|
await handleFetchPromise(
|
||||||
});
|
fetch(`/api/hassio/backups/${backup}/info`, {
|
||||||
const data = (await resp.json()).data;
|
method: "GET",
|
||||||
return data;
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reloadHassioBackups = async (hass: HomeAssistant) => {
|
export const reloadHassioBackups = async (hass: HomeAssistant) => {
|
||||||
@ -236,3 +239,26 @@ export const uploadBackup = async (
|
|||||||
}
|
}
|
||||||
return resp.json();
|
return resp.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const restoreBackup = async (
|
||||||
|
hass: HomeAssistant | undefined,
|
||||||
|
type: HassioBackupDetail["type"],
|
||||||
|
backupSlug: string,
|
||||||
|
backupDetails: HassioPartialBackupCreateParams | HassioFullBackupCreateParams,
|
||||||
|
useSnapshotUrl: boolean
|
||||||
|
): Promise<void> => {
|
||||||
|
if (hass) {
|
||||||
|
await hass.callApi<HassioResponse<{ job_id: string }>>(
|
||||||
|
"POST",
|
||||||
|
`hassio/${useSnapshotUrl ? "snapshots" : "backups"}/${backupSlug}/restore/${type}`,
|
||||||
|
backupDetails
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await handleFetchPromise(
|
||||||
|
fetch(`/api/hassio/backups/${backupSlug}/restore/${type}`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(backupDetails),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
|
||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { showHassioBackupDialog } from "../../hassio/src/dialogs/backup/show-dialog-hassio-backup";
|
import { showHassioBackupDialog } from "../../hassio/src/dialogs/backup/show-dialog-hassio-backup";
|
||||||
import "../../hassio/src/components/hassio-upload-backup";
|
import "../../hassio/src/components/hassio-upload-backup";
|
||||||
import type { LocalizeFunc } from "../common/translations/localize";
|
import type { LocalizeFunc } from "../common/translations/localize";
|
||||||
import "../components/ha-ansi-to-html";
|
import "../components/ha-ansi-to-html";
|
||||||
import "../components/ha-card";
|
import "../components/ha-card";
|
||||||
|
import "../components/ha-alert";
|
||||||
|
import "../components/ha-button";
|
||||||
import { fetchInstallationType } from "../data/onboarding";
|
import { fetchInstallationType } from "../data/onboarding";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./onboarding-loading";
|
import "./onboarding-loading";
|
||||||
@ -16,32 +17,49 @@ import { navigate } from "../common/navigate";
|
|||||||
|
|
||||||
@customElement("onboarding-restore-backup")
|
@customElement("onboarding-restore-backup")
|
||||||
class OnboardingRestoreBackup extends LitElement {
|
class OnboardingRestoreBackup extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public localize!: LocalizeFunc;
|
@property({ attribute: false }) public localize!: LocalizeFunc;
|
||||||
|
|
||||||
@property() public language!: string;
|
@property() public language!: string;
|
||||||
|
|
||||||
@state() public _restoring = false;
|
@state() private _restoring = false;
|
||||||
|
|
||||||
|
@state() private _backupSlug?: string;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`${this._restoring
|
return html`
|
||||||
|
${this._restoring
|
||||||
? html`<h1>
|
? html`<h1>
|
||||||
${this.localize("ui.panel.page-onboarding.restore.in_progress")}
|
${this.localize("ui.panel.page-onboarding.restore.in_progress")}
|
||||||
</h1>
|
</h1>
|
||||||
|
<ha-alert alert-type="info">
|
||||||
|
${this.localize("ui.panel.page-onboarding.restore.in_progress")}
|
||||||
|
</ha-alert>
|
||||||
<onboarding-loading></onboarding-loading>`
|
<onboarding-loading></onboarding-loading>`
|
||||||
: html` <h1>
|
: html` <h1>
|
||||||
${this.localize("ui.panel.page-onboarding.restore.header")}
|
${this.localize("ui.panel.page-onboarding.restore.header")}
|
||||||
</h1>
|
</h1>
|
||||||
<hassio-upload-backup
|
<hassio-upload-backup
|
||||||
@backup-uploaded=${this._backupUploaded}
|
@backup-uploaded=${this._backupUploaded}
|
||||||
|
@backup-cleared=${this._backupCleared}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localize=${this.localize}
|
||||||
></hassio-upload-backup>`}
|
></hassio-upload-backup>`}
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<mwc-button @click=${this._back} .disabled=${this._restoring}>
|
<ha-button @click=${this._back} .disabled=${this._restoring}>
|
||||||
${this.localize("ui.panel.page-onboarding.back")}
|
${this.localize("ui.panel.page-onboarding.back")}
|
||||||
</mwc-button>
|
</ha-button>
|
||||||
</div> `;
|
${this._backupSlug
|
||||||
|
? html`<ha-button
|
||||||
|
@click=${this._showBackupDialog}
|
||||||
|
.disabled=${this._restoring}
|
||||||
|
>
|
||||||
|
${this.localize("ui.panel.page-onboarding.restore.restore")}
|
||||||
|
</ha-button>`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _back(): void {
|
private _back(): void {
|
||||||
@ -50,12 +68,16 @@ class OnboardingRestoreBackup extends LitElement {
|
|||||||
|
|
||||||
private _backupUploaded(ev) {
|
private _backupUploaded(ev) {
|
||||||
const backup = ev.detail.backup;
|
const backup = ev.detail.backup;
|
||||||
this._showBackupDialog(backup.slug);
|
this._backupSlug = backup.slug;
|
||||||
|
this._showBackupDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _backupCleared() {
|
||||||
|
this._backupSlug = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
setInterval(() => this._checkRestoreStatus(), 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _checkRestoreStatus(): Promise<void> {
|
private async _checkRestoreStatus(): Promise<void> {
|
||||||
@ -73,13 +95,18 @@ class OnboardingRestoreBackup extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showBackupDialog(slug: string): void {
|
private _scheduleCheckRestoreStatus(): void {
|
||||||
|
setTimeout(() => this._checkRestoreStatus(), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _showBackupDialog(): void {
|
||||||
showHassioBackupDialog(this, {
|
showHassioBackupDialog(this, {
|
||||||
slug,
|
slug: this._backupSlug!,
|
||||||
onboarding: true,
|
onboarding: true,
|
||||||
localize: this.localize,
|
localize: this.localize,
|
||||||
onRestoring: () => {
|
onRestoring: () => {
|
||||||
this._restoring = true;
|
this._restoring = true;
|
||||||
|
this._scheduleCheckRestoreStatus();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -97,8 +124,9 @@ class OnboardingRestoreBackup extends LitElement {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -7587,7 +7587,10 @@
|
|||||||
"restore": {
|
"restore": {
|
||||||
"header": "Restore a backup",
|
"header": "Restore a backup",
|
||||||
"in_progress": "Restore in progress",
|
"in_progress": "Restore in progress",
|
||||||
|
"failed": "Restore failed",
|
||||||
"upload_backup": "[%key:supervisor::backup::upload_backup%]",
|
"upload_backup": "[%key:supervisor::backup::upload_backup%]",
|
||||||
|
"upload_supports": "Supports .TAR files",
|
||||||
|
"upload_drop": "[%key:ui::components::file-upload::secondary%]",
|
||||||
"show_log": "Show full log",
|
"show_log": "Show full log",
|
||||||
"hide_log": "Hide full log",
|
"hide_log": "Hide full log",
|
||||||
"full_backup": "[%key:supervisor::backup::full_backup%]",
|
"full_backup": "[%key:supervisor::backup::full_backup%]",
|
||||||
@ -7601,12 +7604,17 @@
|
|||||||
"password": "[%key:supervisor::backup::password%]",
|
"password": "[%key:supervisor::backup::password%]",
|
||||||
"confirm_password": "[%key:supervisor::backup::confirm_password%]",
|
"confirm_password": "[%key:supervisor::backup::confirm_password%]",
|
||||||
"confirm_restore_partial_backup_title": "[%key:supervisor::backup::confirm_restore_partial_backup_title%]",
|
"confirm_restore_partial_backup_title": "[%key:supervisor::backup::confirm_restore_partial_backup_title%]",
|
||||||
"confirm_restore_partial_backup_text": "[%key:supervisor::backup::confirm_restore_partial_backup_text%]",
|
"confirm_restore_partial_backup_text": "The backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again and you see the login screen. If it fails it will bring you back to the onboarding.",
|
||||||
"confirm_restore_full_backup_title": "[%key:supervisor::backup::confirm_restore_full_backup_title%]",
|
"confirm_restore_full_backup_title": "[%key:supervisor::backup::confirm_restore_full_backup_title%]",
|
||||||
"confirm_restore_full_backup_text": "[%key:supervisor::backup::confirm_restore_full_backup_text%]",
|
"confirm_restore_full_backup_text": "Your entire system will be wiped and the backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again and you see the login screen. If it fails it will bring you back to the onboarding.",
|
||||||
"restore": "[%key:supervisor::backup::restore%]",
|
"restore": "[%key:supervisor::backup::restore%]",
|
||||||
"close": "[%key:ui::common::close%]",
|
"close": "[%key:ui::common::close%]",
|
||||||
"cancel": "[%key:ui::common::cancel%]"
|
"cancel": "[%key:ui::common::cancel%]",
|
||||||
|
"retry": "Retry",
|
||||||
|
"restore_start_failed": "[%key:supervisor::backup::restore_start_failed%]",
|
||||||
|
"no_backup_found": "[%key:supervisor::backup::no_backup_found%]",
|
||||||
|
"restore_no_home_assistant": "[%key:supervisor::backup::restore_no_home_assistant%]",
|
||||||
|
"unnamed_backup": "[%key:supervisor::backup::unnamed_backup%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
@ -8092,16 +8100,16 @@
|
|||||||
"addons": "Add-ons",
|
"addons": "Add-ons",
|
||||||
"folders": "Folders",
|
"folders": "Folders",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"password": "Backup password",
|
"password": "Backup encryption key",
|
||||||
"confirm_password": "Confirm backup password",
|
"confirm_password": "Confirm encryption key",
|
||||||
"password_protection": "Password protection",
|
"password_protection": "Password protection",
|
||||||
"enter_password": "Please enter a password.",
|
"enter_password": "Please enter a password.",
|
||||||
"passwords_not_matching": "The passwords does not match",
|
"passwords_not_matching": "The passwords does not match",
|
||||||
"backup_already_running": "A backup or restore is already running. Creating a new backup is currently not possible, try again later.",
|
"backup_already_running": "A backup or restore is already running. Creating a new backup is currently not possible, try again later.",
|
||||||
"confirm_restore_partial_backup_title": "Restore partial backup",
|
"confirm_restore_partial_backup_title": "Restore partial backup",
|
||||||
"confirm_restore_partial_backup_text": "The backup will be restored. Depending on the size of the backup, this can take up to 45 min.",
|
"confirm_restore_partial_backup_text": "The backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again.",
|
||||||
"confirm_restore_full_backup_title": "Restore full backup",
|
"confirm_restore_full_backup_title": "Restore full backup",
|
||||||
"confirm_restore_full_backup_text": "Your entire system will be wiped and the backup will be restored. Depending on the size of the backup, this can take up to 45 min.",
|
"confirm_restore_full_backup_text": "Your entire system will be wiped and the backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again.",
|
||||||
"confirm_delete_title": "Delete backup",
|
"confirm_delete_title": "Delete backup",
|
||||||
"confirm_delete_text": "This backup will be permanently deleted and cannot be restored later.",
|
"confirm_delete_text": "This backup will be permanently deleted and cannot be restored later.",
|
||||||
"restore": "Restore",
|
"restore": "Restore",
|
||||||
@ -8111,7 +8119,11 @@
|
|||||||
"download": "Download",
|
"download": "Download",
|
||||||
"more_actions": "More actions",
|
"more_actions": "More actions",
|
||||||
"remote_download_title": "Potentially slow download",
|
"remote_download_title": "Potentially slow download",
|
||||||
"remote_download_text": "You are accessing Home Assistant via remote access. Downloading backups over the Nabu Casa URL will take some time. If you are at home, cancel this dialog and enter your local URL, such as 'http://homeassistant.local:8123'"
|
"remote_download_text": "You are accessing Home Assistant via remote access. Downloading backups over the Nabu Casa URL will take some time. If you are at home, cancel this dialog and enter your local URL, such as 'http://homeassistant.local:8123'",
|
||||||
|
"restore_start_failed": "Failed to start restore. Unknown error.",
|
||||||
|
"no_backup_found": "No backup found.",
|
||||||
|
"restore_no_home_assistant": "Backup does not contain Home Assistant data. To restore Home Assistant you need a backup of Home Assistant core.",
|
||||||
|
"unnamed_backup": "Unnamed backup"
|
||||||
},
|
},
|
||||||
"dialog": {
|
"dialog": {
|
||||||
"network": {
|
"network": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user