Files
gitlab-instance-0a899031_d2…/electron/src/main/appHost.ts
eric b77a8cadb2
Some checks failed
Upstream Sync / Sync latest commits from upstream repo (push) Has been cancelled
'init'
2026-03-24 14:46:41 -05:00

225 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Electron app 模块:开机启动、跳转列表、关于面板、日志路径、角标、优雅重启、指标等。
* 文档:https://www.electronjs.org/docs/latest/api/app
*/
import { app, BrowserWindow, ipcMain, shell } from 'electron'
import fs from 'fs'
import path from 'path'
const VALID_NAV = new Set(['desk', 'record', 'network', 'proxy', 'system', 'wechat'])
export function parseNavFromArgv(argv: string[]): string | null {
for (const a of argv) {
const m = /^--nav=(.+)$/.exec(a)
if (!m) continue
const id = m[1].trim()
if (VALID_NAV.has(id)) return id
}
return null
}
/** 须在 app.ready 之前调用(Windows Toast / 任务栏分组) */
export function setAppUserModelIdEarly(): void {
if (process.platform !== 'win32') return
try {
app.setAppUserModelId('com.douyin.recorder.console')
} catch {
/* ignore */
}
}
function readPackageJsonField(field: 'description' | 'version'): string {
try {
const candidates = [
path.join(app.getAppPath(), 'package.json'),
path.join(process.cwd(), 'package.json'),
]
for (const p of candidates) {
if (fs.existsSync(p)) {
const j = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>
const v = j[field]
if (typeof v === 'string') return v
}
}
} catch {
/* ignore */
}
return ''
}
/** ready 后立即调用 */
export function applyAppHostBootstrap(getBackendRoot: () => string): void {
try {
const logDir = path.join(app.getPath('userData'), 'logs')
app.setAppLogsPath(logDir)
} catch {
/* ignore */
}
const ver = app.getVersion()
const desc = readPackageJsonField('description') || '直播录制与转播控制台'
app.setAboutPanelOptions({
applicationName: app.getName(),
applicationVersion: ver,
copyright: `Copyright © ${new Date().getFullYear()}`,
credits: desc,
})
if (process.env.WEB2_SECURE_DNS === '1') {
try {
app.configureHostResolver({ secureDnsMode: 'automatic' })
} catch {
/* ignore */
}
}
const proxy = process.env.WEB2_ELECTRON_GLOBAL_PROXY?.trim()
if (proxy) {
try {
void app.setProxy({ proxyRules: proxy })
} catch {
/* ignore */
}
}
if (process.platform === 'win32') {
try {
app.setUserTasks([
{
program: process.execPath,
arguments: '--nav=desk',
title: '工作台',
description: '打开工作台仪表盘',
iconPath: process.execPath,
iconIndex: 0,
},
{
program: process.execPath,
arguments: '--nav=record',
title: '录制',
description: '打开录制与转播控制',
iconPath: process.execPath,
iconIndex: 0,
},
{
program: process.execPath,
arguments: '--nav=network',
title: '网络',
description: '网络与远程绑定',
iconPath: process.execPath,
iconIndex: 0,
},
])
} catch {
/* ignore */
}
}
}
export function registerAppHostIpcHandlers(getBackendRoot: () => string): void {
ipcMain.removeHandler('app:get-login-item')
ipcMain.handle('app:get-login-item', () => {
try {
return app.getLoginItemSettings()
} catch {
return { openAtLogin: false }
}
})
ipcMain.removeHandler('app:set-login-item')
ipcMain.handle('app:set-login-item', (_evt, openAtLogin: unknown) => {
if (typeof openAtLogin !== 'boolean') return { ok: false as const, error: '参数无效' }
try {
app.setLoginItemSettings({
openAtLogin,
path: process.execPath,
args: [],
})
return { ok: true as const }
} catch (e) {
return { ok: false as const, error: e instanceof Error ? e.message : String(e) }
}
})
ipcMain.removeHandler('app:relaunch')
ipcMain.handle('app:relaunch', () => {
app.relaunch()
app.exit(0)
})
ipcMain.removeHandler('app:get-metrics')
ipcMain.handle('app:get-metrics', () => {
try {
return app.getAppMetrics()
} catch {
return []
}
})
ipcMain.removeHandler('app:show-about')
ipcMain.handle('app:show-about', () => {
try {
app.showAboutPanel()
} catch {
/* ignore */
}
})
ipcMain.removeHandler('app:set-badge')
ipcMain.handle('app:set-badge', (_evt, count: unknown) => {
const n = typeof count === 'number' && Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0
try {
if (process.platform === 'darwin' || process.platform === 'linux') {
app.setBadgeCount(n)
}
} catch {
/* Windows 无 dock 角标,忽略 */
}
})
ipcMain.removeHandler('app:get-paths')
ipcMain.handle('app:get-paths', () => {
const br = getBackendRoot()
return {
userData: app.getPath('userData'),
logs: app.getPath('logs'),
temp: app.getPath('temp'),
backendRoot: br,
backendConfig: path.join(br, 'config'),
}
})
ipcMain.removeHandler('app:add-recent-config')
ipcMain.handle('app:add-recent-config', () => {
const cfg = path.join(getBackendRoot(), 'config')
try {
if (fs.existsSync(cfg)) {
app.addRecentDocument(cfg)
}
} catch {
/* ignore */
}
})
ipcMain.removeHandler('app:open-logs-folder')
ipcMain.handle('app:open-logs-folder', async () => {
const p = app.getPath('logs')
try {
await fs.promises.mkdir(p, { recursive: true })
} catch {
/* ignore */
}
const err = await shell.openPath(p)
if (err) throw new Error(err)
})
}
export function sendNavToWindow(win: BrowserWindow, nav: string): void {
if (!VALID_NAV.has(nav)) return
try {
win.webContents.send('app:nav', nav)
} catch {
/* ignore */
}
}