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
+206
View File
@@ -0,0 +1,206 @@
# 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.