Files
ske087 46602f1933 Sanitize codebase, reorganize docs, and add missing deploy files
Remove dead code identified in docs/SANITIZATION-REVIEW.md:
- app/blueprints/content_old.py and app/blueprints/playlist.py
- app/models/group.py, app/utils/nginx_config_reader.py
- orphaned templates (content_list, edit_content, upload_content,
  player_page) and the related group/Template references

Result: 6 blueprints, 82 routes, no dead modules or orphan templates.

Add files that deploy.sh and docker-entrypoint.sh already require but
which were never tracked:
- https_manager.py       (referenced by deploy.sh, migrate_network.sh,
                          docker-entrypoint.sh)
- Caddyfile.example      (seeded by deploy.sh; its absence aborts deploy)

Relocate generated Graphify artifacts from graphify-out/ to
docs/graphify-out/ (110 files, no content change) and archive the
superseded docs under docs/.

Ignore hygiene:
- ignore ad-hoc .env backups (.env.bak*) — they contain live secrets
- keep the pre-sanitization snapshots (docs/legacy code/,
  docs/old_code_documentation/) on disk but out of the repo

Fix .env.example: drop a duplicated config block, genericize the
hardcoded host IP, and document HOSTNAME_INTERNAL.
2026-09-11 12:18:34 +03:00

7.9 KiB

03 · Data Model

DigiServer v2 uses SQLAlchemy 2.0 with SQLite (production: instance/dashboard.db, dev: instance/dev.db, tests: in-memory). All models live in app/models/ and are re-exported from app/models/__init__.py.


1. Entity-Relationship Overview

erDiagram
    user ||--o{ server_log : "writes"
    player ||--o{ player_feedback : "sends (cascade)"
    player ||--o{ player_edit : "edits (cascade)"
    player }o--o| playlist : "assigned_to"
    content ||--o{ player_edit : "edited (cascade)"
    content ||--o{ player_feedback : "playing"
    playlist ||--o{ content : "playlist_content M2M (position,duration,muted,edit_on_player_enabled)"
    player_user ||--o{ player_edit : "user_code"
    https_config ||--o| https_config : "singleton row"

2. Tables & Columns

user — human accounts

Column Type Notes
id Integer PK
username String(80) unique, NOT NULL, indexed
password String(120) bcrypt hash, NOT NULL
role String(20) default 'user'; also 'admin', 'viewer'; indexed
theme String(20) default 'light'
created_at DateTime NOT NULL
last_login DateTime nullable

Key methods: is_admin (property), update_last_login(). Mixin: UserMixin (Flask-Login).

player — signage devices

Column Type Notes
id Integer PK
name String(255) NOT NULL
hostname String(255) unique, NOT NULL, indexed
location String(255) nullable
auth_code String(255) unique, NOT NULL, indexed (legacy Bearer auth)
password_hash String(255) NOT NULL (bcrypt)
quickconnect_code String(255) nullable, bcrypt-hashed
orientation String(16) default 'Landscape'
status String(50) default 'offline', indexed
last_seen DateTime indexed
last_heartbeat DateTime indexed
created_at DateTime NOT NULL
playlist_id Integer FK → playlist.id ON DELETE SET NULL, indexed
deployment_status String(50) default 'pending'; pending/deploying/deployed/failed
last_deployment_at DateTime nullable
last_deployment_status String(50) nullable
last_deployment_message Text nullable

Relationships: playlist, feedback (→ PlayerFeedback, cascade delete-orphan).
Methods: is_online (5-min window), update_status(), set_password()/check_password(), set_quickconnect_code()/check_quickconnect_code(), static authenticate(hostname, password, quickconnect_code).

Column Type Notes
id Integer PK
filename String(255) unique, NOT NULL, indexed; may point to edited_media/<id>/... after a player edit
original_filename String(255) nullable, indexed — pristine upload name
content_type String(50) NOT NULL, indexed; image/video/pdf/pptx/weblink/other
duration Integer default 10, nullable
file_size BigInteger nullable
url String(2048) nullable — target URL for weblink content
description Text nullable
uploaded_at DateTime NOT NULL, indexed

Relationships: playlists (M2M via playlist_content).
Properties/methods: file_size_mb, original_display_name, original_media_path, current_media_path, is_image()/is_video()/is_pdf()/is_weblink(), has_player_edits.

playlist — ordered collections of content

Column Type Notes
id Integer PK
name String(100) unique, NOT NULL, indexed
description Text nullable
orientation String(20) default 'Landscape'
version Integer default 1, NOT NULL — sync detection
is_active Boolean default True
created_at DateTime NOT NULL
updated_at DateTime NOT NULL, onupdate

Methods: player_count, content_count, total_duration (properties), increment_version(), get_content_ordered().

playlist_content — association table (M2M playlist ↔ content)

Column Type Notes
playlist_id Integer FK → playlist.id CASCADE, composite PK
content_id Integer FK → content.id CASCADE, composite PK
position Integer default 0 — ordering
duration Integer default 10 — per-playlist override
muted Boolean default True
edit_on_player_enabled Boolean default False — whether player-side editing allowed

player_edit — on-player media edits

Column Type Notes
id Integer PK
player_id Integer FK → player.id CASCADE, indexed
content_id Integer FK → content.id CASCADE, indexed
original_name String(255) NOT NULL
new_name String(255) NOT NULL
version Integer default 1
user String(255) nullable (user code)
time_of_modification DateTime nullable
metadata_path String(512) nullable
edited_file_path String(512) NOT NULL
created_at DateTime NOT NULL, indexed

Relationships: player, content. Method: to_dict().

player_feedback — status messages from players

Column Type Notes
id Integer PK
player_id Integer FK → player.id, NOT NULL, indexed
status String(50) default 'unknown'
current_content_id Integer FK → content.id, nullable
message Text nullable
error Text nullable
timestamp DateTime NOT NULL, indexed

Properties/classmethod: is_error, age_seconds, get_latest_for_player().

player_user — mapping of player edit user codes

Column Type Notes
id Integer PK
user_code String(255) globally unique, NOT NULL, indexed
user_name String(255) nullable
created_at DateTime NOT NULL
updated_at DateTime NOT NULL, onupdate

Method: to_dict(). (Migrated from a per-player table — see migrate_player_user_global.py.)

server_log — DB-backed audit log

Column Type Notes
id Integer PK
level String(20) NOT NULL, indexed, default 'info'
message Text NOT NULL
timestamp DateTime NOT NULL, indexed

Classmethods: log_info, log_warning, log_error.

https_config — HTTPS settings (singleton row)

Column Type Notes
id Integer PK
https_enabled Boolean default False
hostname String(255) nullable
domain String(255) nullable
ip_address String(45) nullable (IPv6-capable)
email String(255) nullable (Let's Encrypt contact)
port Integer default 443
created_at DateTime NOT NULL
updated_at DateTime NOT NULL, onupdate
updated_by String(255) nullable

Classmethods: get_config() (first row), create_or_update(...). Method: to_dict().

group + group_contentREMOVED

The Group model, the group_content association table, Content.groups / Content.group_count, and the group-management utility functions were deleted during the code sanitization pass (the feature was archived and the table had no rows). Player never had a group_id column. See SANITIZATION-REVIEW.md.


3. Relationship Summary

Relationship Cardinality FK / Mechanism
playlistcontent M:N playlist_content (positioned, with extras)
playerplaylist N:1 player.playlist_id (ON DELETE SET NULL)
playerplayer_feedback 1:N player_feedback.player_id (cascade)
playerplayer_edit 1:N player_edit.player_id (cascade)
contentplayer_edit 1:N player_edit.content_id (cascade)
player_userplayer_edit 1:N player_edit.user = player_user.user_code (logical)
https_config 1 row singleton via get_config()

Next: 04 · Application Core