# 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. ```mermaid 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 | ```mermaid 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 | ```mermaid 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: ```mermaid 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/` 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](02-knowledge-graph.md)