fix: 5 letzte 'niedrig'-Priorität Probleme aus Review behoben

- serve.py: explizite if/else Content-Type (GET), PUT antwortet mit passendem CT
- index.html: Original-Indent speichern/wiederverwenden, View-Modal-Status nach Edit
- smoke_test.sh: $$ statt $PORT für Log-PID, pkill mit TERM+Retry+SIGKILL
- agent_verify.sh: try/except FileNotFoundError für node-Check
- validate.py: re.fullmatch statt re.match für Pattern-Validierung
This commit is contained in:
Michael 2026-05-03 14:10:03 +02:00
parent f3a91e940e
commit d808013395
5 changed files with 57 additions and 19 deletions

View file

@ -14,7 +14,11 @@ html = open(sys.argv[1] if len(sys.argv) > 1 else 'web/index.html').read()
for i, s in enumerate(re.findall(r'<script[^>]*>([\s\S]*?)</script>', html)):
with tempfile.NamedTemporaryFile('w', suffix='.js', delete=True) as f:
f.write(s); p = f.name
r = subprocess.run(['node', '--check', p], capture_output=True, text=True)
try:
r = subprocess.run(['node', '--check', p], capture_output=True, text=True)
except FileNotFoundError:
print('WARN: node nicht gefunden — JS-Syntax-Check übersprungen')
sys.exit(0)
if r.returncode != 0:
print(f'JS[{i}] syntax error:\n{r.stderr}')
sys.exit(1)

View file

@ -11,15 +11,21 @@ set -euo pipefail
PORT="${1:-8082}"
[[ "$PORT" =~ ^[0-9]+$ ]] || { echo "Usage: $0 [port]" >&2; exit 1; }
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
LOG="/tmp/smoke.$PORT.log"
LOG="/tmp/smoke.$$.log"
EXIT_CODE=1
MARKER="SMOKE_TEST_SERVER_$PORT"
TIMEOUT=30
START_TIME=$(date +%s)
# Vorherige Instanzen dieses Smoke-Tests beenden
pkill -f "$MARKER" 2>/dev/null || true
sleep 0.1
# Präziser: nur Prozesse mit exaktem Marker-String killen
if pgrep -f "$MARKER" >/dev/null 2>&1; then
pkill -TERM -f "$MARKER" 2>/dev/null || true
sleep 0.5
if pgrep -f "$MARKER" >/dev/null 2>&1; then
pkill -9 -f "$MARKER" 2>/dev/null || true
fi
fi
# Pre-Check: serve.py muss sich kompilieren lassen. Sonst stirbt der
# Server beim Import und das Polling unten wartet umsonst — wir wollen
@ -49,7 +55,13 @@ cleanup() {
kill "$PID" 2>/dev/null || true
wait "$PID" 2>/dev/null || true
# Defensiv: falls der Kill-Signal den Python-Prozess nicht trifft
pkill -f "$MARKER" 2>/dev/null || true
if pgrep -f "$MARKER" >/dev/null 2>&1; then
pkill -TERM -f "$MARKER" 2>/dev/null || true
sleep 0.3
if pgrep -f "$MARKER" >/dev/null 2>&1; then
pkill -9 -f "$MARKER" 2>/dev/null || true
fi
fi
exit "$EXIT_CODE"
}
trap cleanup EXIT INT TERM

View file

@ -85,9 +85,15 @@ def validate_json_template(filepath: Path) -> Tuple[bool, List[str]]:
# Pattern validieren
if "pattern" in schema and isinstance(data[field], str):
if not re.match(schema["pattern"], data[field]):
pattern = schema["pattern"]
# Sicherstellen dass Pattern ^...$ hat für fullmatch
if not (pattern.startswith("^") and pattern.endswith("$")):
fullmatch_pattern = f"^{pattern}$"
else:
fullmatch_pattern = pattern
if not re.fullmatch(fullmatch_pattern, data[field]):
errors.append(
f"❌ Feld '{field}' entspricht nicht dem Pattern: {schema['pattern']}"
f"❌ Feld '{field}' entspricht nicht dem Pattern: {pattern}"
)
# Template prüfen

View file

@ -540,6 +540,8 @@ $ python web/serve.py</div>
let allTemplates = [];
let currentEditTemplate = null;
let editContainerRef = null;
let currentIndent = 2;
let wasViewModalOpen = false;
// XSS-schutz:_esc-Helper
function esc(s) {
@ -557,6 +559,7 @@ $ python web/serve.py</div>
currentEditTemplate = path;
const title = path.split('/').pop();
document.getElementById('edit-title').textContent = `Template bearbeiten: ${title}`;
wasViewModalOpen = document.getElementById('modal').classList.contains('active');
// Inhalt laden und editierbare Formulare abhängig vom Dateityp erstellen
fetch(path)
@ -568,6 +571,9 @@ $ python web/serve.py</div>
if (path.endsWith('.json')) {
try {
const jsonData = JSON.parse(content);
// Original-Indent erkennen
const indentMatch = content.match(/^\s{2,8}/m);
currentIndent = indentMatch ? indentMatch[0].length : 2;
createJsonEditUI(editContainer, jsonData, path);
} catch (e) {
// Falls JSON ungültig, als Text bearbeiten
@ -811,7 +817,7 @@ $ python web/serve.py</div>
}
const updatedData = extractJsonFromForm(formDiv);
const finalJsonString = JSON.stringify(updatedData, null, 2);
const finalJsonString = JSON.stringify(updatedData, null, currentIndent);
// Valid JSON prüfen
try {
@ -830,7 +836,7 @@ $ python web/serve.py</div>
if (response.ok) {
showToast('✓ Änderungen gespeichert');
closeEditModal();
closeModal();
if (wasViewModalOpen) closeModal();
viewTemplate(currentEditTemplate);
} else {
throw new Error(`HTTP ${response.status}`);
@ -848,7 +854,7 @@ $ python web/serve.py</div>
if (response.ok) {
showToast('✓ Änderungen gespeichert');
closeEditModal();
closeModal();
if (wasViewModalOpen) closeModal();
viewTemplate(currentEditTemplate);
} else {
throw new Error(`HTTP ${response.status}`);
@ -867,6 +873,7 @@ $ python web/serve.py</div>
}
function closeModal() {
wasViewModalOpen = true;
document.getElementById('modal').classList.remove('active');
}

View file

@ -121,10 +121,16 @@ class Handler(http.server.SimpleHTTPRequestHandler):
with open(file_path, "wb") as f:
f.write(file_content)
# Response Content-Type je nach Dateityp setzen
if file_path.endswith(".json"):
response_content_type = "application/json"
else:
response_content_type = "text/plain"
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.send_header("Content-type", response_content_type)
self.end_headers()
self.wfile.write(b"File saved successfully")
self.wfile.write("File saved successfully".encode())
except Exception as e:
self.send_error(500, f"Failed to save file: {e}")
@ -138,15 +144,18 @@ class Handler(http.server.SimpleHTTPRequestHandler):
# Anfragen für /templates.json oder /templates/* umleiten
file_path = self._validate_template_path(urlparse(self.path).path)
if file_path is not None:
# Content-Type je nach Dateiendung setzen
if file_path.endswith(".md"):
content_type = "text/plain"
elif file_path.endswith(".json"):
content_type = "application/json"
else:
content_type = "text/plain"
try:
with open(file_path, "rb") as f:
self.send_response(200)
self.send_header(
"Content-type",
"text/plain"
if file_path.endswith(".md")
else "application/json",
)
self.send_header("Content-type", content_type)
self.end_headers()
self.wfile.write(f.read())
return