<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://wiki.megbes.de/index.php?action=history&amp;feed=atom&amp;title=Web-Frontend_entwickeln</id>
	<title>Web-Frontend entwickeln - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.megbes.de/index.php?action=history&amp;feed=atom&amp;title=Web-Frontend_entwickeln"/>
	<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;action=history"/>
	<updated>2026-08-06T21:32:23Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in MegBes Wiki</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=72&amp;oldid=prev</id>
		<title>Clawtronics: Erkenntnisse aus GKP-Frontendprüfung: Hash-Routing, Sitzungsdaten, Hosting und Reproduzierbarkeit</title>
		<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=72&amp;oldid=prev"/>
		<updated>2026-08-06T20:50:08Z</updated>

		<summary type="html">&lt;p&gt;Erkenntnisse aus GKP-Frontendprüfung: Hash-Routing, Sitzungsdaten, Hosting und Reproduzierbarkeit&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;de&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Nächstältere Version&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Version vom 6. August 2026, 20:50 Uhr&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l471&quot;&gt;Zeile 471:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 471:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Mehrfachklicks verhindern, solange ein Request läuft.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Mehrfachklicks verhindern, solange ein Request läuft.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Erst nach erfolgreicher Antwort den nächsten fachlichen Zustand anzeigen.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Erst nach erfolgreicher Antwort den nächsten fachlichen Zustand anzeigen.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Ticketcodes und Pseudo-IDs nicht in der URL oder in dauerhaftem &amp;lt;code&amp;gt;localStorage&amp;lt;/code&amp;gt; speichern.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Ticketcodes und Pseudo-IDs nicht in der URL oder in dauerhaftem &amp;lt;code&amp;gt;localStorage&amp;lt;/code&amp;gt; speichern&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;. Für eine nur bis zum Schließen des Tabs benötigte Sitzung ist &amp;lt;code&amp;gt;sessionStorage&amp;lt;/code&amp;gt; geeigneter; noch besser ist flüchtiger JavaScript-Zustand.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;* Ticketcodes, Pseudo-IDs und vollständige authentifizierte Request-URLs nicht mit &amp;lt;code&amp;gt;console.log&amp;lt;/code&amp;gt; oder in öffentlich zugängliche Telemetrie schreiben&lt;/ins&gt;.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Bei Seitenneuladung &amp;lt;code&amp;gt;/verify&amp;lt;/code&amp;gt; verwenden, bevor eine bestehende Sitzung angenommen wird.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Bei Seitenneuladung &amp;lt;code&amp;gt;/verify&amp;lt;/code&amp;gt; verwenden, bevor eine bestehende Sitzung angenommen wird.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Eine Station erst als verbunden anzeigen, wenn &amp;lt;code&amp;gt;/media-station&amp;lt;/code&amp;gt; erfolgreich war.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Eine Station erst als verbunden anzeigen, wenn &amp;lt;code&amp;gt;/media-station&amp;lt;/code&amp;gt; erfolgreich war.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l490&quot;&gt;Zeile 490:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 491:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Dadurch wird der Code nicht unnötig in kopierten URLs oder späteren Screenshots weitergegeben. Webserver- und Proxy-Logs können den ursprünglichen Request trotzdem bereits enthalten; Ticketcodes bleiben deshalb kurzlebige Einmalwerte.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Dadurch wird der Code nicht unnötig in kopierten URLs oder späteren Screenshots weitergegeben. Webserver- und Proxy-Logs können den ursprünglichen Request trotzdem bereits enthalten; Ticketcodes bleiben deshalb kurzlebige Einmalwerte.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Bei Frontends mit Hash-Routing, zum Beispiel &amp;lt;code&amp;gt;/#/welcome?systemCode=123456&amp;lt;/code&amp;gt;, liegt der Parameter nicht in &amp;lt;code&amp;gt;window.location.search&amp;lt;/code&amp;gt;, sondern innerhalb des Hash-Fragments. Er muss dort separat entfernt werden. Das verwendete Router-Framework soll nach erfolgreicher Übernahme mit einer ersetzenden Navigation (&amp;lt;code&amp;gt;replace&amp;lt;/code&amp;gt;, nicht &amp;lt;code&amp;gt;push&amp;lt;/code&amp;gt;) auf dieselbe Route ohne &amp;lt;code&amp;gt;systemCode&amp;lt;/code&amp;gt; wechseln. So bleibt der Code auch nicht über die Zurück-Funktion erreichbar. Ein Hash-Fragment wird zwar nicht an den Webserver übertragen, bleibt aber in Browserhistorie, kopierten Links und Screenshots sichtbar.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== CORS und Hosting ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== CORS und Hosting ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Der aktuelle MegBes-Stand erlaubt CORS-Anfragen grundsätzlich. Das ist keine Zusage, dass jede zukünftige Instanz beliebige Origins akzeptiert. Bevorzugt werden Frontend und API unter kontrollierten HTTPS-Adressen betrieben. Ein CORS-Fehler wird nicht durch Abschalten von Browsersicherheit oder Zertifikatsprüfung umgangen.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Der aktuelle MegBes-Stand erlaubt CORS-Anfragen grundsätzlich. Das ist keine Zusage, dass jede zukünftige Instanz beliebige Origins akzeptiert. Bevorzugt werden Frontend und API unter kontrollierten HTTPS-Adressen betrieben. Ein CORS-Fehler wird nicht durch Abschalten von Browsersicherheit oder Zertifikatsprüfung umgangen.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Bei Single-Page-Anwendungen müssen öffentlicher Basispfad, Router-Basis und Asset-Pfade zur tatsächlichen Bereitstellung passen. Eine Anwendung, die beispielsweise für &amp;lt;code&amp;gt;/projekt/&amp;lt;/code&amp;gt; gebaut wurde, ist ohne Neubau oder passende Serverkonfiguration nicht automatisch unter &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; oder einem anderen Unterpfad lauffähig. Direkte Aufrufe von Unterseiten benötigen außerdem eine passende Fallback-Regel auf &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Zum Projektarchiv gehören neben den ausgelieferten minifizierten Dateien auch Quellcode, Paketmanifest, Lockdatei, Build-Anweisung und verwendete Laufzeitversionen. Nur ein Build-Ordner reicht nicht aus, um das Frontend später reproduzierbar zu warten oder sicher zu aktualisieren.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Bestätigter Referenztest ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Bestätigter Referenztest ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l503&quot;&gt;Zeile 503:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 510:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Der Test bestätigt den Signalweg bis zum UDP-Versand. Der tatsächliche Empfang und die Reaktion eines Endgeräts müssen bei der Projektabnahme separat geprüft werden. Konkrete IP-Adressen, Ticketcodes und Geheimwerte werden nicht im Wiki festgehalten.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Der Test bestätigt den Signalweg bis zum UDP-Versand. Der tatsächliche Empfang und die Reaktion eines Endgeräts müssen bei der Projektabnahme separat geprüft werden. Konkrete IP-Adressen, Ticketcodes und Geheimwerte werden nicht im Wiki festgehalten.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Eine ergänzende statische Prüfung des umfangreicheren Ionic-/Vue-Frontends des Projekts &amp;lt;code&amp;gt;gkp&amp;lt;/code&amp;gt; bestätigte denselben grundlegenden Vertrag: Aktivierung über &amp;lt;code&amp;gt;/enter-system&amp;lt;/code&amp;gt;, Prüfung über &amp;lt;code&amp;gt;/verify&amp;lt;/code&amp;gt;, Stationsanmeldung und optionaler Stationsstatus sowie Befehle über &amp;lt;code&amp;gt;/video-command&amp;lt;/code&amp;gt; mit dem konfigurierten Authentifizierungsheader. Damit ist der hier beschriebene Ablauf nicht nur mit der Minimalvorlage, sondern auch mit einem realen mehrsprachigen Projektfrontend konsistent. Diese Prüfung bestätigt die API-Verwendung im Frontend; sie ersetzt keinen End-to-End-Test der jeweiligen Installation.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Entwicklungs- und Abnahmetests ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Entwicklungs- und Abnahmetests ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Clawtronics</name></author>
	</entry>
	<entry>
		<id>https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=71&amp;oldid=prev</id>
		<title>Clawtronics: Zeilenumbrueche der neuen Abschnitte korrigiert</title>
		<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=71&amp;oldid=prev"/>
		<updated>2026-08-06T20:29:11Z</updated>

		<summary type="html">&lt;p&gt;Zeilenumbrueche der neuen Abschnitte korrigiert&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;de&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Nächstältere Version&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Version vom 6. August 2026, 20:29 Uhr&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l321&quot;&gt;Zeile 321:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 321:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Die Abmeldung soll über eine sichtbare Bedienhandlung und bei regulärem Abschluss erfolgen. Ein Aufruf in &amp;lt;code&amp;gt;beforeunload&amp;lt;/code&amp;gt; ist allein nicht zuverlässig, da Browser ausstehende Requests beim Schließen abbrechen können. Der projektspezifische Stations-Timer bleibt daher als Rückfallebene erforderlich.&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;`r`n`r`n&lt;/del&gt;&amp;lt;code&amp;gt;DELETE /media-station&amp;lt;/code&amp;gt; gibt nur die aktuell belegte Station frei und setzt deren Stationszustand zurück. Die Ticket- beziehungsweise Benutzersitzung bleibt gültig; ein anschließendes &amp;lt;code&amp;gt;GET /verify&amp;lt;/code&amp;gt; kann daher weiterhin &amp;lt;code&amp;gt;200 OK&amp;lt;/code&amp;gt; liefern. So kann derselbe Besucher danach eine andere Station auswählen.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Die Abmeldung soll über eine sichtbare Bedienhandlung und bei regulärem Abschluss erfolgen. Ein Aufruf in &amp;lt;code&amp;gt;beforeunload&amp;lt;/code&amp;gt; ist allein nicht zuverlässig, da Browser ausstehende Requests beim Schließen abbrechen können. Der projektspezifische Stations-Timer bleibt daher als Rückfallebene erforderlich.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt; &lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;code&amp;gt;DELETE /media-station&amp;lt;/code&amp;gt; gibt nur die aktuell belegte Station frei und setzt deren Stationszustand zurück. Die Ticket- beziehungsweise Benutzersitzung bleibt gültig; ein anschließendes &amp;lt;code&amp;gt;GET /verify&amp;lt;/code&amp;gt; kann daher weiterhin &amp;lt;code&amp;gt;200 OK&amp;lt;/code&amp;gt; liefern. So kann derselbe Besucher danach eine andere Station auswählen.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Vollständiger Ablauf eines Besucher-Frontends ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Vollständiger Ablauf eines Besucher-Frontends ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Clawtronics</name></author>
	</entry>
	<entry>
		<id>https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=70&amp;oldid=prev</id>
		<title>Clawtronics: Erkenntnisse aus Tine-End-to-End-Test und blank-Frontend eingearbeitet</title>
		<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=70&amp;oldid=prev"/>
		<updated>2026-08-06T20:28:41Z</updated>

		<summary type="html">&lt;p&gt;Erkenntnisse aus Tine-End-to-End-Test und blank-Frontend eingearbeitet&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;de&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Nächstältere Version&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Version vom 6. August 2026, 20:28 Uhr&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l32&quot;&gt;Zeile 32:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 32:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Domains, Headername, Projektkennung, Stationsnummern und Befehle dürfen nicht ungeprüft von einem anderen Projekt übernommen werden.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Domains, Headername, Projektkennung, Stationsnummern und Befehle dürfen nicht ungeprüft von einem anderen Projekt übernommen werden.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;== Betriebsarten und Stationsstatus ==&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{| class=&quot;wikitable&quot;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;! Betriebsart&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;! Konfiguration&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;! Verhalten des Frontends&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|-&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;| extern verwaltete Stationsbelegung || &amp;lt;code&amp;gt;megbes.internal.station.controlled=false&amp;lt;/code&amp;gt; || Station über &amp;lt;code&amp;gt;GET /media-station?station=...&amp;lt;/code&amp;gt; belegen; den Status-Endpunkt nicht aufrufen&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|-&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;| durch &amp;lt;code&amp;gt;megbes-internal&amp;lt;/code&amp;gt; kontrollierte Station || &amp;lt;code&amp;gt;megbes.internal.station.controlled=true&amp;lt;/code&amp;gt; || nach der Stationsanmeldung zusätzlich &amp;lt;code&amp;gt;GET /media-station/status/{stationNumber}&amp;lt;/code&amp;gt; auswerten&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Liefert der Status-Endpunkt &amp;lt;code&amp;gt;404 Not Found&amp;lt;/code&amp;gt;, obwohl Anmeldung und Stationsbelegung funktionieren, ist zuerst die Betriebsart zu prüfen. Bei extern verwalteter Stationsbelegung ist &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt; erwartbar und bedeutet nicht, dass die Station belegt oder gestört ist. Die Betriebsart soll als zentraler Projektwert gepflegt und nicht aus einer fehlgeschlagenen Statusabfrage erraten werden.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== API-Übersicht ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== API-Übersicht ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l56&quot;&gt;Zeile 56:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 69:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| &amp;lt;code&amp;gt;GET /media-station/status/{stationNumber}&amp;lt;/code&amp;gt; || Stationszustand abrufen || USER oder ADMIN; nur bei intern kontrollierten Stationen || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;{ &amp;quot;stationStatus&amp;quot;: &amp;quot;...&amp;quot; }&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| &amp;lt;code&amp;gt;GET /media-station/status/{stationNumber}&amp;lt;/code&amp;gt; || Stationszustand abrufen || USER oder ADMIN; nur bei intern kontrollierten Stationen || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;{ &amp;quot;stationStatus&amp;quot;: &amp;quot;...&amp;quot; }&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|-&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|-&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| &amp;lt;code&amp;gt;POST /video-command&amp;lt;/code&amp;gt; || einen oder mehrere UDP-Befehle senden || ohne Security frei, sonst USER oder ADMIN || &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Antwort von &lt;/del&gt;&amp;lt;code&amp;gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;megbes-internal&lt;/del&gt;&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| &amp;lt;code&amp;gt;POST /video-command&amp;lt;/code&amp;gt; || einen oder mehrere UDP-Befehle senden || ohne Security frei, sonst USER oder ADMIN || &amp;lt;code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;200&lt;/ins&gt;&amp;lt;/code&amp;gt; &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;mit leerem Antwortkörper&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|-&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|-&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| &amp;lt;code&amp;gt;DELETE /media-station&amp;lt;/code&amp;gt; || Benutzer abmelden und Station zurücksetzen || USER oder ADMIN || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| &amp;lt;code&amp;gt;DELETE /media-station&amp;lt;/code&amp;gt; || Benutzer abmelden und Station zurücksetzen || USER oder ADMIN || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l294&quot;&gt;Zeile 294:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 307:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Der erfolgreiche Aufruf antwortet derzeit mit &amp;lt;code&amp;gt;200 OK&amp;lt;/code&amp;gt; und leerem Body. JavaScript-Clients dürfen deshalb nicht zwingend JSON parsen. Bei jQuery ist insbesondere &amp;lt;code&amp;gt;dataType: &#039;json&#039;&amp;lt;/code&amp;gt; für diesen Aufruf ungeeignet. Eine Navigation oder Erfolgsmeldung darf nur im Erfolgszweig (&amp;lt;code&amp;gt;.done()&amp;lt;/code&amp;gt; beziehungsweise &amp;lt;code&amp;gt;response.ok&amp;lt;/code&amp;gt;) erfolgen, nicht in &amp;lt;code&amp;gt;.always()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;finally&amp;lt;/code&amp;gt;.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 6. Station abmelden ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 6. Station abmelden ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l306&quot;&gt;Zeile 306:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 321:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Die Abmeldung soll über eine sichtbare Bedienhandlung und bei regulärem Abschluss erfolgen. Ein Aufruf in &amp;lt;code&amp;gt;beforeunload&amp;lt;/code&amp;gt; ist allein nicht zuverlässig, da Browser ausstehende Requests beim Schließen abbrechen können. Der projektspezifische Stations-Timer bleibt daher als Rückfallebene erforderlich.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Die Abmeldung soll über eine sichtbare Bedienhandlung und bei regulärem Abschluss erfolgen. Ein Aufruf in &amp;lt;code&amp;gt;beforeunload&amp;lt;/code&amp;gt; ist allein nicht zuverlässig, da Browser ausstehende Requests beim Schließen abbrechen können. Der projektspezifische Stations-Timer bleibt daher als Rückfallebene erforderlich&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;.`r`n`r`n&amp;lt;code&amp;gt;DELETE /media-station&amp;lt;/code&amp;gt; gibt nur die aktuell belegte Station frei und setzt deren Stationszustand zurück. Die Ticket- beziehungsweise Benutzersitzung bleibt gültig; ein anschließendes &amp;lt;code&amp;gt;GET /verify&amp;lt;/code&amp;gt; kann daher weiterhin &amp;lt;code&amp;gt;200 OK&amp;lt;/code&amp;gt; liefern. So kann derselbe Besucher danach eine andere Station auswählen&lt;/ins&gt;.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Vollständiger Ablauf eines Besucher-Frontends ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Vollständiger Ablauf eines Besucher-Frontends ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l462&quot;&gt;Zeile 462:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 477:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Stationsbefehle nur aus einer festen projektspezifischen Positivliste erzeugen, nicht aus frei eingegebenem Besuchertest.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Stationsbefehle nur aus einer festen projektspezifischen Positivliste erzeugen, nicht aus frei eingegebenem Besuchertest.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;=== Ticketcode aus der URL entfernen ===&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Wird ein Ticketcode per QR-Link als Queryparameter übergeben, soll das Frontend ihn nach erfolgreicher Übernahme sofort aus der sichtbaren URL und der aktuellen Browserhistorie entfernen:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;const cleanUrl = new URL(window.location.href);&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;cleanUrl.searchParams.delete(&quot;systemCode&quot;);&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;window.history.replaceState({}, &quot;&quot;, cleanUrl.pathname + cleanUrl.search + cleanUrl.hash);&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;/pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Dadurch wird der Code nicht unnötig in kopierten URLs oder späteren Screenshots weitergegeben. Webserver- und Proxy-Logs können den ursprünglichen Request trotzdem bereits enthalten; Ticketcodes bleiben deshalb kurzlebige Einmalwerte.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== CORS und Hosting ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== CORS und Hosting ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Der aktuelle MegBes-Stand erlaubt CORS-Anfragen grundsätzlich. Das ist keine Zusage, dass jede zukünftige Instanz beliebige Origins akzeptiert. Bevorzugt werden Frontend und API unter kontrollierten HTTPS-Adressen betrieben. Ein CORS-Fehler wird nicht durch Abschalten von Browsersicherheit oder Zertifikatsprüfung umgangen.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Der aktuelle MegBes-Stand erlaubt CORS-Anfragen grundsätzlich. Das ist keine Zusage, dass jede zukünftige Instanz beliebige Origins akzeptiert. Bevorzugt werden Frontend und API unter kontrollierten HTTPS-Adressen betrieben. Ein CORS-Fehler wird nicht durch Abschalten von Browsersicherheit oder Zertifikatsprüfung umgangen.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;== Bestätigter Referenztest ==&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Am 6. August 2026 wurde der vollständige Ablauf gegen das interne Projekt &amp;lt;code&amp;gt;tine&amp;lt;/code&amp;gt; geprüft:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt; Frontend-Aufruf → megbes-external → megbes-internal → UDP-Versand&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;code&amp;gt;enter-system&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;verify&amp;lt;/code&amp;gt;, Stationsanmeldung, &amp;lt;code&amp;gt;video-command&amp;lt;/code&amp;gt; mit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;stop&amp;lt;/code&amp;gt;, Stationsabmeldung sowie erneutes &amp;lt;code&amp;gt;verify&amp;lt;/code&amp;gt; antworteten jeweils mit &amp;lt;code&amp;gt;200 OK&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;megbes-internal&amp;lt;/code&amp;gt; bestätigte den UDP-Versand beider Befehle. Die Statusabfrage antwortete erwartungsgemäß mit &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt;, da &amp;lt;code&amp;gt;tine&amp;lt;/code&amp;gt; nicht im intern stationsgesteuerten Modus betrieben wird.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Der Test bestätigt den Signalweg bis zum UDP-Versand. Der tatsächliche Empfang und die Reaktion eines Endgeräts müssen bei der Projektabnahme separat geprüft werden. Konkrete IP-Adressen, Ticketcodes und Geheimwerte werden nicht im Wiki festgehalten.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Entwicklungs- und Abnahmetests ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Entwicklungs- und Abnahmetests ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Clawtronics</name></author>
	</entry>
	<entry>
		<id>https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=68&amp;oldid=prev</id>
		<title>Clawtronics: Tabellenzeilen korrekt trennen</title>
		<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=68&amp;oldid=prev"/>
		<updated>2026-08-06T19:43:44Z</updated>

		<summary type="html">&lt;p&gt;Tabellenzeilen korrekt trennen&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;de&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Nächstältere Version&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Version vom 6. August 2026, 19:43 Uhr&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l16&quot;&gt;Zeile 16:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Zeile 16:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|-&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|-&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| API-Basisadresse || HTTPS-Adresse der projektspezifischen External-Instanz&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| API-Basisadresse || HTTPS-Adresse der projektspezifischen External-Instanz&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|-&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Projektkennung || Wert für &amp;lt;code&amp;gt;prSc&amp;lt;/code&amp;gt;; gegen den eingesetzten Release und die Projektkonfiguration prüfen&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Projektkennung || Wert für &amp;lt;code&amp;gt;prSc&amp;lt;/code&amp;gt;; gegen den eingesetzten Release und die Projektkonfiguration prüfen&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|-&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Sicherheitsmodus || ohne Authentifizierung oder zentrale Ticket-/Benutzerauthentifizierung&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Sicherheitsmodus || ohne Authentifizierung oder zentrale Ticket-/Benutzerauthentifizierung&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|-&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Stationssteuerung || extern oder durch &amp;lt;code&amp;gt;megbes-internal&amp;lt;/code&amp;gt; kontrolliert&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Stationssteuerung || extern oder durch &amp;lt;code&amp;gt;megbes-internal&amp;lt;/code&amp;gt; kontrolliert&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|-&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Token-Header || Wert der Laufzeitkonfiguration &amp;lt;code&amp;gt;jwt.header&amp;lt;/code&amp;gt;; nicht pauschal &amp;lt;code&amp;gt;Authorization&amp;lt;/code&amp;gt; annehmen&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Token-Header || Wert der Laufzeitkonfiguration &amp;lt;code&amp;gt;jwt.header&amp;lt;/code&amp;gt;; nicht pauschal &amp;lt;code&amp;gt;Authorization&amp;lt;/code&amp;gt; annehmen&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|-&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Stationsnummern || freigegebene Stationen des Projekts&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Stationsnummern || freigegebene Stationen des Projekts&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;|-&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Stationsbefehle || projektspezifische Befehle, die die jeweilige Station tatsächlich versteht&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| Stationsbefehle || projektspezifische Befehle, die die jeweilige Station tatsächlich versteht&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Clawtronics</name></author>
	</entry>
	<entry>
		<id>https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=61&amp;oldid=prev</id>
		<title>Clawtronics: Codebloecke und Inline-Code an MediaWiki-Syntax angepasst</title>
		<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=61&amp;oldid=prev"/>
		<updated>2026-08-06T19:28:19Z</updated>

		<summary type="html">&lt;p&gt;Codebloecke und Inline-Code an MediaWiki-Syntax angepasst&lt;/p&gt;
&lt;a href=&quot;https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;amp;diff=61&amp;amp;oldid=60&quot;&gt;Änderungen zeigen&lt;/a&gt;</summary>
		<author><name>Clawtronics</name></author>
	</entry>
	<entry>
		<id>https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=60&amp;oldid=prev</id>
		<title>Clawtronics: Nicht installierte SyntaxHighlight-Tags durch kompatible pre-Codebloecke ersetzt</title>
		<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=60&amp;oldid=prev"/>
		<updated>2026-08-06T19:20:35Z</updated>

		<summary type="html">&lt;p&gt;Nicht installierte SyntaxHighlight-Tags durch kompatible pre-Codebloecke ersetzt&lt;/p&gt;
&lt;a href=&quot;https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;amp;diff=60&amp;amp;oldid=58&quot;&gt;Änderungen zeigen&lt;/a&gt;</summary>
		<author><name>Clawtronics</name></author>
	</entry>
	<entry>
		<id>https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=58&amp;oldid=prev</id>
		<title>Clawtronics: Umlaute auch in kopierbaren Codebeispielen normalisiert</title>
		<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=58&amp;oldid=prev"/>
		<updated>2026-08-06T19:06:29Z</updated>

		<summary type="html">&lt;p&gt;Umlaute auch in kopierbaren Codebeispielen normalisiert&lt;/p&gt;
&lt;a href=&quot;https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;amp;diff=58&amp;amp;oldid=56&quot;&gt;Änderungen zeigen&lt;/a&gt;</summary>
		<author><name>Clawtronics</name></author>
	</entry>
	<entry>
		<id>https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=56&amp;oldid=prev</id>
		<title>Clawtronics: Ausfuehrliche HTML- und JavaScript-Entwicklerdokumentation aus aktuellem Repository erstellt</title>
		<link rel="alternate" type="text/html" href="https://wiki.megbes.de/index.php?title=Web-Frontend_entwickeln&amp;diff=56&amp;oldid=prev"/>
		<updated>2026-08-06T19:05:53Z</updated>

		<summary type="html">&lt;p&gt;Ausfuehrliche HTML- und JavaScript-Entwicklerdokumentation aus aktuellem Repository erstellt&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;= Web-Frontend entwickeln =&lt;br /&gt;
&lt;br /&gt;
== Zweck und Geltungsbereich ==&lt;br /&gt;
&lt;br /&gt;
Diese Seite beschreibt die HTML-/JavaScript-Anbindung eines Web-Frontends an `megbes-external`. Sie basiert auf den Controllern und Request-Modellen des aktuellen MegBes-Repositorys. Vor der Freigabe eines Frontends muss trotzdem gepr&amp;amp;uuml;ft werden, welcher MegBes-Release im betreffenden Projekt installiert ist.&lt;br /&gt;
&lt;br /&gt;
Das Frontend spricht ausschlie&amp;amp;szlig;lich `megbes-external` an. Direkte Browseraufrufe an `megbes-internal` sind nicht vorgesehen.&lt;br /&gt;
&lt;br /&gt;
 Browser &amp;amp;rarr; HTTPS &amp;amp;rarr; megbes-external &amp;amp;rarr; megbes-internal &amp;amp;rarr; UDP &amp;amp;rarr; Station&lt;br /&gt;
&lt;br /&gt;
== Vor Beginn festlegen ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Wert&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| API-Basisadresse || HTTPS-Adresse der projektspezifischen External-Instanz&lt;br /&gt;
| Projektkennung || Wert f&amp;amp;uuml;r `prSc`; gegen den eingesetzten Release und die Projektkonfiguration pr&amp;amp;uuml;fen&lt;br /&gt;
| Sicherheitsmodus || ohne Authentifizierung oder zentrale Ticket-/Benutzerauthentifizierung&lt;br /&gt;
| Stationssteuerung || extern oder durch `megbes-internal` kontrolliert&lt;br /&gt;
| Token-Header || Wert der Laufzeitkonfiguration `jwt.header`; nicht pauschal `Authorization` annehmen&lt;br /&gt;
| Stationsnummern || freigegebene Stationen des Projekts&lt;br /&gt;
| Stationsbefehle || projektspezifische Befehle, die die jeweilige Station tats&amp;amp;auml;chlich versteht&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Domains, Headername, Projektkennung, Stationsnummern und Befehle d&amp;amp;uuml;rfen nicht ungepr&amp;amp;uuml;ft von einem anderen Projekt &amp;amp;uuml;bernommen werden.&lt;br /&gt;
&lt;br /&gt;
== API-&amp;amp;Uuml;bersicht ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Methode und Pfad&lt;br /&gt;
! Zweck&lt;br /&gt;
! Berechtigung&lt;br /&gt;
! Erfolgsantwort&lt;br /&gt;
|-&lt;br /&gt;
| `GET /info` || vom Server empfangenen User-Agent pr&amp;amp;uuml;fen || frei || `200` mit User-Agent&lt;br /&gt;
|-&lt;br /&gt;
| `POST /authenticate` || Ticket-Monitor administrativ authentifizieren || frei; ben&amp;amp;ouml;tigt Header `customer` und `authId` || `200` und `{ &amp;quot;id&amp;quot;: &amp;quot;...&amp;quot; }`&lt;br /&gt;
|-&lt;br /&gt;
| `GET /verify-admin` || Administrationskennung pr&amp;amp;uuml;fen || ADMIN || `200`&lt;br /&gt;
|-&lt;br /&gt;
| `GET /ticket` || Ticketcode erzeugen oder vorhandenen unbenutzten Code abrufen || ADMIN || `200` und `{ &amp;quot;id&amp;quot;: &amp;quot;......&amp;quot; }`&lt;br /&gt;
|-&lt;br /&gt;
| `POST /enter-system` || Ticketcode einmalig aktivieren || frei || `200`&lt;br /&gt;
|-&lt;br /&gt;
| `GET /verify` || aktivierte Benutzerkennung pr&amp;amp;uuml;fen || USER oder ADMIN || `200`&lt;br /&gt;
|-&lt;br /&gt;
| `GET /media-station?station=...` || Benutzer an Station anmelden || USER oder ADMIN || `200`&lt;br /&gt;
|-&lt;br /&gt;
| `GET /media-station/status/{stationNumber}` || Stationszustand abrufen || USER oder ADMIN; nur bei intern kontrollierten Stationen || `200` und `{ &amp;quot;stationStatus&amp;quot;: &amp;quot;...&amp;quot; }`&lt;br /&gt;
|-&lt;br /&gt;
| `POST /video-command` || einen oder mehrere UDP-Befehle senden || ohne Security frei, sonst USER oder ADMIN || Antwort von `megbes-internal`&lt;br /&gt;
|-&lt;br /&gt;
| `DELETE /media-station` || Benutzer abmelden und Station zur&amp;amp;uuml;cksetzen || USER oder ADMIN || `200`&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Authentifizierungsregel ==&lt;br /&gt;
&lt;br /&gt;
MegBes liefert dem Browser beziehungsweise Ticket-Monitor eine &amp;#039;&amp;#039;&amp;#039;Pseudo-ID&amp;#039;&amp;#039;&amp;#039;. Der eigentliche JWT verbleibt serverseitig. Bei gesch&amp;amp;uuml;tzten Folgeaufrufen sendet das Frontend die Pseudo-ID als Wert des projektspezifisch konfigurierten Token-Headers.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;konfigurierter-token-header&amp;gt;: &amp;lt;Pseudo-ID&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Headername stammt aus `jwt.header`. Er darf im Frontend nicht geraten werden. Die Pseudo-ID ist wie eine Sitzungskennung zu behandeln: nicht protokollieren, nicht in URLs anh&amp;amp;auml;ngen und nicht ins Wiki kopieren.&lt;br /&gt;
&lt;br /&gt;
== Request-Modelle ==&lt;br /&gt;
&lt;br /&gt;
=== System betreten ===&lt;br /&gt;
&lt;br /&gt;
`POST /enter-system`&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;systemCode&amp;quot;: &amp;quot;123456&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`systemCode` darf nicht leer sein. Ein unbekannter oder bereits verwendeter Code ergibt `401 Unauthorized`.&lt;br /&gt;
&lt;br /&gt;
=== Stationsbefehl ===&lt;br /&gt;
&lt;br /&gt;
`POST /video-command`&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;prSc&amp;quot;: &amp;quot;&amp;lt;Projektkennung&amp;gt;&amp;quot;,&lt;br /&gt;
  &amp;quot;statNoCommands&amp;quot;: [&lt;br /&gt;
    &amp;quot;12-play&amp;quot;&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Regeln des aktuellen Request-Modells:&lt;br /&gt;
&lt;br /&gt;
* `prSc` ist Pflicht und darf nicht leer sein.&lt;br /&gt;
* `statNoCommands` ist eine nicht leere Liste.&lt;br /&gt;
* Jeder Listeneintrag folgt `\d{0,3}-[0-9a-zA-Z]{2,20}`.&lt;br /&gt;
* Vor dem Bindestrich sind damit null bis drei Ziffern zul&amp;amp;auml;ssig; f&amp;amp;uuml;r produktive Stationsbefehle soll eine eindeutige, im Projekt dokumentierte Stationsnummer verwendet werden.&lt;br /&gt;
* Nach dem Bindestrich sind zwei bis zwanzig ASCII-Buchstaben oder Ziffern zul&amp;amp;auml;ssig.&lt;br /&gt;
* Ob ein formal g&amp;amp;uuml;ltiger Befehl fachlich unterst&amp;amp;uuml;tzt wird, entscheidet die jeweilige Stationssoftware.&lt;br /&gt;
&lt;br /&gt;
Beispiele wie `12-play` sind nur Formbeispiele und keine globale MegBes-Befehlsliste.&lt;br /&gt;
&lt;br /&gt;
== Empfohlener JavaScript-API-Client ==&lt;br /&gt;
&lt;br /&gt;
Die Netzwerkanbindung soll an einer Stelle gekapselt werden. Dadurch bleiben Fehlerbehandlung, Header und JSON-Verarbeitung konsistent.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const megBesConfig = Object.freeze({&lt;br /&gt;
  apiBase: &amp;quot;https://&amp;lt;projekt&amp;gt;.megbes.de&amp;quot;,&lt;br /&gt;
  projectCode: &amp;quot;&amp;lt;PROJEKTKENNUNG&amp;gt;&amp;quot;,&lt;br /&gt;
  tokenHeader: &amp;quot;&amp;lt;WERT-AUS-jwt.header&amp;gt;&amp;quot;&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
class MegBesError extends Error {&lt;br /&gt;
  constructor(message, status, responseBody) {&lt;br /&gt;
    super(message);&lt;br /&gt;
    this.name = &amp;quot;MegBesError&amp;quot;;&lt;br /&gt;
    this.status = status;&lt;br /&gt;
    this.responseBody = responseBody;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
async function megBesRequest(path, {&lt;br /&gt;
  method = &amp;quot;GET&amp;quot;,&lt;br /&gt;
  token = null,&lt;br /&gt;
  body = undefined,&lt;br /&gt;
  headers = {}&lt;br /&gt;
} = {}) {&lt;br /&gt;
  const requestHeaders = new Headers(headers);&lt;br /&gt;
&lt;br /&gt;
  if (token) {&lt;br /&gt;
    requestHeaders.set(megBesConfig.tokenHeader, token);&lt;br /&gt;
  }&lt;br /&gt;
  if (body !== undefined) {&lt;br /&gt;
    requestHeaders.set(&amp;quot;Content-Type&amp;quot;, &amp;quot;application/json&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  let response;&lt;br /&gt;
  try {&lt;br /&gt;
    response = await fetch(`${megBesConfig.apiBase}${path}`, {&lt;br /&gt;
      method,&lt;br /&gt;
      headers: requestHeaders,&lt;br /&gt;
      body: body === undefined ? undefined : JSON.stringify(body),&lt;br /&gt;
      cache: &amp;quot;no-store&amp;quot;&lt;br /&gt;
    });&lt;br /&gt;
  } catch (cause) {&lt;br /&gt;
    throw new MegBesError(&amp;quot;MegBes ist nicht erreichbar.&amp;quot;, 0, cause);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const contentType = response.headers.get(&amp;quot;content-type&amp;quot;) || &amp;quot;&amp;quot;;&lt;br /&gt;
  const responseBody = contentType.includes(&amp;quot;application/json&amp;quot;)&lt;br /&gt;
    ? await response.json()&lt;br /&gt;
    : await response.text();&lt;br /&gt;
&lt;br /&gt;
  if (!response.ok) {&lt;br /&gt;
    throw new MegBesError(&lt;br /&gt;
      `MegBes-Aufruf fehlgeschlagen (${response.status}).`,&lt;br /&gt;
      response.status,&lt;br /&gt;
      responseBody&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return responseBody;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Variante A: Frontend ohne Authentifizierung ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante existiert nur, wenn `megbes.security` f&amp;amp;uuml;r die External-Instanz nicht aktiviert ist. Das Frontend darf diesen Modus nicht anhand eines fehlgeschlagenen Loginversuchs erraten; er wird vor der Entwicklung projektspezifisch festgelegt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function sendCommandWithoutAuthentication(station, command) {&lt;br /&gt;
  return megBesRequest(&amp;quot;/video-command&amp;quot;, {&lt;br /&gt;
    method: &amp;quot;POST&amp;quot;,&lt;br /&gt;
    body: {&lt;br /&gt;
      prSc: megBesConfig.projectCode,&lt;br /&gt;
      statNoCommands: [`${station}-${command}`]&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch ohne MegBes-Authentifizierung muss die External-Instanz ausschlie&amp;amp;szlig;lich &amp;amp;uuml;ber HTTPS angesprochen werden. Welche Personen die Seite erreichen d&amp;amp;uuml;rfen, ist au&amp;amp;szlig;erhalb von MegBes abzusichern.&lt;br /&gt;
&lt;br /&gt;
== Variante B: Ticket-Monitor ==&lt;br /&gt;
&lt;br /&gt;
=== 1. Ticket-Monitor authentifizieren ===&lt;br /&gt;
&lt;br /&gt;
Der Ticket-Monitor ruft `POST /authenticate` mit den Headern `customer` und `authId` auf. Zus&amp;amp;auml;tzlich pr&amp;amp;uuml;ft MegBes den User-Agent gegen die Projektkonfiguration.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function authenticateTicketMonitor(customer, authId) {&lt;br /&gt;
  const response = await megBesRequest(&amp;quot;/authenticate&amp;quot;, {&lt;br /&gt;
    method: &amp;quot;POST&amp;quot;,&lt;br /&gt;
    headers: {&lt;br /&gt;
      customer,&lt;br /&gt;
      authId&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
  return response.id;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`authId` ist ein Geheimwert und darf nicht fest in ausgelieferter Browser-JavaScript-Datei stehen. Wenn der Ticket-Monitor nur statische &amp;amp;ouml;ffentliche Dateien ausliefert, ist dieses Verfahren sicherheitlich neu zu bewerten. Die Kennung geh&amp;amp;ouml;rt in eine gesch&amp;amp;uuml;tzte Laufzeitumgebung oder einen vertrauensw&amp;amp;uuml;rdigen lokalen Dienst.&lt;br /&gt;
&lt;br /&gt;
=== 2. Ticketcode abrufen ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function getTicket(adminToken, customer, ticketMonitorNumber) {&lt;br /&gt;
  const query = new URLSearchParams({&lt;br /&gt;
    customer,&lt;br /&gt;
    ticketMonitorNumber: String(ticketMonitorNumber)&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  const response = await megBesRequest(`/ticket?${query}`, {&lt;br /&gt;
    token: adminToken&lt;br /&gt;
  });&lt;br /&gt;
  return response.id;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der gelieferte Code ist im aktuellen Stand sechsstellig. Das Frontend soll ihn als String behandeln, damit eine f&amp;amp;uuml;hrende Null nicht verloren geht.&lt;br /&gt;
&lt;br /&gt;
== Variante C: Besucher- oder Guide-Frontend mit Ticketcode ==&lt;br /&gt;
&lt;br /&gt;
=== 1. Code aktivieren ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function enterSystem(systemCode) {&lt;br /&gt;
  await megBesRequest(&amp;quot;/enter-system&amp;quot;, {&lt;br /&gt;
    method: &amp;quot;POST&amp;quot;,&lt;br /&gt;
    body: { systemCode }&lt;br /&gt;
  });&lt;br /&gt;
  return systemCode;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Eintritt dient derselbe Code als Pseudo-ID f&amp;amp;uuml;r die weiteren Aufrufe. Ein Ticketcode kann nur einmal aktiviert werden.&lt;br /&gt;
&lt;br /&gt;
=== 2. Kennung pr&amp;amp;uuml;fen ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function verifyUser(userToken) {&lt;br /&gt;
  await megBesRequest(&amp;quot;/verify&amp;quot;, { token: userToken });&lt;br /&gt;
  return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3. An Station anmelden ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function loginStation(userToken, stationNumber) {&lt;br /&gt;
  const query = new URLSearchParams({ station: String(stationNumber) });&lt;br /&gt;
  await megBesRequest(`/media-station?${query}`, { token: userToken });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im intern kontrollierten Modus ist die Stationsnummer zwingend. Ein `401` kann neben einer ung&amp;amp;uuml;ltigen Kennung auch bedeuten, dass die Station belegt ist oder die Stationsanmeldung abgelehnt wurde. Die Bedienoberfl&amp;amp;auml;che darf deshalb nicht automatisch behaupten, der Ticketcode sei falsch.&lt;br /&gt;
&lt;br /&gt;
=== 4. Optional Stationsstatus pr&amp;amp;uuml;fen ===&lt;br /&gt;
&lt;br /&gt;
Dieser Endpunkt ist nur verf&amp;amp;uuml;gbar, wenn `megbes.internal.station.controlled=true` gilt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function getStationStatus(userToken, stationNumber) {&lt;br /&gt;
  const response = await megBesRequest(&lt;br /&gt;
    `/media-station/status/${encodeURIComponent(stationNumber)}`,&lt;br /&gt;
    { token: userToken }&lt;br /&gt;
  );&lt;br /&gt;
  return response.stationStatus;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code verwendet unter anderem die Zust&amp;amp;auml;nde `OPEN`, `IDLE`, `REMOTE` und `TOUCHSCREEN`. Die Oberfl&amp;amp;auml;che soll unbekannte k&amp;amp;uuml;nftige Werte neutral anzeigen und nicht abst&amp;amp;uuml;rzen.&lt;br /&gt;
&lt;br /&gt;
=== 5. Befehl senden ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function sendCommand(userToken, stationNumber, command) {&lt;br /&gt;
  return megBesRequest(&amp;quot;/video-command&amp;quot;, {&lt;br /&gt;
    method: &amp;quot;POST&amp;quot;,&lt;br /&gt;
    token: userToken,&lt;br /&gt;
    body: {&lt;br /&gt;
      prSc: megBesConfig.projectCode,&lt;br /&gt;
      statNoCommands: [`${stationNumber}-${command}`]&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6. Station abmelden ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
async function logoutStation(userToken) {&lt;br /&gt;
  await megBesRequest(&amp;quot;/media-station&amp;quot;, {&lt;br /&gt;
    method: &amp;quot;DELETE&amp;quot;,&lt;br /&gt;
    token: userToken&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Abmeldung soll &amp;amp;uuml;ber eine sichtbare Bedienhandlung und bei regul&amp;amp;auml;rem Abschluss erfolgen. Ein Aufruf in `beforeunload` ist allein nicht zuverl&amp;amp;auml;ssig, da Browser ausstehende Requests beim Schlie&amp;amp;szlig;en abbrechen k&amp;amp;ouml;nnen. Der projektspezifische Stations-Timer bleibt daher als R&amp;amp;uuml;ckfallebene erforderlich.&lt;br /&gt;
&lt;br /&gt;
== Vollst&amp;amp;auml;ndiger Ablauf eines Besucher-Frontends ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
let userToken = null;&lt;br /&gt;
let activeStation = null;&lt;br /&gt;
&lt;br /&gt;
async function startSession(systemCode, stationNumber) {&lt;br /&gt;
  userToken = await enterSystem(systemCode);&lt;br /&gt;
  await verifyUser(userToken);&lt;br /&gt;
&lt;br /&gt;
  // Nur im intern kontrollierten Modus vorab verwenden.&lt;br /&gt;
  // const status = await getStationStatus(userToken, stationNumber);&lt;br /&gt;
&lt;br /&gt;
  await loginStation(userToken, stationNumber);&lt;br /&gt;
  activeStation = String(stationNumber);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
async function play(command) {&lt;br /&gt;
  if (!userToken || !activeStation) {&lt;br /&gt;
    throw new Error(&amp;quot;Keine aktive MegBes-Station.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  await sendCommand(userToken, activeStation, command);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
async function endSession() {&lt;br /&gt;
  if (userToken &amp;amp;&amp;amp; activeStation) {&lt;br /&gt;
    await logoutStation(userToken);&lt;br /&gt;
  }&lt;br /&gt;
  userToken = null;&lt;br /&gt;
  activeStation = null;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Minimales HTML-Beispiel ==&lt;br /&gt;
&lt;br /&gt;
Das Beispiel zeigt die Struktur, ist aber ohne projektspezifische Werte und Gestaltung nicht produktionsfertig.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!doctype html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;MegBes-Steuerung&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &amp;lt;main&amp;gt;&lt;br /&gt;
    &amp;lt;form id=&amp;quot;login-form&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;label for=&amp;quot;code&amp;quot;&amp;gt;Ticketcode&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;input id=&amp;quot;code&amp;quot; name=&amp;quot;code&amp;quot; inputmode=&amp;quot;numeric&amp;quot; autocomplete=&amp;quot;one-time-code&amp;quot; required&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;label for=&amp;quot;station&amp;quot;&amp;gt;Station&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;input id=&amp;quot;station&amp;quot; name=&amp;quot;station&amp;quot; inputmode=&amp;quot;numeric&amp;quot; required&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;Station verbinden&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;section id=&amp;quot;controls&amp;quot; hidden&amp;gt;&lt;br /&gt;
      &amp;lt;button type=&amp;quot;button&amp;quot; data-command=&amp;quot;play&amp;quot;&amp;gt;Start&amp;lt;/button&amp;gt;&lt;br /&gt;
      &amp;lt;button type=&amp;quot;button&amp;quot; data-command=&amp;quot;stop&amp;quot;&amp;gt;Stopp&amp;lt;/button&amp;gt;&lt;br /&gt;
      &amp;lt;button type=&amp;quot;button&amp;quot; id=&amp;quot;logout&amp;quot;&amp;gt;Beenden&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;p id=&amp;quot;message&amp;quot; role=&amp;quot;status&amp;quot; aria-live=&amp;quot;polite&amp;quot;&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/main&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;script src=&amp;quot;megbes-api.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script&amp;gt;&lt;br /&gt;
    const form = document.querySelector(&amp;quot;#login-form&amp;quot;);&lt;br /&gt;
    const controls = document.querySelector(&amp;quot;#controls&amp;quot;);&lt;br /&gt;
    const message = document.querySelector(&amp;quot;#message&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    form.addEventListener(&amp;quot;submit&amp;quot;, async event =&amp;gt; {&lt;br /&gt;
      event.preventDefault();&lt;br /&gt;
      message.textContent = &amp;quot;Verbindung wird hergestellt ...&amp;quot;;&lt;br /&gt;
      try {&lt;br /&gt;
        await startSession(form.code.value.trim(), form.station.value.trim());&lt;br /&gt;
        controls.hidden = false;&lt;br /&gt;
        message.textContent = &amp;quot;Station ist verbunden.&amp;quot;;&lt;br /&gt;
      } catch (error) {&lt;br /&gt;
        message.textContent = messageForMegBesError(error);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    controls.addEventListener(&amp;quot;click&amp;quot;, async event =&amp;gt; {&lt;br /&gt;
      const command = event.target.dataset.command;&lt;br /&gt;
      if (!command) return;&lt;br /&gt;
      try {&lt;br /&gt;
        event.target.disabled = true;&lt;br /&gt;
        await play(command);&lt;br /&gt;
        message.textContent = &amp;quot;Befehl wurde gesendet.&amp;quot;;&lt;br /&gt;
      } catch (error) {&lt;br /&gt;
        message.textContent = messageForMegBesError(error);&lt;br /&gt;
      } finally {&lt;br /&gt;
        event.target.disabled = false;&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    document.querySelector(&amp;quot;#logout&amp;quot;).addEventListener(&amp;quot;click&amp;quot;, async () =&amp;gt; {&lt;br /&gt;
      try {&lt;br /&gt;
        await endSession();&lt;br /&gt;
        controls.hidden = true;&lt;br /&gt;
        form.reset();&lt;br /&gt;
        message.textContent = &amp;quot;Sitzung wurde beendet.&amp;quot;;&lt;br /&gt;
      } catch (error) {&lt;br /&gt;
        message.textContent = messageForMegBesError(error);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die sichtbaren Befehle `play` und `stop` sind ebenfalls nur Platzhalter. Sie werden durch die freigegebenen Projektbefehle ersetzt.&lt;br /&gt;
&lt;br /&gt;
== Fehlerbehandlung ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function messageForMegBesError(error) {&lt;br /&gt;
  if (!(error instanceof MegBesError)) {&lt;br /&gt;
    return &amp;quot;Unerwarteter Fehler. Bitte Aufsicht informieren.&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  switch (error.status) {&lt;br /&gt;
    case 0:&lt;br /&gt;
      return &amp;quot;MegBes ist derzeit nicht erreichbar.&amp;quot;;&lt;br /&gt;
    case 400:&lt;br /&gt;
      return &amp;quot;Die Anfrage ist unvollst&amp;amp;auml;ndig oder ung&amp;amp;uuml;ltig.&amp;quot;;&lt;br /&gt;
    case 401:&lt;br /&gt;
      return &amp;quot;Anmeldung, Berechtigung oder Stationsbelegung konnte nicht best&amp;amp;auml;tigt werden.&amp;quot;;&lt;br /&gt;
    case 403:&lt;br /&gt;
      return &amp;quot;Die Station ist f&amp;amp;uuml;r diese Sitzung nicht freigegeben.&amp;quot;;&lt;br /&gt;
    case 404:&lt;br /&gt;
      return &amp;quot;Diese Funktion ist in der aktuellen Betriebsart nicht verf&amp;amp;uuml;gbar.&amp;quot;;&lt;br /&gt;
    default:&lt;br /&gt;
      return &amp;quot;MegBes konnte den Auftrag nicht ausf&amp;amp;uuml;hren.&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keine rohen Backend-Antworten, Stacktraces, Tokens oder internen Adressen an Besucher ausgeben. Technische Details geh&amp;amp;ouml;ren in eine gesch&amp;amp;uuml;tzte Diagnoseansicht oder in bereinigte Logs.&lt;br /&gt;
&lt;br /&gt;
== Zustands- und Bedienregeln ==&lt;br /&gt;
&lt;br /&gt;
* Mehrfachklicks verhindern, solange ein Request l&amp;amp;auml;uft.&lt;br /&gt;
* Erst nach erfolgreicher Antwort den n&amp;amp;auml;chsten fachlichen Zustand anzeigen.&lt;br /&gt;
* Ticketcodes und Pseudo-IDs nicht in der URL oder in dauerhaftem `localStorage` speichern.&lt;br /&gt;
* Bei Seitenneuladung `/verify` verwenden, bevor eine bestehende Sitzung angenommen wird.&lt;br /&gt;
* Eine Station erst als verbunden anzeigen, wenn `/media-station` erfolgreich war.&lt;br /&gt;
* Bei `401` nicht automatisch einen falschen Code behaupten; auch Belegung und Projektzuordnung sind m&amp;amp;ouml;gliche Ursachen.&lt;br /&gt;
* Bei unbekanntem Stationsstatus eine neutrale Fehlermeldung anzeigen.&lt;br /&gt;
* Bedienoberfl&amp;amp;auml;che tastaturbedienbar gestalten und Statusmeldungen mit `aria-live` ausgeben.&lt;br /&gt;
* Stationsbefehle nur aus einer festen projektspezifischen Positivliste erzeugen, nicht aus frei eingegebenem Besuchertest.&lt;br /&gt;
&lt;br /&gt;
== CORS und Hosting ==&lt;br /&gt;
&lt;br /&gt;
Der aktuelle MegBes-Stand erlaubt CORS-Anfragen grunds&amp;amp;auml;tzlich. Das ist keine Zusage, dass jede zuk&amp;amp;uuml;nftige Instanz beliebige Origins akzeptiert. Bevorzugt werden Frontend und API unter kontrollierten HTTPS-Adressen betrieben. Ein CORS-Fehler wird nicht durch Abschalten von Browsersicherheit oder Zertifikatspr&amp;amp;uuml;fung umgangen.&lt;br /&gt;
&lt;br /&gt;
== Entwicklungs- und Abnahmetests ==&lt;br /&gt;
&lt;br /&gt;
# `GET /info` von der vorgesehenen Hardware aufrufen.&lt;br /&gt;
# korrekten Sicherheits- und Stationsmodus best&amp;amp;auml;tigen.&lt;br /&gt;
# g&amp;amp;uuml;ltigen, ung&amp;amp;uuml;ltigen, bereits benutzten und abgelaufenen Ticketcode testen.&lt;br /&gt;
# freie, belegte und unbekannte Station testen.&lt;br /&gt;
# jeden freigegebenen Stationsbefehl einzeln pr&amp;amp;uuml;fen.&lt;br /&gt;
# Mehrfachklick und parallele Requests testen.&lt;br /&gt;
# Abmeldung und erneute Anmeldung testen.&lt;br /&gt;
# Browser-Neuladen sowie Neustart von Frontend-Hardware und Station testen.&lt;br /&gt;
# Ausfall von External, Internal und Stationsnetz jeweils getrennt simulieren.&lt;br /&gt;
# sicherstellen, dass keine Tokens oder Geheimwerte in URL, Konsole, Screenshot oder sichtbarer Fehlermeldung erscheinen.&lt;br /&gt;
# installierten External-/Internal-Release, Frontend-Version und Testergebnis auf der Projektseite dokumentieren.&lt;br /&gt;
&lt;br /&gt;
== Grenzen dieser Referenz ==&lt;br /&gt;
&lt;br /&gt;
Diese Seite dokumentiert den API-Vertrag des aktuellen Repository-Stands. Projektspezifische Stationsbefehle, Gestaltung, Inhalte, Ticketdruck, Hardware-Autostart und konkrete Frontend-Dateipfade werden auf der jeweiligen Projektseite dokumentiert. Bei Abweichungen hat der tats&amp;amp;auml;chlich eingesetzte Release Vorrang.&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[Web-Frontend]], [[Bedienung und Authentifizierung]], [[UDP und Stationszust&amp;amp;auml;nde]], [[Client-Feedback und Stationsmeldungen]] und [[Routing und Signalweg]].&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:MegBes]]&lt;br /&gt;
[[Kategorie:Frontend]]&lt;br /&gt;
[[Kategorie:Entwicklung]]&lt;br /&gt;
[[Kategorie:API]]&lt;/div&gt;</summary>
		<author><name>Clawtronics</name></author>
	</entry>
</feed>