mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-22 16:16:41 +00:00
feat(settings): harden settings API and overhaul SettingsModal
Security: - Strip auth credential keys (auth_username, auth_password_hash, auth_jwt_secret) from GET /api/settings response - Add allowlist guard to POST /api/settings — rejects unknown or auth-namespace keys with a 400 Backend: - Add PATCH /api/settings bulk endpoint with Zod schema validation (type coercion, range checks, URL format) and atomic SQLite transaction - Add system_state table — moves last_janitor_alert_timestamp out of global_settings; adds getSystemState/setSystemState on DatabaseService - Add metrics_retention_hours and log_retention_days configurable settings; MonitorService reads both dynamically each evaluation cycle - Add cleanupOldNotifications(days) to DatabaseService, called each cycle Frontend: - Replace single isLoading flag with per-operation states (isSavingSystem, isSavingDeveloper, isSavingPassword, isSavingRegistry, isSavingAgent/isTestingAgent per agent type) - Add skeleton loader that blocks interaction until fetchSettings resolves - Explicit key-picking in fetchSettings — auth keys cannot enter state - Unsaved-changes amber dot on System Limits and Developer sidebar items - Separate saveSystemSettings / saveDeveloperSettings — no cross-tab clobber - Developer tab gains Data Retention section (metrics hours, log days) - All settings saves use new PATCH /api/settings endpoint
This commit is contained in:
@@ -187,13 +187,14 @@ export class MonitorService {
|
||||
// Only trigger once every while? To avoid spamming, we just check if it's over limit
|
||||
// Let's ensure we only spam once per limit breach. We can use a local static variable.
|
||||
const LAST_JANITOR_ALERT_KEY = 'last_janitor_alert_timestamp';
|
||||
const lastAlert = parseInt(settings[LAST_JANITOR_ALERT_KEY] || '0', 10);
|
||||
const lastAlertRaw = DatabaseService.getInstance().getSystemState(LAST_JANITOR_ALERT_KEY);
|
||||
const lastAlert = parseInt(lastAlertRaw || '0', 10);
|
||||
const janitorCooldown = 24 * 60 * 60 * 1000; // 24 hours cooldown for janitor
|
||||
|
||||
if (reclaimGb >= janitorLimitGb) {
|
||||
if (Date.now() - lastAlert > janitorCooldown) {
|
||||
await notifier.dispatchAlert('info', `Your system has accumulated ${reclaimGb.toFixed(1)} GB of unused Docker data. Consider using the Janitor tool.`);
|
||||
DatabaseService.getInstance().updateGlobalSetting(LAST_JANITOR_ALERT_KEY, Date.now().toString());
|
||||
DatabaseService.getInstance().setSystemState(LAST_JANITOR_ALERT_KEY, Date.now().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,8 +299,14 @@ export class MonitorService {
|
||||
}
|
||||
|
||||
try {
|
||||
db.cleanupOldMetrics(24);
|
||||
} catch (e) { }
|
||||
const settings = db.getGlobalSettings();
|
||||
const retentionHours = parseInt(settings['metrics_retention_hours'] || '24', 10);
|
||||
db.cleanupOldMetrics(isNaN(retentionHours) ? 24 : retentionHours);
|
||||
const retentionDays = parseInt(settings['log_retention_days'] || '30', 10);
|
||||
db.cleanupOldNotifications(isNaN(retentionDays) ? 30 : retentionDays);
|
||||
} catch (e) {
|
||||
console.error('MonitorService: failed to cleanup old data', e);
|
||||
}
|
||||
}
|
||||
|
||||
private evaluateCondition(actual: number, operator: string, threshold: number): boolean {
|
||||
|
||||
Reference in New Issue
Block a user