fix: harden startup flow and auth re-entry

This commit is contained in:
Misaka
2026-03-21 11:02:17 +08:00
parent ed65312fff
commit 9a1f5a483e
10 changed files with 86 additions and 38 deletions

View File

@@ -1,6 +1,5 @@
import { BrowserWindow, app, shell } from 'electron'
import { join } from 'path'
import { is } from '@electron-toolkit/utils'
import icon from '../../../resources/icon.png?asset'
import { fileURLToPath } from 'url'
import { dirname } from 'path'
@@ -8,6 +7,10 @@ import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
function isDevelopment(): boolean {
return Boolean(process.env['ELECTRON_RENDERER_URL']) || process.env.NODE_ENV === 'development'
}
export function createMainWindow(): BrowserWindow {
const mainWindow = new BrowserWindow({
width: 1200,
@@ -32,7 +35,7 @@ export function createMainWindow(): BrowserWindow {
return { action: 'deny' }
})
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
if (isDevelopment() && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))

View File

@@ -1,5 +1,4 @@
import { app, dialog } from 'electron'
import { electronApp, optimizer } from '@electron-toolkit/utils'
import fs from 'fs'
import { join } from 'path'
import { ConfigManager } from '../services/config/config-manager'
@@ -12,10 +11,23 @@ export function configurePlaywrightBrowsersPath(): string {
}
export function setupElectronRuntime(): void {
electronApp.setAppUserModelId('com.electron')
app.setAppUserModelId('com.electron')
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
window.webContents.on('before-input-event', (event, input) => {
const isReloadShortcut = (input.control || input.meta) && input.key.toLowerCase() === 'r'
const isToggleDevTools = input.key === 'F12'
if (!app.isPackaged && isToggleDevTools && input.type === 'keyDown') {
window.webContents.toggleDevTools()
event.preventDefault()
return
}
if (app.isPackaged && isReloadShortcut) {
event.preventDefault()
}
})
})
}

View File

@@ -8,12 +8,10 @@ import {
} from './bootstrap/runtime'
import { setupProcessGuards } from './bootstrap/process-guards'
const playwrightBrowsersPath = configurePlaywrightBrowsersPath()
setupProcessGuards()
registerMainWindowLifecycle()
app.whenReady().then(async () => {
setupProcessGuards()
registerMainWindowLifecycle()
const playwrightBrowsersPath = configurePlaywrightBrowsersPath()
ensurePlaywrightRuntime(playwrightBrowsersPath)
await initializeMainProcessServices()
setupElectronRuntime()

View File

@@ -15,6 +15,8 @@ import type {
const log = createLogger('AuthApplicationService')
export class AuthApplicationService {
private silentLoginPromise: Promise<SilentLoginResponse> | null = null
constructor(
private readonly sessionManager: SessionManager = SessionManager.getInstance(),
private readonly updateService: UpdateService = UpdateService.getInstance()
@@ -25,6 +27,20 @@ export class AuthApplicationService {
}
async silentLogin(): Promise<SilentLoginResponse> {
if (this.silentLoginPromise) {
log.debug('Reusing in-flight silent login request')
return this.silentLoginPromise
}
this.silentLoginPromise = this.performSilentLogin()
try {
return await this.silentLoginPromise
} finally {
this.silentLoginPromise = null
}
}
private async performSilentLogin(): Promise<SilentLoginResponse> {
log.info('Attempting silent login')
const success = await this.sessionManager.loginByComputerName()
const userInfo = this.sessionManager.getUserInfo()

View File

@@ -133,7 +133,7 @@ export class ConfigManager {
if (this.initialized) return
// 检测是否为开发环境
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
const isDev = process.env.NODE_ENV === 'development' || !(app?.isPackaged ?? false)
if (isDev) {
// 开发环境:配置文件放在项目根目录,方便编辑和调试

View File

@@ -115,7 +115,7 @@ export function setLogLevel(level: string): void {
}
// Add error-specific file transport in production
if (app.isPackaged) {
if (app?.isPackaged) {
logger.add(
new DailyRotateFile({
filename: path.join(getLogDir(), 'error-%DATE%.log'),

View File

@@ -44,7 +44,7 @@ function appendPortableLaunchLog(
}
function getCurrentAppVersion(): string {
return typeof app.getVersion === 'function' ? app.getVersion() : '0.0.0'
return typeof app?.getVersion === 'function' ? app.getVersion() : '0.0.0'
}
function getCurrentChannel(): ReleaseChannel {
@@ -79,7 +79,7 @@ function getSupportState(config: UpdateConfig | null): {
return { supported: false, reason: '当前仅支持 Windows 自动更新' }
}
if (app.isPackaged) {
if (app?.isPackaged) {
return { supported: true }
}

View File

@@ -43,9 +43,10 @@ export class SessionManager {
* @returns True if login successful, false otherwise
*/
public async login(username: string, password: string): Promise<boolean> {
let dao: InstanceType<(typeof import('./bip-users-dao'))['BIPUsersDAO']> | null = null
try {
const { BIPUsersDAO } = await import('./bip-users-dao')
const dao = new BIPUsersDAO()
dao = new BIPUsersDAO()
const userInfo = await dao.authenticate(username, password)
if (userInfo) {
@@ -60,6 +61,12 @@ export class SessionManager {
} catch (error) {
console.error('[SessionManager] Login error:', error)
return false
} finally {
if (dao) {
await dao.disconnect().catch((error) => {
console.error('[SessionManager] Login disconnect error:', error)
})
}
}
}
@@ -68,10 +75,11 @@ export class SessionManager {
* @returns True if login successful, false otherwise
*/
public async loginByComputerName(): Promise<boolean> {
let dao: InstanceType<(typeof import('./bip-users-dao'))['BIPUsersDAO']> | null = null
try {
const { BIPUsersDAO } = await import('./bip-users-dao')
const { hostname } = await import('os')
const dao = new BIPUsersDAO()
dao = new BIPUsersDAO()
const computerName = hostname()
const userInfo = await dao.authenticateByComputerName(computerName)
@@ -87,6 +95,12 @@ export class SessionManager {
} catch (error) {
console.error('[SessionManager] Silent login error:', error)
return false
} finally {
if (dao) {
await dao.disconnect().catch((error) => {
console.error('[SessionManager] Silent login disconnect error:', error)
})
}
}
}
@@ -174,13 +188,20 @@ export class SessionManager {
* Get all users from database (for Admin user selection)
*/
public async getAllUsers(): Promise<UserInfo[]> {
let dao: InstanceType<(typeof import('./bip-users-dao'))['BIPUsersDAO']> | null = null
try {
const { BIPUsersDAO } = await import('./bip-users-dao')
const dao = new BIPUsersDAO()
dao = new BIPUsersDAO()
return await dao.getAllUsers()
} catch (error) {
console.error('[SessionManager] Get all users error:', error)
return []
} finally {
if (dao) {
await dao.disconnect().catch((error) => {
console.error('[SessionManager] Get all users disconnect error:', error)
})
}
}
}
}