From bca2e4d630e050168c2f7307cae0a38557e21bce Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Thu, 19 Mar 2026 23:27:21 -0600 Subject: [PATCH] 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) --- .../app/routes/home/routes/settings/index.ts | 37 +++++++++++++++++++ .../src/app/services/api/api.service.ts | 6 +-- .../src/app/services/api/live-api.service.ts | 10 +++-- .../src/app/services/api/mock-api.service.ts | 21 +++++++++-- 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/web/projects/start-tunnel/src/app/routes/home/routes/settings/index.ts b/web/projects/start-tunnel/src/app/routes/home/routes/settings/index.ts index f701a35ad..176b8f804 100644 --- a/web/projects/start-tunnel/src/app/routes/home/routes/settings/index.ts +++ b/web/projects/start-tunnel/src/app/routes/home/routes/settings/index.ts @@ -56,6 +56,22 @@ import { CHANGE_PASSWORD } from './change-password' +
+ + Restart + Restart the VPS + + +
Logout @@ -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() diff --git a/web/projects/start-tunnel/src/app/services/api/api.service.ts b/web/projects/start-tunnel/src/app/services/api/api.service.ts index f25d22b93..9476fa04d 100644 --- a/web/projects/start-tunnel/src/app/services/api/api.service.ts +++ b/web/projects/start-tunnel/src/app/services/api/api.service.ts @@ -30,9 +30,7 @@ export abstract class ApiService { params: T.Tunnel.RemoveDeviceParams, ): Promise // device.show-config // forwards - abstract addForward( - params: T.Tunnel.AddPortForwardParams, - ): Promise // port-forward.add + abstract addForward(params: T.Tunnel.AddPortForwardParams): Promise // port-forward.add abstract deleteForward( params: T.Tunnel.RemovePortForwardParams, ): Promise // port-forward.remove @@ -42,6 +40,8 @@ export abstract class ApiService { abstract setForwardEnabled( params: T.Tunnel.SetPortForwardEnabledParams, ): Promise // port-forward.set-enabled + // system + abstract restart(): Promise // restart // update abstract checkUpdate(): Promise // update.check abstract applyUpdate(): Promise // update.apply diff --git a/web/projects/start-tunnel/src/app/services/api/live-api.service.ts b/web/projects/start-tunnel/src/app/services/api/live-api.service.ts index 83aaa8515..9edba05e8 100644 --- a/web/projects/start-tunnel/src/app/services/api/live-api.service.ts +++ b/web/projects/start-tunnel/src/app/services/api/live-api.service.ts @@ -94,9 +94,7 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'port-forward.add', params }) } - async deleteForward( - params: T.Tunnel.RemovePortForwardParams, - ): Promise { + async deleteForward(params: T.Tunnel.RemovePortForwardParams): Promise { 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 { + return this.rpcRequest({ method: 'restart', params: {} }) + } + // update async checkUpdate(): Promise { diff --git a/web/projects/start-tunnel/src/app/services/api/mock-api.service.ts b/web/projects/start-tunnel/src/app/services/api/mock-api.service.ts index ed3ebe95e..a7989fe19 100644 --- a/web/projects/start-tunnel/src/app/services/api/mock-api.service.ts +++ b/web/projects/start-tunnel/src/app/services/api/mock-api.service.ts @@ -64,7 +64,9 @@ export class MockApiService extends ApiService { return null } - async addSubnet(params: T.Tunnel.SubnetParams & T.Tunnel.AddSubnetParams): Promise { + async addSubnet( + params: T.Tunnel.SubnetParams & T.Tunnel.AddSubnetParams, + ): Promise { await pauseFor(1000) const patch: AddOperation[] = [ @@ -79,7 +81,9 @@ export class MockApiService extends ApiService { return null } - async editSubnet(params: T.Tunnel.SubnetParams & T.Tunnel.AddSubnetParams): Promise { + async editSubnet( + params: T.Tunnel.SubnetParams & T.Tunnel.AddSubnetParams, + ): Promise { await pauseFor(1000) const patch: ReplaceOperation[] = [ @@ -177,7 +181,9 @@ export class MockApiService extends ApiService { return null } - async updateForwardLabel(params: T.Tunnel.UpdatePortForwardLabelParams): Promise { + async updateForwardLabel( + params: T.Tunnel.UpdatePortForwardLabelParams, + ): Promise { await pauseFor(1000) const patch: ReplaceOperation[] = [ @@ -192,7 +198,9 @@ export class MockApiService extends ApiService { return null } - async setForwardEnabled(params: T.Tunnel.SetPortForwardEnabledParams): Promise { + async setForwardEnabled( + params: T.Tunnel.SetPortForwardEnabledParams, + ): Promise { await pauseFor(1000) const patch: ReplaceOperation[] = [ @@ -221,6 +229,11 @@ export class MockApiService extends ApiService { return null } + async restart(): Promise { + await pauseFor(1000) + return null + } + async checkUpdate(): Promise { await pauseFor(1000) return {