mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-22 00:13:09 +08:00
Some checks failed
Auto I18N Weekly / Auto I18N (push) Has been cancelled
* refactor: optimize DatabaseManager and fix libsql crash issues Major improvements: - Created DatabaseManager singleton to centralize database connection management - Auto-initialize database in constructor (no manual initialization needed) - Removed all manual initialize() and ensureInitialized() calls (47 occurrences) - Simplified initialization logic (removed retry loops that could cause crashes) - Removed unused close() and reinitialize() methods - Reduced code from ~270 lines to 172 lines (-36%) Key changes: 1. DatabaseManager.ts (new file): - Singleton pattern with auto-initialization - State management (INITIALIZING, INITIALIZED, FAILED) - Windows compatibility fixes (empty file detection, intMode: 'number') - Simplified waitForInitialization() logic 2. BaseService.ts: - Removed static initialize() and ensureInitialized() methods - Simplified database/rawClient getters to use DatabaseManager 3. Service classes (AgentService, SessionService, SessionMessageService): - Removed all initialize() methods - Removed all ensureInitialized() calls - Services now work out of the box 4. Main entry points (index.ts, server.ts): - Removed explicit database initialization calls - Database initializes automatically on first access Benefits: - Fixes Windows libsql crashes by removing dangerous retry logic - Simpler API - no need to remember to call initialize() - Better separation of concerns - Cleaner codebase with 36% less code 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: wait for database initialization on app startup Issue: "Database is still initializing" error on startup Root cause: Synchronous database getter was called before async initialization completed Solution: - Explicitly wait for database initialization in main index.ts - Import DatabaseManager and call getDatabase() to ensure initialization is complete - This guarantees database is ready before any service methods are called Changes: - src/main/index.ts: Added explicit database initialization wait before API server check 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: use static import for getDatabaseManager - Move import to top of file for better code organization - Remove unnecessary dynamic import 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: streamline database access in service classes - Replaced direct database access with asynchronous calls to getDatabase() in various service classes (AgentService, SessionService, SessionMessageService). - Updated the main index.ts to utilize runAsyncFunction for API server initialization, ensuring proper handling of asynchronous database access. - Improved code organization and readability by consolidating database access logic. This change enhances the reliability of database interactions across the application and ensures that services are correctly initialized before use. * refactor: remove redundant logging in ApiServer initialization - Removed the logging statement for 'AgentService ready' during server initialization. - This change streamlines the startup process by eliminating unnecessary log entries. This update contributes to cleaner logs and improved readability during server startup. * refactor: change getDatabase method to synchronous return type - Updated the getDatabase method in DatabaseManager to return a synchronous LibSQLDatabase instance instead of a Promise. - This change simplifies the database access pattern, aligning with the current initialization logic. This refactor enhances code clarity and reduces unnecessary asynchronous handling in the database access layer. * refactor: simplify sessionMessageRepository by removing transaction handling - Removed transaction handling parameters from message persistence methods in sessionMessageRepository. - Updated database access to use a direct call to getDatabase() instead of passing a transaction client. - Streamlined the upsertMessage and persistExchange methods for improved clarity and reduced complexity. This refactor enhances code readability and simplifies the database interaction logic. --------- Co-authored-by: Claude <noreply@anthropic.com>
97 lines
2.6 KiB
TypeScript
97 lines
2.6 KiB
TypeScript
import { createServer } from 'node:http'
|
|
|
|
import { loggerService } from '@logger'
|
|
import { IpcChannel } from '@shared/IpcChannel'
|
|
|
|
import { windowService } from '../services/WindowService'
|
|
import { app } from './app'
|
|
import { config } from './config'
|
|
|
|
const logger = loggerService.withContext('ApiServer')
|
|
|
|
const GLOBAL_REQUEST_TIMEOUT_MS = 5 * 60_000
|
|
const GLOBAL_HEADERS_TIMEOUT_MS = GLOBAL_REQUEST_TIMEOUT_MS + 5_000
|
|
const GLOBAL_KEEPALIVE_TIMEOUT_MS = 60_000
|
|
|
|
export class ApiServer {
|
|
private server: ReturnType<typeof createServer> | null = null
|
|
|
|
async start(): Promise<void> {
|
|
if (this.server && this.server.listening) {
|
|
logger.warn('Server already running')
|
|
return
|
|
}
|
|
|
|
// Clean up any failed server instance
|
|
if (this.server && !this.server.listening) {
|
|
logger.warn('Cleaning up failed server instance')
|
|
this.server = null
|
|
}
|
|
|
|
// Load config
|
|
const { port, host } = await config.load()
|
|
|
|
// Create server with Express app
|
|
this.server = createServer(app)
|
|
this.applyServerTimeouts(this.server)
|
|
|
|
// Start server
|
|
return new Promise((resolve, reject) => {
|
|
this.server!.listen(port, host, () => {
|
|
logger.info('API server started', { host, port })
|
|
|
|
// Notify renderer that API server is ready
|
|
const mainWindow = windowService.getMainWindow()
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.webContents.send(IpcChannel.ApiServer_Ready)
|
|
}
|
|
|
|
resolve()
|
|
})
|
|
|
|
this.server!.on('error', (error) => {
|
|
// Clean up the server instance if listen fails
|
|
this.server = null
|
|
reject(error)
|
|
})
|
|
})
|
|
}
|
|
|
|
private applyServerTimeouts(server: ReturnType<typeof createServer>): void {
|
|
server.requestTimeout = GLOBAL_REQUEST_TIMEOUT_MS
|
|
server.headersTimeout = Math.max(GLOBAL_HEADERS_TIMEOUT_MS, server.requestTimeout + 1_000)
|
|
server.keepAliveTimeout = GLOBAL_KEEPALIVE_TIMEOUT_MS
|
|
server.setTimeout(0)
|
|
}
|
|
|
|
async stop(): Promise<void> {
|
|
if (!this.server) return
|
|
|
|
return new Promise((resolve) => {
|
|
this.server!.close(() => {
|
|
logger.info('API server stopped')
|
|
this.server = null
|
|
resolve()
|
|
})
|
|
})
|
|
}
|
|
|
|
async restart(): Promise<void> {
|
|
await this.stop()
|
|
await config.reload()
|
|
await this.start()
|
|
}
|
|
|
|
isRunning(): boolean {
|
|
const hasServer = this.server !== null
|
|
const isListening = this.server?.listening || false
|
|
const result = hasServer && isListening
|
|
|
|
logger.debug('isRunning check', { hasServer, isListening, result })
|
|
|
|
return result
|
|
}
|
|
}
|
|
|
|
export const apiServer = new ApiServer()
|