Fix CI timezone and fresh settings initialization
This commit is contained in:
+1
-1
@@ -129,7 +129,7 @@ def discover():
|
||||
def _validate_settings(payload: dict) -> None:
|
||||
try:
|
||||
ZoneInfo(payload.get("timezone") or "UTC")
|
||||
except ZoneInfoNotFoundError as exc:
|
||||
except (ValueError, ZoneInfoNotFoundError) as exc:
|
||||
raise HTTPException(400, "Timezone must be a valid IANA timezone, for example Europe/Berlin or UTC") from exc
|
||||
if payload.get("setup_complete"):
|
||||
missing = [
|
||||
|
||||
@@ -1,14 +1,32 @@
|
||||
from pathlib import Path
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
from .db import db, utc_now
|
||||
|
||||
|
||||
def _normalize_timezone(value: str) -> str:
|
||||
timezone_name = value.strip()
|
||||
if not timezone_name:
|
||||
return "UTC"
|
||||
if timezone_name.startswith("/"):
|
||||
marker = "/zoneinfo/"
|
||||
if marker in timezone_name:
|
||||
timezone_name = timezone_name.split(marker, 1)[1]
|
||||
else:
|
||||
timezone_name = timezone_name.lstrip("/")
|
||||
try:
|
||||
ZoneInfo(timezone_name)
|
||||
except (ValueError, ZoneInfoNotFoundError):
|
||||
return "UTC"
|
||||
return timezone_name
|
||||
|
||||
|
||||
def _system_timezone() -> str:
|
||||
timezone_file = Path("/etc/timezone")
|
||||
if timezone_file.exists():
|
||||
value = timezone_file.read_text().strip()
|
||||
if value:
|
||||
return value
|
||||
return _normalize_timezone(value)
|
||||
return "UTC"
|
||||
|
||||
|
||||
@@ -147,3 +165,17 @@ def run_migrations() -> None:
|
||||
"INSERT INTO schema_migrations(version, applied_at) VALUES (?, ?)",
|
||||
(3, now),
|
||||
)
|
||||
if 4 not in applied:
|
||||
now = utc_now()
|
||||
row = conn.execute("SELECT value FROM settings WHERE key = 'timezone'").fetchone()
|
||||
if row:
|
||||
normalized_timezone = _normalize_timezone(row["value"])
|
||||
if normalized_timezone != row["value"]:
|
||||
conn.execute(
|
||||
"UPDATE settings SET value = ?, updated_at = ? WHERE key = 'timezone'",
|
||||
(normalized_timezone, now),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO schema_migrations(version, applied_at) VALUES (?, ?)",
|
||||
(4, now),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
|
||||
from .db import db, utc_now
|
||||
from .migrations import DEFAULT_SETTINGS
|
||||
from .migrations import DEFAULT_SETTINGS, run_migrations
|
||||
|
||||
|
||||
INT_KEYS = {"default_retention_value", "max_concurrent_backups"}
|
||||
@@ -27,9 +29,20 @@ def serialize_value(key: str, value) -> str:
|
||||
return str(value)
|
||||
|
||||
|
||||
def _is_missing_settings_table(exc: sqlite3.OperationalError) -> bool:
|
||||
return "no such table: settings" in str(exc).lower()
|
||||
|
||||
|
||||
def get_settings() -> dict:
|
||||
with db() as conn:
|
||||
rows = conn.execute("SELECT key, value FROM settings").fetchall()
|
||||
try:
|
||||
with db() as conn:
|
||||
rows = conn.execute("SELECT key, value FROM settings").fetchall()
|
||||
except sqlite3.OperationalError as exc:
|
||||
if not _is_missing_settings_table(exc):
|
||||
raise
|
||||
run_migrations()
|
||||
with db() as conn:
|
||||
rows = conn.execute("SELECT key, value FROM settings").fetchall()
|
||||
raw = {row["key"]: row["value"] for row in rows}
|
||||
for key, value in DEFAULT_SETTINGS.items():
|
||||
raw.setdefault(key, value)
|
||||
@@ -42,22 +55,44 @@ def set_settings(values: dict) -> dict:
|
||||
unknown = set(values) - valid_keys
|
||||
if unknown:
|
||||
raise ValueError(f"Unknown settings: {', '.join(sorted(unknown))}")
|
||||
with db() as conn:
|
||||
for key, value in values.items():
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO settings(key, value, updated_at)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
||||
""",
|
||||
(key, serialize_value(key, value), now),
|
||||
)
|
||||
try:
|
||||
with db() as conn:
|
||||
for key, value in values.items():
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO settings(key, value, updated_at)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
||||
""",
|
||||
(key, serialize_value(key, value), now),
|
||||
)
|
||||
except sqlite3.OperationalError as exc:
|
||||
if not _is_missing_settings_table(exc):
|
||||
raise
|
||||
run_migrations()
|
||||
with db() as conn:
|
||||
for key, value in values.items():
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO settings(key, value, updated_at)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
||||
""",
|
||||
(key, serialize_value(key, value), now),
|
||||
)
|
||||
return get_settings()
|
||||
|
||||
|
||||
def get_setting(key: str, default: str = "") -> str:
|
||||
with db() as conn:
|
||||
row = conn.execute("SELECT value FROM settings WHERE key = ?", (key,)).fetchone()
|
||||
try:
|
||||
with db() as conn:
|
||||
row = conn.execute("SELECT value FROM settings WHERE key = ?", (key,)).fetchone()
|
||||
except sqlite3.OperationalError as exc:
|
||||
if not _is_missing_settings_table(exc):
|
||||
raise
|
||||
run_migrations()
|
||||
with db() as conn:
|
||||
row = conn.execute("SELECT value FROM settings WHERE key = ?", (key,)).fetchone()
|
||||
if row:
|
||||
return row["value"]
|
||||
return default
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
from app import commands
|
||||
from app.main import _validate_settings
|
||||
from app.migrations import _normalize_timezone
|
||||
|
||||
|
||||
def test_system_timezone_absolute_utc_is_normalized():
|
||||
assert _normalize_timezone("/UTC") == "UTC"
|
||||
assert _normalize_timezone("/usr/share/zoneinfo/Europe/Berlin") == "Europe/Berlin"
|
||||
|
||||
|
||||
def test_plain_onedrive_remote_is_accepted(monkeypatch):
|
||||
|
||||
Reference in New Issue
Block a user