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.
This commit is contained in:
2026-09-11 12:18:34 +03:00
parent 1c5186463a
commit 46602f1933
226 changed files with 3999 additions and 15737 deletions
+36 -51
View File
@@ -8,7 +8,9 @@ import bcrypt
from typing import Optional, Dict, List
from app.extensions import db, cache
from app.models import Player, Content, PlayerFeedback, ServerLog
from app.models import (
Player, Playlist, Content, PlayerFeedback, ServerLog,
)
from app.utils.logger import log_action
api_bp = Blueprint('api', __name__, url_prefix='/api')
@@ -86,6 +88,25 @@ def verify_player_auth(f):
return decorated_function
def get_assigned_playlist(player: Player) -> Optional[Playlist]:
"""Return the playlist assigned to *player*, or ``None`` if unassigned.
Centralises playlist lookup so every endpoint reports the same sync
version. Playlist edits bump ``Playlist.version``; players poll that
value to decide whether their cached content is stale. A player with no
assigned playlist has nothing to sync and resolves to version 0.
Args:
player: The player whose assigned playlist should be resolved.
Returns:
The assigned ``Playlist`` instance, or ``None`` when unassigned.
"""
if not player.playlist_id:
return None
return db.session.get(Playlist, player.playlist_id)
@api_bp.route('/health', methods=['GET'])
def health_check():
"""API health check endpoint."""
@@ -113,7 +134,7 @@ def authenticate_player():
quickconnect_code: Quick connect code (optional if using password)
Returns:
JSON with auth_code, player_id, group_id, and configuration
JSON with auth_code, player_id, playlist_id, and configuration
"""
data = request.get_json()
@@ -265,12 +286,8 @@ def get_playlist_by_quickconnect():
db.session.commit()
# Get playlist version from the assigned playlist
playlist_version = 1
if player.playlist_id:
from app.models import Playlist
assigned_playlist = Playlist.query.get(player.playlist_id)
if assigned_playlist:
playlist_version = assigned_playlist.version
assigned_playlist = get_assigned_playlist(player)
playlist_version = assigned_playlist.version if assigned_playlist else 0
# Hash the quickconnect code for validation on client side
hashed_quickconnect = bcrypt.hashpw(
@@ -322,12 +339,8 @@ def get_player_playlist(player_id: int):
db.session.commit()
# Get playlist version from the assigned playlist
playlist_version = 1
if player.playlist_id:
from app.models import Playlist
assigned_playlist = Playlist.query.get(player.playlist_id)
if assigned_playlist:
playlist_version = assigned_playlist.version
assigned_playlist = get_assigned_playlist(player)
playlist_version = assigned_playlist.version if assigned_playlist else 0
return jsonify({
'player_id': player_id,
@@ -363,10 +376,16 @@ def get_playlist_version(player_id: int):
player.last_seen = datetime.utcnow()
db.session.commit()
# Player syncs against the version of its assigned playlist; the
# content count comes from that same playlist (Content has no
# player_id column - it reaches players through the playlist).
assigned_playlist = get_assigned_playlist(player)
return jsonify({
'player_id': player_id,
'playlist_version': player.playlist_version,
'content_count': Content.query.filter_by(player_id=player_id).count()
'playlist_id': player.playlist_id,
'playlist_version': assigned_playlist.version if assigned_playlist else 0,
'content_count': assigned_playlist.contents.count() if assigned_playlist else 0
})
except Exception as e:
@@ -378,7 +397,6 @@ def get_playlist_version(player_id: int):
def get_cached_playlist(player_id: int) -> List[Dict]:
"""Get cached playlist for a player based on assigned playlist."""
from flask import url_for
from app.models import Playlist
player = Player.query.get(player_id)
if not player or not player.playlist_id:
@@ -556,7 +574,6 @@ def get_player_status(player_id: int):
'player_id': player_id,
'name': player.name,
'location': player.location,
'group_id': player.group_id,
'status': player.status,
'is_online': is_online,
'last_seen': player.last_seen.isoformat() if player.last_seen else None,
@@ -593,7 +610,6 @@ def system_info():
try:
# Get counts
total_players = Player.query.count()
total_groups = Group.query.count()
total_content = Content.query.count()
# Count online players (seen in last 5 minutes)
@@ -610,7 +626,6 @@ def system_info():
'total': total_players,
'online': online_players
},
'groups': total_groups,
'content': total_content,
'logs_24h': recent_logs,
'timestamp': datetime.utcnow().isoformat()
@@ -621,35 +636,6 @@ def system_info():
return jsonify({'error': 'Internal server error'}), 500
# DEPRECATED: Groups functionality has been archived
# @api_bp.route('/groups', methods=['GET'])
# @rate_limit(max_requests=60, window=60)
# def list_groups():
# """List all groups with basic information."""
# try:
# groups = Group.query.order_by(Group.name).all()
#
# groups_data = []
# for group in groups:
# groups_data.append({
# 'id': group.id,
# 'name': group.name,
# 'description': group.description,
# 'player_count': group.players.count(),
# 'content_count': group.contents.count()
# })
#
# return jsonify({
# 'groups': groups_data,
# 'count': len(groups_data)
# })
#
# except Exception as e:
# log_action('error', f'Error listing groups: {str(e)}')
# return jsonify({'error': 'Internal server error'}), 500
@api_bp.route('/content', methods=['GET'])
@rate_limit(max_requests=60, window=60)
def list_content():
@@ -665,8 +651,7 @@ def list_content():
'type': content.content_type,
'duration': content.duration,
'size': content.file_size,
'uploaded_at': content.uploaded_at.isoformat(),
'group_count': content.groups.count()
'uploaded_at': content.uploaded_at.isoformat()
})
return jsonify({