Compare commits
2 Commits
ae60273782
...
f112046178
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f112046178 | ||
|
|
319b5ec03b |
@@ -8,8 +8,8 @@
|
|||||||
"timeout": 30000
|
"timeout": 30000
|
||||||
},
|
},
|
||||||
"erp": {
|
"erp": {
|
||||||
"baseUrl": "https://68.11.34.30:8082",
|
"baseUrl": "",
|
||||||
"ignoreHttpsErrors": true
|
"ignoreHttpsErrors": false
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"tempDir": "./data/temp",
|
"tempDir": "./data/temp",
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ export class ConfigManager {
|
|||||||
return this.instance;
|
return this.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load default config
|
try {
|
||||||
|
// Load default config (minimal, no sensitive data)
|
||||||
const defaultConfig = this.getDefaultConfig();
|
const defaultConfig = this.getDefaultConfig();
|
||||||
|
|
||||||
// Load environment-specific config
|
// Load environment-specific config
|
||||||
@@ -19,28 +20,43 @@ export class ConfigManager {
|
|||||||
|
|
||||||
let envConfig: Partial<AppConfig> = {};
|
let envConfig: Partial<AppConfig> = {};
|
||||||
if (fs.existsSync(envConfigPath)) {
|
if (fs.existsSync(envConfigPath)) {
|
||||||
envConfig = JSON.parse(fs.readFileSync(envConfigPath, 'utf-8'));
|
try {
|
||||||
|
const envConfigContent = fs.readFileSync(envConfigPath, 'utf-8');
|
||||||
|
envConfig = JSON.parse(envConfigContent);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to load env config from ${envConfigPath}:`, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load environment variables
|
// Load environment variables (with validation)
|
||||||
const envVars: Partial<AppConfig> = this.loadFromEnv();
|
const envVars = this.loadFromEnv();
|
||||||
|
|
||||||
this.instance = {
|
// Deep merge configurations
|
||||||
...defaultConfig,
|
this.instance = this.deepMerge(defaultConfig, envConfig, envVars);
|
||||||
...envConfig,
|
|
||||||
...envVars,
|
// Validate required configuration
|
||||||
};
|
this.validateConfig(this.instance);
|
||||||
|
|
||||||
return this.instance;
|
return this.instance;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load configuration:', error);
|
||||||
|
throw new Error('Configuration loading failed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getDefaultConfig(): AppConfig {
|
private static getDefaultConfig(): AppConfig {
|
||||||
const appConfigPath = path.join(__dirname, '../../../config/app.json');
|
const appConfigPath = path.join(__dirname, '../../../config/app.json');
|
||||||
|
|
||||||
|
try {
|
||||||
if (fs.existsSync(appConfigPath)) {
|
if (fs.existsSync(appConfigPath)) {
|
||||||
return JSON.parse(fs.readFileSync(appConfigPath, 'utf-8'));
|
const configContent = fs.readFileSync(appConfigPath, 'utf-8');
|
||||||
|
return JSON.parse(configContent);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to load app.json, using minimal defaults:`, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback defaults
|
// Minimal fallback defaults (NO sensitive data)
|
||||||
return {
|
return {
|
||||||
appName: 'ERPAuto',
|
appName: 'ERPAuto',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
@@ -52,9 +68,9 @@ export class ConfigManager {
|
|||||||
},
|
},
|
||||||
databases: {
|
databases: {
|
||||||
sqlServer: {
|
sqlServer: {
|
||||||
server: '192.168.110.114',
|
server: '', // MUST be set via env var
|
||||||
database: 'CompanyDB',
|
database: '',
|
||||||
username: 'peng',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
driver: 'ODBC Driver 18 for SQL Server',
|
driver: 'ODBC Driver 18 for SQL Server',
|
||||||
trustServerCertificate: 'yes',
|
trustServerCertificate: 'yes',
|
||||||
@@ -62,14 +78,14 @@ export class ConfigManager {
|
|||||||
mysql: {
|
mysql: {
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: 3306,
|
port: 3306,
|
||||||
database: 'erp_db',
|
database: '',
|
||||||
username: 'root',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
erp: {
|
erp: {
|
||||||
baseUrl: 'https://68.11.34.30:8082',
|
baseUrl: '', // MUST be set via env var
|
||||||
ignoreHttpsErrors: true,
|
ignoreHttpsErrors: false,
|
||||||
},
|
},
|
||||||
paths: {
|
paths: {
|
||||||
tempDir: './data/temp',
|
tempDir: './data/temp',
|
||||||
@@ -80,24 +96,63 @@ export class ConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static loadFromEnv(): Partial<AppConfig> {
|
private static loadFromEnv(): Partial<AppConfig> {
|
||||||
return {
|
const sqlServer = {
|
||||||
databases: {
|
|
||||||
sqlServer: {
|
|
||||||
server: process.env.SQL_SERVER_SERVER || '',
|
server: process.env.SQL_SERVER_SERVER || '',
|
||||||
database: process.env.SQL_SERVER_DATABASE || '',
|
database: process.env.SQL_SERVER_DATABASE || '',
|
||||||
username: process.env.SQL_SERVER_USERNAME || '',
|
username: process.env.SQL_SERVER_USERNAME || '',
|
||||||
password: process.env.SQL_SERVER_PASSWORD || '',
|
password: process.env.SQL_SERVER_PASSWORD || '',
|
||||||
driver: process.env.SQL_SERVER_DRIVER || '',
|
driver: process.env.SQL_SERVER_DRIVER || 'ODBC Driver 18 for SQL Server',
|
||||||
trustServerCertificate: process.env.SQL_SERVER_TRUST_CERT || 'yes',
|
trustServerCertificate: process.env.SQL_SERVER_TRUST_CERT || 'yes',
|
||||||
},
|
};
|
||||||
mysql: {
|
|
||||||
|
const mysql = {
|
||||||
host: process.env.MYSQL_HOST || 'localhost',
|
host: process.env.MYSQL_HOST || 'localhost',
|
||||||
port: parseInt(process.env.MYSQL_PORT || '3306'),
|
port: parseInt(process.env.MYSQL_PORT || '3306'),
|
||||||
database: process.env.MYSQL_DATABASE || '',
|
database: process.env.MYSQL_DATABASE || '',
|
||||||
username: process.env.MYSQL_USERNAME || '',
|
username: process.env.MYSQL_USERNAME || '',
|
||||||
password: process.env.MYSQL_PASSWORD || '',
|
password: process.env.MYSQL_PASSWORD || '',
|
||||||
},
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
databases: {
|
||||||
|
sqlServer,
|
||||||
|
mysql,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static deepMerge(...configs: Partial<AppConfig>[]): AppConfig {
|
||||||
|
const result = configs[0] as AppConfig;
|
||||||
|
|
||||||
|
for (let i = 1; i < configs.length; i++) {
|
||||||
|
const config = configs[i];
|
||||||
|
for (const key in config) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(config, key)) {
|
||||||
|
const value = (config as any)[key];
|
||||||
|
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||||
|
(result as any)[key] = { ...(result as any)[key], ...value };
|
||||||
|
} else {
|
||||||
|
(result as any)[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static validateConfig(config: AppConfig): void {
|
||||||
|
// Validate critical configuration
|
||||||
|
if (!config.databases.sqlServer.server && process.env.NODE_ENV === 'production') {
|
||||||
|
throw new Error('SQL Server server address must be configured via environment variable');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.databases.sqlServer.database) {
|
||||||
|
throw new Error('SQL Server database name must be configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.erp.baseUrl) {
|
||||||
|
console.warn('Warning: ERP base URL not configured, ERP features will not work');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/main/models/logger.types.ts
Normal file
6
src/main/models/logger.types.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export interface LogEntry {
|
||||||
|
timestamp: string;
|
||||||
|
level: string;
|
||||||
|
message: string;
|
||||||
|
details?: any;
|
||||||
|
}
|
||||||
94
src/main/services/logger.service.ts
Normal file
94
src/main/services/logger.service.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import winston from 'winston';
|
||||||
|
import path from 'path';
|
||||||
|
import { app } from 'electron';
|
||||||
|
import { LogEntry } from '../models/logger.types';
|
||||||
|
|
||||||
|
export class LoggerService {
|
||||||
|
private static instance: winston.Logger | null = null;
|
||||||
|
private static uiLogCallbacks: Set<(logEntry: LogEntry) => void> = new Set();
|
||||||
|
|
||||||
|
static initialize(): winston.Logger {
|
||||||
|
if (this.instance) {
|
||||||
|
return this.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logDir = path.join(app.getPath('userData'), 'logs');
|
||||||
|
|
||||||
|
this.instance = winston.createLogger({
|
||||||
|
level: 'info',
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||||
|
winston.format.errors({ stack: true }),
|
||||||
|
winston.format.printf(({ level, message, timestamp, stack }) => {
|
||||||
|
if (stack) {
|
||||||
|
return `[${timestamp}] [${level.toUpperCase()}] ${message}\n${stack}`;
|
||||||
|
}
|
||||||
|
return `[${timestamp}] [${level.toUpperCase()}] ${message}`;
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transports: [
|
||||||
|
new winston.transports.Console({
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.colorize(),
|
||||||
|
winston.format.simple()
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
new winston.transports.File({
|
||||||
|
filename: path.join(logDir, 'app.log'),
|
||||||
|
maxsize: 10 * 1024 * 1024,
|
||||||
|
maxFiles: 5,
|
||||||
|
}),
|
||||||
|
new winston.transports.File({
|
||||||
|
filename: path.join(logDir, 'error.log'),
|
||||||
|
level: 'error',
|
||||||
|
maxsize: 10 * 1024 * 1024,
|
||||||
|
maxFiles: 5,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static onUILog(callback: (logEntry: LogEntry) => void): () => void {
|
||||||
|
this.uiLogCallbacks.add(callback);
|
||||||
|
return () => {
|
||||||
|
this.uiLogCallbacks.delete(callback);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static notifyUI(level: string, message: string, details?: any): void {
|
||||||
|
const logEntry: LogEntry = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
level,
|
||||||
|
message,
|
||||||
|
details,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.uiLogCallbacks.forEach((callback) => callback(logEntry));
|
||||||
|
}
|
||||||
|
|
||||||
|
static info(message: string, details?: any): void {
|
||||||
|
if (!this.instance) this.initialize();
|
||||||
|
this.instance!.info(message, details);
|
||||||
|
this.notifyUI('info', message, details);
|
||||||
|
}
|
||||||
|
|
||||||
|
static warn(message: string, details?: any): void {
|
||||||
|
if (!this.instance) this.initialize();
|
||||||
|
this.instance!.warning(message, details);
|
||||||
|
this.notifyUI('warn', message, details);
|
||||||
|
}
|
||||||
|
|
||||||
|
static error(message: string, details?: any): void {
|
||||||
|
if (!this.instance) this.initialize();
|
||||||
|
this.instance!.error(message, details);
|
||||||
|
this.notifyUI('error', message, details);
|
||||||
|
}
|
||||||
|
|
||||||
|
static debug(message: string, details?: any): void {
|
||||||
|
if (!this.instance) this.initialize();
|
||||||
|
this.instance!.debug(message, details);
|
||||||
|
this.notifyUI('debug', message, details);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user