updates to digiserver app server
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
# 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/<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](02-knowledge-graph.md)
|
||||
Reference in New Issue
Block a user