feat: add restart button to start-tunnel settings page

Adds a VPS restart button to the settings page, above logout. Shows a
spinner while the RPC completes, then a dialog telling the user to wait
1-2 minutes and refresh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Hill
2026-03-19 23:27:21 -06:00
parent f41fc75024
commit bca2e4d630
4 changed files with 64 additions and 10 deletions

View File

@@ -56,6 +56,22 @@ import { CHANGE_PASSWORD } from './change-password'
</span>
<button tuiButton size="s" (click)="onChangePassword()">Change</button>
</div>
<div tuiCell>
<span tuiTitle>
<strong>Restart</strong>
<span tuiSubtitle>Restart the VPS</span>
</span>
<button
tuiButton
size="s"
appearance="secondary"
iconStart="@tui.rotate-cw"
[loading]="restarting()"
(click)="onRestart()"
>
Restart
</button>
</div>
<div tuiCell>
<span tuiTitle>
<strong>Logout</strong>
@@ -95,6 +111,7 @@ export default class Settings {
protected readonly update = inject(UpdateService)
protected readonly checking = signal(false)
protected readonly applying = signal(false)
protected readonly restarting = signal(false)
protected onChangePassword(): void {
this.dialogs.open(CHANGE_PASSWORD, { label: 'Change Password' }).subscribe()
@@ -124,6 +141,26 @@ export default class Settings {
}
}
protected async onRestart() {
this.restarting.set(true)
try {
await this.api.restart()
this.dialogs
.open(
'The VPS is restarting. Please wait 1\u20132 minutes, then refresh the page.',
{
label: 'Restarting',
},
)
.subscribe()
} catch (e: any) {
this.errorService.handleError(e)
} finally {
this.restarting.set(false)
}
}
protected async onLogout() {
const loader = this.loading.open('').subscribe()

View File

@@ -30,9 +30,7 @@ export abstract class ApiService {
params: T.Tunnel.RemoveDeviceParams,
): Promise<string> // device.show-config
// forwards
abstract addForward(
params: T.Tunnel.AddPortForwardParams,
): Promise<null> // port-forward.add
abstract addForward(params: T.Tunnel.AddPortForwardParams): Promise<null> // port-forward.add
abstract deleteForward(
params: T.Tunnel.RemovePortForwardParams,
): Promise<null> // port-forward.remove
@@ -42,6 +40,8 @@ export abstract class ApiService {
abstract setForwardEnabled(
params: T.Tunnel.SetPortForwardEnabledParams,
): Promise<null> // port-forward.set-enabled
// system
abstract restart(): Promise<null> // restart
// update
abstract checkUpdate(): Promise<T.Tunnel.TunnelUpdateResult> // update.check
abstract applyUpdate(): Promise<T.Tunnel.TunnelUpdateResult> // update.apply

View File

@@ -94,9 +94,7 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'port-forward.add', params })
}
async deleteForward(
params: T.Tunnel.RemovePortForwardParams,
): Promise<null> {
async deleteForward(params: T.Tunnel.RemovePortForwardParams): Promise<null> {
return this.rpcRequest({ method: 'port-forward.remove', params })
}
@@ -112,6 +110,12 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'port-forward.set-enabled', params })
}
// system
async restart(): Promise<null> {
return this.rpcRequest({ method: 'restart', params: {} })
}
// update
async checkUpdate(): Promise<T.Tunnel.TunnelUpdateResult> {

View File

@@ -64,7 +64,9 @@ export class MockApiService extends ApiService {
return null
}
async addSubnet(params: T.Tunnel.SubnetParams & T.Tunnel.AddSubnetParams): Promise<null> {
async addSubnet(
params: T.Tunnel.SubnetParams & T.Tunnel.AddSubnetParams,
): Promise<null> {
await pauseFor(1000)
const patch: AddOperation<T.Tunnel.WgSubnetConfig>[] = [
@@ -79,7 +81,9 @@ export class MockApiService extends ApiService {
return null
}
async editSubnet(params: T.Tunnel.SubnetParams & T.Tunnel.AddSubnetParams): Promise<null> {
async editSubnet(
params: T.Tunnel.SubnetParams & T.Tunnel.AddSubnetParams,
): Promise<null> {
await pauseFor(1000)
const patch: ReplaceOperation<string>[] = [
@@ -177,7 +181,9 @@ export class MockApiService extends ApiService {
return null
}
async updateForwardLabel(params: T.Tunnel.UpdatePortForwardLabelParams): Promise<null> {
async updateForwardLabel(
params: T.Tunnel.UpdatePortForwardLabelParams,
): Promise<null> {
await pauseFor(1000)
const patch: ReplaceOperation<string | null>[] = [
@@ -192,7 +198,9 @@ export class MockApiService extends ApiService {
return null
}
async setForwardEnabled(params: T.Tunnel.SetPortForwardEnabledParams): Promise<null> {
async setForwardEnabled(
params: T.Tunnel.SetPortForwardEnabledParams,
): Promise<null> {
await pauseFor(1000)
const patch: ReplaceOperation<boolean>[] = [
@@ -221,6 +229,11 @@ export class MockApiService extends ApiService {
return null
}
async restart(): Promise<null> {
await pauseFor(1000)
return null
}
async checkUpdate(): Promise<T.Tunnel.TunnelUpdateResult> {
await pauseFor(1000)
return {