Files
digiserver-v2/docs/01-architecture.md
T

8.1 KiB
Raw Blame History

01 · System Architecture

Generated from the Graphify knowledge graph (41 communities) and source analysis of digiserver-v2.


1. System Overview

DigiServer v2 is a Flask application serving three audiences:

  1. Humans (admins) — manage content, playlists, players, users, HTTPS via the web UI.
  2. Players (physical signage) — poll the REST API for playlists, send status feedback, upload edited media.
  3. Operators — deploy/update player software remotely over SSH.
flowchart LR
    subgraph Browser["Web UI (Admins)"]
        A[Login]
        B[Dashboard / Admin / Content / Players]
    end

    subgraph App["digiserver-app (Flask :5000)"]
        C[Gunicorn x4]
        D[7 Blueprints]
        E[SQLAlchemy + SQLite]
        F[Cache / Background tasks]
    end

    subgraph Players["Signage Players"]
        G[Player 1]
        H[Player N]
    end

    subgraph Proxy["Reverse Proxy"]
        I[Caddy :80/:443]
        J[Umbrella nginx gateway - optional SSO]
    end

    I -->|reverse_proxy digiserver-app:5000| C
    J -.X-Auth-Username .-> C
    D --> E
    G <-->|"/api/*"| D
    H <-->|"/api/*"| D
    Browser --> I
    C -.SSH deploy/rsync.-> G
    C -.SSH deploy/rsync.-> H

2. Layered Architecture

Graphify assigns each node a level (0 = entry/global → 3 = utility):

Layer Level Components
L0 — Entry / Global 0 create_app(), app.py, config classes, error handlers, CLI commands
L1 — Strategic / Core 1 Blueprint route handlers (players, content, api, admin), model classes
L2 — Implementation 2 Playlist/group management helpers, processing helpers, model methods
L3 — Utility 3 logger.py, ssh_deploy.py, caddy_manager.py, uploads.py, pptx_converter.py, migrations
flowchart TD
    subgraph L0["L0 · Entry"]
        create_app["create_app()"]
        ProxyFix
        ScriptNameFix
        portal_sso["init_portal_sso()"]
    end
    subgraph L1["L1 · Blueprints + Models"]
        bp["main · auth · admin · players · content · playlist · api"]
        models["User Player Content Playlist PlayerEdit PlayerFeedback PlayerUser HTTPSConfig ServerLog"]
    end
    subgraph L2["L2 · Implementation"]
        bg["run_background_task()"]
        dep["deploy_player_to_host()"]
        caddy["CaddyConfigGenerator"]
        pb["build_player_files() / write_base_config()"]
    end
    subgraph L3["L3 · Utility"]
        log["log_action()"]
        up["uploads.py"]
        pptx["pptx_converter.py"]
        nginx["NginxConfigReader"]
    end
    create_app --> bp
    create_app --> models
    bp --> log
    bp --> bg
    bp --> dep
    bp --> caddy
    bp --> pb
    bp --> up
    bp --> pptx

3. Component Map (files → communities)

Graphify clustered the code into 41 communities. The 12 meaningful ones are shown below (communities 1340 are mostly migrations, isolated helper functions, and archived code).

Community Domain (derived) Files Role
C0 (90) Authentication + legacy groups blueprints/auth.py, blueprints/content_old.py, models/server_log.py, utils/logger.py, utils/group_player_management.py, old_code_documentation/blueprint_groups.py Auth flows + audit logging + (legacy) group features
C1 (80) Admin + HTTPS + player users blueprints/admin.py, models/https_config.py, models/player_user.py, utils/caddy_manager.py, migrations/add_player_user_table.py Admin panel, Caddy HTTPS generation, editing-user registry
C2 (68) Playlist & content workflows blueprints/content.py, blueprints/playlist.py, models/playlist.py Modern playlist-centric content management + legacy redirects
C3 (62) Player API + edits blueprints/api.py, models/player_edit.py Player-facing REST, edited-media pipeline
C4 (55) Application core app.py, config.py, models/user.py, utils/portal_sso.py, utils/script_name_fix.py App factory, config, auth identity, middleware
C5 (54) Content & player models models/content.py, models/player.py, models/player_feedback.py, blueprints/main.py Media model, player model, feedback, dashboard
C6 (51) Player management UI blueprints/players.py, migrations/add_https_config_table.py Player CRUD, manage page, deployment polling
C7 (42) Groups (legacy) models/group.py, utils/nginx_config_reader.py Archived groups feature + legacy nginx reader
C8 (24) Dev tooling old_code_documentation/test_edit_media_api.py, Colors, integrity checker Test/analysis utilities
C9 (21) Upload processing utils/uploads.py Upload progress, video/image processing
C10 (20) Deployment utils/background_tasks.py, utils/ssh_deploy.py Background SSH deployment engine
C11 (19) Player build/staging utils/player_build.py Stage player source code on server
C12 (8) PPTX conversion utils/pptx_converter.py LibreOffice PPTX → PDF → PNG
flowchart TD
    C0["C0 · Auth & logging"]
    C1["C1 · Admin & HTTPS"]
    C2["C2 · Playlists & content"]
    C3["C3 · Player API & edits"]
    C4["C4 · App core"]
    C5["C5 · Content/player models"]
    C6["C6 · Player management UI"]
    C9["C9 · Upload processing"]
    C10["C10 · Deployment"]
    C11["C11 · Player build"]
    C12["C12 · PPTX conversion"]

    C1 -->|27 edges| C3
    C2 -->|25 edges| C3
    C2 -->|25 edges| C0
    C3 -->|25 edges| C1
    C1 -->|21 edges| C0
    C6 -->|16 edges| C1
    C6 -->|16 edges| C3
    C3 -->|14 edges| C0
    C6 -->|14 edges| C0
    C5 -->|9 edges| C3
    C9 -->|5 edges| C0
    C1 -->|3 edges| C11
    C10 --> C6
    C11 --> C10
    C12 --> C9

(edge weights from graphify-out/metadata.json communityLinks)


4. Middleware & Request Pipeline

Every request flows through three layers before reaching a blueprint:

flowchart LR
    R["Raw request"] --> PF["werkzeug ProxyFix"]
    PF --> SF["ScriptNameFix"]
    SF --> SSO["portal_sso (before_request)"]
    SSO --> BP["Blueprint route"]
    BP --> DB[("SQLAlchemy / SQLite")]
  1. ProxyFix — trusts X-Forwarded-For/Proto/Host/Port from Caddy/nginx (1 hop).
  2. ScriptNameFix — reads X-Script-Name (e.g. /digiserver) so url_for() produces correct prefixed URLs behind an umbrella nginx gateway.
  3. Portal SSO (portal_sso.py) — if the umbrella nginx sends X-Auth-Username / X-Auth-Role, automatically create + log in the local user.

5. Key Architectural Decisions

Decision Implementation
Application factory create_app(config_name) — clean testability, per-env config
Blueprint isolation 7 blueprints with clear URL prefixes; API separate from UI
Playlist versioning Playlist.version incremented on every mutation → players poll /api/playlist-version/<id> for refresh
In-memory caching Flask-Caching @cache.memoize(300) on playlist builders; cache.clear() on mutations
DB-backed audit log log_action() writes ServerLog rows; surfaced on dashboard & admin
Two auth systems Humans: Flask-Login (User). Players: auth_code Bearer + quickconnect_code (bcrypt)
Remote provisioning SSH + rsync/git staged code → write app_config.json → run install/start scripts
HTTPS automation CaddyConfigGenerator writes Caddyfile; reloads Caddy via admin API on :2019

6. Runtime Dependencies (requirements.txt)

Runtime: Flask 3.1, Werkzeug 3.1, SQLAlchemy 2.0.37, Flask-SQLAlchemy 3.1.1, Flask-Migrate, Flask-Bcrypt, Flask-Login, Flask-Caching, Flask-Cors, Flask-Talisman, pdf2image, Pillow, ffmpeg-python, python-magic, bcrypt, cryptography, gunicorn 23, psutil, python-dotenv.

System binaries (Docker): poppler-utils, ffmpeg, libmagic1, LibreOffice (core/impress/writer), fonts-noto-color-emoji, sshpass, openssh-client, rsync, git.

Dev: black, flake8, pytest, pytest-cov. (gevent is commented out — incompatible with Python 3.13.)


Next: 02 · Knowledge Graph