Files
digiserver-v2/docs/SANITIZATION-REVIEW.md
T
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

207 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DigiServer v2 — Code Sanitization Review
**Generated:** 2026-09-10
**Snapshot:** `docs/legacy code/` (1.7 MB, 171 files, 49 Python files) — full restore point.
Analysed **42 Python files / 240 functions / 104 routes / 32 templates** under `app/` and `migrations/`.
> Review each section below and reply with the IDs you want deleted (e.g. `A1, A2, B1`).
> Nothing is deleted until you confirm. Everything is recoverable from `docs/legacy code/`.
---
## ✅ Status — Applied 2026-09-10
**Removed (A1, A2, D1, D2):**
| ID | Removed | Notes |
|---|---|---|
| A1 | `app/blueprints/content_old.py` | + 3 templates orphaned by its removal |
| A2 | `app/utils/nginx_config_reader.py` | |
| D1 | 5 group functions + their `__init__.py` exports | `get_player_status_info` kept (live) |
| D2 | `app/models/group.py`, `Content.groups`, `Content.group_count` | |
**Extra cleanup triggered by A1/D2:**
- Deleted orphaned templates: `upload_content.html` (278), `edit_content.html` (11)
- Removed the dead `groups` key from `/api/system-info` and `group_count` from `/api/content`
- Removed the `'group_id': getattr(player, 'group_id', None)` compat shim from `/api/player-status`
- Updated 8 documentation files
**Also removed (B1, B2) — applied in a second pass:**
| ID | Removed | Notes |
|---|---|---|
| B1 | `app/blueprints/playlist.py` (310 LOC) + its registration in `app.py` | Whole legacy blueprint; its only real route redirected to `content.manage_playlist_content` |
| B2 | 3 routes in `players.py`: `reorder_content`, `reorder_playlist`, `remove_from_playlist` (~103 LOC) | Two queried nonexistent columns (`Content.player_id`, `Content.position`, `Player.playlist_version`) → guaranteed 500s |
**Extra cleanup triggered by B1:**
- Deleted `content_list.html` (202 lines) — its only remaining reference was `url_for('playlist.manage_playlist')`. It was **already orphaned** (no Python file rendered it) after `content_old.py` was deleted, so it has been removed for real this time.
- Deleted `players/player_page.html` (227 lines) — it was **never rendered** by any view (the `players.player_page` route redirects to `manage_player`), so it was dead UI. Corrects the earlier "false positive" note below.
> ⚠️ **Functional note:** `players.regenerate_auth_code` (`POST /players/<id>/regenerate-auth`) is now
> referenced by **no template** — `player_page.html` was its only caller. The endpoint still works if
> invoked directly. The equivalent control lives in `manage_player.html` via the quickconnect flow.
> Restore `player_page.html` from `docs/legacy code/` if you want that button back.
**Result (A + B + D combined):**
```
42 → 38 Python modules 9,311 → 8,296 LOC
104 → 82 routes 32 → 28 templates
7 → 6 blueprints dead modules: 2 → 0, orphan templates: 0
```
**Verified:** all files compile; smoke test passes on a fresh DB (every API endpoint + key UI route returns
< 500); `db.metadata` no longer registers `group`/`group_content`; app boots against a migrated copy of the
real `dashboard.db` with all data intact; `app.blueprints` no longer contains `playlist`.
> ⚠️ **Still open:** the real `data/instance/dashboard.db` predates the `original_filename` migration.
> On startup the entrypoint now applies it automatically. To fix locally, run:
> ```
> DATABASE_URL=sqlite:///$PWD/data/instance/dashboard.db ./.venv/bin/python migrations/add_original_filename_to_content.py
> ```
---
## Section A — Dead modules (zero importers)
| ID | Target | LOC | Evidence | Risk |
|---|---|---|---|---|
| **A1** | `app/blueprints/content_old.py` | 500 | `app.py` imports `content.py`; this file's `content_bp` is **never registered**. Superseded "old" content workflow. | **Low** |
| **A2** | `app/utils/nginx_config_reader.py` | 120 | Never imported anywhere. Stack migrated nginx → Caddy, so the reader is obsolete. | **Low** |
```mermaid
graph LR
app_py["app.py<br/>register_blueprints()"] --> content["content.py<br/>content_bp ✅ ACTIVE"]
content_old["content_old.py<br/>content_bp ❌ DEAD"] -.->|never imported| x1[" "]
nginx["nginx_config_reader.py<br/>❌ DEAD"] -.->|never imported| x2[" "]
caddy["caddy_manager.py<br/>✅ ACTIVE"] --> app_py
style content_old fill:#7f1d1d,color:#fff
style nginx fill:#7f1d1d,color:#fff
```
---
## Section B — Legacy duplicate route surface — ✅ **REMOVED**
Two blueprints exposed **parallel implementations of the same operations**. Only the `content.*`
versions were wired to the UI; the legacy ones had no template references.
| ID | Target | LOC | Evidence | Risk |
|---|---|---|---|---|
| **B1** ✅ | `app/blueprints/playlist.py` (whole file + registration) | 310 | Entire file was legacy per-player playlist. Its `manage_playlist` route did nothing but **redirect** to the modern `content.manage_playlist_content`. The other 6 routes had **no template reference**. | **LowMed** |
| **B2** ✅ | 3 routes in `players.py`: `reorder_content`, `reorder_playlist`, `remove_from_playlist` | ~103 | Superseded by `content.*`. Two queried nonexistent columns (`Content.player_id`, `.position`, `Player.playlist_version`) → guaranteed HTTP 500 if called. | **Low** |
**Duplicate operation matrix**
| Operation | Modern (LIVE) | Legacy (DEAD) |
|---|---|---|
| Add content | `content.add_content_to_playlist` | `playlist.add_to_playlist` |
| Remove content | `content.remove_content_from_playlist` | `playlist.remove_from_playlist`, `players.remove_from_playlist` ⚠️broken |
| Reorder | `content.reorder_playlist_content` | `playlist.reorder_playlist`, `players.reorder_content` ⚠️broken, `players.reorder_playlist` ⚠️broken |
| Set duration | `content.update_playlist_content_duration` | `playlist.update_duration` |
| Mute audio | `content.update_playlist_content_muted` | `playlist.update_muted` |
| Toggle edit | `content.update_playlist_content_edit_enabled` | — |
| Clear | — | `playlist.clear_playlist` |
---
## Section C — Broken code: references to columns that do not exist
Confirmed against the live DB schema. These raised `AttributeError`/`OperationalError` at runtime.
**All resolved by deleting A1 + B2.**
| ID | Location | Broken reference | Reachable? |
|---|---|---|---|
| **C1** ✅ | `players.py:768,775` (`remove_from_playlist`) | `player.playlist_version` | Via route only (no UI link) |
| **C2** ✅ | `players.py:~700` (`reorder_playlist`) | `Content.player_id`, `Content.position` | Via route only (no UI link) |
| **C3** | `content_old.py:189-190` | `player.playlist_version` | No (dead module) |
| **C4** | `content_old.py:50,57` | `content.player_id`, `player.group` | No (dead module) |
> ✅ **Resolved by deleting A1 + B2.** If you keep those files, they must be rewritten.
**Not a bug (verified by hand):** `Content._playlist_duration` /
`._playlist_position` / `._playlist_muted` in `api.py` are set **dynamically** by
`Playlist.get_content_ordered()`. These are intentional and work correctly — the
static analyzer flags them because it only sees model class attributes.
---
## Section D — Legacy groups subsystem
Groups are fully deprecated (0 rows, `/api/groups` already commented out), but the code lingers.
| ID | Target | LOC | Evidence | Risk |
|---|---|---|---|---|
| **D1** | 5 group functions in `app/utils/group_player_management.py`: `get_group_statistics`, `assign_player_to_group`, `bulk_assign_players_to_group`, `get_online_players_count`, `get_players_by_status` + their `__init__.py` exports | ~130 | **Zero callers outside the module.** All three group functions reference `player.group_id`, **which does not exist** → broken. | **Low** |
| **D2** | `app/models/group.py` (Group model + `group_content` table) | 71 | Retained only because `Content.groups` FK relationship and `Content.group_count` reference it. Requires touching the Content model. | **Medium** |
> ⚠️ **Keep:** `get_player_status_info()` (top of the same file) is **live** — used at
> `players.py:28` and `players.py:432`. Only the group functions should go.
---
## Section E — Legacy redirect stubs
Thin compatibility shims that only redirect to the modern UI. They're harmless but keep dead
URL surface alive.
| ID | Target | Evidence |
|---|---|---|
| **E1** | `players.player_page` (`/players/<id>`) | Body is a single `redirect(url_for('players.manage_player'))`. Still `url_for`-referenced by 2 templates, so keeping it is fine. |
| **E2** | `playlist.manage_playlist` (`/playlist/<id>`) | Part of B1 — already covered by deleting that file. |
---
## Section F — Orphan templates / assets
| ID | Target | LOC | Evidence |
|---|---|---|---|
| — | (none) | — | After the B1/B2 pass the codebase has **0 orphan templates**. |
---
## Recommended batches — ✅ **ALL APPLIED**
| Batch | Contents | Total removed | Status |
|---|---|---|---|
| **Batch 1 — Safe clean** | **A1, A2** | ~620 LOC | ✅ done |
| **Batch 2 — Legacy playlist** | **B1, B2** | ~413 LOC | ✅ done |
| **Batch 3 — Groups** | **D1** | ~130 LOC | ✅ done |
| **Batch 4 — Group model** | **D2** | ~71 LOC | ✅ done |
**Every batch was followed by:** compile-all + app-factory boot + route smoke test, so hidden
dependencies were caught before moving on.
---
## How I verified this (so you can trust it)
| Check | Method |
|---|---|
| Module reachability | AST import extraction + `register_blueprint` cross-reference |
| Route usage | `url_for('endpoint')` **and** literal path matching against all templates/JS |
| Broken columns | Compared every `var.attr` access against live SQLite `PRAGMA table_info` |
| Dynamic attributes | Manually inspected `get_content_ordered()` to rule out false positives |
| Duplicate bodies | `ast.dump` body hashing across all functions (result: 0 exact duplicates) |
**Reproduce anytime:**
```
./.venv/bin/python docs/tools/sanitize_report.py # dead code + broken refs
./.venv/bin/python docs/tools/sanitize_audit.py # full function inventory
./.venv/bin/python docs/tools/sanitize_templates.py # orphan templates
./.venv/bin/python docs/tools/smoke_test.py # post-change smoke test
```
---
## ⚠️ Rollback / hygiene notes
1. **`docs/legacy code/` is excluded from the Docker image** (via `legacy code/` and
`**/legacy code/` in `.dockerignore`) so it never
ships to production or bloats the build.
2. It is **not** git-ignored, so it will appear in `git status`. Decide:
- commit it as a recovery point, or
- add `legacy code/` to `.gitignore` if you'd rather rely on git history.
3. Deleting files listed here is **not** recoverable from git unless committed first — the
`docs/legacy code/` copy is your safety net.