Translate the whole site to English: English routes with 301 redirects from the Dutch paths, English UI, docs, seeds and captions, one-time live content migration

This commit is contained in:
2026-10-05 11:25:41 +02:00
parent bbdecdc95b
commit ff547e1677
153 changed files with 1095 additions and 774 deletions
+14 -14
View File
@@ -160,17 +160,17 @@ async def process_backup_async(row, codec):
class BackupService(BaseService):
name = "backup"
title = "Back-ups"
description = "Verwerkt de back-upwachtrij, plant automatische back-ups en snoeit oude archieven."
title = "Backups"
description = "Works through the backup queue, schedules automatic backups and prunes old archives."
default_interval = 10
min_interval = 5
metrics_interval = 30
config_fields = [
ConfigField("schedule_hours", "int", 24, "Automatisch elke N uur (0 = uit)", minimum=0, maximum=720, group="Planning"),
ConfigField("schedule_target", "select", "database", "Automatisch doel", options=list(TARGETS), group="Planning"),
ConfigField("keep_last", "int", 14, "Bewaar laatste N", minimum=1, maximum=1000, group="Bewaren"),
ConfigField("codec", "select", "zstd", "Compressie", options=["zstd", "brotli"], group="Archief"),
ConfigField("offload", "bool", "0", "Offload via rclone", group="Archief"),
ConfigField("schedule_hours", "int", 24, "Automatic every N hours (0 = off)", minimum=0, maximum=720, group="Schedule"),
ConfigField("schedule_target", "select", "database", "Automatic target", options=list(TARGETS), group="Schedule"),
ConfigField("keep_last", "int", 14, "Keep last N", minimum=1, maximum=1000, group="Retention"),
ConfigField("codec", "select", "zstd", "Compression", options=["zstd", "brotli"], group="Archive"),
ConfigField("offload", "bool", "0", "Offload via rclone", group="Archive"),
]
def __init__(self):
@@ -190,30 +190,30 @@ class BackupService(BaseService):
cfg = self.get_config()
if self._schedule_due(int(cfg["schedule_hours"])):
enqueue_backup(cfg["schedule_target"], "system")
self.log(f"automatische back-up ingepland: {cfg['schedule_target']}")
self.log(f"automatic backup scheduled: {cfg['schedule_target']}")
for row in pending_backups():
update_backup(row["uid"], status="running")
ok = await process_backup_async(row, cfg["codec"])
self.processed += 1
self.log(f"back-up {row['target']} {'klaar' if ok else 'mislukt'}")
self.log(f"backup {row['target']} {'finished' if ok else 'failed'}")
primary = admin_uids_ordered()[:1]
for uid in primary:
create_notification(uid, "backup.finished" if ok else "backup.failed", f"Back-up {row['target']} {'klaar' if ok else 'mislukt'}", "/admin/backups")
create_notification(uid, "backup.finished" if ok else "backup.failed", f"Backup {row['target']} {'finished' if ok else 'failed'}", "/admin/backups")
if ok and cfg["offload"] == "1":
done = [item for item in list_backups(limit=5) if item["uid"] == row["uid"]]
if done:
self.log(f"offload: {offload(backup_path(done[0]))}")
pruned = prune_backups(int(cfg["keep_last"]))
if pruned:
self.log(f"{pruned} oude back-ups gesnoeid")
self.log(f"{pruned} old backups pruned")
def collect_metrics(self):
rows = list_backups(limit=200)
return {
"cards": [
{"label": "Archieven", "value": sum(1 for row in rows if row["status"] == "done")},
{"label": "Wachtrij", "value": sum(1 for row in rows if row["status"] == "pending")},
{"label": "Verwerkt sinds start", "value": self.processed},
{"label": "Archives", "value": sum(1 for row in rows if row["status"] == "done")},
{"label": "Queue", "value": sum(1 for row in rows if row["status"] == "pending")},
{"label": "Processed since start", "value": self.processed},
],
"table": None,
}
+5 -5
View File
@@ -24,7 +24,7 @@ FIELD_TYPES = ("int", "str", "url", "text", "password", "bool", "select")
class ConfigField:
def __init__(self, key, type="str", default="", label="", help="", minimum=None, maximum=None, options=None, secret=False, group="Algemeen"):
def __init__(self, key, type="str", default="", label="", help="", minimum=None, maximum=None, options=None, secret=False, group="General"):
if type not in FIELD_TYPES:
raise ValueError(f"unknown field type {type}")
self.key = key
@@ -109,10 +109,10 @@ class BaseService:
def fields(self):
framework = [
ConfigField("enabled", "bool", "1" if self.default_enabled else "0", "Ingeschakeld", group="Algemeen"),
ConfigField("interval", "int", self.default_interval, "Interval (s)", minimum=self.min_interval, group="Algemeen"),
ConfigField("enabled", "bool", "1" if self.default_enabled else "0", "Enabled", group="General"),
ConfigField("interval", "int", self.default_interval, "Interval (s)", minimum=self.min_interval, group="General"),
]
tail = [ConfigField("log_size", "int", 200, "Logregels", minimum=10, maximum=5000, group="Logs")]
tail = [ConfigField("log_size", "int", 200, "Log lines", minimum=10, maximum=5000, group="Logs")]
return framework + list(self.config_fields) + tail
def field(self, key):
@@ -196,7 +196,7 @@ class BaseService:
try:
await self.run_once()
except Exception as exc:
self.log(f"fout: {exc}")
self.log(f"error: {exc}")
logger.exception("service %s failed", self.name)
self._next_run = datetime.now(timezone.utc) + timedelta(seconds=self.current_interval())
write_service_state(self._row_id, last_run=now_iso())
+5 -5
View File
@@ -8,7 +8,7 @@ from molodetz.services.base import BaseService
class HousekeepingService(BaseService):
name = "housekeeping"
title = "Housekeeping"
description = "Ruimt verlopen sessies op en snoeit het auditlog volgens de bewaartermijn."
description = "Clears expired sessions and prunes the audit log to the retention period."
default_interval = 3600
min_interval = 60
metrics_interval = 300
@@ -23,14 +23,14 @@ class HousekeepingService(BaseService):
days = max(1, get_int_setting("audit_retention_days", 365))
cutoff = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()
self.last_audit = prune_audit(cutoff)
self.log(f"sessies verwijderd: {self.last_sessions}, auditregels gesnoeid: {self.last_audit}")
self.log(f"sessions removed: {self.last_sessions}, audit rows pruned: {self.last_audit}")
def collect_metrics(self):
return {
"cards": [
{"label": "Auditregels", "value": count_audit()},
{"label": "Laatste sessie-opruiming", "value": self.last_sessions},
{"label": "Laatste auditsnoei", "value": self.last_audit},
{"label": "Audit rows", "value": count_audit()},
{"label": "Last session cleanup", "value": self.last_sessions},
{"label": "Last audit prune", "value": self.last_audit},
],
"table": None,
}
+2 -2
View File
@@ -7,7 +7,7 @@ from molodetz.utils.presence import compute_roster
class PresenceService(BaseService):
name = "presence"
title = "Presence"
description = "Publiceert de online-set uit de laatste activiteit van accounts."
description = "Publishes the online set from recent account activity."
default_interval = 5
min_interval = 2
metrics_interval = 5
@@ -24,7 +24,7 @@ class PresenceService(BaseService):
"roster": self.roster,
"cards": [
{"label": "Online", "value": len(self.roster[: config.PRESENCE_ONLINE_LIMIT])},
{"label": "Gevolgd", "value": len(self.roster)},
{"label": "Tracked", "value": len(self.roster)},
],
"table": None,
}