125 lines · 4.1 KB · TypeScript
RawDownload
import * as FS from "node:fs";import * as Path from "node:path";import type { DesktopServerExposureMode, DesktopUpdateChannel } from "@t3tools/contracts"; import { resolveDefaultDesktopUpdateChannel } from "./updateChannels.ts"; export interface DesktopSettings {  readonly serverExposureMode: DesktopServerExposureMode;  readonly tailscaleServeEnabled: boolean;  readonly tailscaleServePort: number;  readonly updateChannel: DesktopUpdateChannel;  readonly updateChannelConfiguredByUser: boolean;} export const DEFAULT_TAILSCALE_SERVE_PORT = 443; export const DEFAULT_DESKTOP_SETTINGS: DesktopSettings = {  serverExposureMode: "local-only",  tailscaleServeEnabled: false,  tailscaleServePort: DEFAULT_TAILSCALE_SERVE_PORT,  updateChannel: "latest",  updateChannelConfiguredByUser: false,}; export function resolveDefaultDesktopSettings(appVersion: string): DesktopSettings {  return {    ...DEFAULT_DESKTOP_SETTINGS,    updateChannel: resolveDefaultDesktopUpdateChannel(appVersion),  };} export function setDesktopServerExposurePreference(  settings: DesktopSettings,  requestedMode: DesktopServerExposureMode,): DesktopSettings {  return settings.serverExposureMode === requestedMode    ? settings    : {        ...settings,        serverExposureMode: requestedMode,      };} export function setDesktopTailscaleServePreference(  settings: DesktopSettings,  input: { readonly enabled: boolean; readonly port?: number },): DesktopSettings {  const port =    input.port === undefined      ? settings.tailscaleServePort      : normalizeTailscaleServePort(input.port);  return settings.tailscaleServeEnabled === input.enabled && settings.tailscaleServePort === port    ? settings    : {        ...settings,        tailscaleServeEnabled: input.enabled,        tailscaleServePort: port,      };} export function normalizeTailscaleServePort(value: unknown): number {  return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 65_535    ? value    : DEFAULT_TAILSCALE_SERVE_PORT;} export function setDesktopUpdateChannelPreference(  settings: DesktopSettings,  requestedChannel: DesktopUpdateChannel,): DesktopSettings {  return {    ...settings,    updateChannel: requestedChannel,    updateChannelConfiguredByUser: true,  };} export function readDesktopSettings(settingsPath: string, appVersion: string): DesktopSettings {  const defaultSettings = resolveDefaultDesktopSettings(appVersion);   try {    if (!FS.existsSync(settingsPath)) {      return defaultSettings;    }     const raw = FS.readFileSync(settingsPath, "utf8");    const parsed = JSON.parse(raw) as {      readonly serverExposureMode?: unknown;      readonly tailscaleServeEnabled?: unknown;      readonly tailscaleServePort?: unknown;      readonly updateChannel?: unknown;      readonly updateChannelConfiguredByUser?: unknown;    };    const parsedUpdateChannel =      parsed.updateChannel === "nightly" || parsed.updateChannel === "latest"        ? parsed.updateChannel        : null;    const isLegacySettings = parsed.updateChannelConfiguredByUser === undefined;    const updateChannelConfiguredByUser =      parsed.updateChannelConfiguredByUser === true ||      (isLegacySettings && parsedUpdateChannel === "nightly");     return {      serverExposureMode:        parsed.serverExposureMode === "network-accessible" ? "network-accessible" : "local-only",      tailscaleServeEnabled: parsed.tailscaleServeEnabled === true,      tailscaleServePort: normalizeTailscaleServePort(parsed.tailscaleServePort),      updateChannel:        updateChannelConfiguredByUser && parsedUpdateChannel !== null          ? parsedUpdateChannel          : defaultSettings.updateChannel,      updateChannelConfiguredByUser,    };  } catch {    return defaultSettings;  }} export function writeDesktopSettings(settingsPath: string, settings: DesktopSettings): void {  const directory = Path.dirname(settingsPath);  const tempPath = `${settingsPath}.${process.pid}.${Date.now()}.tmp`;  FS.mkdirSync(directory, { recursive: true });  FS.writeFileSync(tempPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");  FS.renameSync(tempPath, settingsPath);}