From 1d3abc8cba058d30948aaef69cf7da2a77df687e Mon Sep 17 00:00:00 2001 From: Do Siki Date: Tue, 18 Aug 2026 13:20:44 +0200 Subject: [PATCH] =?UTF-8?q?feat(cms):=20add=20maintained=20user=20guide=20?= =?UTF-8?q?with=20S=C3=BAg=C3=B3=20menu=20entry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/felhasznaloi-utmutato.md: user guide for the website and the CMS (login, editing, arrays, save/validation, publish, security) - /guide endpoint renders the markdown auth-protected via a dependency-free renderer (scripts/markdown-render.js) in the CMS dark theme - new ❓ Súgó entry in the CMS bottom bar - steering rule: the guide must be updated in the same commit as any CMS or website feature change Closes MITHOME-57 --- .agent/steering/development-rules.md | 6 ++ TODO.md | 1 + content-editor.js | 63 ++++++++++++++- docs/felhasznaloi-utmutato.md | 87 +++++++++++++++++++++ scripts/markdown-render.js | 95 ++++++++++++++++++++++ scripts/test-content-editor-guide.js | 113 +++++++++++++++++++++++++++ 6 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 docs/felhasznaloi-utmutato.md create mode 100644 scripts/markdown-render.js create mode 100644 scripts/test-content-editor-guide.js diff --git a/.agent/steering/development-rules.md b/.agent/steering/development-rules.md index dc4b1bc..b48d62c 100644 --- a/.agent/steering/development-rules.md +++ b/.agent/steering/development-rules.md @@ -74,6 +74,12 @@ Kritikus hibák javításánál az alábbi sorrendet KÖTELEZŐ követni: git commit -m "fix(): " ``` +## Felhasználói dokumentáció karbantartása + +- A CMS **❓ Súgó** menüpontja a `docs/felhasznaloi-utmutato.md` fájlt rendereli (`/guide`). +- **Kötelező**: minden CMS- vagy honlapfunkció változtatásánál (új gomb, viselkedésváltozás, útvonal) ugyanabban a commitban frissítsd az útmutatót. +- Támogatott markdown forma a `scripts/markdown-render.js` részhalmaza: címsorok, **félkövér**, `kód`, listák, linkek, `---` elválasztók (táblázat nem). + ## Nyelvhasználat - **Kód, kommentek, commit üzenetek**: Angol diff --git a/TODO.md b/TODO.md index 405c092..a911f00 100755 --- a/TODO.md +++ b/TODO.md @@ -89,6 +89,7 @@ Next.js 15 alapú weboldal a mozdIT Bt. számára, Docker Compose-szal deployolv | MITHOME-39 | Production: közös natív MongoDB több alkalmazás biztonságos kiszolgálására | 📋 | | MITHOME-40 | Production MongoDB: automatizált mentés, visszaállítási próba és monitoring | 📋 | | MITHOME-55 | Contact API válaszformátum igazítása a projekt konvencióhoz | 📋 | +| MITHOME-57 | Felhasználói útmutató (CMS + Honlap) Súgó menüponttal | 📋 | --- diff --git a/content-editor.js b/content-editor.js index 8b7d462..bff15cd 100644 --- a/content-editor.js +++ b/content-editor.js @@ -14,12 +14,14 @@ const path = require('path'); const { exec } = require('child_process'); const crypto = require('crypto'); const { validateContent } = require('./proto/src/content/schema'); +const { renderMarkdown } = require('./scripts/markdown-render'); const PORT = Number(process.env.CONTENT_EDITOR_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 = process.env.CONTENT_EDITOR_AUDIT_FILE || path.join(__dirname, '.content-editor-audit.jsonl'); +const GUIDE_FILE = process.env.CONTENT_EDITOR_GUIDE_FILE || path.join(__dirname, 'docs', 'felhasznaloi-utmutato.md'); const RATE_LIMIT_WINDOW_MS = 15 * 60 * 1000; const AUTH_MAX_ATTEMPTS = 5; const PUBLISH_MAX_ATTEMPTS = 3; @@ -151,6 +153,7 @@ ${message ? `
${messag 🔗 Előnézet → + ❓ Súgó
@@ -458,8 +461,51 @@ render(DATA, document.getElementById('editor')); // Auto-dismiss toast const toast = document.querySelector('.toast'); -if (toast) setTimeout(() => toast.remove(), 3500); - + if (toast) setTimeout(() => toast.remove(), 3500); + + +`; + +// User guide page — renders docs/felhasznaloi-utmutato.md with the shared dark theme. +const GUIDE_PAGE = (contentHtml) => ` + + + + + mozdIT — Felhasználói útmutató + + + + +
+

mozdIT — Felhasználói útmutató

+ ← Vissza a szerkesztőhöz +
+ +
+${contentHtml} +
+ `; @@ -572,6 +618,19 @@ const server = http.createServer(async (req, res) => { return; } + // GET /guide — user guide rendered from the maintained markdown in the repo. + if (req.method === 'GET' && u.pathname === '/guide') { + let contentHtml; + try { + contentHtml = renderMarkdown(fs.readFileSync(GUIDE_FILE, 'utf8')); + } catch (error) { + contentHtml = '

Az útmutató jelenleg nem elérhető. Kérlek, szólj a fejlesztőnek.

'; + } + res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); + res.end(GUIDE_PAGE(contentHtml)); + return; + } + // POST /save — JSON body if (req.method === 'POST' && u.pathname === '/save') { let body = ''; diff --git a/docs/felhasznaloi-utmutato.md b/docs/felhasznaloi-utmutato.md new file mode 100644 index 0000000..2a112ae --- /dev/null +++ b/docs/felhasznaloi-utmutato.md @@ -0,0 +1,87 @@ +# mozdIT — Felhasználói útmutató + +Ez az útmutató a mozdIT weboldalt és a hozzá tartozó **Content Editor** (CMS) felületet írja le nem műszaki felhasználóknak. + +A dokumentum a repó része, és **folyamatosan karbantartott**: minden funkcióváltozásnál a fejlesztő frissíti. A CMS ❓ Súgó menüpontja ezt a fájlt jeleníti meg. + +--- + +## 1. A weboldal + +### Hol érhető el? + +- **Staging (teszt) oldal**: [https://stage.mozdit.hu](https://stage.mozdit.hu) — itt ellenőrizhetők a friss változtatások éles környezetben, még a véglegesítés előtt. +- A staging oldal tetején **sárga figyelmeztető sáv** jelzi, hogy tesztkörnyezetet látsz. + +### Oldalak + +- **Kezdőlap** — `https://stage.mozdit.hu/` +- **Rólunk** — `/rolunk` +- **Szolgáltatások** — `/szolgaltatasok` +- **Kapcsolat** — `/kapcsolat` (űrlap, ami beérkező üzenetként tárolódik) +- **Adatvédelmi tájékoztató** — `/adatvedelem` +- **Felhasználási feltételek** — `/felhasznalasi-feltetelek` + +### Hogyan változik a weboldal tartalma? + +1. A szerkesztő a **Content Editorban** módosítja a szövegeket (2. fejezet). +2. **💾 Mentés** — a módosítás elmentődik, azonnali biztonsági mentéssel. +3. **🚀 Publikálás** — a változtatás bekerül a Git repóba, és automatikusan deployol a staging oldalra. +4. Az éles (production) weboldalra a tartalom csak ellenőrzött, szándékos deploy lépéssel kerül fel — a CMS-ből soha nem publisholódik automatikusan productionre. + +--- + +## 2. Content Editor (CMS) + +### Belépés és kilépés + +- A CMS a kiadott címen érhető el (staging: `https://cms.stage.llmdev.mozdit.hu`). +- Belépés: a megadott **felhasználónév + jelszó** párossal (ezt az adminisztrátor adja). +- **🚪 Kilépés**: az alsó sáv gombja — anélkül jelentkezel ki, hogy be kellene zárnod a böngészőt. + +### Felület áttekintés + +- **Fájl fülek** (felül): oldalankénti tartalom — Kezdőlap, Rólunk, Szolgáltatások, Kapcsolat, jogi oldalak, közös szövegek. +- **Szerkesztőfelület**: a kiválasztott oldal összes szerkeszthető mezője. +- **Alsó sáv**: 💾 Mentés, 🚀 Publikálás, 🔗 Előnézet, ❓ Súgó, 🚪 Kilépés. + +### Szöveg szerkesztése + +- A mezők fölötti **útvonal** (pl. `hero.title`) jelzi, hol jelenik meg a szöveg az oldalon. +- Mezőtípusok: + - **Egysoros / több soros szövegmező** — általános szöveg; a hosszabb szöveg automatikusan nagyobb mezőben szerkeszthető. + - **Jelölőnégyzet** — be/ki (igen/nem) érték. + - **Számmező** — numerikus érték. +- A módosítás **nem kerül azonnal az oldalra** — ahhoz Mentés, majd Publikálás kell. + +### Listák szerkesztése + +- Lista elem (pl. egy jelszó, egy szolgáltatás tulajdonság): **❌ gombbal törölhető**. +- **➕ Új elem hozzáadása** gomb: új elem beszúrása a lista végére (üres, a meglévőkhöz hasonló űrlappal). +- Kártyás listáknál (pl. szolgáltatások) minden kártya külön törölhető a kártya alján lévő gombbal. + +### 💾 Mentés + +- A Mentés **ellenőrzi a tartalmat**: hiányzó vagy rossz típusú mező esetén hibaüzenetet kapsz, és a mentés nem történik meg — az oldal így nem tud elromlani. +- Minden sikeres mentés **biztonsági mentést** készít a szerveren (`.content-backups/`), és naplózza a műveletet. +- Ha a Mentés sikeres, a mentett állapotot **Előnézet** gombbal nézheted meg a staging oldalon. + +### 🚀 Publikálás + +- A Publikálás **commitolja és feltolja** a változtatásokat, majd elindítja a staging deployt. +- „No changes to commit" üzenet: nincs új változtatás — ez **nem hiba**. +- A publikálás korlátozva van (3 próbálkozás / 15 perc) a véletlen tömeges deploy elkerülésére. +- A deploy eltarthat 1-2 percig; az eredményt az Előnézet gombbal ellenőrizheted. + +### Biztonság + +- Több **sikertelen belépési kísérlet** után a rendszer átmenetileg letiltja a belépést a gépedről (kb. 15 percre). +- Minden mentés és publikálás **naplózva** van (audit log) a nyomonkövethetőség érdekében. + +--- + +## Karbantartás (fejlesztőknek) + +- Forrás: `docs/felhasznaloi-utmutato.md` — a CMS a `/guide` útvonalon rendereli ki. +- **Szabály**: minden CMS- vagy honlapfunkció változásnál frissítsd ezt a fájlt ugyanabban a commitban. +- Az útmutató támogatott formátuma: címsorok, **félkövér**, `kód`, listák, linkek, elválasztó vonalak. diff --git a/scripts/markdown-render.js b/scripts/markdown-render.js new file mode 100644 index 0000000..f59f561 --- /dev/null +++ b/scripts/markdown-render.js @@ -0,0 +1,95 @@ +// WHY: the Content Editor runs on system Node without node_modules, so the user +// guide (docs/felhasznaloi-utmutato.md) is rendered by this small dependency-free +// markdown renderer instead of an external library. +// Supported subset: headings (#..####), bold, inline code, links, ul/ol lists, +// fenced code blocks, horizontal rules, paragraphs. HTML is escaped first. + +function escapeHtml(value) { + return String(value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +function renderInline(text) { + return escapeHtml(text) + .replace(/`([^`]+)`/g, '$1') + .replace(/\*\*([^*]+)\*\*/g, '$1') + .replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '$1'); +} + +function renderMarkdown(markdown) { + const lines = String(markdown).split('\n'); + const out = []; + let listTag = null; // 'ul' | 'ol' + let inCode = false; + + const closeList = () => { + if (listTag) { + out.push(``); + listTag = null; + } + }; + + for (const raw of lines) { + const line = raw.trimEnd(); + + if (line.trim().startsWith('```')) { + closeList(); + out.push(inCode ? '' : '
');
+      inCode = !inCode;
+      continue;
+    }
+    if (inCode) {
+      out.push(escapeHtml(raw));
+      continue;
+    }
+    if (!line.trim()) {
+      closeList();
+      continue;
+    }
+
+    const heading = line.match(/^(#{1,4})\s+(.*)$/);
+    if (heading) {
+      closeList();
+      const level = heading[1].length;
+      out.push(`${renderInline(heading[2])}`);
+      continue;
+    }
+    if (/^(-{3,}|\*{3,})$/.test(line.trim())) {
+      closeList();
+      out.push('
'); + continue; + } + const unordered = line.match(/^\s*[-*]\s+(.*)$/); + if (unordered) { + if (listTag !== 'ul') { + closeList(); + out.push('
    '); + listTag = 'ul'; + } + out.push(`
  • ${renderInline(unordered[1])}
  • `); + continue; + } + const ordered = line.match(/^\s*\d+\.\s+(.*)$/); + if (ordered) { + if (listTag !== 'ol') { + closeList(); + out.push('
      '); + listTag = 'ol'; + } + out.push(`
    1. ${renderInline(ordered[1])}
    2. `); + continue; + } + + closeList(); + out.push(`

      ${renderInline(line)}

      `); + } + + closeList(); + if (inCode) out.push('
'); + return out.join('\n'); +} + +module.exports = { renderMarkdown, renderInline, escapeHtml }; diff --git a/scripts/test-content-editor-guide.js b/scripts/test-content-editor-guide.js new file mode 100644 index 0000000..16c11fa --- /dev/null +++ b/scripts/test-content-editor-guide.js @@ -0,0 +1,113 @@ +#!/usr/bin/env node + +/** + * Tests for the CMS user guide: + * 1. markdown renderer unit checks (headings, bold, code, lists, links, escaping) + * 2. /guide endpoint integration — auth-protected, serves the rendered guide + * 3. the main editor page contains the Súgó menu link + */ +const assert = require('assert/strict'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { spawn } = require('child_process'); + +const { renderMarkdown } = require('../scripts/markdown-render'); + +// ── 1. Markdown renderer ───────────────────────────────────────────────────── + +const rendered = renderMarkdown([ + '# Cím', + '', + 'Ez **félkövér** és `kód`, valamint [link](https://example.com).', + '', + '- első', + '- második', + '', + '1. lépés', + '2. lépés', + '', + '---', + '', + '', +].join('\n')); + +assert.match(rendered, /

Cím<\/h1>/); +assert.match(rendered, /félkövér<\/strong>/); +assert.match(rendered, /kód<\/code>/); +assert.match(rendered, /]*>link<\/a>/); +assert.match(rendered, /
    \s*
  • első<\/li>\s*
  • második<\/li>\s*<\/ul>/); +assert.match(rendered, /
      \s*
    1. lépés<\/li>\s*
    2. lépés<\/li>\s*<\/ol>/); +assert.match(rendered, /
      /); +// Raw HTML must be escaped, never executable +assert.doesNotMatch(rendered, /