diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..5480c3f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Melde einen Fehler oder ein unerwartetes Verhalten. +title: "bug: " +labels: bug +assignees: '' +--- + +## Beschreibe den Fehler + +Schreibe kurz, was passiert, und wie man es reproduziert. + +## Reproduktion + +Schritte um den Fehler lokal zu reproduzieren: + +1. Schritt 1 +2. Schritt 2 + +## Erwartetes Verhalten + +Was hättest du erwartet? + +## Logs / Fehlermeldungen + +Füge relevante Fehlermeldungen, Stacktraces oder Links zu CI-Logs hinzu. diff --git a/.github/ISSUE_TEMPLATE/release-request.md b/.github/ISSUE_TEMPLATE/release-request.md new file mode 100644 index 0000000..2db044e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/release-request.md @@ -0,0 +1,30 @@ +--- +name: Release / Version bump +about: Verwende diese Vorlage, um eine neue Version oder Release anzufragen. +title: "chore(release): bump version to " +labels: release +assignees: '' +--- + +## Ziel + +Welche Version soll veröffentlicht werden? (SemVer) + +- Neue Version: `0.1.1` + +## Änderungen + +Kurze Liste der Änderungen, die in diesem Release enthalten sind (linke PRs oder Commits): + +- PR #12 — Fix: something +- PR #11 — Feat: add scaffold + +## Risiko und Rollback + +Gibt es breaking changes? Wie kann man zurückrollen? + +## Release-Steps (für Maintainer) + +1. Update `VERSION` auf die neue SemVer +2. Ergänze `CHANGELOG.md` mit einem Abschnitt für die Version +3. Commit, Tag und Push (siehe `.github/copilot-instructions.md`) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..cbc8850 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,216 @@ +````instructions +## Kurzanleitung für KI-Coding-Agenten + +Dieses Projekt enthält aktuell nur minimale Metadaten (z. B. `README.md`, `LICENSE`). Es liegen keine Quellcode-Dateien, Build- oder CI-Konfigurationen vor. Verwende die folgenden, konkreten Schritte, um produktiv zu starten, Entscheidungen nachzuweisen und sinnvolle PR-Vorschläge zu machen. + +1) Schnelle Repo-Discovery + +- Öffne und lese `README.md` und die Projekt-Root (bereits vorhanden). +- Prüfe systematisch auf typische Build-/Sprach-Indikatoren (Beispiele): + +```bash +# prüfe nach Node/Python/Go/Rust/Java/Gradle/Maven/Elterndateien +git ls-files | grep -E "package.json|pyproject.toml|requirements.txt|setup.py|go.mod|Cargo.toml|pom.xml|build.gradle|Makefile|Dockerfile" -n || true +``` + +- Falls keine der Dateien vorhanden ist (wie aktuell), dokumentiere das kurz in deinem PR und schlage eine sinnvolle erste Aufgabe vor (z. B. Projekt-Scaffold, README-Erweiterung, CI-Workflow). + +2) Architektur & "Big Picture" + +- In diesem Repository sind keine Komponenten oder Services gefunden. Wenn du neue Code-Dateien hinzufügst, beschreibe in der PR-Beschreibung immer: + - Ziel und Verantwortlichkeit der Komponente (Input/Output, Fehlerfälle) + - Wo Code leben soll (z. B. `src/`, `cmd/`, `pkg/` oder `lib/` je nach Sprache) + - Minimale Lauf-/Test-Schritte zum Verifizieren + +3) Projekt-spezifische Workflows (aktuell keine vorhanden) + +- Da kein Build/Test definiert ist, liefere in deinem Vorschlag eindeutige, automatisierte Schritte: + - Beispiel für Node.js: `npm install && npm test` + - Beispiel für Python: `python -m venv .venv && .venv/bin/pip install -r requirements.txt && pytest` + +- Wenn du CI hinzufügen willst, erstelle eine einfache GitHub Actions Workflow-Datei in `.github/workflows/` mit einem sehr schlanken Job (checkout + Setup + test). Verweise in der PR auf die minimalen Erfolgskriterien. + +4) Konventionen & Patterns (was du hier finden/setzen sollst) + +- Namenskonvention: Falls du ein Sprach-Scaffold anlegst, folge den üblichen Konventionen der Sprache (z. B. `src/` für Python/TS, `cmd/` + `pkg/` für Go). Dokumentiere die gewählte Konvention in `README.md`. +- Commit/PR-Nachrichten: kurze Titelzeile, 1–2 Zeilen Beschreibung, ein Abschnitt "How to test" mit den minimalen Schritten. + +5) Integration & externe Abhängigkeiten + +- Es sind keine Integration-Punkte (APIs, DBs, cloud infra) detektierbar. Wenn du solche hinzufügst, nenne in der PR: + - Endpunkte/Umgebungsvariablen (nur Namen, keine Geheimnisse) + - Minimal reproduzierbare Local-Run-Anleitung + +6) Beispiele & Templates (benutze diese Vorlagen in PRs) + +- PR-Beschreibung (kurz): + - Was wurde geändert + - Warum (Motivation) + - Wie zu testen (copy-paste Befehle) + +- Commit-Beispiel-Titel: `chore: scaffold project (language)` oder `feat: add initial CI workflow` + +7) Wenn du unsicher bist + +- Stelle eine präzise Frage in der PR-Description (z. B. "Welche Sprache bevorzugst du für dieses Projekt?") und biete 2-3 vorgeschlagene Optionen (z. B. Node.js scaffold, Python package, minimal Go module). + +8) Files to reference + +- `README.md` — aktuell die einzige inhaltliche Datei; erweitere sie bei allen größeren Änderungen. + +--- +Wenn du möchtest, kann ich sofort ein erstes Scaffolding vorschlagen (z. B. `node` oder `python`) und eine PR mit README-, CI- und Minimal-Test hinzufügen — nenne kurz welche Sprache/Stack du bevorzugst oder lass mich Vorschläge machen. + +## Versionierung & Releases + +Dieses Repository verwendet aktuell noch kein automatisches Release-System. Lege folgenden, einfachen Workflow an, damit Änderungen nachvollziehbar und reproduzierbar sind: + +- Datei `VERSION`: enthält die aktuelle Release-Version (SemVer), z. B. `0.1.0`. +- Datei `CHANGELOG.md`: chronologische Liste von Releases mit kurzen Beschreibungen. +- Release-Prozess (manuell für Start): + +```bash +# 1. Inkrementiere VERSION (z. B. 0.1.0 -> 0.1.1) +echo "0.1.1" > VERSION +# 2. Ergänze CHANGELOG.md mit Einträgen für die neue Version +# 3. Commit + Tag +git add VERSION CHANGELOG.md +git commit -m "chore(release): bump version to 0.1.1" +git tag -a v0.1.1 -m "Release v0.1.1" +git push --follow-tags +``` + +Hinweis für Agenten: Verwende SemVer (MAJOR.MINOR.PATCH). Schreibe in Pull Requests kurz in den Titel oder das Release-Issue, welche SemVer-Schritte erforderlich sind (z. B. `patch` für Bugfixes, `minor` für neue Features, `major` für breaking changes). + +Automatisierungsempfehlung: Später kann eine GitHub Actions-Workflowdatei (z. B. `.github/workflows/release.yml`) hinzugefügt werden, die bei Merge in `main` automatisch die Version taggt und ein GitHub Release erstellt. + +## Issue-Vorlage für Releases + +Im Ordner `.github/ISSUE_TEMPLATE/` liegt eine Vorlage, die verwendet werden soll, um Release/Version-Requests einheitlich zu erfassen. Nutze diese Vorlage, wenn du ein Release anstößt oder eine Versionserhöhung vorschlägst. + +--- + +## NETMASTER WIKI (lokal) + +Ich habe ein kleines lokales Wiki unter `netmaster_wiki/` hinzugefügt. Es ist ein Minimal‑Flask-Service, der folgende Funktionen bietet: + +- Web-UI zum Einfügen von Markdown (`/`) +- Seiten werden als `.md` in `netmaster_wiki/pages/` gespeichert (Front-Matter: title/date/tags) +- Button "Tags generieren" erzeugt Vorschläge per einfacher Heuristik (`/autotags`) +- Feedback/Autoupdater-Einträge werden in `netmaster_wiki/feedback.json` gesammelt (`/feedback`) + +Schnellstart: + +```bash +cd netmaster_wiki +python3 -m venv .venv +. .venv/bin/activate +pip install -r requirements.txt +python app.py +# Öffne http://127.0.0.1:5000 +``` + +Use-cases für Agenten: +- Beim Scaffolding: fülle `pages/` mit initialen HowTo‑Markdowns. +- Nach Änderungen an `VERSION`/`CHANGELOG.md`: erstelle optional einen Release-Eintrag im Wiki. +- Autoupdater-Feedback kann via UI oder POST an `/feedback` eingereicht werden. + +Dateien: +- `netmaster_wiki/app.py` — Hauptserver +- `netmaster_wiki/templates/` — UI-Templates +- `netmaster_wiki/static/` — CSS +- `netmaster_wiki/pages/` — gespeicherte Seiten (werden bei Bedarf angelegt) +- `netmaster_wiki/feedback.json` — gespeichertes Feedback (JSON-Array) + +```` +## Kurzanleitung für KI-Coding-Agenten + +Dieses Projekt enthält aktuell nur minimale Metadaten (z. B. `README.md`, `LICENSE`). Es liegen keine Quellcode-Dateien, Build- oder CI-Konfigurationen vor. Verwende die folgenden, konkreten Schritte, um produktiv zu starten, Entscheidungen nachzuweisen und sinnvolle PR-Vorschläge zu machen. + +1) Schnelle Repo-Discovery + +- Öffne und lese `README.md` und die Projekt-Root (bereits vorhanden). +- Prüfe systematisch auf typische Build-/Sprach-Indikatoren (Beispiele): + +```bash +# prüfe nach Node/Python/Go/Rust/Java/Gradle/Maven/Elterndateien +git ls-files | grep -E "package.json|pyproject.toml|requirements.txt|setup.py|go.mod|Cargo.toml|pom.xml|build.gradle|Makefile|Dockerfile" -n || true +``` + +- Falls keine der Dateien vorhanden ist (wie aktuell), dokumentiere das kurz in deinem PR und schlage eine sinnvolle erste Aufgabe vor (z. B. Projekt-Scaffold, README-Erweiterung, CI-Workflow). + +2) Architektur & "Big Picture" + +- In diesem Repository sind keine Komponenten oder Services gefunden. Wenn du neue Code-Dateien hinzufügst, beschreibe in der PR-Beschreibung immer: + - Ziel und Verantwortlichkeit der Komponente (Input/Output, Fehlerfälle) + - Wo Code leben soll (z. B. `src/`, `cmd/`, `pkg/` oder `lib/` je nach Sprache) + - Minimale Lauf-/Test-Schritte zum Verifizieren + +3) Projekt-spezifische Workflows (aktuell keine vorhanden) + +- Da kein Build/Test definiert ist, liefere in deinem Vorschlag eindeutige, automatisierte Schritte: + - Beispiel für Node.js: `npm install && npm test` + - Beispiel für Python: `python -m venv .venv && .venv/bin/pip install -r requirements.txt && pytest` + +- Wenn du CI hinzufügen willst, erstelle eine einfache GitHub Actions Workflow-Datei in `.github/workflows/` mit einem sehr schlanken Job (checkout + Setup + test). Verweise in der PR auf die minimalen Erfolgskriterien. + +4) Konventionen & Patterns (was du hier finden/setzen sollst) + +- Namenskonvention: Falls du ein Sprach-Scaffold anlegst, folge den üblichen Konventionen der Sprache (z. B. `src/` für Python/TS, `cmd/` + `pkg/` für Go). Dokumentiere die gewählte Konvention in `README.md`. +- Commit/PR-Nachrichten: kurze Titelzeile, 1–2 Zeilen Beschreibung, ein Abschnitt "How to test" mit den minimalen Schritten. + +5) Integration & externe Abhängigkeiten + +- Es sind keine Integration-Punkte (APIs, DBs, cloud infra) detektierbar. Wenn du solche hinzufügst, nenne in der PR: + - Endpunkte/Umgebungsvariablen (nur Namen, keine Geheimnisse) + - Minimal reproduzierbare Local-Run-Anleitung + +6) Beispiele & Templates (benutze diese Vorlagen in PRs) + +- PR-Beschreibung (kurz): + - Was wurde geändert + - Warum (Motivation) + - Wie zu testen (copy-paste Befehle) + +- Commit-Beispiel-Titel: `chore: scaffold project (language)` oder `feat: add initial CI workflow` + +7) Wenn du unsicher bist + +- Stelle eine präzise Frage in der PR-Description (z. B. "Welche Sprache bevorzugst du für dieses Projekt?") und biete 2-3 vorgeschlagene Optionen (z. B. Node.js scaffold, Python package, minimal Go module). + +8) Files to reference + +- `README.md` — aktuell die einzige inhaltliche Datei; erweitere sie bei allen größeren Änderungen. + +--- +Wenn du möchtest, kann ich sofort ein erstes Scaffolding vorschlagen (z. B. `node` oder `python`) und eine PR mit README-, CI- und Minimal-Test hinzufügen — nenne kurz welche Sprache/Stack du bevorzugst oder lass mich Vorschläge machen. + +## Versionierung & Releases + +Dieses Repository verwendet aktuell noch kein automatisches Release-System. Lege folgenden, einfachen Workflow an, damit Änderungen nachvollziehbar und reproduzierbar sind: + +- Datei `VERSION`: enthält die aktuelle Release-Version (SemVer), z. B. `0.1.0`. +- Datei `CHANGELOG.md`: chronologische Liste von Releases mit kurzen Beschreibungen. +- Release-Prozess (manuell für Start): + +```bash +# 1. Inkrementiere VERSION (z. B. 0.1.0 -> 0.1.1) +echo "0.1.1" > VERSION +# 2. Ergänze CHANGELOG.md mit Einträgen für die neue Version +# 3. Commit + Tag +git add VERSION CHANGELOG.md +git commit -m "chore(release): bump version to 0.1.1" +git tag -a v0.1.1 -m "Release v0.1.1" +git push --follow-tags +``` + +Hinweis für Agenten: Verwende SemVer (MAJOR.MINOR.PATCH). Schreibe in Pull Requests kurz in den Titel oder das Release-Issue, welche SemVer-Schritte erforderlich sind (z. B. `patch` für Bugfixes, `minor` für neue Features, `major` für breaking changes). + +Automatisierungsempfehlung: Später kann eine GitHub Actions-Workflowdatei (z. B. `.github/workflows/release.yml`) hinzugefügt werden, die bei Merge in `main` automatisch die Version taggt und ein GitHub Release erstellt. + +## Issue-Vorlage für Releases + +Im Ordner `.github/ISSUE_TEMPLATE/` liegt eine Vorlage, die verwendet werden soll, um Release/Version-Requests einheitlich zu erfassen. Nutze diese Vorlage, wenn du ein Release anstößt oder eine Versionserhöhung vorschlägst. + +--- + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0e4f232 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +Alle signifikanten Änderungen an diesem Projekt werden in diesem Dokument festgehalten. + +## [0.1.0] - 2025-11-01 +- Initiales Repository-Scaffold: `README.md`, `LICENSE`, `.github/copilot-instructions.md`, `VERSION`. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/netmaster_wiki/README.md b/netmaster_wiki/README.md new file mode 100644 index 0000000..3294407 --- /dev/null +++ b/netmaster_wiki/README.md @@ -0,0 +1,51 @@ +# NETMASTER WIKI + +Ein kleines, lokales Wiki für das NETCODE-Repository. Ziel ist ein schneller, lokaler Editor, mit dem Markdown-Seiten erstellt werden können, sowie eine einfache Feedback-Schnittstelle (z. B. für Autoupdater-Feedback). + +Start (lokal, Entwicklung): + +```bash +python3 -m venv .venv +. .venv/bin/activate +pip install -r requirements.txt +python app.py +# dann im Browser öffnen: http://127.0.0.1:5000 +``` + +Konzept +- Seiten werden unter `pages/` als Markdown-Dateien mit einfacher Front-Matter (title/date/tags) gespeichert. +- Tags werden automatisch per Heuristik aus dem Inhalt vorgeschlagen; es gibt ein UI-Button "Tags generieren". +- Feedback wird in `feedback.json` gesammelt. + +Hinweis: Dieses Tool ist minimal und für lokale Nutzung gedacht. Für produktive Nutzung sollte man Authentifizierung, Validation, Versionskontrolle und robuste Tagging-Logik ergänzen. + +Unsichere Quellen +----------------- +Das Wiki unterstützt optionales server-seitiges Abrufen externer URLs über den Endpoint `/fetch`. +Unsichere TLS-Verbindungen (z. B. verify=False) werden nur zugelassen, wenn: + +- Du einen `admin_token` in `netmaster_wiki/config.json` gesetzt hast (ändert `CHANGE_ME_TOKEN`). +- Der Request das Token in Header `X-Admin-Token` oder im JSON-Feld `token` liefert. +- Und entweder `allow_insecure_global` in `config.json` auf `true` gesetzt ist oder der Zielhost in `trusted_hosts` gelistet ist. + +Beispiel (curl) — sicherer Fetch: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{"url":"https://example.com"}' http://127.0.0.1:5000/fetch +``` + +Beispiel (curl) — unsicherer Fetch erlaubt (nur wenn token & trusted host konfiguriert sind): + +```bash +curl -X POST -H "Content-Type: application/json" -H "X-Admin-Token: CHANGE_ME_TOKEN" -d '{"url":"https://self-signed.example.local","insecure":true}' http://127.0.0.1:5000/fetch +``` + +Warnung: Diese Funktion ist mächtig und kann Sicherheitsrisiken bergen. Aktiviere sie nur in kontrollierten, lokalen oder vertrauenswürdigen Umgebungen. + +Browser-Admin-UI +---------------- +Im Wiki findest du jetzt eine Admin-Sektion auf der Index-Seite, in der du dein `admin_token` lokal im Browser (localStorage) speichern und serverseitige Fetches ausführen kannst. Das Token wird nicht an anderen Stellen im Repo gespeichert. + +Hinweis: Das Speichern des Tokens im Browser ist praktisch für lokale Betreiber, aber nicht so sicher wie ein serverseitig verwalteter Secret-Store. Nutze es nur lokal. + +Siehe auch die öffentliche Dokumentation zur Index-UI: `netmaster_wiki/docs/index.md` — dort sind UI‑Abläufe, Beispiele und Sicherheits‑Hinweise zusammengefasst. diff --git a/netmaster_wiki/__pycache__/app.cpython-312.pyc b/netmaster_wiki/__pycache__/app.cpython-312.pyc new file mode 100644 index 0000000..2c5df53 Binary files /dev/null and b/netmaster_wiki/__pycache__/app.cpython-312.pyc differ diff --git a/netmaster_wiki/app.py b/netmaster_wiki/app.py new file mode 100644 index 0000000..fd27d83 --- /dev/null +++ b/netmaster_wiki/app.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +"""Ein kleines, lokales Wiki für NETMASTER. + +Funktionen: +- Web-UI zum Einfügen von Markdown (Formular) +- Seiten als Markdown-Dateien in `pages/` speichern +- Automatische Schlagwort-Generierung (einfaches Heuristik-Counter) +- Feedback/Autoupdater-Einträge in `feedback.json` speichern + +Minimal abhängig von: Flask, markdown +""" +from flask import Flask, render_template, request, redirect, url_for, jsonify, abort +import os +import json +import re +from datetime import datetime +from collections import Counter +import urllib.parse +import requests + +# load config +CONFIG_PATH = os.path.join(BASE_DIR, "config.json") +if os.path.exists(CONFIG_PATH): + try: + with open(CONFIG_PATH, encoding='utf-8') as cf: + CONFIG = json.load(cf) + except Exception: + CONFIG = {} +else: + CONFIG = {} + +ADMIN_TOKEN = CONFIG.get('admin_token') +ALLOW_INSECURE_GLOBAL = bool(CONFIG.get('allow_insecure_global', False)) +TRUSTED_HOSTS = CONFIG.get('trusted_hosts', []) + +BASE_DIR = os.path.dirname(__file__) +PAGES_DIR = os.path.join(BASE_DIR, "pages") +FEEDBACK_FILE = os.path.join(BASE_DIR, "feedback.json") + +os.makedirs(PAGES_DIR, exist_ok=True) + +app = Flask(__name__, template_folder="templates", static_folder="static") + +# very small stopword list for tag extraction +STOPWORDS = set([ + "und","oder","der","die","das","ein","eine","in","auf","zu","mit", + "ist","sind","auch","von","für","als","an","bei","nach","als", + "the","a","of","and","to","is" +]) + + +def slugify(s: str) -> str: + s = s.lower() + s = re.sub(r"[^a-z0-9]+", "-", s) + s = s.strip("-") + s = re.sub(r"-+", "-", s) + return s or "untitled" + + +def extract_tags(text: str, top_n: int = 6): + words = re.findall(r"[a-zA-ZäöüÄÖÜß0-9]{3,}", text.lower()) + words = [w for w in words if w not in STOPWORDS] + cnt = Counter(words) + tags = [w for w, _ in cnt.most_common(top_n)] + return tags + + +def is_trusted_host(url: str) -> bool: + try: + p = urllib.parse.urlparse(url) + host = p.hostname or '' + for th in TRUSTED_HOSTS: + if host == th or host.endswith('.' + th): + return True + except Exception: + return False + return False + + +@app.route('/fetch', methods=['POST']) +def fetch_url(): + """Fetch a URL server-side. Allows insecure TLS only when explicitly authorized. + + JSON body: { "url": "...", "insecure": true/false } + Header or json field: 'X-Admin-Token' or 'token' must match CONFIG.admin_token to allow insecure fetches. + Only hosts listed in `trusted_hosts` may be fetched with insecure TLS when requested. + """ + data = request.get_json(force=True) or {} + url = data.get('url') or request.form.get('url') + if not url: + return jsonify({'ok': False, 'error': 'url required'}), 400 + insecure_req = bool(data.get('insecure', False) or request.form.get('insecure', False)) + token = request.headers.get('X-Admin-Token') or data.get('token') or request.form.get('token') + + # authorize insecure only if token matches admin and host is trusted or global flag set + if insecure_req: + if not ADMIN_TOKEN: + return jsonify({'ok': False, 'error': 'server not configured for insecure fetches'}), 403 + if token != ADMIN_TOKEN: + return jsonify({'ok': False, 'error': 'invalid admin token'}), 403 + if not (ALLOW_INSECURE_GLOBAL or is_trusted_host(url)): + return jsonify({'ok': False, 'error': 'host not allowed for insecure fetch'}), 403 + + try: + resp = requests.get(url, timeout=10, verify=not insecure_req) + return (resp.content, resp.status_code, {'Content-Type': resp.headers.get('Content-Type', 'application/octet-stream')}) + except Exception as e: + return jsonify({'ok': False, 'error': str(e)}), 502 + + + +def page_path(slug: str) -> str: + return os.path.join(PAGES_DIR, f"{slug}.md") + + +@app.route("/") +def index(): + # simple form to create pages and submit feedback + pages = [] + for fn in sorted(os.listdir(PAGES_DIR)): + if fn.endswith('.md'): + pages.append(fn[:-3]) + return render_template("index.html", pages=pages) + + +@app.route("/autotags", methods=["POST"]) +def autotags(): + data = request.get_json(force=True) or {} + text = data.get("text", "") + tags = extract_tags(text) + return jsonify({"tags": tags}) + + +@app.route("/save", methods=["POST"]) +def save(): + title = request.form.get("title", "Untitled").strip() + content = request.form.get("content", "").strip() + tags_in = request.form.get("tags", "").strip() + if not title and not content: + abort(400, "Title or content required") + slug = slugify(title or content[:40]) + if tags_in: + tags = [t.strip() for t in tags_in.split(',') if t.strip()] + else: + tags = extract_tags(title + "\n" + content) + meta = { + "title": title or slug, + "date": datetime.utcnow().isoformat() + "Z", + "tags": tags, + } + md = "---\n" + md += f"title: {meta['title']}\n" + md += f"date: {meta['date']}\n" + md += f"tags: {json.dumps(meta['tags'])}\n" + md += "---\n\n" + md += content + "\n" + with open(page_path(slug), "w", encoding="utf-8") as f: + f.write(md) + return redirect(url_for('view_page', slug=slug)) + + +@app.route("/pages") +def pages_list(): + pages = [] + for fn in sorted(os.listdir(PAGES_DIR)): + if fn.endswith('.md'): + pages.append(fn[:-3]) + return render_template("pages.html", pages=pages) + + +@app.route("/page/") +def view_page(slug): + path = page_path(slug) + if not os.path.exists(path): + abort(404) + with open(path, encoding="utf-8") as f: + text = f.read() + # naive split frontmatter + parts = text.split('---', 2) + if len(parts) >= 3: + _, meta_raw, body = parts + else: + meta_raw = "" + body = text + # try to parse tags from meta + tags = [] + m = re.search(r"tags:\s*(\[.*\])", meta_raw) + if m: + try: + tags = json.loads(m.group(1)) + except Exception: + tags = [] + # render markdown (if markdown lib is available) + try: + import markdown as md + html = md.markdown(body) + except Exception: + # fallback: show raw markdown in pre + html = f"
{body}
" + return render_template("page.html", title=slug, content=html, tags=tags) + + +@app.route("/feedback", methods=["POST"]) +def feedback(): + data = request.get_json(force=True) or {} + msg = data.get('message') or data.get('msg') or '' + source = data.get('source') or 'web' + who = data.get('who') or '' + if not msg: + return jsonify({"ok": False, "error": "message required"}), 400 + entry = { + "message": msg, + "who": who, + "source": source, + "ts": datetime.utcnow().isoformat() + 'Z' + } + if os.path.exists(FEEDBACK_FILE): + with open(FEEDBACK_FILE, encoding='utf-8') as f: + try: + arr = json.load(f) + except Exception: + arr = [] + else: + arr = [] + arr.append(entry) + with open(FEEDBACK_FILE, 'w', encoding='utf-8') as f: + json.dump(arr, f, indent=2, ensure_ascii=False) + return jsonify({"ok": True}) + + +if __name__ == '__main__': + # simple dev server + app.run(host='127.0.0.1', port=5000, debug=True) diff --git a/netmaster_wiki/config.json b/netmaster_wiki/config.json new file mode 100644 index 0000000..1200c56 --- /dev/null +++ b/netmaster_wiki/config.json @@ -0,0 +1,8 @@ +{ + "admin_token": "CHANGE_ME_TOKEN", + "allow_insecure_global": false, + "trusted_hosts": [ + "localhost", + "127.0.0.1" + ] +} diff --git a/netmaster_wiki/docs/index.md b/netmaster_wiki/docs/index.md new file mode 100644 index 0000000..86d56e3 --- /dev/null +++ b/netmaster_wiki/docs/index.md @@ -0,0 +1,70 @@ +# NETMASTER WIKI — Public Docs (Index UI) + +Diese Seite dokumentiert die öffentliche Benutzeroberfläche, die in `netmaster_wiki/templates/index.html` liegt. Sie beschreibt die Funktionalitäten, typische Abläufe und Sicherheits-Hinweise für Betreiber. + +Kurzüberblick + +- Editor: Ein einfaches Formular zum Anlegen von Markdown-Seiten (Titel + Inhalt). Beim Speichern wird die Seite unter `netmaster_wiki/pages/.md` abgelegt. +- Tags generieren: Button "Tags generieren" ruft `/autotags` auf und füllt das Tag-Feld mit Vorschlägen. +- Seitenliste: Unterhalb des Formulars werden vorhandene Seiten verlinkt. +- Autoupdater-Feedback: Formular sendet JSON an `/feedback` und speichert Einträge in `netmaster_wiki/feedback.json`. +- Admin-Fetch (nur Betreiber): Ein kleiner Admin‑Bereich erlaubt das Speichern eines Admin-Tokens im Browser (localStorage) und das Ausführen serverseitiger Fetches an `/fetch` (optional: unsichere TLS-Verbindungen, nur mit Token). + +How-to (quick) + +1. Lokalen Server starten (im Ordner `netmaster_wiki`): + +```bash +python3 -m venv .venv +. .venv/bin/activate +pip install -r requirements.txt +python app.py +# Öffne http://127.0.0.1:5000 +``` + +2. Neue Seite erstellen +- Titel und Markdown eingeben → Speichern. +- Tags: entweder manuell eingeben (kommagetrennt) oder "Tags generieren" klicken. + +3. Feedback absenden +- Nachricht in "Autoupdater Feedback" eingeben → Senden (POST `/feedback`). + +Admin-Fetch (Schritt-für-Schritt) + +1. Konfig: Lege `netmaster_wiki/config.json` an und setze `admin_token` (nicht `CHANGE_ME_TOKEN`), füge vertrauenswürdige Hosts zu `trusted_hosts` oder setze `allow_insecure_global` auf `true` (vorsichtig!). +2. In der Index-UI: Token eingeben → "Token speichern" (wird in localStorage gehalten). +3. URL eingeben, ggf. "Unsichere TLS zulassen" wählen und "Fetch ausführen" klicken. + +Beispiel: Server-seitiger Fetch (curl) + +Sicherer Fetch (kein Token nötig): + +```bash +curl -X POST -H "Content-Type: application/json" \ + -d '{"url":"https://example.com"}' \ + http://127.0.0.1:5000/fetch +``` + +Unsicherer Fetch (nur mit Token und Trusted Host): + +```bash +curl -X POST -H "Content-Type: application/json" -H "X-Admin-Token: " \ + -d '{"url":"https://self-signed.local","insecure":true}' \ + http://127.0.0.1:5000/fetch +``` + +Wichtige Sicherheits-Hinweise + +- Das Admin-Token, das in der UI gespeichert wird, liegt lokal im Browser (localStorage). Es wird nicht serverseitig geschützt; verwende diese Funktion nur lokal oder in einem vertrauenswürdigen Umfeld. +- Aktivieren von `allow_insecure_global` oder das Zulassen unsicherer TLS-Verbindungen öffnet Angriffsflächen. Nutze stattdessen `trusted_hosts` und setze dort explizit interne Domains. +- Für produktive Nutzung: HTTPS für das Wiki, serverseitige Secrets (Umgebungsvariablen), Authentifizierung, CSRF-Schutz und Ratenbegrenzung implementieren. + +Wichtige Dateipfade + +- `netmaster_wiki/templates/index.html` — die UI-Quelle. +- `netmaster_wiki/app.py` — Endpoints: `/`, `/save`, `/autotags`, `/pages`, `/page/`, `/feedback`, `/fetch`. +- `netmaster_wiki/pages/` — gespeicherte Markdown-Seiten. +- `netmaster_wiki/feedback.json` — gesammeltes Feedback. +- `netmaster_wiki/config.json` — (optional) Admin-Token, trusted hosts, flags. + +Wenn du möchtest, kann ich diese Docs als HTML-Seite rendern oder eine kleine Navigation im Repo-Root ergänzen (z. B. `docs/netmaster_index.md`), damit sie leichter öffentlich erreichbar ist. Sag Bescheid, wie du die Docs veröffentlichen willst. diff --git a/netmaster_wiki/requirements.txt b/netmaster_wiki/requirements.txt new file mode 100644 index 0000000..9d576e8 --- /dev/null +++ b/netmaster_wiki/requirements.txt @@ -0,0 +1,3 @@ +Flask>=2.0 +markdown>=3.0 +requests>=2.0 diff --git a/netmaster_wiki/static/style.css b/netmaster_wiki/static/style.css new file mode 100644 index 0000000..854cb9f --- /dev/null +++ b/netmaster_wiki/static/style.css @@ -0,0 +1,8 @@ +body{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial;margin:0;padding:1rem;background:#f7f7fb;color:#111} +main{max-width:900px;margin:0 auto;background:#fff;padding:1.25rem;border-radius:8px;box-shadow:0 6px 20px rgba(0,0,0,.06)} +label{display:block;margin:0.5rem 0} +input[type=text],textarea{width:100%;padding:.5rem;border:1px solid #ddd;border-radius:6px;font-family:inherit} +button{background:#2563eb;color:white;padding:.5rem .75rem;border-radius:6px;border:0} +.tags{margin:.5rem 0} +.tag{background:#eef2ff;padding:.25rem .5rem;border-radius:4px;margin-right:.25rem} +.content{border-top:1px dashed #eee;padding-top:1rem} diff --git a/netmaster_wiki/templates/index.html b/netmaster_wiki/templates/index.html new file mode 100644 index 0000000..7ffe9a2 --- /dev/null +++ b/netmaster_wiki/templates/index.html @@ -0,0 +1,141 @@ + + + + + + NETMASTER WIKI + + + +
+

NETMASTER WIKI

+
+
+ + + + +
+ + +
+
+
+ +
+

Seiten

+
    + {% for p in pages %} +
  • {{ p }}
  • + {% else %} +
  • Keine Seiten vorhanden
  • + {% endfor %} +
+
+ +
+

Autoupdater Feedback

+
+ +
+
+
+
+ +
+

Admin: Fetch externer Ressourcen

+

Nur für Betreiber: Du kannst hier ein Admin-Token lokal im Browser speichern und damit serverseitige Fetches (auch unsicher) anstoßen. Das Token wird nur im lokalen localStorage gespeichert.

+ +
+ + + +
+ + + +
+ +
+
+
+
+ + + + diff --git a/netmaster_wiki/templates/page.html b/netmaster_wiki/templates/page.html new file mode 100644 index 0000000..cb4bad7 --- /dev/null +++ b/netmaster_wiki/templates/page.html @@ -0,0 +1,19 @@ + + + + + + {{ title }} + + + +
+ ← Zurück +

{{ title }}

+ {% if tags %} +
Tags: {% for t in tags %}{{ t }}{% endfor %}
+ {% endif %} +
{{ content|safe }}
+
+ + diff --git a/netmaster_wiki/templates/pages.html b/netmaster_wiki/templates/pages.html new file mode 100644 index 0000000..1fdaf5a --- /dev/null +++ b/netmaster_wiki/templates/pages.html @@ -0,0 +1,22 @@ + + + + + + Seiten + + + +
+ ← Zurück +

Alle Seiten

+
    + {% for p in pages %} +
  • {{ p }}
  • + {% else %} +
  • Keine Seiten vorhanden
  • + {% endfor %} +
+
+ +