From a3cefeef5248e45bd29234ce987bee733c1105e1 Mon Sep 17 00:00:00 2001 From: hafroese Date: Thu, 2 Apr 2026 23:42:14 +0200 Subject: [PATCH] feat: deployment setup - nginx, Caddy configs, deploy script, API smoke tests --- setup/caddy-edu.txt | 11 +++++++++ setup/deploy.sh | 25 ++++++++++++++++++++ setup/nginx-edu.conf | 31 ++++++++++++++++++++++++ tests/test-api.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 setup/caddy-edu.txt create mode 100644 setup/deploy.sh create mode 100644 setup/nginx-edu.conf create mode 100644 tests/test-api.sh diff --git a/setup/caddy-edu.txt b/setup/caddy-edu.txt new file mode 100644 index 0000000..2f02d9b --- /dev/null +++ b/setup/caddy-edu.txt @@ -0,0 +1,11 @@ +# Append to /opt/caddy/Caddyfile on the Pi + +edu.senex.de { + reverse_proxy webapps:8080 + header { + X-Frame-Options "DENY" + X-Content-Type-Options "nosniff" + Strict-Transport-Security "max-age=31536000; includeSubDomains" + Referrer-Policy "strict-origin-when-cross-origin" + } +} diff --git a/setup/deploy.sh b/setup/deploy.sh new file mode 100644 index 0000000..d192aac --- /dev/null +++ b/setup/deploy.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Deploy edu.senex.de to Pi +set -e + +PI_HOST="hafroes@192.168.10.65" +PI_PATH="/mnt/nas-services/webapps/sites/edu" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +LOCAL_EDU="$SCRIPT_DIR/../edu" + +echo "=== Deploying edu.senex.de ===" + +echo "Creating directories on Pi..." +ssh $PI_HOST "mkdir -p $PI_PATH/{css,js,api,content/flashcards,content/tutorials,content/cheatsheets}" + +echo "Uploading files..." +scp -r "$LOCAL_EDU/index.html" "$PI_HOST:$PI_PATH/" +scp -r "$LOCAL_EDU/css/"* "$PI_HOST:$PI_PATH/css/" +scp -r "$LOCAL_EDU/js/"* "$PI_HOST:$PI_PATH/js/" +scp -r "$LOCAL_EDU/api/"* "$PI_HOST:$PI_PATH/api/" +scp -r "$LOCAL_EDU/content/"* "$PI_HOST:$PI_PATH/content/" 2>/dev/null || true + +echo "Restarting PHP-FPM (opcache)..." +ssh $PI_HOST "docker restart php-fpm" + +echo "=== Deployed! Visit https://edu.senex.de ===" diff --git a/setup/nginx-edu.conf b/setup/nginx-edu.conf new file mode 100644 index 0000000..d9b076a --- /dev/null +++ b/setup/nginx-edu.conf @@ -0,0 +1,31 @@ +server { + listen 80; + server_name edu.senex.de; + root /var/www/html/edu; + index index.html; + charset utf-8; + + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/ { + try_files $uri /api/index.php$is_args$args; + } + + location ~ \.php$ { + fastcgi_pass php-fpm:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 7d; + add_header Cache-Control "public"; + } +} diff --git a/tests/test-api.sh b/tests/test-api.sh new file mode 100644 index 0000000..e43c152 --- /dev/null +++ b/tests/test-api.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# API smoke tests for edu.senex.de +BASE="${1:-https://edu.senex.de}" +COOKIE="/tmp/edu-test-cookies.txt" +PASS=0; FAIL=0 + +test_ep() { + local desc="$1" method="$2" path="$3" data="$4" expect="$5" + local code + if [ "$method" = "POST" ]; then + code=$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "Content-Type: application/json" -d "$data" -b "$COOKIE" -c "$COOKIE" "${BASE}${path}") + elif [ "$method" = "DELETE" ]; then + code=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE -b "$COOKIE" -c "$COOKIE" "${BASE}${path}") + else + code=$(curl -s -o /dev/null -w "%{http_code}" -b "$COOKIE" "${BASE}${path}") + fi + if [ "$code" = "$expect" ]; then echo " PASS: $desc ($code)"; ((PASS++)) + else echo " FAIL: $desc (expected $expect, got $code)"; ((FAIL++)); fi +} + +echo "=== edu.senex.de API Tests ===" +echo "Base: $BASE" + +echo "--- Auth ---" +test_ep "me without auth" GET "/api/me" "" "401" +test_ep "login wrong pass" POST "/api/login" '{"username":"harry","password":"wrong"}' "401" +test_ep "login" POST "/api/login" '{"username":"harry","password":"changeme"}' "200" +test_ep "me with auth" GET "/api/me" "" "200" + +echo "--- Decks ---" +test_ep "list decks" GET "/api/decks" "" "200" + +echo "--- Dashboard ---" +test_ep "dashboard" GET "/api/dashboard" "" "200" + +echo "--- Tutorials ---" +test_ep "list tutorials" GET "/api/tutorials" "" "200" + +echo "--- Cheatsheets ---" +test_ep "list cheatsheets" GET "/api/cheatsheets" "" "200" + +echo "--- Quiz ---" +test_ep "generate quiz" GET "/api/quiz/generate?count=3" "" "200" +test_ep "quiz history" GET "/api/quiz/history" "" "200" + +echo "--- Admin ---" +test_ep "import content" POST "/api/admin/import" '{}' "200" + +echo "--- Logout ---" +test_ep "logout" POST "/api/logout" '{}' "200" +test_ep "me after logout" GET "/api/me" "" "401" + +echo "" +echo "=== Results: $PASS passed, $FAIL failed ===" +rm -f "$COOKIE" +[ "$FAIL" -eq 0 ] && exit 0 || exit 1