Wunsch: "die sollen nirgendwo Hilfe anfordern koennen, sondern nur Rueckmeldungen." Es ist auch die klarere Loesung. Es gab zwei Wege, dasselbe zu sagen: einen Knopf, der eine Aufgabe erzeugte, und ein Textfeld, das eine Nachricht schrieb. Zwei Wege fuer eine Sache heisst, dass niemand weiss, welcher der richtige ist -- und dass Antworten mal als Aufgabe und mal als Nachricht landen. Wer dann nachsieht, findet die Haelfte nicht. Geblieben ist die Rueckmeldung an jedem Punkt: ein Verlauf, in dem beide Seiten schreiben koennen, sichtbar dort, wo es hingehoert -- am Punkt selbst und nicht in einer zweiten Liste. VOLLSTAENDIG entfernt, nicht nur ausgeblendet: * der Knopf in der Checkliste * der Knopf und die Marke "Hilfe moeglich" im Vorlagenblock * die Route /workspace/api/vorlagen/hilfe * 47 Datenfelder hilfe: true/false in den Vorlagen * die zugehoerigen Stile * der Abschnitt in pruef-vorlagen.mjs Datenfelder, die nichts mehr bewirken, und Routen, die niemand mehr aufruft, werden beim naechsten Mal fuer lebenden Code gehalten und mitgepflegt. Das ist teurer als das Entfernen heute. Co-Authored-By: Claude Opus 5 <[email protected]>
377 lines
23 KiB
JavaScript
377 lines
23 KiB
JavaScript
/* =====================================================================
|
||
workspace-vorlagen.js — fertige Inhalte, die man übernehmen kann.
|
||
|
||
Der Wunsch vom 01.09.2026, an fünf Stellen gleichlautend: "ich will
|
||
dass da schon fertige Sachen stehen". Eine leere Seite mit einem
|
||
Knopf "Neuer Eintrag" verlangt vom Creator genau das, was er noch
|
||
nicht kann — zu wissen, was überhaupt hineingehört.
|
||
|
||
WARUM VORLAGEN UND KEINE VORAUSGEFÜLLTEN DATEN.
|
||
Man könnte beim Anlegen eines Creators dreißig Einträge in seine
|
||
Datenbank schreiben. Das wäre falsch:
|
||
* Sie wären ab dem ersten Tag "seine" Einträge -- und damit
|
||
Altlast, sobald sich etwas ändert.
|
||
* Ändert Filipe später eine Formulierung, gilt sie nur für neue
|
||
Creator. Die alten behalten die alte für immer.
|
||
* Und die Liste sähe voll aus, obwohl noch nichts geschehen ist --
|
||
genau das Gegenteil einer ehrlichen Übersicht.
|
||
|
||
Vorlagen bleiben deshalb VORSCHLÄGE, bis jemand sie übernimmt. Sie
|
||
stehen im Code, gelten für alle sofort, und werden erst dann zu Daten,
|
||
wenn sie tatsächlich gebraucht werden. Wer sie übernimmt, bekommt
|
||
einen ganz normalen Eintrag, den er ändern und löschen kann.
|
||
|
||
FACHLICHE GRUNDLAGE der Inhalte:
|
||
* Die ersten ein bis drei Sekunden eines Kurzvideos entscheiden über
|
||
die Verbreitung -- deshalb hat jede Content-Vorlage einen Hook,
|
||
und zwar als eigenes Feld.
|
||
* Ein LIVE steht und fällt mit dem, was VOR der Sendung passiert.
|
||
Deshalb sind die Vorbereitungspunkte die längste Liste.
|
||
* Was während der Sendung auffällt, ist am nächsten Tag vergessen.
|
||
Deshalb gibt es Punkte für "während LIVE", die man in Sekunden
|
||
anklicken kann, statt sie zu tippen.
|
||
* 70/20/10: etwa 70 % Inhalte mit Wert, 20 % Community, 10 %
|
||
Eigenwerbung. Die Content-Vorlagen sind danach verteilt.
|
||
===================================================================== */
|
||
|
||
import express from "express";
|
||
import {
|
||
db, sitzungLesen, istLeitung, darfCreator, protokolliere, echteIp,
|
||
} from "./workspace.js";
|
||
|
||
export const vorlagenRouter = express.Router();
|
||
|
||
const jetzt = () => new Date().toISOString();
|
||
|
||
/* Welche Bereiche haben Punktelisten? Eine Zuordnung statt drei
|
||
Sonderfaellen im Code. */
|
||
const BEREICH_PUNKTE = {};
|
||
|
||
/* =====================================================================
|
||
CONTENT-IDEEN
|
||
|
||
Jede Vorlage bringt mit, was ein Video braucht, bevor man es dreht:
|
||
einen Aufhänger, ein Format und die Säule, zu der es gehört. Der
|
||
Aufhänger ist ausformuliert, nicht beschrieben -- "mach was
|
||
Persönliches" hilft niemandem, "Das hätte ich am Anfang gern gewusst"
|
||
kann man sagen.
|
||
===================================================================== */
|
||
|
||
export const CONTENT_IDEEN = [
|
||
/* ---- Wert (70 %) ---------------------------------------------------- */
|
||
{ schluessel: "das-haette-ich-am-anfang-gern-gewusst", titel: "Das hätte ich am Anfang gern gewusst",
|
||
hook: "„Ich hab drei Monate gebraucht, um das zu kapieren – du brauchst 30 Sekunden.\"",
|
||
format: "Talking Head", saeule: "Wert",
|
||
text: "Ein Fehler, den du selbst gemacht hast, und was du heute anders machst. "
|
||
+ "Konkret bleiben: ein Fehler, eine Lösung. Wer drei Dinge sagt, sagt nichts." },
|
||
{ schluessel: "mein-setup-fuer-unter-100", titel: "Mein Setup für unter 100 €",
|
||
hook: "„Alle sagen, du brauchst teure Technik. Hier ist meins für 87 Euro.\"",
|
||
format: "Zeigen & Erklären", saeule: "Wert",
|
||
text: "Was du wirklich benutzt, mit Preisen. Ehrlich sein, wo es hakt – "
|
||
+ "das ist der Teil, den niemand sonst zeigt." },
|
||
{ schluessel: "drei-einstellungen-die-sofort-was-bringe", titel: "Drei Einstellungen, die sofort was bringen",
|
||
hook: "„Wenn dein Stream unscharf aussieht, liegt es an einer davon.\"",
|
||
format: "Bildschirmaufnahme", saeule: "Wert",
|
||
text: "Nicht mehr als drei. Jede in unter 15 Sekunden erklärt, mit Vorher/Nachher." },
|
||
{ schluessel: "was-ich-in-einer-woche-gelernt-habe", titel: "Was ich in einer Woche gelernt habe",
|
||
hook: "„Sieben Tage, eine Erkenntnis – die hier hat alles verändert.\"",
|
||
format: "Talking Head", saeule: "Wert",
|
||
text: "Rückblick auf eine echte Woche. Funktioniert nur mit einer konkreten Zahl "
|
||
+ "oder einem konkreten Moment." },
|
||
{ schluessel: "antwort-auf-die-frage-die-immer-kommt", titel: "Antwort auf die Frage, die immer kommt",
|
||
hook: "„Das werde ich jeden Tag gefragt – hier einmal in Ruhe.\"",
|
||
format: "Talking Head", saeule: "Wert",
|
||
text: "Aus dem Chat sammeln, welche Frage sich wiederholt. Die Antwort einmal "
|
||
+ "gut aufnehmen und immer wieder verwenden." },
|
||
{ schluessel: "so-bereite-ich-einen-stream-vor", titel: "So bereite ich einen Stream vor",
|
||
hook: "„Zwei Stunden vor LIVE mache ich immer dasselbe.\"",
|
||
format: "Vlog", saeule: "Wert",
|
||
text: "Der Ablauf hinter der Sendung. Zeigt Verlässlichkeit und gibt Anfängern "
|
||
+ "eine Vorlage." },
|
||
{ schluessel: "fehler-die-ich-bei-anderen-sehe", titel: "Fehler, die ich bei anderen sehe",
|
||
hook: "„Bitte mach das nicht – ich sehe es jeden Tag.\"",
|
||
format: "Talking Head", saeule: "Wert",
|
||
text: "Niemanden bloßstellen. Das Verhalten beschreiben, nicht die Person." },
|
||
|
||
/* ---- Community (20 %) ------------------------------------------------ */
|
||
{ schluessel: "der-beste-moment-aus-dem-letzten-live", titel: "Der beste Moment aus dem letzten LIVE",
|
||
hook: "„Das ist gestern passiert, und ich rede immer noch davon.\"",
|
||
format: "Zusammenschnitt", saeule: "Community",
|
||
text: "Ein Ausschnitt, keine Zusammenfassung. Wer alles zeigt, zeigt nichts." },
|
||
{ schluessel: "ich-beantworte-eure-kommentare", titel: "Ich beantworte eure Kommentare",
|
||
hook: "„Ihr habt gefragt – jetzt rede ich.\"",
|
||
format: "Talking Head", saeule: "Community",
|
||
text: "Drei bis fünf echte Kommentare, sichtbar eingeblendet. Auch die kritischen." },
|
||
{ schluessel: "danke-an-die-die-immer-da-sind", titel: "Danke an die, die immer da sind",
|
||
hook: "„Ihr seid seit dem ersten Stream dabei. Das hier ist für euch.\"",
|
||
format: "Talking Head", saeule: "Community",
|
||
text: "Namen nennen, wenn die Leute einverstanden sind. Sonst allgemein bleiben – "
|
||
+ "aber ehrlich." },
|
||
{ schluessel: "eine-entscheidung-bei-der-ihr-mitredet", titel: "Eine Entscheidung, bei der ihr mitredet",
|
||
hook: "„Ich kann mich nicht entscheiden. Ihr habt 24 Stunden.\"",
|
||
format: "Umfrage", saeule: "Community",
|
||
text: "Echte Wahl, keine Scheinfrage. Das Ergebnis danach auch wirklich umsetzen." },
|
||
|
||
/* ---- Eigenwerbung (10 %) --------------------------------------------- */
|
||
{ schluessel: "wann-und-wo-ich-streame", titel: "Wann und wo ich streame",
|
||
hook: "„Immer dienstags und donnerstags – und ich fange pünktlich an.\"",
|
||
format: "Kurzclip", saeule: "Eigenwerbung",
|
||
text: "Fester Sendeplan als eigener Clip. Wiederholbar, sobald sich etwas ändert." },
|
||
{ schluessel: "was-diese-woche-ansteht", titel: "Was diese Woche ansteht",
|
||
hook: "„Drei Sachen, und eine davon habe ich noch nie gemacht.\"",
|
||
format: "Kurzclip", saeule: "Eigenwerbung",
|
||
text: "Vorschau auf die Woche. Kurz halten, sonst wird es eine Ankündigung "
|
||
+ "statt einer Einladung." },
|
||
];
|
||
|
||
/* =====================================================================
|
||
LIVE-PUNKTE
|
||
|
||
Drei Listen entlang der Sendung. Die mittlere ist die wichtigste und
|
||
zugleich die, die es sonst nirgends gibt: Was während des Streams
|
||
auffällt, ist am nächsten Tag weg. Deshalb sind diese Punkte so
|
||
formuliert, dass man sie in einem Moment anklicken kann, in dem man
|
||
eigentlich keine Zeit hat.
|
||
|
||
===================================================================== */
|
||
|
||
export const LIVE_PUNKTE = {
|
||
vorbereitung: [
|
||
{ schluessel: "ton-geprueft", titel: "Ton geprüft", text: "Eine Minute aufnehmen und anhören. Ton ist der Grund Nummer eins, warum Leute wieder gehen." },
|
||
{ schluessel: "licht-sitzt", titel: "Licht sitzt", text: "Gesicht heller als der Hintergrund. Kein Fenster im Rücken." },
|
||
{ schluessel: "verbindung-getestet", titel: "Verbindung getestet", text: "Upload-Geschwindigkeit geprüft, Kabel statt WLAN wenn möglich." },
|
||
{ schluessel: "titel-und-thema-stehen", titel: "Titel und Thema stehen", text: "Wer den Titel liest, weiß, was ihn erwartet – und ob es für ihn ist." },
|
||
{ schluessel: "ablauf-grob-im-kopf", titel: "Ablauf grob im Kopf", text: "Drei Blöcke reichen. Ein Stream ohne roten Faden fühlt sich für Zuschauer wie Warten an." },
|
||
{ schluessel: "angekuendigt", titel: "Angekündigt", text: "Mindestens einmal vorher gesagt, dass es losgeht – Story, Post oder Nachricht." },
|
||
{ schluessel: "moderation-geklaert", titel: "Moderation geklärt", text: "Weiß jemand Bescheid, der eingreifen kann, wenn es im Chat kippt?" },
|
||
{ schluessel: "technik-ersatz-bereit", titel: "Technik-Ersatz bereit", text: "Was passiert, wenn der Stream abbricht? Handy als Notlösung bereitlegen." },
|
||
],
|
||
mitschrift: [
|
||
{ schluessel: "guter-moment-aufheben", titel: "Guter Moment – aufheben", text: "Etwas ist gut gelaufen. Kurz notieren, woran es lag – daraus wird später ein Clip." },
|
||
{ schluessel: "hier-sind-leute-gegangen", titel: "Hier sind Leute gegangen", text: "Zeitpunkt merken. Später nachsehen, was in dem Moment passiert ist." },
|
||
{ schluessel: "frage-aus-dem-chat-die-wiederkommt", titel: "Frage aus dem Chat, die wiederkommt", text: "Wird oft gefragt? Dann gehört sie in ein eigenes Video." },
|
||
{ schluessel: "technik-hat-gehakt", titel: "Technik hat gehakt", text: "Ton weg, Bild eingefroren, Verzögerung – aufschreiben, solange man es noch weiß." },
|
||
{ schluessel: "unangenehme-situation-im-chat", titel: "Unangenehme Situation im Chat", text: "Was war es, wie ist es ausgegangen? Auch wenn es gut lief." },
|
||
{ schluessel: "neue-zuschauer-aufgetaucht", titel: "Neue Zuschauer aufgetaucht", text: "Woher kamen sie? Wurde jemand begrüßt oder ging es unter?" },
|
||
{ schluessel: "ich-wusste-nicht-weiter", titel: "Ich wusste nicht weiter", text: "Stelle merken, an der es gestockt hat. Dafür gibt es Hilfe." },
|
||
],
|
||
auswertung: [
|
||
{ schluessel: "was-hat-diesmal-getragen", titel: "Was hat diesmal getragen?", text: "Ein Ding. Nicht drei. Das eine, das man wiederholen will." },
|
||
{ schluessel: "was-war-zaeh", titel: "Was war zäh?", text: "Ehrlich sein. Die zähen Stellen sind die, an denen man wächst." },
|
||
{ schluessel: "zahlen-im-vergleich", titel: "Zahlen im Vergleich", text: "Zuschauer, Verweildauer, Kommentare – gegen das letzte Mal, nicht gegen andere." },
|
||
{ schluessel: "clips-gesichert", titel: "Clips gesichert", text: "Die guten Momente rausgeschnitten und abgelegt, solange man sie noch findet." },
|
||
{ schluessel: "eine-sache-fuer-naechstes-mal", titel: "Eine Sache für nächstes Mal", text: "Genau eine. Wer sich fünf Dinge vornimmt, macht keins davon." },
|
||
{ schluessel: "braucht-besprechung", titel: "Braucht Besprechung", text: "Etwas, das man nicht allein lösen kann – geht an die Betreuung." },
|
||
],
|
||
};
|
||
|
||
/* =====================================================================
|
||
CALL-LEITFÄDEN
|
||
|
||
Jeder Call endet mit To-dos -- das steht schon auf der Seite. Was
|
||
fehlte, ist der Weg dorthin. Diese Leitfäden sind bewusst kurz: Ein
|
||
Gespräch nach Fragebogen ist ein Verhör, kein Gespräch.
|
||
===================================================================== */
|
||
|
||
/* =====================================================================
|
||
COMMUNITY-PUNKTE
|
||
|
||
Moderation ist die Arbeit, die niemand sieht, solange sie
|
||
funktioniert. Die Punkte hier sind so gewaehlt, dass sie VOR dem
|
||
Ernstfall abgehakt werden koennen -- danach ist es zu spaet.
|
||
===================================================================== */
|
||
|
||
export const COMMUNITY_PUNKTE = {
|
||
moderation: [
|
||
{ schluessel: "regeln-stehen-sichtbar", titel: "Regeln stehen sichtbar", text: "Kurz, verstaendlich, an einer Stelle, die man findet. Drei Saetze reichen." },
|
||
{ schluessel: "jemand-kann-eingreifen", titel: "Jemand kann eingreifen", text: "Waehrend des Streams ist eine zweite Person mit Rechten da -- oder es ist klar, dass keine da ist." },
|
||
{ schluessel: "wortfilter-eingerichtet", titel: "Wortfilter eingerichtet", text: "Die offensichtlichen Sachen automatisch weg, damit man sich um den Rest kuemmern kann." },
|
||
{ schluessel: "was-passiert-bei-einem-verstoss", titel: "Was passiert bei einem Verstoss?", text: "Verwarnen, stummschalten, sperren -- vorher entscheiden, nicht im Moment." },
|
||
{ schluessel: "umgang-mit-minderjaehrigen", titel: "Umgang mit Minderjaehrigen", text: "Wie erkennt man sie, und was gilt dann? Das ist keine Ermessensfrage." },
|
||
],
|
||
aktion: [
|
||
{ schluessel: "neue-begruessen", titel: "Neue begruessen", text: "Wer zum ersten Mal da ist, wird angesprochen. Kostet zwei Sekunden und entscheidet, ob jemand bleibt." },
|
||
{ schluessel: "stammleute-erkennen", titel: "Stammleute erkennen", text: "Namen merken oder notieren. Wiedererkannt zu werden ist der halbe Grund wiederzukommen." },
|
||
{ schluessel: "etwas-geben-das-nur-hier-gibt", titel: "Etwas geben, das nur hier gibt", text: "Ein Insider, ein Ritual, ein Spitzname. Community entsteht aus Gemeinsamem." },
|
||
{ schluessel: "ausserhalb-des-streams-antworten", titel: "Ausserhalb des Streams antworten", text: "Kommentare und Nachrichten -- die Community lebt auch zwischen den Sendungen." },
|
||
],
|
||
konflikt: [
|
||
{ schluessel: "ruhig-bleiben-und-nicht-oeffentlich-stre", titel: "Ruhig bleiben und nicht oeffentlich streiten", text: "Im Chat gewinnt niemand. Kurz ansagen, dann privat klaeren." },
|
||
{ schluessel: "was-ist-genau-passiert", titel: "Was ist genau passiert?", text: "Zeitpunkt, Beteiligte, Wortlaut. Ohne das ist jede Entscheidung angreifbar." },
|
||
{ schluessel: "beweise-sichern", titel: "Beweise sichern", text: "Bildschirmfoto oder Ausschnitt, solange es noch da ist. Chats verschwinden." },
|
||
{ schluessel: "betreuung-einbeziehen", titel: "Betreuung einbeziehen", text: "Bei allem, was groesser ist als ein blöder Kommentar. Dafuer ist sie da." },
|
||
{ schluessel: "danach-was-hat-gefehlt", titel: "Danach: was hat gefehlt?", text: "Regel unklar, niemand zum Eingreifen da, zu spaet gemerkt -- daraus wird der naechste Punkt oben." },
|
||
],
|
||
};
|
||
|
||
/* =====================================================================
|
||
TECHNIK-PUNKTE
|
||
|
||
Technik faellt immer im ungeeignetsten Moment aus. Die Punkte sind
|
||
deshalb danach sortiert, was am haeufigsten schiefgeht -- und Ton
|
||
steht vorn, weil schlechter Ton der Grund Nummer eins ist, warum
|
||
Leute einen Stream verlassen.
|
||
===================================================================== */
|
||
|
||
export const TECHNIK_PUNKTE = {
|
||
setup: [
|
||
{ schluessel: "mikrofon-richtig-eingestellt", titel: "Mikrofon richtig eingestellt", text: "Nicht uebersteuert, nicht zu leise, kein Rauschen. Einmal aufnehmen und anhoeren." },
|
||
{ schluessel: "kamera-scharf-und-gut-belichtet", titel: "Kamera scharf und gut belichtet", text: "Gesicht heller als der Hintergrund. Automatik oft abschalten -- sie pumpt." },
|
||
{ schluessel: "szenen-im-streaming-programm-angelegt", titel: "Szenen im Streaming-Programm angelegt", text: "Start, Vollbild, Pause, Ende. Wer waehrend der Sendung baut, sendet Chaos." },
|
||
{ schluessel: "bitrate-zur-leitung-passend", titel: "Bitrate zur Leitung passend", text: "Lieber stabil und etwas weicher als scharf mit Aussetzern." },
|
||
{ schluessel: "alles-einmal-komplett-getestet", titel: "Alles einmal komplett getestet", text: "Eine Probesendung von fuenf Minuten, aufgezeichnet und angesehen." },
|
||
],
|
||
problem: [
|
||
{ schluessel: "ton-weg-oder-verzerrt", titel: "Ton weg oder verzerrt", text: "Wann genau, welches Geraet, was lief gleichzeitig? Ohne das findet es niemand." },
|
||
{ schluessel: "bild-ruckelt-oder-friert-ein", titel: "Bild ruckelt oder friert ein", text: "Auslastung, Leitung, Aufloesung -- eins nach dem anderen ausschliessen." },
|
||
{ schluessel: "stream-bricht-ab", titel: "Stream bricht ab", text: "Zeitpunkt notieren und im Protokoll des Programms nachsehen." },
|
||
{ schluessel: "ton-und-bild-laufen-auseinander", titel: "Ton und Bild laufen auseinander", text: "Meist ein Puffer. Wann faellt es auf, sofort oder erst spaeter?" },
|
||
],
|
||
loesung: [
|
||
{ schluessel: "was-hat-es-behoben", titel: "Was hat es behoben?", text: "Genau die eine Aenderung aufschreiben, nicht alles was probiert wurde." },
|
||
{ schluessel: "kommt-es-wieder", titel: "Kommt es wieder?", text: "Beim naechsten Mal nachsehen. Einmal weg ist nicht geloest." },
|
||
{ schluessel: "einstellung-gesichert", titel: "Einstellung gesichert", text: "Damit sie nach einem Update nicht wieder weg ist." },
|
||
],
|
||
};
|
||
export const CALL_LEITFAEDEN = [
|
||
{ schluessel: "erstgespraech", titel: "Erstgespräch",
|
||
text: "Wer bist du, was machst du, was willst du erreichen? · Was läuft schon gut? · "
|
||
+ "Was nervt dich gerade am meisten? · Wie viel Zeit hast du wirklich? · "
|
||
+ "Was erwartest du von uns – und was erwarten wir von dir?" },
|
||
{ schluessel: "wochen-call", titel: "Wochen-Call",
|
||
text: "Was ist seit dem letzten Mal passiert? · Was ist liegen geblieben und warum? · "
|
||
+ "Zahlen kurz ansehen – eine Auffälligkeit reicht · Was steht diese Woche an? · "
|
||
+ "Brauchst du irgendwo Hilfe?" },
|
||
{ schluessel: "wenn-es-gerade-nicht-laeuft", titel: "Wenn es gerade nicht läuft",
|
||
text: "Zuerst zuhören, nicht lösen · Seit wann geht es so? · Was hat sich geändert? · "
|
||
+ "Was war das letzte Mal, als es gut war? · Eine kleine Sache vereinbaren, "
|
||
+ "nicht fünf große" },
|
||
{ schluessel: "monats-review", titel: "Monats-Review",
|
||
text: "Die drei Ziele vom letzten Mal durchgehen · Was ist erreicht, was nicht? · "
|
||
+ "Woran lag es – Zeit, Können oder Motivation? · Drei neue Ziele, verbindlich · "
|
||
+ "Termin für das nächste Review festhalten" },
|
||
];
|
||
|
||
/* Erst hier gefuellt -- die Listen stehen weiter oben und muessen
|
||
definiert sein, bevor sie eingetragen werden. */
|
||
BEREICH_PUNKTE.live = LIVE_PUNKTE;
|
||
BEREICH_PUNKTE.community = COMMUNITY_PUNKTE;
|
||
BEREICH_PUNKTE.technik = TECHNIK_PUNKTE;
|
||
|
||
/* ---------- Schnittstelle ------------------------------------------------ */
|
||
|
||
function angemeldet(req, res, next) {
|
||
const person = sitzungLesen(req);
|
||
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||
req.person = person;
|
||
next();
|
||
}
|
||
vorlagenRouter.use("/workspace/api/vorlagen", angemeldet);
|
||
|
||
vorlagenRouter.get("/workspace/api/vorlagen", (req, res) => {
|
||
res.json({
|
||
content: CONTENT_IDEEN,
|
||
live: LIVE_PUNKTE,
|
||
community: COMMUNITY_PUNKTE,
|
||
technik: TECHNIK_PUNKTE,
|
||
calls: CALL_LEITFAEDEN,
|
||
});
|
||
});
|
||
|
||
/* ---------- Übernehmen ---------------------------------------------------
|
||
|
||
Aus einer Vorlage wird ein ganz normaler Eintrag: änderbar, löschbar,
|
||
und ab dann unabhängig von der Vorlage. Wer die Vorlage später
|
||
ändert, ändert damit NICHT, was schon übernommen wurde -- das wäre
|
||
eine böse Überraschung.
|
||
|
||
Creator dürfen ausdrücklich selbst übernehmen. Es ist ihre Arbeit;
|
||
sie sollen nicht darauf warten müssen, dass ein Betreuer für sie eine
|
||
Idee anlegt. */
|
||
|
||
function gleicheHerkunft(req, res, next) {
|
||
const herkunft = req.get("origin");
|
||
if (!herkunft) return next();
|
||
let erlaubt;
|
||
try { erlaubt = new URL(herkunft).host === req.get("host"); } catch { erlaubt = false; }
|
||
if (!erlaubt) return res.status(403).json({ fehler: "fremde_herkunft" });
|
||
next();
|
||
}
|
||
|
||
vorlagenRouter.post("/workspace/api/vorlagen/uebernehmen", gleicheHerkunft,
|
||
express.json({ limit: "16kb" }), (req, res) => {
|
||
try {
|
||
const art = String(req.body.art || "");
|
||
const nr = Number(req.body.nr);
|
||
/* Für wen? Ein Creator immer für sich; Betreuer für den Creator,
|
||
den sie gerade ansehen. */
|
||
let creatorId = req.person.rolle === "creator"
|
||
? req.person.id
|
||
: Number(req.body.creator_id) || null;
|
||
|
||
if (req.person.rolle !== "creator" && creatorId && !darfCreator(req.person, creatorId)) {
|
||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||
}
|
||
|
||
const angelegt = [];
|
||
const einfuegen = (bereich, unterart, titel, text, zusatz = {}) => {
|
||
db().prepare(`INSERT INTO eintraege
|
||
(bereich, art, titel, text, datum, status, creator_id, hook, format,
|
||
erstellt, erstellt_von)
|
||
VALUES (?,?,?,?,?,?,?,?,?,?,?)`)
|
||
.run(bereich, unterart, titel, text, jetzt().slice(0, 10), "offen",
|
||
creatorId, zusatz.hook || null, zusatz.format || null,
|
||
jetzt(), req.person.id);
|
||
angelegt.push(titel);
|
||
};
|
||
|
||
if (art === "content") {
|
||
const v = CONTENT_IDEEN[nr];
|
||
if (!v) return res.status(400).json({ fehler: "Unbekannte Vorlage." });
|
||
einfuegen("content", "idee", v.titel, v.text, { hook: v.hook, format: v.format });
|
||
|
||
} else if (art === "content_alle") {
|
||
for (const v of CONTENT_IDEEN) {
|
||
einfuegen("content", "idee", v.titel, v.text, { hook: v.hook, format: v.format });
|
||
}
|
||
|
||
} else if (BEREICH_PUNKTE[art]) {
|
||
/* live, community, technik -- alle drei funktionieren gleich.
|
||
Drei fast gleiche Bloecke waeren der sichere Weg dazu, dass
|
||
der dritte irgendwann anders reagiert als die ersten beiden. */
|
||
const unterart = String(req.body.unterart || "");
|
||
const liste = BEREICH_PUNKTE[art][unterart];
|
||
if (!liste) return res.status(400).json({ fehler: "Unbekannter Abschnitt." });
|
||
const v = liste[nr];
|
||
if (!v) return res.status(400).json({ fehler: "Unbekannte Vorlage." });
|
||
einfuegen(art, unterart, v.titel, v.text);
|
||
|
||
} else if (BEREICH_PUNKTE[art.replace(/_alle$/, "")] && art.endsWith("_alle")) {
|
||
const bereich = art.replace(/_alle$/, "");
|
||
const unterart = String(req.body.unterart || "");
|
||
const liste = BEREICH_PUNKTE[bereich][unterart];
|
||
if (!liste) return res.status(400).json({ fehler: "Unbekannter Abschnitt." });
|
||
for (const v of liste) einfuegen(bereich, unterart, v.titel, v.text);
|
||
|
||
} else {
|
||
return res.status(400).json({ fehler: "Unbekannte Art." });
|
||
}
|
||
|
||
protokolliere("vorlage_uebernommen", req.person, echteIp(req),
|
||
`${art}: ${angelegt.length} Eintrag/Einträge`);
|
||
res.json({ angelegt: angelegt.length, titel: angelegt });
|
||
} catch (fehler) {
|
||
console.error("[workspace] Vorlage übernehmen:", fehler?.message);
|
||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||
}
|
||
});
|
||
|
||
/* Die frühere Route /vorlagen/hilfe ist am 01.09.2026 entfallen.
|
||
Sie machte aus einer Bitte eine Aufgabe. Das war gut gemeint, aber
|
||
es gab damit zwei Wege, dasselbe zu sagen -- und Antworten landeten
|
||
mal als Aufgabe, mal als Nachricht. Geblieben ist die Rückmeldung an
|
||
jedem Punkt (workspace-checkliste.js).
|
||
|
||
Entfernt statt auskommentiert: Eine Route, die niemand mehr aufruft,
|
||
wird beim nächsten Mal für lebenden Code gehalten und mitgepflegt. */
|