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:
69
app/models.py
Normal file
69
app/models.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import UniqueConstraint
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class Replica(SQLModel, table=True):
|
||||
__tablename__ = "replicas" # type: ignore[assignment]
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
url: str
|
||||
api_token: str # Fernet-encrypted
|
||||
enabled: bool = True
|
||||
sync_interval_seconds: Optional[int] = None
|
||||
last_sync_ts: Optional[datetime] = None
|
||||
consecutive_failures: int = 0
|
||||
suspended_at: Optional[datetime] = None
|
||||
last_alert_at: Optional[datetime] = None
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
class SyncMap(SQLModel, table=True):
|
||||
__tablename__ = "sync_map" # type: ignore[assignment]
|
||||
__table_args__ = (UniqueConstraint("replica_id", "master_doc_id"),) # type: ignore[assignment]
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
replica_id: int = Field(foreign_key="replicas.id")
|
||||
master_doc_id: int
|
||||
replica_doc_id: Optional[int] = None
|
||||
task_id: Optional[str] = None
|
||||
last_synced: Optional[datetime] = None
|
||||
file_checksum: Optional[str] = None
|
||||
status: str = "pending"
|
||||
error_msg: Optional[str] = None
|
||||
retry_count: int = 0
|
||||
|
||||
|
||||
class SyncRun(SQLModel, table=True):
|
||||
__tablename__ = "sync_runs" # type: ignore[assignment]
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
replica_id: Optional[int] = Field(default=None, foreign_key="replicas.id")
|
||||
started_at: Optional[datetime] = None
|
||||
finished_at: Optional[datetime] = None
|
||||
triggered_by: Optional[str] = None
|
||||
docs_synced: int = 0
|
||||
docs_failed: int = 0
|
||||
timed_out: bool = False
|
||||
|
||||
|
||||
class Log(SQLModel, table=True):
|
||||
__tablename__ = "logs" # type: ignore[assignment]
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
run_id: Optional[int] = Field(default=None, foreign_key="sync_runs.id")
|
||||
replica_id: Optional[int] = Field(default=None, foreign_key="replicas.id")
|
||||
level: Optional[str] = None
|
||||
message: Optional[str] = None
|
||||
doc_id: Optional[int] = None
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
class Setting(SQLModel, table=True):
|
||||
__tablename__ = "settings" # type: ignore[assignment]
|
||||
|
||||
key: str = Field(primary_key=True)
|
||||
value: Optional[str] = None
|
||||
Reference in New Issue
Block a user