feat: optional auth + backup/restore reliability fixes
Some checks failed
Lint & Test / test (push) Failing after 29s
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:
@@ -103,23 +103,13 @@ async def lifespan(app: FastAPI):
|
||||
print(f" Open http://localhost:{config.server.port} in your browser")
|
||||
print(" =============================================\n")
|
||||
|
||||
# Validate authentication configuration
|
||||
# Log authentication mode
|
||||
if not config.auth.api_keys:
|
||||
logger.error("=" * 70)
|
||||
logger.error("CRITICAL: No API keys configured!")
|
||||
logger.error("Authentication is REQUIRED for all API requests.")
|
||||
logger.error("Please add API keys to your configuration:")
|
||||
logger.error(" 1. Generate keys: openssl rand -hex 32")
|
||||
logger.error(" 2. Add to config/default_config.yaml under auth.api_keys")
|
||||
logger.error(" 3. Format: label: \"your-generated-key\"")
|
||||
logger.error("=" * 70)
|
||||
raise RuntimeError("No API keys configured - server cannot start without authentication")
|
||||
|
||||
# Log authentication status
|
||||
logger.info(f"API Authentication: ENFORCED ({len(config.auth.api_keys)} clients configured)")
|
||||
client_labels = ", ".join(config.auth.api_keys.keys())
|
||||
logger.info(f"Authorized clients: {client_labels}")
|
||||
logger.info("All API requests require valid Bearer token authentication")
|
||||
logger.info("Authentication disabled (no API keys configured)")
|
||||
else:
|
||||
logger.info(f"Authentication enabled ({len(config.auth.api_keys)} API key(s) configured)")
|
||||
client_labels = ", ".join(config.auth.api_keys.keys())
|
||||
logger.info(f"Authorized clients: {client_labels}")
|
||||
|
||||
# Create MQTT service (shared broker connection)
|
||||
mqtt_service = MQTTService(config.mqtt)
|
||||
@@ -144,6 +134,21 @@ async def lifespan(app: FastAPI):
|
||||
storage_config=config.storage,
|
||||
)
|
||||
|
||||
# Verify STORE_MAP covers all StorageConfig file fields.
|
||||
# Catches missed additions early (at startup) rather than silently
|
||||
# excluding new stores from backups.
|
||||
storage_attrs = {
|
||||
attr for attr in config.storage.model_fields
|
||||
if attr.endswith("_file")
|
||||
}
|
||||
mapped_attrs = set(STORE_MAP.values())
|
||||
unmapped = storage_attrs - mapped_attrs
|
||||
if unmapped:
|
||||
logger.warning(
|
||||
f"StorageConfig fields not in STORE_MAP (missing from backups): "
|
||||
f"{sorted(unmapped)}"
|
||||
)
|
||||
|
||||
# Initialize API dependencies
|
||||
init_dependencies(
|
||||
device_store, template_store, processor_manager,
|
||||
|
||||
Reference in New Issue
Block a user