feat: sentry integration (#51)

* feat: integrate sentry error reporting

* feat: report logger errors to sentry with 30s log context
This commit is contained in:
vin authored and GitHub committed 2026-01-30 11:48:49 +08:00
1 parent 1265d044f6
commit a785640d7a
22 files changed
+2143 -227

No files matched your search

+8 -3
View File
@@ -1,15 +1,20 @@
import { AppConfig } from '../../types/config';
import { ConfigManager } from './manager';
import { syncAutoStart } from '../../utils/autoStart';
import { logger } from '../../utils/logger';
export function loadConfig(): AppConfig {
return ConfigManager.loadConfig();
}
export function saveConfig(config: AppConfig): void {
export async function saveConfig(config: AppConfig): Promise<void> {
// Logic to notify proxy server if configuration changes (hot update)
// Logic to update Tray if language changes
// For now just save
ConfigManager.saveConfig(config);
syncAutoStart(config);
const previous = ConfigManager.getCachedConfig() ?? ConfigManager.loadConfig();
await ConfigManager.saveConfig(config);
logger.setErrorReportingEnabled(config.error_reporting_enabled);
if (previous.auto_startup !== config.auto_startup) {
syncAutoStart(config);
}
}
+29 -12
View File
@@ -7,6 +7,9 @@ import { logger } from '../../utils/logger';
const CONFIG_FILENAME = 'gui_config.json';
export class ConfigManager {
private static cachedConfig: AppConfig | null = null;
private static saveQueue: Promise<void> = Promise.resolve();
private static getConfigPath(): string {
const appDataDir = getAppDataDir();
if (!fs.existsSync(appDataDir)) {
@@ -20,6 +23,7 @@ export class ConfigManager {
const configPath = this.getConfigPath();
if (!fs.existsSync(configPath)) {
logger.info(`Config: File not found at ${configPath}, returning default`);
this.cachedConfig = DEFAULT_APP_CONFIG;
return DEFAULT_APP_CONFIG;
}
@@ -28,7 +32,7 @@ export class ConfigManager {
// Merge with default to ensure new fields are present
// Zod parse helps validate
const merged = {
const merged: AppConfig = {
...DEFAULT_APP_CONFIG,
...raw,
proxy: { ...DEFAULT_APP_CONFIG.proxy, ...(raw.proxy || {}) },
@@ -45,22 +49,35 @@ export class ConfigManager {
// Handle Anthropic Mapping Map vs Object
// In JSON it's object
return merged as AppConfig;
this.cachedConfig = merged;
return merged;
} catch (e) {
logger.error('Config: Failed to load config', e);
this.cachedConfig = DEFAULT_APP_CONFIG;
return DEFAULT_APP_CONFIG;
}
}
static saveConfig(config: AppConfig): void {
try {
const configPath = this.getConfigPath();
const content = JSON.stringify(config, null, 2);
fs.writeFileSync(configPath, content, 'utf-8');
logger.info(`Config: Saved to ${configPath}`);
} catch (e) {
logger.error('Config: Failed to save config', e);
throw e;
}
static getCachedConfig(): AppConfig | null {
return this.cachedConfig;
}
static async saveConfig(config: AppConfig): Promise<void> {
const configPath = this.getConfigPath();
const content = JSON.stringify(config, null, 2);
this.saveQueue = this.saveQueue
.catch(() => undefined)
.then(async () => {
await fs.promises.writeFile(configPath, content, 'utf-8');
this.cachedConfig = config;
logger.info(`Config: Saved to ${configPath}`);
})
.catch((e) => {
logger.error('Config: Failed to save config', e);
throw e;
});
return this.saveQueue;
}
}
+1 -1
View File
@@ -12,6 +12,6 @@ export const configRouter = os.router({
.input(AppConfigSchema)
.output(z.void())
.handler(async ({ input }) => {
saveConfig(input);
await saveConfig(input);
}),
});
+1 -1
View File
@@ -53,7 +53,7 @@ export const generateApiKey = async (): Promise<string> => {
// Save to config
const config = ConfigManager.loadConfig();
config.proxy.api_key = newKey;
ConfigManager.saveConfig(config);
await ConfigManager.saveConfig(config);
return newKey;
};