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,179 @@
|
||||
#!/bin/bash
|
||||
# End-to-end runtime test of the HTTP/HTTPS + fallback behaviour.
|
||||
#
|
||||
# Spins up a stub backend + Caddy on a throwaway Docker network and proves:
|
||||
# 1. HTTPS disabled -> http://<ip>:PORT answers on plain HTTP
|
||||
# 2. HTTPS enabled -> https://<ip>:PORT answers over TLS (internal CA)
|
||||
# 3. HTTPS enabled -> http://<ip>:PORT STILL answers (fallback)
|
||||
# 4. Both the IP and a hostname resolve to the app
|
||||
#
|
||||
# Uses a stub backend so no 1.17 GB app image build is needed; the reverse-proxy
|
||||
# behaviour under test is entirely Caddy's.
|
||||
set -u
|
||||
|
||||
NET=e2e-caddy-net
|
||||
BACKEND=e2e-backend
|
||||
CADDY=e2e-caddy
|
||||
HTTP_PORT=18080
|
||||
HTTPS_PORT=18443
|
||||
IP=127.0.0.1
|
||||
|
||||
cleanup() {
|
||||
docker rm -f "$CADDY" "$BACKEND" >/dev/null 2>&1 || true
|
||||
docker network rm "$NET" >/dev/null 2>&1 || true
|
||||
rm -f /tmp/e2e_Caddyfile
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
cleanup
|
||||
docker network create "$NET" >/dev/null
|
||||
|
||||
# Stub "digiserver-app" serving a recognisable body on :5000
|
||||
docker run -d --name "$BACKEND" --network "$NET" --network-alias digiserver-app \
|
||||
python:3.13-slim \
|
||||
python -c "from http.server import BaseHTTPRequestHandler,HTTPServer
|
||||
class H(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
self.send_response(200); self.send_header('Content-Type','text/plain'); self.end_headers()
|
||||
self.wfile.write(b'BACKEND-OK')
|
||||
def log_message(self,*a): pass
|
||||
HTTPServer(('0.0.0.0',5000),H).serve_forever()" >/dev/null
|
||||
|
||||
echo "waiting for stub backend..."
|
||||
for i in $(seq 1 20); do
|
||||
docker exec "$BACKEND" python -c "
|
||||
import urllib.request,sys
|
||||
try:
|
||||
urllib.request.urlopen('http://localhost:5000/',timeout=1); sys.exit(0)
|
||||
except Exception: sys.exit(1)" 2>/dev/null && break
|
||||
sleep 1
|
||||
done
|
||||
echo "stub backend ready"
|
||||
echo
|
||||
|
||||
start_caddy() { # $1 = caddyfile content
|
||||
printf '%s' "$1" > /tmp/e2e_Caddyfile
|
||||
docker rm -f "$CADDY" >/dev/null 2>&1 || true
|
||||
docker run -d --name "$CADDY" --network "$NET" \
|
||||
-p "${HTTP_PORT}:80" -p "${HTTPS_PORT}:443" \
|
||||
-v /tmp/e2e_Caddyfile:/etc/caddy/Caddyfile:ro \
|
||||
caddy:2-alpine >/dev/null
|
||||
# wait for the admin API to accept connections
|
||||
for i in $(seq 1 25); do
|
||||
docker exec "$CADDY" wget -q -O- http://localhost:2019/config/ >/dev/null 2>&1 && return 0
|
||||
sleep 1
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
result() { # $1 label, $2 expected substring, $3 actual body
|
||||
if printf '%s' "$3" | grep -q "$2"; then
|
||||
echo " PASS $1"
|
||||
return 0
|
||||
fi
|
||||
echo " FAIL $1 (got: $(printf '%s' "$3" | head -c 80))"
|
||||
return 1
|
||||
}
|
||||
|
||||
FAILED=0
|
||||
|
||||
# ── Case 1: HTTPS disabled -> plain HTTP only ────────────────────────────────
|
||||
echo "CASE 1: HTTPS disabled -> plain HTTP on port $HTTP_PORT"
|
||||
start_caddy '{
|
||||
admin 0.0.0.0:2019
|
||||
}
|
||||
|
||||
:80 {
|
||||
reverse_proxy digiserver-app:5000
|
||||
}
|
||||
' || echo " (caddy admin not ready; continuing)"
|
||||
|
||||
BODY=$(curl -sS -m 5 "http://${IP}:${HTTP_PORT}/" 2>&1)
|
||||
result "http://IP answers" "BACKEND-OK" "$BODY" || FAILED=1
|
||||
|
||||
BODY=$(curl -sS -m 5 -H "Host: digiserver" "http://${IP}:${HTTP_PORT}/" 2>&1)
|
||||
result "http with Host: digiserver answers (catch-all)" "BACKEND-OK" "$BODY" || FAILED=1
|
||||
|
||||
echo
|
||||
|
||||
# ── Case 2/3: HTTPS on, internal CA, with HTTP fallback ──────────────────────
|
||||
echo "CASE 2+3: HTTPS on (internal CA) + HTTP fallback"
|
||||
# Generated by CaddyConfigGenerator for ip=127.0.0.1, hostname=digiserver.
|
||||
# `default_sni` is REQUIRED: browsers send no SNI when the URL is an IP, so
|
||||
# without it Caddy matches no certificate and aborts with
|
||||
# "no certificate available for '<container-ip>'".
|
||||
start_caddy "{
|
||||
admin 0.0.0.0:2019
|
||||
email admin@example.com
|
||||
default_sni ${IP}
|
||||
}
|
||||
|
||||
:80 {
|
||||
reverse_proxy digiserver-app:5000
|
||||
}
|
||||
|
||||
http://${IP} {
|
||||
reverse_proxy digiserver-app:5000
|
||||
}
|
||||
|
||||
http://digiserver {
|
||||
reverse_proxy digiserver-app:5000
|
||||
}
|
||||
|
||||
https://${IP} {
|
||||
tls internal
|
||||
reverse_proxy digiserver-app:5000
|
||||
}
|
||||
|
||||
https://digiserver {
|
||||
tls internal
|
||||
reverse_proxy digiserver-app:5000
|
||||
}
|
||||
" || echo " (caddy admin not ready; continuing)"
|
||||
|
||||
echo " (waiting for internal CA issuance)"
|
||||
for i in $(seq 1 15); do
|
||||
OUT=$(curl -sS -k -m 4 "https://${IP}:${HTTPS_PORT}/" 2>&1)
|
||||
printf '%s' "$OUT" | grep -q "BACKEND-OK" && break
|
||||
sleep 1
|
||||
done
|
||||
result "https://IP answers over TLS (SNI-less)" "BACKEND-OK" "$OUT" || FAILED=1
|
||||
|
||||
OUT2=$(curl -sS -k -m 6 --resolve "digiserver:${HTTPS_PORT}:${IP}" \
|
||||
"https://digiserver:${HTTPS_PORT}/" 2>&1)
|
||||
result "https://hostname answers over TLS (with SNI)" "BACKEND-OK" "$OUT2" || FAILED=1
|
||||
|
||||
BODY=$(curl -sS -m 5 "http://${IP}:${HTTP_PORT}/" 2>&1)
|
||||
result "http://IP STILL answers (fallback)" "BACKEND-OK" "$BODY" || FAILED=1
|
||||
|
||||
BODY=$(curl -sS -m 5 -H "Host: digiserver" "http://${IP}:${HTTP_PORT}/" 2>&1)
|
||||
result "http:// with Host: digiserver answers" "BACKEND-OK" "$BODY" || FAILED=1
|
||||
|
||||
HANDSHAKE_ERRORS=$(docker logs "$CADDY" 2>&1 | grep -ci "handshake error" || true)
|
||||
if [ "$HANDSHAKE_ERRORS" -eq 0 ]; then
|
||||
echo " PASS no TLS handshake errors logged"
|
||||
else
|
||||
echo " FAIL $HANDSHAKE_ERRORS TLS handshake error(s) logged"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
# Certificate must come from Caddy's local CA
|
||||
ISSUER=$(echo | openssl s_client -connect "${IP}:${HTTPS_PORT}" -servername localhost 2>/dev/null \
|
||||
| openssl x509 -noout -issuer 2>/dev/null)
|
||||
if printf '%s' "$ISSUER" | grep -qi "local\|caddy"; then
|
||||
echo " PASS certificate issued by the internal CA"
|
||||
echo " $ISSUER"
|
||||
else
|
||||
echo " WARN unexpected issuer: ${ISSUER:-none}"
|
||||
fi
|
||||
|
||||
echo
|
||||
docker logs "$CADDY" 2>&1 | grep -iE "error|cannot|fail" | head -5 || true
|
||||
|
||||
echo
|
||||
if [ "$FAILED" -eq 0 ]; then
|
||||
echo "ALL RUNTIME CHECKS PASSED"
|
||||
else
|
||||
echo "SOME RUNTIME CHECKS FAILED"
|
||||
fi
|
||||
exit "$FAILED"
|
||||
Reference in New Issue
Block a user