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:
@@ -0,0 +1,119 @@
|
||||
"""Verify the HTTPS-verify + automatic HTTP fallback in https_manager._apply.
|
||||
|
||||
This is the safety net the user asked for: if HTTPS is enabled but does not
|
||||
actually work, the server must not be left unreachable — it falls back to HTTP.
|
||||
|
||||
The TLS probe is monkey-patched so no live server is required.
|
||||
|
||||
Run: PYTHONPATH=$(pwd) ./.venv/bin/python docs/tools/test_https_fallback.py
|
||||
"""
|
||||
import importlib.util
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
TMPDIR = tempfile.mkdtemp()
|
||||
os.environ['DATABASE_URL'] = f'sqlite:///{TMPDIR}/fb.db'
|
||||
|
||||
from app.app import create_app # noqa: E402
|
||||
from app.extensions import db # noqa: E402
|
||||
from app.models.https_config import HTTPSConfig # noqa: E402
|
||||
|
||||
REPO = '/home/scheianu/digiserver-v2'
|
||||
TARGET = os.path.join(TMPDIR, 'Caddyfile')
|
||||
|
||||
app = create_app('production')
|
||||
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
|
||||
def load_manager(verify_result):
|
||||
"""Import https_manager with I/O redirected and the probe stubbed."""
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
'hm_fb', os.path.join(REPO, 'https_manager.py'))
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
mod.CaddyConfigGenerator.write_caddyfile = staticmethod(
|
||||
lambda content, path=TARGET: (open(TARGET, 'w').write(content), True)[1])
|
||||
mod.CaddyConfigGenerator.reload_caddy = staticmethod(lambda: True)
|
||||
mod.verify_https = lambda *a, **k: verify_result
|
||||
return mod
|
||||
|
||||
|
||||
def blocks():
|
||||
return [ln.strip() for ln in open(TARGET).read().splitlines()
|
||||
if ln.strip().startswith(('http://', 'https://', ':80'))]
|
||||
|
||||
|
||||
def config():
|
||||
with app.app_context():
|
||||
return HTTPSConfig.get_config()
|
||||
|
||||
|
||||
print('=' * 68)
|
||||
print('CASE A — HTTPS verifies OK -> stays enabled')
|
||||
print('=' * 68)
|
||||
open(TARGET, 'w').write(':80 {\n respond "x"\n}\n')
|
||||
hm = load_manager((True, 'HTTP 200 from https://192.168.0.152:443/api/health'))
|
||||
rc = hm._apply(app, https_enabled=True, hostname='digiserver', domain='',
|
||||
email=None, ip_address='192.168.0.152', port=443,
|
||||
http_fallback=True, verify=True)
|
||||
c = config()
|
||||
print('exit code:', rc, '| https_enabled:', c.https_enabled)
|
||||
print('blocks:', blocks())
|
||||
assert rc == 0
|
||||
assert c.https_enabled is True
|
||||
assert 'tls internal' in open(TARGET).read()
|
||||
print('PASS: HTTPS left enabled\n')
|
||||
|
||||
print('=' * 68)
|
||||
print('CASE B — HTTPS probe FAILS -> automatic HTTP fallback')
|
||||
print('=' * 68)
|
||||
open(TARGET, 'w').write(':80 {\n respond "x"\n}\n')
|
||||
hm = load_manager((False, 'ConnectionRefusedError: refused'))
|
||||
rc = hm._apply(app, https_enabled=True, hostname='digiserver', domain='',
|
||||
email=None, ip_address='192.168.0.152', port=443,
|
||||
http_fallback=True, verify=True)
|
||||
c = config()
|
||||
text = open(TARGET).read()
|
||||
print('exit code:', rc, '| https_enabled:', c.https_enabled)
|
||||
print('blocks:', blocks())
|
||||
assert rc == 1, f'expected exit 1 signalling the fallback, got {rc}'
|
||||
assert c.https_enabled is False, 'must revert to HTTP-only'
|
||||
assert 'tls internal' not in text, 'TLS must be removed after fallback'
|
||||
assert ':80 {' in text, 'HTTP must still be served'
|
||||
print('PASS: reverted to plain HTTP so the site stays reachable\n')
|
||||
|
||||
print('=' * 68)
|
||||
print('CASE C — verify disabled -> trusts the config, no probe')
|
||||
print('=' * 68)
|
||||
open(TARGET, 'w').write(':80 {\n respond "x"\n}\n')
|
||||
hm = load_manager((False, 'should never be called'))
|
||||
rc = hm._apply(app, https_enabled=True, hostname='digiserver', domain='',
|
||||
email=None, ip_address='192.168.0.152', port=443,
|
||||
http_fallback=True, verify=False)
|
||||
c = config()
|
||||
print('exit code:', rc, '| https_enabled:', c.https_enabled)
|
||||
assert rc == 0
|
||||
assert c.https_enabled is True, 'no probe => config trusted'
|
||||
assert 'tls internal' in open(TARGET).read()
|
||||
print('PASS: configuration trusted without probing\n')
|
||||
|
||||
print('=' * 68)
|
||||
print('CASE D — HTTP only (https disabled) is never probed')
|
||||
print('=' * 68)
|
||||
open(TARGET, 'w').write(':80 {\n respond "x"\n}\n')
|
||||
hm = load_manager((False, 'n/a'))
|
||||
rc = hm._apply(app, https_enabled=False, hostname=None, domain=None,
|
||||
email=None, ip_address=None, port=443,
|
||||
http_fallback=True, verify=True)
|
||||
c = config()
|
||||
print('exit code:', rc, '| https_enabled:', c.https_enabled)
|
||||
assert rc == 0
|
||||
assert c.https_enabled is False
|
||||
assert 'tls internal' not in open(TARGET).read()
|
||||
print('PASS: no TLS emitted, no probe performed\n')
|
||||
|
||||
shutil.rmtree(TMPDIR, ignore_errors=True)
|
||||
print('ALL FALLBACK CASES PASSED')
|
||||
Reference in New Issue
Block a user