8.1 KiB
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:
- Humans (admins) — manage content, playlists, players, users, HTTPS via the web UI.
- Players (physical signage) — poll the REST API for playlists, send status feedback, upload edited media.
- 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 13–40 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")]
- ProxyFix — trusts
X-Forwarded-For/Proto/Host/Portfrom Caddy/nginx (1 hop). - ScriptNameFix — reads
X-Script-Name(e.g./digiserver) sourl_for()produces correct prefixed URLs behind an umbrella nginx gateway. - Portal SSO (
portal_sso.py) — if the umbrella nginx sendsX-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