diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3efb303..d85723d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,6 +42,7 @@ Enhancement suggestions are tracked as GitHub issues. When creating an enhanceme ## Development Process 1. **Set up your development environment:** + ```bash git clone https://github.com/YOUR_USERNAME/data-pup.git cd data-pup @@ -56,6 +57,7 @@ Enhancement suggestions are tracked as GitHub issues. When creating an enhanceme - Update documentation as needed 3. **Test your changes:** + ```bash npm run build npm run preview @@ -66,6 +68,18 @@ Enhancement suggestions are tracked as GitHub issues. When creating an enhanceme - Include the relevant issue number if applicable - Screenshots/GIFs for UI changes are helpful +## Logging Usage + +This project uses [electron-log](https://github.com/megahertz/electron-log) for logging purposes. + +The logger is initialized in `src/main/utils/logger.ts` + +The logs are written to the following file locations: + +- **on Linux** : `~/.config/DataPup/logs/[YYYY-MM-DD].log` +- **on macOS** : `~/Library/Logs/DataPup/[YYYY-MM-DD].log` +- **on Windows** : `%USERPROFILE%\AppData\Roaming\DataPup\logs\[YYYY-MM-DD].log` + ## Style Guide ### Git Commit Messages @@ -119,4 +133,4 @@ By contributing, you agree that your contributions will be licensed under the MI --- -Thank you for contributing to Data-Pup! 🐶✨ \ No newline at end of file +Thank you for contributing to Data-Pup! 🐶✨ diff --git a/package.json b/package.json index 975b1a2..d5ec65b 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "@types/react-syntax-highlighter": "^15.5.13", "@types/uuid": "^10.0.0", "better-sqlite3": "^12.2.0", + "electron-log": "^5.4.2", "framer-motion": "^12.23.0", "langchain": "^0.3.30", "monaco-editor": "^0.52.2", diff --git a/src/main/database/manager.ts b/src/main/database/manager.ts index b8a789a..8e7ca44 100644 --- a/src/main/database/manager.ts +++ b/src/main/database/manager.ts @@ -14,7 +14,7 @@ import { TableQueryOptions } from './interface' import { DatabaseManagerFactory } from './factory' - +import { logger } from '../utils/logger' class DatabaseManager { private factory: DatabaseManagerFactory private activeConnection: { id: string; type: string; manager: DatabaseManagerInterface } | null = @@ -62,7 +62,7 @@ class DatabaseManager { return result } catch (error) { - console.error('Database test connection error:', error) + logger.error('Database test connection error:', error) return { success: false, message: 'Failed to test connection', @@ -111,7 +111,7 @@ class DatabaseManager { return result } catch (error) { - console.error('Database connection error:', error) + logger.error('Database connection error:', error) return { success: false, message: 'Failed to connect to database', @@ -136,7 +136,7 @@ class DatabaseManager { return result } catch (error) { - console.error('Database disconnection error:', error) + logger.error('Database disconnection error:', error) return { success: false, message: 'Failed to disconnect from database' @@ -157,7 +157,7 @@ class DatabaseManager { // Execute query using the specific manager return await this.activeConnection.manager.query(connectionId, sql, sessionId) } catch (error) { - console.error('Database query error:', error) + logger.error('Database query error:', error) return { success: false, message: 'Query execution failed', @@ -180,7 +180,7 @@ class DatabaseManager { return await this.activeConnection.manager.cancelQuery(connectionId, queryId) } catch (error) { - console.error('Query cancellation error:', error) + logger.error('Query cancellation error:', error) return { success: false, message: error instanceof Error ? error.message : 'Failed to cancel query' @@ -204,7 +204,7 @@ class DatabaseManager { return await this.activeConnection.manager.queryTable(connectionId, options, sessionId) } catch (error) { - console.error('Table query error:', error) + logger.error('Table query error:', error) return { success: false, message: 'Table query execution failed', @@ -226,7 +226,7 @@ class DatabaseManager { return await this.activeConnection.manager.getDatabases(connectionId) } catch (error) { - console.error('Error getting databases:', error) + logger.error('Error getting databases:', error) return { success: false, message: 'Failed to get databases' @@ -248,7 +248,7 @@ class DatabaseManager { return await this.activeConnection.manager.getTables(connectionId, database) } catch (error) { - console.error('Error getting tables:', error) + logger.error('Error getting tables:', error) return { success: false, message: 'Failed to get tables' @@ -271,7 +271,7 @@ class DatabaseManager { return await this.activeConnection.manager.getTableSchema(connectionId, tableName, database) } catch (error) { - console.error('Error getting table schema:', error) + logger.error('Error getting table schema:', error) return { success: false, message: 'Failed to get table schema' @@ -317,7 +317,7 @@ class DatabaseManager { const connections = manager.getAllConnections() allConnections.push(...connections) } catch (error) { - console.error(`Error getting connections from ${dbType} manager:`, error) + logger.error(`Error getting connections from ${dbType} manager:`, error) } } } @@ -362,7 +362,7 @@ class DatabaseManager { return await this.activeConnection.manager.insertRow(connectionId, table, data, database) } catch (error) { - console.error('Insert error:', error) + logger.error('Insert error:', error) return { success: false, message: 'Insert operation failed', @@ -396,7 +396,7 @@ class DatabaseManager { database ) } catch (error) { - console.error('Update error:', error) + logger.error('Update error:', error) return { success: false, message: 'Update operation failed', @@ -429,7 +429,7 @@ class DatabaseManager { database ) } catch (error) { - console.error('Delete error:', error) + logger.error('Delete error:', error) return { success: false, message: 'Delete operation failed', @@ -458,7 +458,7 @@ class DatabaseManager { database ) } catch (error) { - console.error('Get table full schema error:', error) + logger.error('Get table full schema error:', error) return { success: false, message: error instanceof Error ? error.message : 'Failed to get table schema' diff --git a/src/main/database/postgresql.ts b/src/main/database/postgresql.ts index 86e9ce6..79e825a 100644 --- a/src/main/database/postgresql.ts +++ b/src/main/database/postgresql.ts @@ -10,7 +10,7 @@ import { TableQueryOptions, TableFilter } from './interface' - +import { logger } from '../utils/logger' interface PostgreSQLConfig { host: string port: number @@ -96,7 +96,7 @@ class PostgreSQLManager extends BaseDatabaseManager { await connection.client.end() connection.isConnected = false } catch (error) { - console.error('Error disconnecting from PostgreSQL:', error) + logger.error('Error disconnecting from PostgreSQL:', error) } finally { this.connections.delete(connectionId) } @@ -136,17 +136,13 @@ class PostgreSQLManager extends BaseDatabaseManager { return String(value) } - async query( - connectionId: string, - sql: string, - queryId?: string - ): Promise { - console.log('PostgreSQL query called with connectionId:', connectionId) - console.log('Available connections:', Array.from(this.connections.keys())) - + async query(connectionId: string, sql: string, queryId?: string): Promise { + logger.info('PostgreSQL query called with connectionId:', connectionId) + logger.info('Available connections:', Array.from(this.connections.keys())) + const connection = this.connections.get(connectionId) if (!connection || !connection.isConnected) { - console.log('Connection not found or not connected:', connection) + logger.info('Connection not found or not connected:', connection) throw new Error('No active PostgreSQL connection') } @@ -167,7 +163,7 @@ class PostgreSQLManager extends BaseDatabaseManager { try { const result = await connection.client.query(sql) - + return { success: true, data: result.rows || [], @@ -203,70 +199,84 @@ class PostgreSQLManager extends BaseDatabaseManager { return typeMap[typeId] || 'unknown' } - async getDatabases(connectionId: string): Promise<{ success: boolean; databases?: string[]; message: string }> { - console.log('PostgreSQL getDatabases called for connectionId:', connectionId) - + async getDatabases( + connectionId: string + ): Promise<{ success: boolean; databases?: string[]; message: string }> { + logger.info('PostgreSQL getDatabases called for connectionId:', connectionId) + const result = await this.query( connectionId, 'SELECT datname FROM pg_database WHERE datistemplate = false ORDER BY datname' ) - - console.log('PostgreSQL getDatabases query result:', result) - + + logger.info('PostgreSQL getDatabases query result:', result) + if (result.success && result.data) { const databases = result.data.map((row: any) => row.datname) - console.log('PostgreSQL databases found:', databases) + logger.info('PostgreSQL databases found:', databases) return { success: true, databases, message: `Found ${databases.length} databases` } } - console.log('PostgreSQL getDatabases failed:', result.error) + logger.info('PostgreSQL getDatabases failed:', result.error) return { success: false, message: result.error || 'Failed to get databases' } } - async getTables(connectionId: string, database?: string): Promise<{ success: boolean; tables?: string[]; message: string }> { - console.log('PostgreSQL getTables called for connectionId:', connectionId, 'database:', database) - + async getTables( + connectionId: string, + database?: string + ): Promise<{ success: boolean; tables?: string[]; message: string }> { + logger.info( + 'PostgreSQL getTables called for connectionId:', + connectionId, + 'database:', + database + ) + const result = await this.query( connectionId, - `SELECT tablename FROM pg_tables - WHERE schemaname = 'public' + `SELECT tablename FROM pg_tables + WHERE schemaname = 'public' ORDER BY tablename` ) - - console.log('PostgreSQL getTables query result:', result) - + + logger.info('PostgreSQL getTables query result:', result) + if (result.success && result.data) { const tables = result.data.map((row: any) => row.tablename) - console.log('PostgreSQL tables found:', tables) + logger.info('PostgreSQL tables found:', tables) return { success: true, tables, message: `Found ${tables.length} tables` } } - console.log('PostgreSQL getTables failed:', result.error) + logger.info('PostgreSQL getTables failed:', result.error) return { success: false, message: result.error || 'Failed to get tables' } } - async getTableSchema(connectionId: string, tableName: string, database?: string): Promise<{ success: boolean; schema?: any[]; message: string }> { + async getTableSchema( + connectionId: string, + tableName: string, + database?: string + ): Promise<{ success: boolean; schema?: any[]; message: string }> { const result = await this.query( connectionId, - `SELECT + `SELECT column_name, data_type, is_nullable, column_default - FROM information_schema.columns - WHERE table_name = '${tableName}' + FROM information_schema.columns + WHERE table_name = '${tableName}' AND table_schema = 'public' ORDER BY ordinal_position` ) @@ -292,18 +302,22 @@ class PostgreSQLManager extends BaseDatabaseManager { } } - async getTableFullSchema(connectionId: string, tableName: string, database?: string): Promise<{ success: boolean; schema?: TableSchema; message: string }> { + async getTableFullSchema( + connectionId: string, + tableName: string, + database?: string + ): Promise<{ success: boolean; schema?: TableSchema; message: string }> { try { // Get column information const columnResult = await this.query( connectionId, - `SELECT + `SELECT column_name, data_type, is_nullable, column_default - FROM information_schema.columns - WHERE table_name = '${tableName}' + FROM information_schema.columns + WHERE table_name = '${tableName}' AND table_schema = 'public' ORDER BY ordinal_position` ) @@ -331,8 +345,8 @@ class PostgreSQLManager extends BaseDatabaseManager { WHERE i.indrelid = '${tableName}'::regclass AND i.indisprimary` ) - const primaryKeys: string[] = pkResult.success && pkResult.data ? - pkResult.data.map((row: any) => row.attname) : [] + const primaryKeys: string[] = + pkResult.success && pkResult.data ? pkResult.data.map((row: any) => row.attname) : [] const schema: TableSchema = { columns, @@ -358,13 +372,20 @@ class PostgreSQLManager extends BaseDatabaseManager { options: TableQueryOptions, sessionId?: string ): Promise { - console.log('PostgreSQL queryTable called with options:', options) + logger.info('PostgreSQL queryTable called with options:', options) const { database, table, filters, orderBy, limit, offset } = options // In PostgreSQL, ignore the 'database' parameter and always use 'public' schema // The database is already selected in the connection, tables are in schemas const schema = 'public' - console.log('PostgreSQL queryTable - database param:', database, 'using schema:', schema, 'table:', table) + logger.info( + 'PostgreSQL queryTable - database param:', + database, + 'using schema:', + schema, + 'table:', + table + ) const qualifiedTable = `${this.escapeIdentifier(schema)}.${this.escapeIdentifier(table)}` let sql = `SELECT * FROM ${qualifiedTable}` @@ -379,7 +400,9 @@ class PostgreSQLManager extends BaseDatabaseManager { // Add ORDER BY clause if (orderBy && orderBy.length > 0) { - const orderClauses = orderBy.map((o) => `${this.escapeIdentifier(o.column)} ${o.direction.toUpperCase()}`) + const orderClauses = orderBy.map( + (o) => `${this.escapeIdentifier(o.column)} ${o.direction.toUpperCase()}` + ) sql += ` ORDER BY ${orderClauses.join(', ')}` } @@ -391,7 +414,7 @@ class PostgreSQLManager extends BaseDatabaseManager { sql += ` OFFSET ${offset}` } - console.log('PostgreSQL queryTable SQL:', sql) + logger.info('PostgreSQL queryTable SQL:', sql) return this.query(connectionId, sql, sessionId) } @@ -413,7 +436,7 @@ class PostgreSQLManager extends BaseDatabaseManager { case 'IN': case 'NOT IN': if (Array.isArray(value)) { - const values = value.map(v => this.escapeValue(v)).join(', ') + const values = value.map((v) => this.escapeValue(v)).join(', ') return `${escapedColumn} ${operator} (${values})` } return `${escapedColumn} ${operator} (${this.escapeValue(value)})` @@ -434,4 +457,4 @@ class PostgreSQLManager extends BaseDatabaseManager { } } -export { PostgreSQLManager } \ No newline at end of file +export { PostgreSQLManager } diff --git a/src/main/index.ts b/src/main/index.ts index ca993c6..8386e4c 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,12 +1,14 @@ import { app, BrowserWindow, ipcMain, shell } from 'electron' import { join } from 'path' +import * as fs from 'fs' import { electronApp, optimizer, is } from '@electron-toolkit/utils' + import { SecureStorage, DatabaseConnection } from './secureStorage' import { DatabaseManager } from './database/manager' import { DatabaseConfig, TableQueryOptions } from './database/interface' import { LangChainAgent } from './llm/langchainAgent' import QueryHistoryService from './services/QueryHistoryService' -import * as fs from 'fs' +import { logger } from './utils/logger' function createWindow(): void { const iconPath = is.dev @@ -87,11 +89,11 @@ app.on('window-all-closed', () => { // IPC handlers for database operations ipcMain.handle('db:testConnection', async (_, connectionConfig) => { try { - console.log('Testing connection with config:', connectionConfig) + logger.info('Testing connection with config:', connectionConfig) const result = await databaseManager.testConnection(connectionConfig as DatabaseConfig) return result } catch (error) { - console.error('Test connection error:', error) + logger.error('Test connection error:', error) return { success: false, message: 'Test connection failed', @@ -102,8 +104,8 @@ ipcMain.handle('db:testConnection', async (_, connectionConfig) => { ipcMain.handle('db:connect', async (_, connectionConfig) => { try { - console.log('Main process received connection config:', connectionConfig) - console.log('Secure flag in config:', connectionConfig.secure) + logger.info('Main process received connection config:', connectionConfig) + logger.info('Secure flag in config:', connectionConfig.secure) // Generate a unique ID for the connection const connectionId = `conn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` @@ -148,7 +150,7 @@ ipcMain.handle('db:connect', async (_, connectionConfig) => { } } } catch (error) { - console.error('Connection error:', error) + logger.error('Connection error:', error) return { success: false, message: 'Connection failed', @@ -168,7 +170,7 @@ ipcMain.handle('db:disconnect', async (_, connectionId?: string) => { return { success: true, message: 'All connections closed' } } } catch (error) { - console.error('Disconnection error:', error) + logger.error('Disconnection error:', error) return { success: false, message: 'Failed to disconnect', @@ -200,7 +202,7 @@ ipcMain.handle('db:query', async (_, connectionId: string, query: string, sessio return result } catch (error) { - console.error('Query execution error:', error) + logger.error('Query execution error:', error) const errorMessage = error instanceof Error ? error.message : 'Unknown error' // Also log failed queries @@ -233,7 +235,7 @@ ipcMain.handle( const result = await databaseManager.queryTable(connectionId, options, sessionId) return result } catch (error) { - console.error('Table query execution error:', error) + logger.error('Table query execution error:', error) return { success: false, message: 'Table query execution failed', @@ -248,7 +250,7 @@ ipcMain.handle('db:cancelQuery', async (_, connectionId: string, queryId: string const result = await databaseManager.cancelQuery(connectionId, queryId) return result } catch (error) { - console.error('Query cancellation error:', error) + logger.error('Query cancellation error:', error) return { success: false, message: 'Failed to cancel query', @@ -263,7 +265,7 @@ ipcMain.handle('connections:getAll', async () => { const connections = secureStorage.getConnections() return { success: true, connections } } catch (error) { - console.error('Error getting connections:', error) + logger.error('Error getting connections:', error) return { success: false, connections: [] } } }) @@ -273,7 +275,7 @@ ipcMain.handle('connections:getById', async (_, id: string) => { const connection = secureStorage.getConnection(id) return { success: true, connection } } catch (error) { - console.error('Error getting connection:', error) + logger.error('Error getting connection:', error) return { success: false, connection: null } } }) @@ -283,7 +285,7 @@ ipcMain.handle('connections:delete', async (_, id: string) => { const deleted = secureStorage.deleteConnection(id) return { success: deleted } } catch (error) { - console.error('Error deleting connection:', error) + logger.error('Error deleting connection:', error) return { success: false } } }) @@ -293,7 +295,7 @@ ipcMain.handle('connections:updateLastUsed', async (_, id: string) => { secureStorage.updateLastUsed(id) return { success: true } } catch (error) { - console.error('Error updating last used:', error) + logger.error('Error updating last used:', error) return { success: false } } }) @@ -303,7 +305,7 @@ ipcMain.handle('connections:update', async (_, id: string, updates: any) => { const updated = secureStorage.updateConnection(id, updates) return { success: updated } } catch (error) { - console.error('Error updating connection:', error) + logger.error('Error updating connection:', error) return { success: false, message: error instanceof Error ? error.message : 'Unknown error' } } }) @@ -314,7 +316,7 @@ ipcMain.handle('db:getDatabases', async (_, connectionId: string) => { const result = await databaseManager.getDatabases(connectionId) return result } catch (error) { - console.error('Error getting databases:', error) + logger.error('Error getting databases:', error) return { success: false, message: 'Failed to get databases', @@ -328,7 +330,7 @@ ipcMain.handle('db:getTables', async (_, connectionId: string, database?: string const result = await databaseManager.getTables(connectionId, database) return result } catch (error) { - console.error('Error getting tables:', error) + logger.error('Error getting tables:', error) return { success: false, message: 'Failed to get tables', @@ -344,7 +346,7 @@ ipcMain.handle( const result = await databaseManager.getTableSchema(connectionId, tableName, database) return result } catch (error) { - console.error('Error getting table schema:', error) + logger.error('Error getting table schema:', error) return { success: false, message: 'Failed to get table schema', @@ -359,7 +361,7 @@ ipcMain.handle('db:isConnected', async (_, connectionId: string) => { const isConnected = databaseManager.isConnected(connectionId) return { success: true, isConnected } } catch (error) { - console.error('Error checking connection status:', error) + logger.error('Error checking connection status:', error) return { success: false, isConnected: false } } }) @@ -369,7 +371,7 @@ ipcMain.handle('db:isReadOnly', async (_, connectionId: string) => { const isReadOnly = databaseManager.isReadOnly(connectionId) return { success: true, isReadOnly } } catch (error) { - console.error('Error checking read-only status:', error) + logger.error('Error checking read-only status:', error) return { success: false, isReadOnly: false } } }) @@ -378,7 +380,7 @@ ipcMain.handle('db:supportsTransactions', async (_, connectionId: string) => { try { return await databaseManager.supportsTransactions(connectionId) } catch (error) { - console.error('Error checking transaction support:', error) + logger.error('Error checking transaction support:', error) return false } }) @@ -387,7 +389,7 @@ ipcMain.handle('db:executeBulkOperations', async (_, connectionId: string, opera try { return await databaseManager.executeBulkOperations(connectionId, operations) } catch (error) { - console.error('Bulk operations error:', error) + logger.error('Bulk operations error:', error) return { success: false, results: [], @@ -402,7 +404,7 @@ ipcMain.handle( try { return await databaseManager.getPrimaryKeys(connectionId, table, database) } catch (error) { - console.error('Error getting primary keys:', error) + logger.error('Error getting primary keys:', error) return [] } } @@ -413,7 +415,7 @@ ipcMain.handle('db:getSupportedTypes', async () => { const supportedTypes = databaseManager.getSupportedDatabaseTypes() return { success: true, types: supportedTypes } } catch (error) { - console.error('Error getting supported database types:', error) + logger.error('Error getting supported database types:', error) return { success: false, types: [] } } }) @@ -423,7 +425,7 @@ ipcMain.handle('db:getAllConnections', async () => { const connections = databaseManager.getAllConnections() return { success: true, connections } } catch (error) { - console.error('Error getting all connections:', error) + logger.error('Error getting all connections:', error) return { success: false, connections: [] } } }) @@ -433,7 +435,7 @@ ipcMain.handle('db:getConnectionInfo', async (_, connectionId: string) => { const info = databaseManager.getConnectionInfo(connectionId) return { success: true, info } } catch (error) { - console.error('Error getting connection info:', error) + logger.error('Error getting connection info:', error) return { success: false, info: null } } }) @@ -441,12 +443,12 @@ ipcMain.handle('db:getConnectionInfo', async (_, connectionId: string) => { // IPC handler for AI processing ipcMain.handle('ai:process', async (_, request) => { try { - console.log('Processing AI query:', request.query) + logger.info('Processing AI query:', request.query) const result = await aiAgent.processQuery(request) - console.log('AI query result:', result) + logger.info('AI query result:', result) return result } catch (error) { - console.error('AI query error:', error) + logger.error('AI query error:', error) return { success: false, error: error instanceof Error ? error.message : 'Unknown error occurred' @@ -460,7 +462,7 @@ ipcMain.handle('secureStorage:get', async (_, key: string) => { const value = secureStorage.get(key) return { success: true, value } } catch (error) { - console.error('Error getting from secure storage:', error) + logger.error('Error getting from secure storage:', error) return { success: false, value: null } } }) @@ -470,7 +472,7 @@ ipcMain.handle('secureStorage:set', async (_, key: string, value: string) => { secureStorage.set(key, value) return { success: true } } catch (error) { - console.error('Error setting in secure storage:', error) + logger.error('Error setting in secure storage:', error) return { success: false } } }) @@ -480,7 +482,7 @@ ipcMain.handle('secureStorage:delete', async (_, key: string) => { secureStorage.delete(key) return { success: true } } catch (error) { - console.error('Error deleting from secure storage:', error) + logger.error('Error deleting from secure storage:', error) return { success: false } } }) @@ -491,7 +493,7 @@ ipcMain.handle('query-history:get', async (_, filter) => { const history = queryHistoryService.getQueryHistory(filter) return { success: true, history } } catch (error) { - console.error('Error getting query history:', error) + logger.error('Error getting query history:', error) return { success: false, history: [] } } }) @@ -501,7 +503,7 @@ ipcMain.handle('query-history:clear', async (_, connectionId?: string) => { const deletedCount = queryHistoryService.clearHistory(connectionId) return { success: true, deletedCount } } catch (error) { - console.error('Error clearing query history:', error) + logger.error('Error clearing query history:', error) return { success: false, deletedCount: 0 } } }) @@ -511,7 +513,7 @@ ipcMain.handle('query-history:delete', async (_, id: number) => { const deleted = queryHistoryService.deleteHistoryEntry(id) return { success: deleted } } catch (error) { - console.error('Error deleting query history entry:', error) + logger.error('Error deleting query history entry:', error) return { success: false } } }) @@ -521,7 +523,7 @@ ipcMain.handle('query-history:statistics', async (_, connectionId?: string) => { const stats = queryHistoryService.getStatistics(connectionId) return { success: true, stats } } catch (error) { - console.error('Error getting query statistics:', error) + logger.error('Error getting query statistics:', error) return { success: false, stats: null } } }) @@ -532,7 +534,7 @@ ipcMain.handle('saved-queries:save', async (_, query) => { const id = queryHistoryService.saveQuery(query) return { success: true, id } } catch (error) { - console.error('Error saving query:', error) + logger.error('Error saving query:', error) return { success: false, id: null } } }) @@ -542,7 +544,7 @@ ipcMain.handle('saved-queries:get', async (_, filter) => { const queries = queryHistoryService.getSavedQueries(filter) return { success: true, queries } } catch (error) { - console.error('Error getting saved queries:', error) + logger.error('Error getting saved queries:', error) return { success: false, queries: [] } } }) @@ -552,7 +554,7 @@ ipcMain.handle('saved-queries:update', async (_, id: number, updates) => { const updated = queryHistoryService.updateSavedQuery(id, updates) return { success: updated } } catch (error) { - console.error('Error updating saved query:', error) + logger.error('Error updating saved query:', error) return { success: false } } }) @@ -562,7 +564,7 @@ ipcMain.handle('saved-queries:delete', async (_, id: number) => { const deleted = queryHistoryService.deleteSavedQuery(id) return { success: deleted } } catch (error) { - console.error('Error deleting saved query:', error) + logger.error('Error deleting saved query:', error) return { success: false } } }) @@ -575,7 +577,7 @@ ipcMain.handle( const result = await databaseManager.getTableFullSchema(connectionId, tableName, database) return result } catch (error) { - console.error('Error getting table full schema:', error) + logger.error('Error getting table full schema:', error) return { success: false, message: 'Failed to get table full schema', @@ -592,7 +594,7 @@ ipcMain.handle( const result = await databaseManager.insertRow(connectionId, table, data, database) return result } catch (error) { - console.error('Error inserting row:', error) + logger.error('Error inserting row:', error) return { success: false, message: 'Failed to insert row', @@ -622,7 +624,7 @@ ipcMain.handle( ) return result } catch (error) { - console.error('Error updating row:', error) + logger.error('Error updating row:', error) return { success: false, message: 'Failed to update row', @@ -645,7 +647,7 @@ ipcMain.handle( const result = await databaseManager.deleteRow(connectionId, table, primaryKey, database) return result } catch (error) { - console.error('Error deleting row:', error) + logger.error('Error deleting row:', error) return { success: false, message: 'Failed to delete row', diff --git a/src/main/llm/langchainAgent.ts b/src/main/llm/langchainAgent.ts index ccf6267..460ac17 100644 --- a/src/main/llm/langchainAgent.ts +++ b/src/main/llm/langchainAgent.ts @@ -11,7 +11,7 @@ import { SecureStorage } from '../secureStorage' import { AITools } from './tools' import { logger } from '../utils/logger' import { BrowserWindow } from 'electron' - +import { logger } from '../utils/logger' interface AgentRequest { connectionId: string query: string diff --git a/src/main/llm/tools/schemaIntrospector.ts b/src/main/llm/tools/schemaIntrospector.ts index b441147..b214c0f 100644 --- a/src/main/llm/tools/schemaIntrospector.ts +++ b/src/main/llm/tools/schemaIntrospector.ts @@ -1,6 +1,6 @@ import { DatabaseManager } from '../../database/manager' import { DatabaseSchema, TableSchema, ColumnSchema } from '../interface' - +import { logger } from '../../utils/logger' class SchemaIntrospector { private databaseManager: DatabaseManager @@ -54,7 +54,7 @@ class SchemaIntrospector { tables } } catch (error) { - console.error('Error getting database schema:', error) + logger.error('Error getting database schema:', error) return null } } @@ -77,7 +77,7 @@ class SchemaIntrospector { } } } catch (error) { - console.error('Error getting sample data:', error) + logger.error('Error getting sample data:', error) } return sampleData @@ -99,7 +99,7 @@ class SchemaIntrospector { // based on table names and the natural language query return tablesResult.tables } catch (error) { - console.error('Error getting relevant tables:', error) + logger.error('Error getting relevant tables:', error) return [] } } diff --git a/src/main/secureStorage.ts b/src/main/secureStorage.ts index 531d4a8..1f5c631 100644 --- a/src/main/secureStorage.ts +++ b/src/main/secureStorage.ts @@ -2,7 +2,7 @@ import { app } from 'electron' import { join } from 'path' import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs' import { createHash, createCipheriv, createDecipheriv, randomBytes } from 'crypto' - +import { logger } from './utils/logger' interface DatabaseConnection { id: string name: string @@ -91,7 +91,7 @@ class SecureStorage { const data = readFileSync(this.storagePath, 'utf8') return JSON.parse(data) } catch (error) { - console.error('Error loading connections:', error) + logger.error('Error loading connections:', error) return [] } } @@ -100,7 +100,7 @@ class SecureStorage { try { writeFileSync(this.storagePath, JSON.stringify(connections, null, 2), 'utf8') } catch (error) { - console.error('Error saving connections:', error) + logger.error('Error saving connections:', error) throw error } } @@ -221,7 +221,7 @@ class SecureStorage { const decrypted = this.decrypt(encrypted) return testData === decrypted } catch (error) { - console.error('Encryption test failed:', error) + logger.error('Encryption test failed:', error) return false } } @@ -247,7 +247,7 @@ class SecureStorage { return decryptedData } catch (error) { - console.error('Error loading generic storage:', error) + logger.error('Error loading generic storage:', error) return {} } } @@ -263,7 +263,7 @@ class SecureStorage { writeFileSync(path, JSON.stringify(encryptedData, null, 2), 'utf8') } catch (error) { - console.error('Error saving generic storage:', error) + logger.error('Error saving generic storage:', error) throw error } } diff --git a/src/main/utils/logger.ts b/src/main/utils/logger.ts index f53522a..bc8903f 100644 --- a/src/main/utils/logger.ts +++ b/src/main/utils/logger.ts @@ -1,9 +1,23 @@ +import log from 'electron-log/main' + export enum LogLevel { ERROR = 0, WARN = 1, INFO = 2, DEBUG = 3 } +const formatDate = () => { + const date = new Date() + const year = date.getFullYear() + const month = (date.getMonth() + 1).toString().padStart(2, '0') + const day = date.getDate().toString().padStart(2, '0') + return `${year}-${month}-${day}` +} + +log.initialize({ spyRendererConsole: true }) +log.transports.file.fileName = `${formatDate()}.log` +log.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}]' +log.transports.file.maxSize = 5 * 1024 * 1024 class Logger { private level: LogLevel = LogLevel.INFO @@ -23,25 +37,25 @@ class Logger { error(message: string, ...args: any[]) { if (this.level >= LogLevel.ERROR) { - console.error(`[ERROR] ${message}`, ...args) + log.error(`${message}`, ...args) } } warn(message: string, ...args: any[]) { if (this.level >= LogLevel.WARN) { - console.warn(`[WARN] ${message}`, ...args) + log.warn(`${message}`, ...args) } } info(message: string, ...args: any[]) { if (this.level >= LogLevel.INFO) { - console.log(`[INFO] ${message}`, ...args) + log.info(`${message}`, ...args) } } debug(message: string, ...args: any[]) { if (this.level >= LogLevel.DEBUG) { - console.log(`[DEBUG] ${message}`, ...args) + log.debug(`${message}`, ...args) } } } diff --git a/src/preload/index.ts b/src/preload/index.ts index 6314d91..989b751 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,6 +1,6 @@ import { contextBridge, ipcRenderer } from 'electron' import { electronAPI } from '@electron-toolkit/preload' - +import log from 'electron-log/node' // Custom APIs for renderer const api = { database: { @@ -95,15 +95,15 @@ const api = { // renderer only if context isolation is enabled, otherwise // just add to the DOM global. if (process.contextIsolated) { - console.log('context isolated') + log.info('context isolated') try { contextBridge.exposeInMainWorld('electron', electronAPI) contextBridge.exposeInMainWorld('api', api) } catch (error) { - console.error(error) + log.error(error) } } else { - console.log('not context isolated') + log.info('not context isolated') // @ts-ignore (define in dts) window.electron = electronAPI // @ts-ignore (define in dts) diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 54d81a0..b920c21 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -156,9 +156,7 @@ function App() { return [...prev, connection] } else { // Updated connection - return prev.map((conn) => - conn.id === connection.id ? { ...conn, ...connection } : conn - ) + return prev.map((conn) => (conn.id === connection.id ? { ...conn, ...connection } : conn)) } }) diff --git a/src/renderer/components/QueryWorkspace/QueryWorkspace.tsx b/src/renderer/components/QueryWorkspace/QueryWorkspace.tsx index 58e5726..7272673 100644 --- a/src/renderer/components/QueryWorkspace/QueryWorkspace.tsx +++ b/src/renderer/components/QueryWorkspace/QueryWorkspace.tsx @@ -35,11 +35,7 @@ interface QueryWorkspaceProps { } export const QueryWorkspace = forwardRef( - ({ - connectionId, - onOpenTableTab, - onRegisterNewTabHandler -}, ref) => { + ({ connectionId, onOpenTableTab, onRegisterNewTabHandler }, ref) => { const [tabs, setTabs] = useState([ { id: '1', @@ -96,18 +92,18 @@ export const QueryWorkspace = forwardRef( } }) - editor.addAction({ - id: 'new-query-tab', - label: 'New Query Tab', - keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyN], - contextMenuGroupId: 'navigation', - contextMenuOrder: 1.6, - run: () => { - if (newTabRef.current) { - newTabRef.current() + editor.addAction({ + id: 'new-query-tab', + label: 'New Query Tab', + keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyN], + contextMenuGroupId: 'navigation', + contextMenuOrder: 1.6, + run: () => { + if (newTabRef.current) { + newTabRef.current() + } } - } - }) + }) }, []) // Tab management functions @@ -184,17 +180,17 @@ export const QueryWorkspace = forwardRef( } }, [openTableTab, onOpenTableTab]) - // Update the ref whenever handleNewTab changes - useEffect(() => { - newTabRef.current = handleNewTab - }, [handleNewTab]) + // Update the ref whenever handleNewTab changes + useEffect(() => { + newTabRef.current = handleNewTab + }, [handleNewTab]) - // Register the new tab handler with parent component - useEffect(() => { - if (onRegisterNewTabHandler) { - onRegisterNewTabHandler(handleNewTab) - } - }, [handleNewTab, onRegisterNewTabHandler]) + // Register the new tab handler with parent component + useEffect(() => { + if (onRegisterNewTabHandler) { + onRegisterNewTabHandler(handleNewTab) + } + }, [handleNewTab, onRegisterNewTabHandler]) const executeQuery = useCallback( async (queryToExecute: string, forceUnlimited = false) => {