feat(emain): wire remote mode into Electron main process

- Switch auth injector to X-Remote-Password in remote mode (emain.ts, emain-tabview.ts)
- Skip dock menu, auto-updater, and global hotkeys in remote mode
- Set window title to "Wave — [remote: host:port]" in remote mode
- Use waveapp-remote-<suffix>.log for log files in remote mode
- Return null from get-data-dir / get-config-dir IPC in remote mode
- Add config key constants for remote:password/listenport/bindaddr
This commit is contained in:
dfbb 2026-05-17 13:13:02 +08:00
parent 0e39d7921c
commit a76abeada8
6 changed files with 58 additions and 18 deletions

View file

@ -5,18 +5,29 @@ import fs from "fs";
import path from "path";
import { format } from "util";
import winston from "winston";
import { getWaveDataDir, isDev } from "./emain-platform";
import { getRemoteState, getWaveDataDir, isDev } from "./emain-platform";
const oldConsoleLog = console.log;
function getLogBaseName(): string {
const remote = getRemoteState();
if (remote.isRemote && remote.safeSuffix) {
return `waveapp-remote-${remote.safeSuffix}`;
}
return "waveapp";
}
const LogBaseName = getLogBaseName();
function findHighestLogNumber(logsDir: string): number {
if (!fs.existsSync(logsDir)) {
return 0;
}
const files = fs.readdirSync(logsDir);
let maxNum = 0;
const pattern = new RegExp(`^${LogBaseName}\\.(\\d+)\\.log$`);
for (const file of files) {
const match = file.match(/^waveapp\.(\d+)\.log$/);
const match = file.match(pattern);
if (match) {
const num = parseInt(match[1], 10);
if (num > maxNum) {
@ -67,7 +78,7 @@ function pruneOldLogs(logsDir: string): { pruned: string[]; error: any } {
function rotateLogIfNeeded(): string | null {
const waveDataDir = getWaveDataDir();
const logFile = path.join(waveDataDir, "waveapp.log");
const logFile = path.join(waveDataDir, `${LogBaseName}.log`);
const logsDir = path.join(waveDataDir, "logs");
if (!fs.existsSync(logsDir)) {
@ -81,7 +92,7 @@ function rotateLogIfNeeded(): string | null {
const stats = fs.statSync(logFile);
if (stats.size > 10 * 1024 * 1024) {
const nextNum = findHighestLogNumber(logsDir) + 1;
const rotatedPath = path.join(logsDir, `waveapp.${nextNum}.log`);
const rotatedPath = path.join(logsDir, `${LogBaseName}.${nextNum}.log`);
fs.renameSync(logFile, rotatedPath);
return rotatedPath;
}
@ -103,7 +114,7 @@ try {
}
const loggerTransports: winston.transport[] = [
new winston.transports.File({ filename: path.join(getWaveDataDir(), "waveapp.log"), level: "info" }),
new winston.transports.File({ filename: path.join(getWaveDataDir(), `${LogBaseName}.log`), level: "info" }),
];
if (isDev) {
loggerTransports.push(new winston.transports.Console());

View file

@ -231,10 +231,10 @@ ipcMain.on("get-webview-preload", (event) => {
event.returnValue = path.join(getElectronAppBasePath(), "preload", "preload-webview.cjs");
});
ipcMain.on("get-data-dir", (event) => {
event.returnValue = getWaveDataDir();
event.returnValue = remoteState.isRemote ? null : getWaveDataDir();
});
ipcMain.on("get-config-dir", (event) => {
event.returnValue = getWaveConfigDir();
event.returnValue = remoteState.isRemote ? null : getWaveConfigDir();
});
ipcMain.on("get-home-dir", (event) => {
event.returnValue = app.getPath("home");

View file

@ -9,7 +9,8 @@ import { createNewWaveWindow, getWaveWindowById } from "emain/emain-window";
import path from "path";
import { configureAuthKeyRequestInjection } from "./authkey";
import { setWasActive } from "./emain-activity";
import { getElectronAppBasePath, isDevVite, unamePlatform } from "./emain-platform";
import { getElectronAppBasePath, getRemoteState, isDevVite, unamePlatform } from "./emain-platform";
import { configureRemotePasswordInjection } from "./remoteauth";
import {
decreaseZoomLevel,
handleCtrlShiftFocus,
@ -353,7 +354,12 @@ export async function getOrCreateWebViewForTab(waveWindowId: string, tabId: stri
tabView.webContents.on("blur", () => {
handleCtrlShiftFocus(tabView.webContents, false);
});
configureAuthKeyRequestInjection(tabView.webContents.session);
const remote = getRemoteState();
if (remote.isRemote) {
configureRemotePasswordInjection(tabView.webContents.session);
} else {
configureAuthKeyRequestInjection(tabView.webContents.session);
}
return [tabView, false];
}

View file

@ -17,7 +17,7 @@ import {
setWasInFg,
} from "./emain-activity";
import { log } from "./emain-log";
import { getElectronAppBasePath, isDev, unamePlatform } from "./emain-platform";
import { getElectronAppBasePath, getRemoteState, isDev, unamePlatform } from "./emain-platform";
import { getOrCreateWebViewForTab, getWaveTabViewByWebContentsId, WaveTabView } from "./emain-tabview";
import { delay, ensureBoundsAreVisible, waveKeyToElectronKey } from "./emain-util";
import { ElectronWshClient } from "./emain-wsh";
@ -158,6 +158,11 @@ export class WaveBrowserWindow extends BaseWindow {
console.log("create win", waveWindow.oid);
const winBounds = calculateWindowBounds(waveWindow.winsize, waveWindow.pos, settings);
const remote = getRemoteState();
const winTitle =
remote.isRemote && remote.target
? `Wave — [remote: ${remote.target.host}:${remote.target.port}]`
: "Wave";
const winOpts: BaseWindowConstructorOptions = {
x: winBounds.x,
y: winBounds.y,
@ -166,6 +171,7 @@ export class WaveBrowserWindow extends BaseWindow {
minWidth: MinWindowWidth,
minHeight: MinWindowHeight,
show: false,
title: winTitle,
};
const isTransparent = settings?.["window:transparent"] ?? false;

View file

@ -10,6 +10,7 @@ import * as services from "../frontend/app/store/services";
import { initElectronWshrpc, shutdownWshrpc } from "../frontend/app/store/wshrpcutil-base";
import { fireAndForget, sleep } from "../frontend/util/util";
import { AuthKey, configureAuthKeyRequestInjection } from "./authkey";
import { configureRemotePasswordInjection, setRemotePassword } from "./remoteauth";
import {
getActivityState,
getAndClearTermCommandsDurable,
@ -33,6 +34,7 @@ import {
checkIfRunningUnderARM64Translation,
getElectronAppBasePath,
getElectronAppUnpackedBasePath,
getRemoteState,
getWaveConfigDir,
getWaveDataDir,
isDev,
@ -398,13 +400,20 @@ async function appMain() {
const ready = await getWaveSrvReady();
console.log("wavesrv ready signal received", ready, Date.now() - startTs, "ms");
await electronApp.whenReady();
configureAuthKeyRequestInjection(electron.session.defaultSession);
const remote = getRemoteState();
if (remote.isRemote) {
setRemotePassword(remote.password!);
configureRemotePasswordInjection(electron.session.defaultSession);
} else {
configureAuthKeyRequestInjection(electron.session.defaultSession);
}
initIpcHandlers();
await sleep(10); // wait a bit for wavesrv to be ready
try {
initElectronWshClient();
initElectronWshrpc(ElectronWshClient, { authKey: AuthKey });
const wshOpts = remote.isRemote ? { remotePassword: remote.password! } : { authKey: AuthKey };
initElectronWshrpc(ElectronWshClient, wshOpts);
initMenuEventSubscriptions();
} catch (e) {
console.log("error initializing wshrpc", e);
@ -420,8 +429,10 @@ async function appMain() {
setTimeout(sendDisplaysTDataEvent, 5000);
makeAndSetAppMenu();
makeDockTaskbar();
await configureAutoUpdater();
if (!remote.isRemote) {
makeDockTaskbar();
await configureAutoUpdater();
}
setGlobalIsStarting(false);
if (fullConfig?.settings?.["window:maxtabcachesize"] != null) {
setMaxTabCacheSize(fullConfig.settings["window:maxtabcachesize"]);
@ -453,11 +464,13 @@ async function appMain() {
}
});
});
const rawGlobalHotKey = launchSettings?.["app:globalhotkey"];
if (rawGlobalHotKey) {
registerGlobalHotkey(rawGlobalHotKey);
if (!remote.isRemote) {
const rawGlobalHotKey = launchSettings?.["app:globalhotkey"];
if (rawGlobalHotKey) {
registerGlobalHotkey(rawGlobalHotKey);
}
initGlobalHotkeyEventSubscription();
}
initGlobalHotkeyEventSubscription();
}
appMain().catch((e) => {

View file

@ -131,5 +131,9 @@ const (
ConfigKey_TsunamiSdkReplacePath = "tsunami:sdkreplacepath"
ConfigKey_TsunamiSdkVersion = "tsunami:sdkversion"
ConfigKey_TsunamiGoPath = "tsunami:gopath"
ConfigKey_RemotePassword = "remote:password"
ConfigKey_RemoteListenPort = "remote:listenport"
ConfigKey_RemoteBindAddr = "remote:bindaddr"
)