Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExp
setMode: () => Effect.die("unexpected setMode"),
setTailscaleServeEnabled: () => Effect.die("unexpected setTailscaleServeEnabled"),
getAdvertisedEndpoints: Effect.succeed([]),
resolveTailscaleHttpsEndpoint: Effect.succeed(null),
} satisfies DesktopServerExposure.DesktopServerExposure["Service"]);

function makeEnvironmentLayer(
Expand Down
530 changes: 516 additions & 14 deletions apps/desktop/src/backend/DesktopServerExposure.test.ts

Large diffs are not rendered by default.

305 changes: 280 additions & 25 deletions apps/desktop/src/backend/DesktopServerExposure.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions apps/desktop/src/ipc/DesktopIpcHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import {
getAdvertisedEndpoints,
getServerExposureState,
resolveTailscaleHttpsEndpoint,
setServerExposureMode,
setTailscaleServeEnabled,
} from "./methods/serverExposure.ts";
Expand Down Expand Up @@ -72,6 +73,7 @@ export const installDesktopIpcHandlers = Effect.fn("desktop.ipc.installHandlers"
yield* ipc.handle(setServerExposureMode);
yield* ipc.handle(setTailscaleServeEnabled);
yield* ipc.handle(getAdvertisedEndpoints);
yield* ipc.handle(resolveTailscaleHttpsEndpoint);

yield* ipc.handle(getWslState);
yield* ipc.handle(setWslBackendEnabled);
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/ipc/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const GET_SERVER_EXPOSURE_STATE_CHANNEL = "desktop:get-server-exposure-st
export const SET_SERVER_EXPOSURE_MODE_CHANNEL = "desktop:set-server-exposure-mode";
export const SET_TAILSCALE_SERVE_ENABLED_CHANNEL = "desktop:set-tailscale-serve-enabled";
export const GET_ADVERTISED_ENDPOINTS_CHANNEL = "desktop:get-advertised-endpoints";
export const RESOLVE_TAILSCALE_HTTPS_ENDPOINT_CHANNEL = "desktop:resolve-tailscale-https-endpoint";
export const GET_WSL_STATE_CHANNEL = "desktop:get-wsl-state";
export const SET_WSL_BACKEND_ENABLED_CHANNEL = "desktop:set-wsl-backend-enabled";
export const SET_WSL_DISTRO_CHANNEL = "desktop:set-wsl-distro";
Expand Down
15 changes: 14 additions & 1 deletion apps/desktop/src/ipc/methods/serverExposure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import * as DesktopIpc from "../DesktopIpc.ts";

const SetTailscaleServeEnabledInput = Schema.Struct({
enabled: Schema.Boolean,
port: Schema.optionalKey(Schema.Number),
// Reject out-of-range ports at the boundary. Settings persistence silently
// normalizes anything invalid to 443, so a bare Schema.Number would let the
// CLI bind one port while settings recorded another.
port: Schema.optionalKey(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 65_535 }))),
});

export const getServerExposureState = DesktopIpc.makeIpcMethod({
Expand Down Expand Up @@ -67,3 +70,13 @@ export const getAdvertisedEndpoints = DesktopIpc.makeIpcMethod({
return yield* serverExposure.getAdvertisedEndpoints;
}),
});

export const resolveTailscaleHttpsEndpoint = DesktopIpc.makeIpcMethod({
channel: IpcChannels.RESOLVE_TAILSCALE_HTTPS_ENDPOINT_CHANNEL,
payload: Schema.Void,
result: Schema.NullOr(AdvertisedEndpoint),
handler: Effect.fn("desktop.ipc.serverExposure.resolveTailscaleHttpsEndpoint")(function* () {
const serverExposure = yield* DesktopServerExposure.DesktopServerExposure;
return yield* serverExposure.resolveTailscaleHttpsEndpoint;
}),
});
2 changes: 2 additions & 0 deletions apps/desktop/src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ contextBridge.exposeInMainWorld("desktopBridge", {
setTailscaleServeEnabled: (input) =>
ipcRenderer.invoke(IpcChannels.SET_TAILSCALE_SERVE_ENABLED_CHANNEL, input),
getAdvertisedEndpoints: () => ipcRenderer.invoke(IpcChannels.GET_ADVERTISED_ENDPOINTS_CHANNEL),
resolveTailscaleHttpsEndpoint: () =>
ipcRenderer.invoke(IpcChannels.RESOLVE_TAILSCALE_HTTPS_ENDPOINT_CHANNEL),
getWslState: () => ipcRenderer.invoke(IpcChannels.GET_WSL_STATE_CHANNEL),
setWslBackendEnabled: (enabled) =>
ipcRenderer.invoke(IpcChannels.SET_WSL_BACKEND_ENABLED_CHANNEL, enabled),
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/window/DesktopWindow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopSe
setMode: () => Effect.die("unexpected setMode"),
setTailscaleServeEnabled: () => Effect.die("unexpected setTailscaleServeEnabled"),
getAdvertisedEndpoints: Effect.die("unexpected getAdvertisedEndpoints"),
resolveTailscaleHttpsEndpoint: Effect.die("unexpected resolveTailscaleHttpsEndpoint"),
} satisfies DesktopServerExposure.DesktopServerExposure["Service"]);

const electronMenuLayer = Layer.succeed(ElectronMenu.ElectronMenu, {
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/wsl/DesktopWslBackend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExp
setMode: () => Effect.die("unexpected setMode"),
setTailscaleServeEnabled: () => Effect.die("unexpected setTailscaleServeEnabled"),
getAdvertisedEndpoints: Effect.succeed([]),
resolveTailscaleHttpsEndpoint: Effect.succeed(null),
} satisfies DesktopServerExposure.DesktopServerExposure["Service"]);

const backendConfigurationLayer = Layer.succeed(
Expand Down
7 changes: 6 additions & 1 deletion apps/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ import {
import { orchestrationHttpApiLayer } from "./orchestration/http.ts";
import * as NetService from "@t3tools/shared/Net";
import * as RelayClient from "@t3tools/shared/relayClient";
import { disableTailscaleServe, ensureTailscaleServe } from "@t3tools/tailscale";
import {
disableTailscaleServe,
ensureTailscaleServe,
formatTailscaleServeUserMessage,
} from "@t3tools/tailscale";

// Effect's default preemptive shutdown waits 20s before finalizing request scopes.
// T3's primary transport is long-lived WebSocket RPC, whose Effect scope finalizer
Expand Down Expand Up @@ -480,6 +484,7 @@ export const makeServerLayer = Layer.unwrap(
Effect.catch((cause) =>
Effect.logWarning("Failed to configure Tailscale Serve", {
cause,
message: formatTailscaleServeUserMessage(cause),
localPort,
servePort: config.tailscaleServePort,
}).pipe(Effect.as(null)),
Expand Down
Loading
Loading