From 0047e1b441648e5cdeba823cd4579cd39ff97d86 Mon Sep 17 00:00:00 2001 From: Do Siki Date: Mon, 17 Aug 2026 16:08:57 +0200 Subject: [PATCH] feat: harden content workflows and staging smoke tests --- .gitea/workflows/ci.yml | 87 ++++++ .gitignore | 5 + content-editor.js | 271 ++++++++++++++---- docs/contact-submission-persistence.md | 24 ++ docs/content-editor-recovery.md | 79 +++++ docs/staging-ui-smoke-test-plan.md | 64 +++++ proto/e2e/staging-smoke.spec.ts | 56 ++++ proto/package-lock.json | 85 ++++-- proto/package.json | 2 + proto/playwright.config.ts | 15 + proto/src/__tests__/integration.test.ts | 24 +- proto/src/app/api/contact/route.ts | 29 +- proto/src/app/api/contact/route.unit.test.ts | 53 ++++ proto/src/app/page.tsx | 2 +- proto/src/app/szolgaltatasok/page.tsx | 2 +- proto/src/content/index.ts | 25 +- proto/src/content/pages/about.json | 56 ++-- proto/src/content/pages/adatvedelem.json | 66 ++--- .../content/pages/hasznalati-feltetelek.json | 66 ++--- proto/src/content/pages/home.json | 160 +++++------ proto/src/content/pages/services.json | 128 +++++---- proto/src/content/schema.js | 102 +++++++ proto/src/content/types.ts | 27 +- proto/src/lib/mongodb.ts | 8 +- scripts/test-content-editor-save.js | 24 ++ scripts/test-content-editor-security.js | 23 ++ scripts/test-content-editor-serializer.js | 57 ++++ scripts/test-content-schema.js | 28 ++ 28 files changed, 1208 insertions(+), 360 deletions(-) create mode 100644 docs/contact-submission-persistence.md create mode 100644 docs/content-editor-recovery.md create mode 100644 docs/staging-ui-smoke-test-plan.md create mode 100644 proto/e2e/staging-smoke.spec.ts create mode 100644 proto/playwright.config.ts create mode 100644 proto/src/content/schema.js create mode 100644 scripts/test-content-editor-save.js create mode 100644 scripts/test-content-editor-security.js create mode 100644 scripts/test-content-editor-serializer.js create mode 100644 scripts/test-content-schema.js diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index bec2f25..ae8ed9f 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -8,6 +8,9 @@ on: branches: [main, develop] pull_request: branches: [main] + workflow_dispatch: + schedule: + - cron: '17 4 * * *' env: NODE_VERSION: '20' @@ -71,3 +74,87 @@ jobs: docker cp mozdit-app-ci:/app/.next/BUILD_ID ./build-id 2>/dev/null || \ echo "BUILD_ID nem elérhető — build verification skip" docker rm mozdit-app-ci + + docker-integration: + name: 🐳 Docker integration & API E2E + runs-on: ubuntu-latest + needs: test + steps: + - name: 📥 Checkout code + uses: actions/checkout@v4 + + - name: 📦 Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: proto/package-lock.json + + - name: 📥 Install dependencies + working-directory: ./proto + run: npm ci + + - name: 🐳 Start Docker development stack + run: docker compose -f docker-compose.dev.yml up --build -d + + - name: ⏳ Wait for application health + run: | + for attempt in $(seq 1 30); do + if curl --fail --silent http://localhost:8080/api/health > /dev/null; then + exit 0 + fi + sleep 2 + done + docker compose -f docker-compose.dev.yml logs --tail=100 + exit 1 + + - name: 🔗 Run integration tests + working-directory: ./proto + run: npm run test:integration + + - name: 🔄 Run API E2E tests + working-directory: ./proto + run: npm run test:e2e + + - name: 🧹 Stop Docker development stack + if: always() + run: docker compose -f docker-compose.dev.yml down -v + + staging-smoke: + name: 🌐 Staging Playwright smoke + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' + steps: + - name: 📥 Checkout code + uses: actions/checkout@v4 + + - name: 📦 Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: proto/package-lock.json + + - name: 📥 Install dependencies + working-directory: ./proto + run: npm ci + + - name: 🌐 Install Chromium for Playwright + working-directory: ./proto + run: npx playwright install --with-deps chromium + + - name: 🧪 Run staging smoke suite + working-directory: ./proto + env: + BASE_URL: https://stage.mozdit.hu + run: npm run test:smoke:staging + + - name: 📦 Upload Playwright artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: staging-smoke-artifacts + path: | + proto/playwright-report/ + proto/test-results/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index a708661..1430edf 100755 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,8 @@ mcp.json # Plane sync backup fájlok TODO.md.backup.* +.content-backups/ +.content-editor-audit.jsonl +.playwright-mcp/ +proto/playwright-report/ +proto/test-results/ diff --git a/content-editor.js b/content-editor.js index 6afc477..0f06c34 100644 --- a/content-editor.js +++ b/content-editor.js @@ -12,9 +12,17 @@ const http = require('http'); const fs = require('fs'); const path = require('path'); const { exec } = require('child_process'); +const crypto = require('crypto'); +const { validateContent } = require('./proto/src/content/schema'); const PORT = 4001; const CONTENT_DIR = path.join(__dirname, 'proto', 'src', 'content'); +const BACKUP_DIR = path.join(__dirname, '.content-backups'); +const MAX_REQUEST_BODY_BYTES = 256 * 1024; +const AUDIT_LOG_FILE = path.join(__dirname, '.content-editor-audit.jsonl'); +const RATE_LIMIT_WINDOW_MS = 15 * 60 * 1000; +const AUTH_MAX_ATTEMPTS = 5; +const PUBLISH_MAX_ATTEMPTS = 3; const FILES = { common: path.join(CONTENT_DIR, 'common.json'), @@ -36,7 +44,7 @@ const FILE_LABELS = { hasznalatiFeltetelek: '⚖️ ÁSZF', }; -const HTML = (activeFile, jsonData, message) => ` +const HTML = (activeFile, jsonData, message, csrfToken) => ` @@ -145,6 +153,7 @@ ${message ? `
${messag