feat: implement pngx-controller with Gitea CI/CD deployment
All checks were successful
Deploy / deploy (push) Successful in 30s
All checks were successful
Deploy / deploy (push) Successful in 30s
- Full FastAPI sync engine: master→replica document sync via paperless REST API - Web UI: dashboard, replicas, logs, settings (Jinja2 + HTMX + Pico CSS) - APScheduler background sync, SSE live log stream, Prometheus metrics - Fernet encryption for API tokens at rest - pngx.env credential file: written on save, pre-fills forms on load - Dockerfile with layer-cached uv build, Python healthcheck - docker-compose with host networking for Tailscale access - Gitea Actions workflow: version bump, secret injection, docker compose deploy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
52
app/envfile.py
Normal file
52
app/envfile.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""Read and write a plain-text pngx.env credential file alongside the database."""
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _path() -> Path:
|
||||
from .config import get_config
|
||||
return Path(get_config().db_path).parent / "pngx.env"
|
||||
|
||||
|
||||
def read() -> dict[str, str]:
|
||||
"""Return all key=value pairs from pngx.env, ignoring comments/blanks."""
|
||||
p = _path()
|
||||
if not p.exists():
|
||||
return {}
|
||||
result: dict[str, str] = {}
|
||||
for line in p.read_text().splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
if "=" in line:
|
||||
key, _, value = line.partition("=")
|
||||
result[key.strip()] = value.strip()
|
||||
return result
|
||||
|
||||
|
||||
def write(updates: dict[str, str]) -> None:
|
||||
"""Merge updates into pngx.env, preserving unrelated existing lines."""
|
||||
p = _path()
|
||||
lines: list[str] = []
|
||||
key_to_line: dict[str, int] = {}
|
||||
|
||||
if p.exists():
|
||||
for i, line in enumerate(p.read_text().splitlines()):
|
||||
stripped = line.strip()
|
||||
if stripped and not stripped.startswith("#") and "=" in stripped:
|
||||
k, _, _ = stripped.partition("=")
|
||||
key_to_line[k.strip()] = i
|
||||
lines.append(line)
|
||||
|
||||
for key, value in updates.items():
|
||||
if key in key_to_line:
|
||||
lines[key_to_line[key]] = f"{key}={value}"
|
||||
else:
|
||||
lines.append(f"{key}={value}")
|
||||
|
||||
p.write_text("\n".join(lines) + "\n")
|
||||
|
||||
|
||||
def replica_keys(name: str) -> tuple[str, str]:
|
||||
"""Return the env var names for a replica (URL key, token key)."""
|
||||
safe = name.upper().replace(" ", "_").replace("-", "_")
|
||||
return f"REPLICA_{safe}_URL", f"REPLICA_{safe}_TOKEN"
|
||||
Reference in New Issue
Block a user