feat: add update management system with version check and one-click update

- Bake version info (commit, branch, date) into /app/version.json at build time
  via Docker ARG GIT_COMMIT/GIT_BRANCH/GIT_COMMIT_DATE
- Mount source directory as /app-source for in-container git operations
- Add git config safe.directory for /app-source (ownership mismatch fix)
- Add SystemConfig fields: git_repo_url, git_branch, git_token_encrypted
- Add DB migrations for the three new columns
- Add git_token encryption in update_settings() handler
- New endpoints:
    GET  /api/settings/version  — current version + latest from Gitea API
    POST /api/settings/update   — DB backup + git pull + docker compose rebuild
- New service: app/services/update_service.py
    get_current_version()  — reads /app/version.json
    check_for_updates()    — queries Gitea API for latest commit on branch
    backup_database()      — timestamped SQLite copy to /app/backups/
    trigger_update()       — git pull + fire-and-forget compose rebuild
- New script: update.sh — SSH-based manual update with health check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 21:33:43 +01:00
parent 7793ca3666
commit f92cdfbbef
9 changed files with 376 additions and 4 deletions

View File

@@ -194,6 +194,11 @@ class SystemConfig(Base):
)
ldap_group_dn: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
# Update management
git_repo_url: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
git_branch: Mapped[Optional[str]] = mapped_column(String(100), default="main")
git_token_encrypted: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
@@ -245,6 +250,9 @@ class SystemConfig(Base):
"ldap_base_dn": self.ldap_base_dn or "",
"ldap_user_filter": self.ldap_user_filter or "(sAMAccountName={username})",
"ldap_group_dn": self.ldap_group_dn or "",
"git_repo_url": self.git_repo_url or "",
"git_branch": self.git_branch or "main",
"git_token_set": bool(self.git_token_encrypted),
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}