Add admin maintenance console

Admin-only page reachable from a new card on the admin panel (replacing the
Quick Actions card) that centralises housekeeping operations:

* Database integrity: PRAGMA integrity_check / quick_check / foreign_key_check,
  plus VACUUM + ANALYZE to reclaim free space.
* Leftover records: detects and removes orphaned rows left behind by deleted
  players/content (player_feedback, player_edit, playlist_content, dangling
  player.playlist_id) and orphaned edited-media folders on disk.
* Player authentication tokens: manual reset (single or all), a configurable
  automatic reset interval, and cancellation of a pending reset.

Resetting a token rotates player.auth_code and sets auth_reset_required, so the
next heartbeat (POST /api/player-feedback) answers 401 + auth_required and the
client restarts its authentication flow. A successful authentication clears the
flag and stamps auth_token_issued_at, from which the interval is measured.

New: PlayerAuthConfig model, add_player_auth_reset migration (registered in
docker-entrypoint.sh), admin/maintenance.html.
This commit is contained in:
2026-10-14 22:13:18 +03:00
parent 2539f82868
commit 88827be7be
10 changed files with 1124 additions and 19 deletions
+68
View File
@@ -0,0 +1,68 @@
"""Add player authentication token reset support.
Adds:
* ``player.auth_token_issued_at`` — when the current auth token was issued
* ``player.auth_reset_required`` — force re-authentication on next heartbeat
* ``player_auth_config`` — single-row token reset interval policy
Idempotent: safe to run on every container start.
"""
import sys
sys.path.insert(0, '/app')
from app.app import create_app
from app.extensions import db
from sqlalchemy import text
app = create_app()
with app.app_context():
print("Adding player authentication token reset support...")
# 1. New columns on the player table.
columns = [
("auth_token_issued_at", "DATETIME"),
("auth_reset_required", "BOOLEAN DEFAULT 0"),
]
for col_name, col_type in columns:
try:
db.session.execute(text(f"ALTER TABLE player ADD COLUMN {col_name} {col_type}"))
db.session.commit()
print(f"✓ '{col_name}' column added to player table.")
except Exception as e:
if 'duplicate column' in str(e).lower() or 'already exists' in str(e).lower():
print(f"✓ '{col_name}' column already exists, skipping.")
else:
print(f"✗ Error adding '{col_name}': {e}")
raise
# 2. Settings table holding the automatic reset interval.
try:
db.session.execute(text("""
CREATE TABLE IF NOT EXISTS player_auth_config (
id INTEGER PRIMARY KEY,
reset_interval_days INTEGER NOT NULL DEFAULT 0,
updated_at DATETIME,
updated_by VARCHAR(255)
)
"""))
db.session.commit()
print("✓ 'player_auth_config' table ready.")
except Exception as e:
print(f"✗ Error creating 'player_auth_config': {e}")
raise
# 3. Backfill: treat existing tokens as issued now so the interval starts
# counting from the migration rather than expiring every player at once.
try:
result = db.session.execute(text(
"UPDATE player SET auth_token_issued_at = CURRENT_TIMESTAMP "
"WHERE auth_token_issued_at IS NULL"
))
db.session.commit()
print(f"✓ Backfilled auth_token_issued_at for {result.rowcount} player(s).")
except Exception as e:
print(f"⚠ Could not backfill auth_token_issued_at: {e}")
db.session.rollback()
print("Done.")