feat: optional auth + backup/restore reliability fixes
Some checks failed
Lint & Test / test (push) Failing after 29s

Auth is now optional: when `auth.api_keys` is empty, all endpoints are
open (no login screen, no Bearer tokens). Health endpoint reports
`auth_required` so the frontend knows which mode to use.

Backup/restore fixes:
- Auto-backup uses atomic writes (was `write_text`, risked corruption)
- Startup backup skipped if recent backup exists (<5 min cooldown),
  preventing rapid restarts from rotating out good backups
- Restore rejects all-empty backups to prevent accidental data wipes
- Store saves frozen after restore to prevent stale in-memory data
  from overwriting freshly-restored files before restart completes
- Missing stores during restore logged as warnings
- STORE_MAP completeness verified at startup against StorageConfig
This commit is contained in:
2026-03-23 14:50:25 +03:00
parent cd3137b0ec
commit 4975a74ff3
18 changed files with 189 additions and 67 deletions

View File

@@ -3,7 +3,7 @@
*/
// Layer 0: state
import { apiKey, setApiKey, refreshInterval } from './core/state.ts';
import { apiKey, setApiKey, authRequired, refreshInterval } from './core/state.ts';
import { Modal } from './core/modal.ts';
import { queryEl } from './core/dom-utils.ts';
@@ -180,6 +180,9 @@ import {
import { switchTab, initTabs, startAutoRefresh, handlePopState } from './features/tabs.ts';
import { navigateToCard } from './core/navigation.ts';
import { openCommandPalette, closeCommandPalette, initCommandPalette } from './core/command-palette.ts';
import {
applyStylePreset, applyBgEffect, renderAppearanceTab, initAppearance,
} from './features/appearance.ts';
import {
openSettingsModal, closeSettingsModal, switchSettingsTab,
downloadBackup, handleRestoreFileSelected,
@@ -548,6 +551,11 @@ Object.assign(window, {
setLogLevel,
saveExternalUrl,
getBaseOrigin,
// appearance
applyStylePreset,
applyBgEffect,
renderAppearanceTab,
});
// ─── Global keyboard shortcuts ───
@@ -626,6 +634,7 @@ document.addEventListener('DOMContentLoaded', async () => {
// Initialize visual effects
initCardGlare();
initBgAnim();
initAppearance();
initTabIndicator();
updateBgAnimTheme(document.documentElement.getAttribute('data-theme') !== 'light');
const accent = localStorage.getItem('accentColor') || '#4CAF50';
@@ -659,11 +668,15 @@ document.addEventListener('DOMContentLoaded', async () => {
if (addDeviceForm) addDeviceForm.addEventListener('submit', handleAddDevice);
// Always monitor server connection (even before login)
loadServerInfo();
await loadServerInfo();
startConnectionMonitor();
// Show modal if no API key is stored
if (!apiKey) {
// Expose auth state for inline scripts (after loadServerInfo sets it)
(window as any)._authRequired = authRequired;
if (typeof window.updateAuthUI === 'function') window.updateAuthUI();
// Show login modal only when auth is enabled and no API key is stored
if (authRequired && !apiKey) {
setTimeout(() => {
if (typeof window.showApiKeyModal === 'function') {
window.showApiKeyModal(null, true);