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:
parent
f3a91e940e
commit
d808013395
5 changed files with 57 additions and 19 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}`);
|
||||
|
|
@ -838,7 +844,7 @@ $ python web/serve.py</div>
|
|||
} else {
|
||||
const textarea = document.getElementById('edit-textarea');
|
||||
const content = textarea.value;
|
||||
|
||||
|
||||
const response = await fetch(currentEditTemplate, {
|
||||
method: 'PUT',
|
||||
headers: {'Content-Type': 'text/plain'},
|
||||
|
|
@ -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');
|
||||
}
|
||||
|
||||
|
|
|
|||
25
web/serve.py
25
web/serve.py
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue