403Webshell
Server IP : 202.61.199.114  /  Your IP : 216.73.217.139
Web Server : nginx/1.22.1
System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64
User : web20 ( 1018)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/arni-solutions.de/web/terminal/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/arni-solutions.de/web/terminal/README.md
# Terminal-System (Grundstruktur)

Neutrales, PIN-gesichertes Terminal-Portal. Ein **Terminal** ist ein dauerhafter,
PIN-gesicherter Zugang mit festem Hash-Link. Einem Terminal werden **Ansichten**
zugeordnet, die über das Terminal genutzt werden können (Inhalt der Ansichten folgt später).

> **Verwaltung:** Terminals & Ansichten werden in der **ArNi CRM** unter
> **System → Terminals** verwaltet (Admin-Login der CRM). Dieses Verzeichnis enthält
> nur noch das öffentliche Portal – es gibt **kein eigenes Admin-Login mehr**.

## Struktur
```
terminal/
  config.inc.php            Konfiguration (DB, lädt Secret + Libs)
  includes/
    secret.php              Gemeinsames HMAC-Secret (auch von der CRM gelesen)
    db.php                  PDO-Verbindung + Tabellen-Präfix (term_)
    helpers.php             Escaping, Signatur, t_html()/t_css()
    terminals.php           Terminal-Bibliothek (PIN, Cookie, Laden, Zuordnung, grants)
  terminal.php?h=<hash>     Öffentliches Portal: PIN-Gate → Startseite
  view.php?t=<token>&term=<hash>   Ansicht über ein Terminal (Stub)
  sql/schema.sql            Tabellen term_terminals, term_views, term_terminal_access
```

## Datenbank
Nutzt die ArNi-CRM-Datenbank (`arni_shopping`) mit Präfix `term_`.
Schema einspielen (idempotent):
```
php -r 'require "config.inc.php"; term_db()->exec(file_get_contents("sql/schema.sql"));'
```

## Verwaltung (in der CRM)
ArNi CRM → **System → Terminals** (nur Admin).
- Terminal anlegen: Bezeichnung + E-Mail (optional) + PIN (4–8 Ziffern) + Aktiv + Ansichts-Zuordnung.
- Es entsteht ein fester Link `…/terminal/terminal.php?h=<hash>` (inkl. QR). Gültig bis Deaktivieren/Löschen.
- Ansichten (neutrale Platzhalter) anlegen/aktivieren/löschen.

Die CRM teilt das HMAC-Secret über `includes/secret.php` (Pfad in der CRM-Config
unter `terminal.secret_file`). Dadurch sind im CRM gesetzte PINs im Portal gültig.

## Portal-Ablauf
1. `terminal.php?h=<hash>` aufrufen → PIN-Abfrage (Terminal-PIN).
2. Nach Eingabe wird ein Freischalt-Cookie `term_<hash>` gesetzt (Standard 1 Jahr).
3. Startseite im **Premium-Dashboard-Stil** (`assets/home.{css,js}`): dunkle
   Fläche `#0C0F15`, Karten `#161B24` (24px-Radius, weiche Schatten, Hover
   `scale(1.02)`), Primär-Verlauf `#5D5FEF → #7A5AF8`; Grün nur für den
   VPN-Verbunden-Status, Rot nur für Warnungen (`warn`-Flag). Begrüßung + Uhr
   im fixierten Header, Pfeiltasten-Navigation.
   Kachel-Layout: Icon links, Titel/Beschreibung floaten daneben; darunter
   **klickbare Vorschau-Zeilen** (`{ic,t,s,url,img,ok,warn}`, PHP
   `term_tv_item()` = JS `itemNode()`), die Datensätze **direkt aus der Karte
   öffnen** – Medien per `&item=<id>` (öffnet die Detailansicht), Webseiten
   per `&site=<id>` (Vorauswahl), Worklog/Monatsliste per `&m=YYYY-MM`,
   Pinnwand-Beiträge per `&post=<id>`. Worklog/Monatsliste rendern ihre
   Zeilen direkt (lokale Daten); VPN, Medien, Arbeitszeit, Rechnungen,
   Webseiten und Slots laden **asynchron** über die Modul-Aktion `api=tile`
   nach (bis dahin Skeleton-Zeilen). Antwort-Varianten: `variant=duo`
   (VPN: zwei gleich große Infofelder – Land/Status | Exit-IP) und
   `variant=rail` (Medien: horizontale **Poster-Rail** mit echten Covern
   3:4 aus der CRM, Play-Button, Deep-Link je Titel).
   Die Pinnwand ist eine Hero-Kachel im Social-Feed-Stil: 3 neueste Beiträge
   mit Avatar, Name, Zeit und **echtem Vorschaubild rechts** (Bild-Thumbnail,
   Video-Poster mit Play-Overlay, „+N“-Stapel-Badge bei mehreren Bildern,
   gecachte Link-/YouTube-Vorschau – alles über `pinnwand-media.php`),
   dünne Trennlinien, Ungelesen-Badge und **Schnell-Beitrag** (postet direkt
   über `view.php?…&api=post_create`, ohne die Ansicht zu öffnen; Eingabefeld
   mit Fokus-Glow, runder Senden-Button).
4. Eine Ansicht öffnet `view.php?t=<token>&term=<hash>`; `term_terminal_grants()`
   prüft das Freischalt-Cookie – eine evtl. eigene PIN der Ansicht wird übersprungen.
5. **Einstellungen** (`?settings=1`, über den Initialen-Button im Header) in Tabs:
   „PIN" (PIN ändern), „Startseite" (Widgets per ▲▼ sortieren und per Schalter
   ein-/ausblenden; nur Nutzer-Präferenz – die Zuordnung/Berechtigung bleibt in
   der CRM, daher jederzeit wieder aktivierbar; Speicherung in
   `term_terminal_meta` `tile_order`/`tile_hidden`, Tabelle wird zur Laufzeit
   angelegt) sowie „Slots" (Guthaben & Spielauswahl, falls zugeordnet).

## Sicherheit
- PIN wird als HMAC (`term_sign`, Secret in `includes/secret.php`) gespeichert, nie im Klartext.
- Freischalt-Cookies sind `HttpOnly`, `SameSite=Lax`, `Secure` (bei HTTPS), auf `/terminal` begrenzt.
- Öffentliche Seiten setzen `Cache-Control: no-store`.

## Modul „Pinnwand" (öffentlicher Feed)
Ansichtsmodul `pinnwand` (`includes/modules/pinnwand.php` + `assets/modules/pinnwand.{css,js}`):
gemeinsamer Feed je Ansicht – am sinnvollsten als **globale** Ansicht (ein Feed für alle Terminals).

- **Beiträge:** Text (Limit konfigurierbar, Standard 2000 Zeichen), mehrere Bilder,
  Video(s), Link mit Vorschau. Autor = Terminal (Initialen-Avatar). Bearbeiten
  (nur Text, „bearbeitet"-Badge) & Löschen (Soft-Delete) durch den Autor.
- **Feed:** Cursor-Pagination (id DESC), Infinite Scroll, Filter
  (Alle/Bilder/Videos/Links/Eigene/Angepinnt; „Videos" umfasst auch
  YouTube-Links), Suche, 15-s-Polling (neue Beiträge
  werden direkt eingefügt, bei vielen ein Hinweis-Button; kein Feed-Springen),
  Skeleton-/Leer-/Fehler-/Offline-Zustände,
  Entwurf in `localStorage`. Direktlink je Beitrag: `view.php?t=…&term=…&post=<id>`.
- **Uploads:** Chunk-Upload (~900 KB je Chunk, wegen nginx-Limit 1M auf arni-solutions.de);
  Bilder clientseitig vorverkleinert, serverseitig per GD neu kodiert (EXIF-Rotation,
  **Metadaten entfernt**, 4 Größen: Original ≤2560 / `_m` 1280 / `_t` 360 /
  `_s` 160 **Mini** für Kachel-/Kommentar-Thumbs; Bestandsdateien werden bei der
  ersten Mini-Anfrage lazy aus `_t`/`_p`/Link-Bild erzeugt und gecacht,
  `s=s|ls` in `pinnwand-media.php`), Videos nur MP4/WebM per **Magic Bytes**
  (MOV nur, wenn `ffmpeg` vorhanden → Remux; ffmpeg liefert optional Poster+Dauer).
  **Video-Poster ohne ffmpeg:** der Client zieht einen Frame aufs Canvas
  (`capturePoster`), prüft die Helligkeit und nimmt bei (fast) schwarzem Frame
  (Fade-In) einen späteren Zeitpunkt (1/3/6/10 s, sonst hellster Versuch) und
  reicht ihn per `up_poster` nach (`{base}_p.jpg`, GD-Re-Encode). Bestandsvideos
  ohne Poster werden beim Betrachten im Feed **lazy nachgezogen** (erster
  Schreiber gewinnt, fremde Poster werden nie überschrieben; 1 Versuch je
  Gerät via localStorage). Auch der ffmpeg-Pfad prüft auf Schwarzframes
  (`module_pinnwand_frame_dark`) und probiert spätere Zeitpunkte. Mit dem
  Poster erscheint das Video-Thumb auch auf der Startseiten-Kachel (Mini `_s`).
  Zufällige Dateinamen, Ablage `data/pinnwand/<view_id>/`, Auslieferung ausschließlich
  über `pinnwand-media.php` (Cookie-Auth wie `view.php`, HTTP-Range für Video).
- **Link-Vorschau:** SSRF-geschützt (nur http/https, DNS-Auflösung mit Sperrung
  privater/reservierter IP-Bereiche je Redirect-Hop, max. 3 Redirects, 512-KB-Cap);
  og:image wird lokal gecacht (kein Hotlinking). Cache je URL 7 Tage (`term_post_links`).
  YouTube-Links (watch/shorts/live/youtu.be) laufen über **oEmbed** (Titel, Kanal,
  Thumbnail; kein API-Key nötig), da YouTube dem Bot nur die Consent-Seite liefert;
  im Feed startet der Klick einen **eingebetteten Player** (youtube-nocookie,
  lädt erst nach dem Klick) statt zu YouTube abzuspringen. Über den eigenen
  ⛶-Button („Vollbild mit Kommentaren") faden **nur im Vollbild** die
  Kommentare des Beitrags als Ticker über der Steuerleiste ein (Glas-Pille,
  nacheinander in Schleife; YouTubes interner Vollbildmodus kann technisch
  nicht überlagert werden); Klick auf einen Ticker-Kommentar verlässt das
  Vollbild, öffnet das Kommentar-Modal und **springt zum angeklickten
  Kommentar** (zentriert + kurz hervorgehoben, lädt bei Bedarf ältere Seiten nach).
  Existiert eine Vorschau-Karte, wird die nackte URL im Beitragstext ausgeblendet.
- **Profile:** je Terminal & Ansicht: Anzeigename, Profilfoto, Header-Foto
  (Zuschnitt per Drag/Zoom im Client, serverseitig GD-Re-Encode). Eigenes Profil über
  den Avatar in der Sticky-Filterleiste; Klick auf fremde Avatare öffnet die
  Profilansicht mit allen Beiträgen der Person (Tabelle `term_post_profiles`,
  Auslieferung `pinnwand-media.php?s=a|h`).
- **Live-Refresh:** 15-s-Polling liefert neue Beiträge direkt mit und fügt sie oben
  ein (Scroll-Position wird kompensiert); bei vielen neuen Beiträgen Hinweis-Button.
- **Reaktionen:** Facebook-Stil – Zusammenfassung (Emoji-Stapel + Summe links,
  „N Kommentare" rechts) über der Aktionszeile „Gefällt mir | Kommentieren".
  Tippen auf „Gefällt mir" setzt/entfernt 👍; **Gedrückthalten** öffnet die
  Auswahl (Standard: 👍 ❤️ 😂 😘 😎 😡 😢, Set konfigurierbar). **Eine** Reaktion je
  Terminal & Beitrag – eine andere Auswahl ersetzt die bisherige.
  Klick auf die Zusammenfassung öffnet das Modal **„Reaktionen"** (`reaction_list`):
  wer hat wie reagiert, mit Filter-Chips je Emoji (bei mehreren Typen).
- **Kommentare:** je Beitrag (💬 in der Reaktionsleiste), Chat-Stil (neueste unten,
  ältere oben nachladbar), Bearbeiten/Löschen (Soft-Delete) durch Autor bzw. Moderator,
  Zeichenlimit konfigurierbar (Standard 500), Rate-Limit = 3× Beitrags-Limit.
  Abschaltbar (`comments_enabled`); bestehende Kommentare bleiben dann lesbar.
  **Bild-Anhänge** (📷, falls Bilder erlaubt; max. `min(4, max_images)` je Kommentar,
  auch ohne Text): gleiche Upload-Pipeline wie Beiträge – Client-Resize + Chunk-Upload
  + serverseitige GD-Neukodierung, d. h. zu große oder ungeeignete Formate werden
  automatisch konvertiert. Ablage in `term_post_media` über die Spalte `comment_id`
  (wird bei Bestandsinstallationen zur Laufzeit nachgerüstet); Anzeige als
  Mini-Galerie in der Kommentar-Blase mit Lightbox.
  **Handy-Sticker** (iOS/Android: über die Tastatur eingefügte Bild-Sticker):
  lösen im Textfeld ein `paste`-Event mit Bilddatei aus → werden wie ein
  📷-Anhang behandelt (Komposer UND Kommentare, `pastedImages()` in
  `pinnwand.js`). **Transparenz bleibt erhalten:** Bilder mit Alphakanal werden
  clientseitig als PNG hochgeladen (Stichprobe in `resizeImage`) und in
  `module_pinnwand_store_image` in allen 4 Größen als PNG gespeichert
  (`ext='png'` in `term_post_media`; Erkennung per 48×48-Alpha-Stichprobe,
  deckende Bilder werden weiter platzsparend JPEG). Transparente Ränder werden
  per `imagecropauto(IMG_CROP_TRANSPARENT)` abgeschnitten (Sticker-Apps polstern
  auf eine quadratische Leinwand); Anzeige in natürlicher Größe
  (`width:auto; max-width:100%`), nicht auf volle Breite gezogen. Auslieferung:
  `pinnwand-media.php` fällt je Größe von `.jpg` auf `.png` zurück; der
  Serializer liefert `alpha:true` → `.is-alpha` (kein schwarzer Kasten/Rahmen,
  `object-fit: contain`). Link-Vorschaubilder und Profilfotos bleiben immer
  JPEG (`$keepAlpha = false`).
- **Mitteilungen:** 🔔 oben rechts mit Ungelesen-Badge (fremde Kommentare auf eigene
  Beiträge, Zähler kommt mit dem 15-s-Polling). Öffnen der Liste markiert alles als
  gelesen (`notif_seen_at` in `term_post_profiles`); Klick springt zum Beitrag und
  öffnet die Kommentare.
- **Startseiten-Badge:** auf der Terminal-Startseite zeigt die Pinnwand-Karte einen
  roten Zähler mit neuen fremden Beiträgen + Kommentaren seit dem letzten Öffnen
  (`feed_seen_at`, wird beim Öffnen/Polling der Pinnwand aktualisiert).
- **Meldungen/Moderation:** Gründe Spam/Beleidigend/Unangemessen/Datenschutz/Falschinfo/Sonstiges,
  1 Meldung je Terminal & Beitrag, optional Auto-Verbergen ab Schwellwert.
  Moderator-Terminals (in den Einstellungen) können im Portal anpinnen/verbergen/löschen
  und Meldungen abarbeiten; vollständige Moderation zusätzlich im CRM.
- **Verwaltung:** CRM → System → Terminals → Zahnrad an der Pinnwand-Ansicht:
  Einstellungen (Beiträge an/aus, Limits, Formate, Reaktionen, Kommentare an/aus
  + Kommentar-Zeichenlimit, Rate-Limit, Auto-Verbergen-Schwelle, Moderatoren)
  + offene Meldungen. Speicherung in
  `term_widget_meta` (`pin:settings`).
- **Schema:** Tabellen `term_posts`, `term_post_media`, `term_post_reactions`,
  `term_post_reports`, `term_post_links`, `term_post_profiles`, `term_post_comments`
  – werden beim ersten Aufruf **selbst angelegt** (Referenz in `sql/schema.sql`).

## Hinweis
Module: `monthly_list`, `worklog`, `websites`, `time_tracking`, `invoices`,
`media`, `slots`, `slf`, `pinnwand`, `vpn` (Registry: `includes/modules/registry.php`,
Spiegel in der CRM: `TerminalController::modules()`).

Worklog: Erfassungsformular mit optionalem Feld **„Deck"** (max. 40 Zeichen,
Zahl oder Freitext; `deck` im Eintrags-JSON) – schmales Eingabefeld neben der
Auftrags-Auswahl. Anzeige als ⚓-Tag in der Tagesliste; im PDF in der Spalte
„Auftrag" (`Auftragsname · Deck 3`) sowie in den Bildunterschriften der
Foto-Seiten (Zuordnung der Fotos zum Schiffsbereich). Eingaben wie „3" werden
zu „Deck 3" normalisiert. Alt-Einträge ohne `deck`-Schlüssel bleiben gültig.

Youez - 2016 - github.com/yon3zu
LinuXploit