mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Add system rebuild and disk repair to Diagnostic UI (#2093)
* add system rebuild and disk repair to diagnostic * add `diagnostic.rebuild` Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -6,6 +6,7 @@ use rpc_toolkit::yajrc::RpcError;
|
|||||||
|
|
||||||
use crate::context::DiagnosticContext;
|
use crate::context::DiagnosticContext;
|
||||||
use crate::disk::repair;
|
use crate::disk::repair;
|
||||||
|
use crate::init::SYSTEM_REBUILD_PATH;
|
||||||
use crate::logs::{fetch_logs, LogResponse, LogSource};
|
use crate::logs::{fetch_logs, LogResponse, LogSource};
|
||||||
use crate::shutdown::Shutdown;
|
use crate::shutdown::Shutdown;
|
||||||
use crate::util::display_none;
|
use crate::util::display_none;
|
||||||
@@ -13,7 +14,7 @@ use crate::Error;
|
|||||||
|
|
||||||
pub const SYSTEMD_UNIT: &'static str = "embassy-init";
|
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> {
|
pub fn diagnostic() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -51,6 +52,12 @@ pub fn restart(#[context] ctx: DiagnosticContext) -> Result<(), Error> {
|
|||||||
Ok(())
|
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))]
|
#[command(subcommands(forget_disk, repair))]
|
||||||
pub fn disk() -> Result<(), Error> {
|
pub fn disk() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -7,54 +7,62 @@
|
|||||||
>
|
>
|
||||||
embassyOS - Diagnostic Mode
|
embassyOS - Diagnostic Mode
|
||||||
</h1>
|
</h1>
|
||||||
<h2
|
|
||||||
style="
|
<ng-container *ngIf="error">
|
||||||
padding-bottom: 16px;
|
<h2
|
||||||
font-size: calc(1vw + 14px);
|
style="
|
||||||
font-weight: bold;
|
padding-bottom: 16px;
|
||||||
"
|
font-size: calc(1vw + 14px);
|
||||||
>
|
font-weight: bold;
|
||||||
embassyOS launch error:
|
"
|
||||||
</h2>
|
>
|
||||||
<div class="code-block">
|
embassyOS launch error:
|
||||||
<code>
|
</h2>
|
||||||
<ion-text color="warning">{{ error.problem }}</ion-text>
|
<div class="code-block">
|
||||||
<span *ngIf="error.details">
|
<code>
|
||||||
<br />
|
<ion-text color="warning">{{ error.problem }}</ion-text>
|
||||||
<br />
|
<span *ngIf="error.details">
|
||||||
<ion-text color="warning">{{ error.details }}</ion-text>
|
<br />
|
||||||
</span>
|
<br />
|
||||||
</code>
|
<ion-text color="warning">{{ error.details }}</ion-text>
|
||||||
</div>
|
</span>
|
||||||
<ion-button routerLink="logs"> View Logs </ion-button>
|
</code>
|
||||||
<h2
|
</div>
|
||||||
style="
|
<ion-button routerLink="logs"> View Logs </ion-button>
|
||||||
padding: 32px 0 16px 0;
|
<h2
|
||||||
font-size: calc(1vw + 12px);
|
style="
|
||||||
font-weight: bold;
|
padding: 32px 0 16px 0;
|
||||||
"
|
font-size: calc(1vw + 12px);
|
||||||
>
|
font-weight: bold;
|
||||||
Possible solution:
|
"
|
||||||
</h2>
|
>
|
||||||
<div class="code-block">
|
Possible solutions:
|
||||||
<code><ion-text color="success">{{ error.solution }}</ion-text></code>
|
</h2>
|
||||||
</div>
|
<div class="code-block">
|
||||||
<ion-button (click)="restart()"> Restart Embassy </ion-button>
|
<code><ion-text color="success">{{ error.solution }}</ion-text></code>
|
||||||
<div
|
</div>
|
||||||
*ngIf="error.code === 15 || error.code === 25"
|
<ion-button (click)="restart()"> Restart Embassy </ion-button>
|
||||||
class="ion-padding-top"
|
<ion-button
|
||||||
>
|
class="ion-padding-start"
|
||||||
<ion-button *ngIf="error.code === 15" (click)="forgetDrive()">
|
*ngIf="error.code === 15 || error.code === 25"
|
||||||
|
(click)="forgetDrive()"
|
||||||
|
>
|
||||||
{{ error.code === 15 ? 'Setup Current Drive' : 'Enter Recovery Mode'
|
{{ error.code === 15 ? 'Setup Current Drive' : 'Enter Recovery Mode'
|
||||||
}}
|
}}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</div>
|
|
||||||
<div
|
<div class="ion-padding-top">
|
||||||
*ngIf="error.code === 2 || error.code === 48"
|
<ion-button (click)="presentAlertSystemRebuild()" color="warning"
|
||||||
class="ion-padding-top"
|
>System Rebuild</ion-button
|
||||||
>
|
>
|
||||||
<ion-button (click)="repairDrive()"> {{ 'Repair Drive' }} </ion-button>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="ion-padding-top">
|
||||||
|
<ion-button (click)="presentAlertRepairDisk()" color="danger"
|
||||||
|
>Repair Drive</ion-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #refresh>
|
<ng-template #refresh>
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import { ApiService } from 'src/app/services/api/api.service'
|
|||||||
styleUrls: ['home.page.scss'],
|
styleUrls: ['home.page.scss'],
|
||||||
})
|
})
|
||||||
export class HomePage {
|
export class HomePage {
|
||||||
error: {
|
error?: {
|
||||||
code: number
|
code: number
|
||||||
problem: string
|
problem: string
|
||||||
solution: string
|
solution: string
|
||||||
details?: string
|
details?: string
|
||||||
} = {} as any
|
}
|
||||||
solutions: string[] = []
|
solutions: string[] = []
|
||||||
restarted = false
|
restarted = false
|
||||||
|
|
||||||
@@ -118,7 +118,80 @@ export class HomePage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async repairDrive(): Promise<void> {
|
async presentAlertSystemRebuild() {
|
||||||
|
const alert = await this.alertCtrl.create({
|
||||||
|
header: 'Warning',
|
||||||
|
message:
|
||||||
|
'<p>This action will tear down all service containers and rebuild them from scratch. No data will be deleted.</p><p>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.</p><p>It may take up to an hour to complete. During this time, you will lose all connectivity to your Embassy.</p>',
|
||||||
|
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:
|
||||||
|
'<p>This action should only be executed if directed by a Start9 support specialist.</p><p>If anything happens to the device during the reboot, such as losing power or unplugging the drive, the filesystem <i>will</i> be in an unrecoverable state. Please proceed with caution.</p>',
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
cssClass: 'loader',
|
cssClass: 'loader',
|
||||||
})
|
})
|
||||||
@@ -134,36 +207,4 @@ export class HomePage {
|
|||||||
loader.dismiss()
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export abstract class ApiService {
|
|||||||
abstract restart(): Promise<void>
|
abstract restart(): Promise<void>
|
||||||
abstract forgetDrive(): Promise<void>
|
abstract forgetDrive(): Promise<void>
|
||||||
abstract repairDisk(): Promise<void>
|
abstract repairDisk(): Promise<void>
|
||||||
|
abstract systemRebuild(): Promise<void>
|
||||||
abstract getLogs(params: ServerLogsReq): Promise<LogsRes>
|
abstract getLogs(params: ServerLogsReq): Promise<LogsRes>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,35 +12,42 @@ import { LogsRes, ServerLogsReq } from '@start9labs/shared'
|
|||||||
export class LiveApiService implements ApiService {
|
export class LiveApiService implements ApiService {
|
||||||
constructor(private readonly http: HttpService) {}
|
constructor(private readonly http: HttpService) {}
|
||||||
|
|
||||||
getError(): Promise<GetErrorRes> {
|
async getError(): Promise<GetErrorRes> {
|
||||||
return this.rpcRequest<GetErrorRes>({
|
return this.rpcRequest<GetErrorRes>({
|
||||||
method: 'diagnostic.error',
|
method: 'diagnostic.error',
|
||||||
params: {},
|
params: {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
restart(): Promise<void> {
|
async restart(): Promise<void> {
|
||||||
return this.rpcRequest<void>({
|
return this.rpcRequest<void>({
|
||||||
method: 'diagnostic.restart',
|
method: 'diagnostic.restart',
|
||||||
params: {},
|
params: {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
forgetDrive(): Promise<void> {
|
async forgetDrive(): Promise<void> {
|
||||||
return this.rpcRequest<void>({
|
return this.rpcRequest<void>({
|
||||||
method: 'diagnostic.disk.forget',
|
method: 'diagnostic.disk.forget',
|
||||||
params: {},
|
params: {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
repairDisk(): Promise<void> {
|
async repairDisk(): Promise<void> {
|
||||||
return this.rpcRequest<void>({
|
return this.rpcRequest<void>({
|
||||||
method: 'diagnostic.disk.repair',
|
method: 'diagnostic.disk.repair',
|
||||||
params: {},
|
params: {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getLogs(params: ServerLogsReq): Promise<LogsRes> {
|
async systemRebuild(): Promise<void> {
|
||||||
|
return this.rpcRequest<void>({
|
||||||
|
method: 'diagnostic.rebuild',
|
||||||
|
params: {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLogs(params: ServerLogsReq): Promise<LogsRes> {
|
||||||
return this.rpcRequest<LogsRes>({
|
return this.rpcRequest<LogsRes>({
|
||||||
method: 'diagnostic.logs',
|
method: 'diagnostic.logs',
|
||||||
params,
|
params,
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ export class MockApiService implements ApiService {
|
|||||||
await pauseFor(1000)
|
await pauseFor(1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async systemRebuild(): Promise<void> {
|
||||||
|
await pauseFor(1000)
|
||||||
|
}
|
||||||
|
|
||||||
async getLogs(params: ServerLogsReq): Promise<LogsRes> {
|
async getLogs(params: ServerLogsReq): Promise<LogsRes> {
|
||||||
await pauseFor(1000)
|
await pauseFor(1000)
|
||||||
let entries: Log[]
|
let entries: Log[]
|
||||||
|
|||||||
@@ -139,3 +139,21 @@ ion-modal {
|
|||||||
content: '...';
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export class ServerShowPage {
|
|||||||
async presentAlertRepairDisk() {
|
async presentAlertRepairDisk() {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: 'Warning',
|
header: 'Warning',
|
||||||
message: `<p>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.</p><p>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 <i>will</i> be in an unrecoverable state. Please proceed with caution.</p>`,
|
message: `<p>This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.</p><p>If anything happens to the device during the reboot, such as losing power or unplugging the drive, the filesystem <i>will</i> be in an unrecoverable state. Please proceed with caution.</p>`,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: 'Cancel',
|
text: 'Cancel',
|
||||||
|
|||||||
@@ -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 {
|
.notification-detail-alert {
|
||||||
.alert-wrapper {
|
.alert-wrapper {
|
||||||
--max-height: 80% !important;
|
--max-height: 80% !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user