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>
70 lines
2.3 KiB
Python
70 lines
2.3 KiB
Python
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
|