Archive location (Gitea issue 7): ARCHIVE_ROOT, Admin file-locations API, archive-copy job, doctor check

A filed packet's sealed data book, documents and a manifest are copied to the archive folder by the worker.
A failed copy never blocks filing: it is retried with backoff and raises the archive_failed condition.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-30 17:50:04 -06:00
co-authored by Claude Opus 5.5
parent 3fdf063b70
commit da31b0a87a
21 changed files with 1537 additions and 11 deletions
+42 -1
View File
@@ -23,7 +23,7 @@ from . import __version__
from .settings import SecretFileError, Settings
PASS, WARN, FAIL, SKIP = "PASS", "WARN", "FAIL", "SKIP"
SKIPPABLE = ("visual", "mfiles", "ldap", "smtp", "db", "volume", "clamav")
SKIPPABLE = ("visual", "mfiles", "ldap", "smtp", "db", "volume", "clamav", "archive")
FREE_WARN_BYTES = 2 * 1024**3
EXPECTED_PS_GROUPS = 50
IN_CHAIN = "1.2.840.113556.1.4.1941"
@@ -261,6 +261,46 @@ def check_volume(s: Settings) -> list[Check]:
return out
# ---------------------------------------------------------------- archive
def check_archive(s: Settings) -> list[Check]:
"""The archive folder check (probe write, read back, delete) against the saved folder, audited like the Admin's."""
import psycopg
from . import db
from .domain import archive
root = archive.root_of(s)
if root is None:
return [Check("archive", SKIP, "ARCHIVE_ROOT not set: filed packets are not copied to an archive")]
try:
with db.connect_as("app", s, autocommit=True) as conn:
archive_dir = archive.load(conn)[0]
except (SecretFileError, psycopg.Error) as e:
return [Check("archive", FAIL, f"can't read the saved archive folder: {_first_line(e)}")]
if archive_dir is None:
return [Check("archive", SKIP, f"ARCHIVE_ROOT={root}, no archive folder saved (Admin, File locations)")]
_, result = archive.check(root, archive_dir)
note = ""
try:
with db.connect_as("app", s, autocommit=True) as conn:
archive.audit_check(conn, result, archive_dir=archive_dir, source="doctor")
except (SecretFileError, psycopg.Error) as e:
note = f" (not audited: {_first_line(e)})"
if not result.ok:
return [
Check(
"archive",
FAIL,
f"{archive_dir} under {root}: {result.message}{note}",
"is the share mounted and writable by uid 10001?",
)
]
free = f", {result.free_bytes / 1024**3:.1f} GB free" if result.free_bytes is not None else ""
return [Check("archive", WARN if note else PASS, f"{result.resolved} writable{free}{note}")]
# ---------------------------------------------------------------- clamav
@@ -439,6 +479,7 @@ def run(
("db", check_db),
("volume", check_volume),
("clamav", check_clamav),
("archive", check_archive),
("ldap", check_ldap),
("smtp", lambda st: check_smtp(st, send_test)),
]