diff --git a/backend/src/diagnostic.rs b/backend/src/diagnostic.rs index 47ad2339f..78e05226f 100644 --- a/backend/src/diagnostic.rs +++ b/backend/src/diagnostic.rs @@ -6,6 +6,7 @@ use rpc_toolkit::yajrc::RpcError; use crate::context::DiagnosticContext; use crate::disk::repair; +use crate::init::SYSTEM_REBUILD_PATH; use crate::logs::{fetch_logs, LogResponse, LogSource}; use crate::shutdown::Shutdown; use crate::util::display_none; @@ -13,7 +14,7 @@ use crate::Error; pub const SYSTEMD_UNIT: &'static str = "embassy-init"; -#[command(subcommands(error, logs, exit, restart, forget_disk, disk))] +#[command(subcommands(error, logs, exit, restart, forget_disk, disk, rebuild))] pub fn diagnostic() -> Result<(), Error> { Ok(()) } @@ -51,6 +52,12 @@ pub fn restart(#[context] ctx: DiagnosticContext) -> Result<(), Error> { Ok(()) } +#[command(display(display_none))] +pub async fn rebuild(#[context] ctx: DiagnosticContext) -> Result<(), Error> { + tokio::fs::write(SYSTEM_REBUILD_PATH, b"").await?; + restart(ctx) +} + #[command(subcommands(forget_disk, repair))] pub fn disk() -> Result<(), Error> { Ok(()) diff --git a/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.html b/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.html index c7294dac4..47823ff77 100644 --- a/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.html +++ b/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.html @@ -7,54 +7,62 @@ > embassyOS - Diagnostic Mode -

- embassyOS launch error: -

-
- - {{ error.problem }} - -
-
- {{ error.details }} -
-
-
- View Logs -

- Possible solution: -

-
- {{ error.solution }} -
- Restart Embassy -
- + + +

+ embassyOS launch error: +

+
+ + {{ error.problem }} + +
+
+ {{ error.details }} +
+
+
+ View Logs +

+ Possible solutions: +

+
+ {{ error.solution }} +
+ Restart Embassy + {{ error.code === 15 ? 'Setup Current Drive' : 'Enter Recovery Mode' }} -
-
- {{ 'Repair Drive' }} -
+ +
+ System Rebuild +
+ +
+ Repair Drive +
+ diff --git a/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.ts b/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.ts index 8214ab86a..920775b31 100644 --- a/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.ts +++ b/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.ts @@ -8,12 +8,12 @@ import { ApiService } from 'src/app/services/api/api.service' styleUrls: ['home.page.scss'], }) export class HomePage { - error: { + error?: { code: number problem: string solution: string details?: string - } = {} as any + } solutions: string[] = [] restarted = false @@ -118,7 +118,80 @@ export class HomePage { } } - async repairDrive(): Promise { + async presentAlertSystemRebuild() { + const alert = await this.alertCtrl.create({ + header: 'Warning', + message: + '

This action will tear down all service containers and rebuild them from scratch. No data will be deleted.

A system rebuild can be useful if your system gets into a bad state, and it should only be performed if you are experiencing general performance or reliability issues.

It may take up to an hour to complete. During this time, you will lose all connectivity to your Embassy.

', + buttons: [ + { + text: 'Cancel', + role: 'cancel', + }, + { + text: 'Rebuild', + handler: () => { + try { + this.systemRebuild() + } catch (e) { + console.error(e) + } + }, + }, + ], + cssClass: 'alert-warning-message', + }) + await alert.present() + } + + async presentAlertRepairDisk() { + const alert = await this.alertCtrl.create({ + header: 'Warning', + message: + '

This action should only be executed if directed by a Start9 support specialist.

If anything happens to the device during the reboot, such as losing power or unplugging the drive, the filesystem will be in an unrecoverable state. Please proceed with caution.

', + buttons: [ + { + text: 'Cancel', + role: 'cancel', + }, + { + text: 'Repair', + handler: () => { + try { + this.repairDisk() + } catch (e) { + console.error(e) + } + }, + }, + ], + cssClass: 'alert-error-message', + }) + await alert.present() + } + + refreshPage(): void { + window.location.reload() + } + + private async systemRebuild(): Promise { + const loader = await this.loadingCtrl.create({ + cssClass: 'loader', + }) + await loader.present() + + try { + await this.api.systemRebuild() + await this.api.restart() + this.restarted = true + } catch (e) { + console.error(e) + } finally { + loader.dismiss() + } + } + + private async repairDisk(): Promise { const loader = await this.loadingCtrl.create({ cssClass: 'loader', }) @@ -134,36 +207,4 @@ export class HomePage { loader.dismiss() } } - - async presentAlertRepairDisk() { - const alert = await this.alertCtrl.create({ - header: 'Warning', - message: - 'This action will attempt to preform a disk repair operation and system reboot. No data will be deleted. This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action. If anything happens to the device during the reboot, such as losing power, a power surge, unplugging the drive, or unplugging the Embassy, the filesystem *will* be in an unrecoverable state. Please proceed with caution.', - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: 'Repair', - handler: () => { - try { - this.api.repairDisk().then(_ => { - this.restart() - }) - } catch (e) { - console.error(e) - } - }, - }, - ], - cssClass: 'alert-warning-message', - }) - await alert.present() - } - - refreshPage(): void { - window.location.reload() - } } diff --git a/frontend/projects/diagnostic-ui/src/app/services/api/api.service.ts b/frontend/projects/diagnostic-ui/src/app/services/api/api.service.ts index d1885a46f..562d486c3 100644 --- a/frontend/projects/diagnostic-ui/src/app/services/api/api.service.ts +++ b/frontend/projects/diagnostic-ui/src/app/services/api/api.service.ts @@ -5,6 +5,7 @@ export abstract class ApiService { abstract restart(): Promise abstract forgetDrive(): Promise abstract repairDisk(): Promise + abstract systemRebuild(): Promise abstract getLogs(params: ServerLogsReq): Promise } diff --git a/frontend/projects/diagnostic-ui/src/app/services/api/live-api.service.ts b/frontend/projects/diagnostic-ui/src/app/services/api/live-api.service.ts index ca0794745..bbde6e5ba 100644 --- a/frontend/projects/diagnostic-ui/src/app/services/api/live-api.service.ts +++ b/frontend/projects/diagnostic-ui/src/app/services/api/live-api.service.ts @@ -12,35 +12,42 @@ import { LogsRes, ServerLogsReq } from '@start9labs/shared' export class LiveApiService implements ApiService { constructor(private readonly http: HttpService) {} - getError(): Promise { + async getError(): Promise { return this.rpcRequest({ method: 'diagnostic.error', params: {}, }) } - restart(): Promise { + async restart(): Promise { return this.rpcRequest({ method: 'diagnostic.restart', params: {}, }) } - forgetDrive(): Promise { + async forgetDrive(): Promise { return this.rpcRequest({ method: 'diagnostic.disk.forget', params: {}, }) } - repairDisk(): Promise { + async repairDisk(): Promise { return this.rpcRequest({ method: 'diagnostic.disk.repair', params: {}, }) } - getLogs(params: ServerLogsReq): Promise { + async systemRebuild(): Promise { + return this.rpcRequest({ + method: 'diagnostic.rebuild', + params: {}, + }) + } + + async getLogs(params: ServerLogsReq): Promise { return this.rpcRequest({ method: 'diagnostic.logs', params, diff --git a/frontend/projects/diagnostic-ui/src/app/services/api/mock-api.service.ts b/frontend/projects/diagnostic-ui/src/app/services/api/mock-api.service.ts index 848915a49..c8fea5ad6 100644 --- a/frontend/projects/diagnostic-ui/src/app/services/api/mock-api.service.ts +++ b/frontend/projects/diagnostic-ui/src/app/services/api/mock-api.service.ts @@ -26,6 +26,10 @@ export class MockApiService implements ApiService { await pauseFor(1000) } + async systemRebuild(): Promise { + await pauseFor(1000) + } + async getLogs(params: ServerLogsReq): Promise { await pauseFor(1000) let entries: Log[] diff --git a/frontend/projects/shared/styles/shared.scss b/frontend/projects/shared/styles/shared.scss index 62d1571ce..eefee9fd4 100644 --- a/frontend/projects/shared/styles/shared.scss +++ b/frontend/projects/shared/styles/shared.scss @@ -139,3 +139,21 @@ ion-modal { content: '...'; } } + +.alert-error-message { + .alert-title { + color: var(--ion-color-danger); + } +} + +.alert-warning-message { + .alert-title { + color: var(--ion-color-warning); + } +} + +.alert-success-message { + .alert-title { + color: var(--ion-color-success); + } +} diff --git a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts index 9ad51606f..7b5463fb8 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts @@ -184,7 +184,7 @@ export class ServerShowPage { async presentAlertRepairDisk() { const alert = await this.alertCtrl.create({ header: 'Warning', - message: `

This action will attempt to preform a disk repair operation and system reboot. No data will be deleted. This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.

If anything happens to the device during the reboot, such as losing power, a power surge, unplugging the drive, or unplugging the Embassy, the filesystem will be in an unrecoverable state. Please proceed with caution.

`, + message: `

This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.

If anything happens to the device during the reboot, such as losing power or unplugging the drive, the filesystem will be in an unrecoverable state. Please proceed with caution.

`, buttons: [ { text: 'Cancel', diff --git a/frontend/projects/ui/src/styles.scss b/frontend/projects/ui/src/styles.scss index b11b0bdcd..0f0313c5c 100644 --- a/frontend/projects/ui/src/styles.scss +++ b/frontend/projects/ui/src/styles.scss @@ -221,24 +221,6 @@ ion-button { } } -.alert-error-message { - .alert-title { - color: var(--ion-color-danger); - } -} - -.alert-warning-message { - .alert-title { - color: var(--ion-color-warning); - } -} - -.alert-success-message { - .alert-title { - color: var(--ion-color-success); - } -} - .notification-detail-alert { .alert-wrapper { --max-height: 80% !important;