# ic-web — IC-Webhosting Ein IC-Hostinganbieter: er vergibt Domänen, richtet je Domäne einen Zugang ein, und die Domäneninhaber pflegen darauf ihre Webseite und ihre Postfächer. Aufgerufen werden die Seiten im Browser von **pc-live** unter `ic://domäne`. ## Die drei Ebenen | Rolle | Zugang | Darf | |---|---|---| | **Anbieter** (`superadmin`) | `admin@liveinvader.ls` | Domänen anlegen, übertragen, sperren, löschen; überall Zugänge und Seiten | | **Domänenadmin** (`admin`) | `@` | Seiten der eigenen Domäne pflegen, dort Zugänge anlegen und verwalten | | **Redakteur** (`editor`) | `@` | nur Seiten der eigenen Domäne pflegen | Job und Serverrechte spielen keine Rolle. Wer den Zugang kennt, verwaltet die Seite — unabhängig davon, welchen Charakter er gerade spielt. ## Ein Zugang ist ein Postfach `presse@weazel-news.ls` ist Benutzername, Passwort **und** Mailadresse in einem. Beim Anmelden wird der Zugriff auf das Postfach in `ic-mail` erteilt, beim Abmelden wieder entzogen. Deshalb können **mehrere Personen gleichzeitig dasselbe Postfach offen haben** — ein Firmenpostfach gehört der Firma, nicht einer Person. Wird ein Zugang gelöscht, bleibt das Postfach mit seinen Nachrichten bestehen. Aufgeräumt wird **beim Start**, nicht beim Herunterfahren: ic-web widerruft dann alle Postfachzugriffe, die es je erteilt hat (`granted_by = 'ic-web'`). Beim Stoppen ginge das nicht — der Aufruf nach ic-mail wartet auf eine Datenbankantwort, und eine wartende Koroutine lässt sich während des Herunterfahrens nicht mehr fortsetzen; der Server meldet dann „Execution of function reference in script host failed". Der Umweg über den Start deckt außerdem den Fall ab, den ein Stop-Handler nie erwischt: den Absturz. ## Postfächer im Mailclient Der Mailclient von pc-live kann weitere Postfächer aufnehmen: *Postfächer → **+ Postfach hinzufügen*** fragt Adresse und Passwort ab — dieselbe Prüfung wie die Anmeldung, aber **ohne Verwaltungsrechte**. Man kann zehn Postfächer offen haben (`Config.MaxOpenMailboxes`) und trotzdem nur für eines Seiten pflegen dürfen. Freigeschaltete Postfächer tragen ein ✕ zum Entfernen; das eigene und dienstlich zugewiesene nicht — die gehören einem nicht. Beim Verlassen des Servers werden alle wieder geschlossen, ebenso beim nächsten Start von ic-web. Das Abmelden von der Verwaltung nimmt ein eigenständig geöffnetes Postfach nicht weg. ## Signaturen Die Signatur gehört zum **Postfach**, nicht zur Person: eine Firmenadresse trägt dieselbe Fußzeile, egal wer sie gerade bedient. Ändern darf sie jeder mit Zugriff auf das Postfach (*Signatur* im Mailclient). Beim Schreiben wird sie unter den Text gesetzt, getrennt durch eine Zeile mit `--`. Beim Wechsel des Absenders tauscht sie sich aus, der geschriebene Text bleibt stehen. Gespeichert wird in `ic_mail_accounts.signature` (ic-mail, Migration `sql/migration_signaturen.sql`). ## Ordner je Postfach Ordner gehören zum Postfach, nicht zur Person (`pc_live_mail_folders.address`, Migration `pc-live/sql/migration_postfach_ordner.sql`). Ein geteiltes Postfach hat für alle dieselbe Struktur. Eine Mail an ein geteiltes Postfach wird je Empfänger einmal gespeichert. Damit das Einsortieren trotzdem für alle gilt, tragen alle Kopien einer Zustellung dieselbe `delivery_id`: wird eine Kopie verschoben, wandern die anderen mit. Gelesen und gelöscht bleibt persönlich — das ist keine Eigenschaft der Nachricht, sondern der Person. Welches Postfach gemeint ist, schickt der Client mit; der Server prüft es über `ic-mail:CanAccessAddress` und fällt ohne Zugriff auf das persönliche Postfach zurück, statt fremde Ordner herauszugeben. ## Kalender Dieselbe Mechanik wie bei Postfächern und Ordnern (`pc_live_calendar.address` + `visibility`, Migration `pc-live/sql/migration_kalender.sql`): | Sichtbarkeit | gehört | sieht | bearbeitet | |---|---|---|---| | `private` | der Person | nur sie | nur sie | | `shared` | einem Postfach | wer es bedient | wer es bedient | | `public` | einem Postfach | jeder auf dem Server | wer es bedient | Ein öffentlicher Termin hängt bewusst auch an einem Postfach — sonst gäbe es niemanden, der ihn später ändern oder absagen könnte. Wer ein Postfach bedient, darf dessen Termine auch dann ändern, wenn jemand anderes sie eingetragen hat; genau das macht sie geteilt. Änderungen erreichen alle Beteiligten sofort, sonst hätte jeder eine andere Vorstellung vom Dienstplan, bis er den PC neu öffnet. ## Ablauf 1. Der Anbieter meldet sich in der App **Webhosting** mit `admin@liveinvader.ls` an (Startpasswort siehe unten) und ändert sein Passwort. 2. Er legt unter *Verwaltung* eine Domäne an, wählt den Besitzer-Job und den ersten Zugang. Benutzername und Passwort werden **einmal** angezeigt. 3. Der Domänenadmin meldet sich damit an, ändert sein Passwort und legt unter *Zugänge* weitere Zugänge für seine Leute an. 4. Alle Zugänge dieser Domäne pflegen die Seiten unter *Meine Seite* und veröffentlichen sie. ## Ersteinrichtung Beim ersten Start legt ic-web den Anbieterzugang aus `Config.MainAdmin` an und schreibt Benutzername und Startpasswort in die Serverkonsole. Vorgabe: ``` admin@liveinvader.ls / liveinvader ``` Beim ersten Anmelden wird zum Ändern aufgefordert. Passwort vergessen? In der Serverkonsole: ``` webpasswd admin@liveinvader.ls neues-passwort ``` ## Inhalte sind kein HTML Seiten bestehen aus **typisierten Blöcken** (`shared/blocks.lua`), nicht aus Markup. Der Grund: die Seiten werden im NUI von pc-live angezeigt — im selben Kontext wie der PC selbst. Wer dort Markup einschleusen kann, kann auch dessen NUI-Callbacks aufrufen. Ein Blockmodell schließt das konstruktiv aus, statt es filtern zu müssen; HTML zu filtern ist ein Wettlauf, den man langfristig verliert. `B.Validate` baut jeden Block aus dem Schema **neu auf**. Was nicht im Schema steht, fällt weg — es wird nicht bereinigt, sondern verworfen. Blocktypen: `heading`, `text`, `image`, `list`, `button`, `contact`, `divider`. Der Renderer in pc-live setzt Text ausschließlich über `textContent`, niemals über `innerHTML`. Deshalb bleiben `<` und `>` im Text erhalten statt verstümmelt zu werden. ## Domänennamen Form: `name.endung`, nur Kleinbuchstaben, Ziffern und Bindestriche, 3–30 Zeichen, Endung aus `Config.Domain.allowedTlds` (`.ls .sa .gov .org .net`). `Config.Domain.reserved` sperrt Namen **nur gegen die Selbstanmeldung** — der Anbieter darf `lspd.ls` sehr wohl vergeben, dafür ist er da. Unabhängig davon gesperrt sind Domänen des Mailsystems: existiert der Realm schon in ic-mail als `is_default`, `system` oder `citizen` (etwa `mail.ls`), lehnt `D.EnsureRealm` ab — für alle. Sonst hinge eine Firmenwebseite an der Domäne sämtlicher persönlicher Postfächer. Eine herrenlose *Firmen*-Domäne (`type = company`) wird dagegen übernommen. ## Bilder und Meldungen Bilder werden extern verlinkt — NUI kann keine Dateien hochladen. Erlaubt sind nur `https` und die Hosts aus `Config.ImageHosts` (Subdomains eingeschlossen). Die Hostliste ist der Rahmen, die Meldefunktion fängt den Rest — sie greift aber erst nachträglich. Jeder Spieler kann eine Seite melden (Link unten auf jeder Seite), einmal je Seite und Stunde. Der Anbieter sieht die Meldungen unter *Meldungen*. ## Passwörter Gespeichert wird `SHA2(salt || passwort)`, gerechnet in der Datenbank — das Passwort läuft nur als Abfrageparameter durch den Server. Fünf Fehlversuche je Benutzername innerhalb von fünf Minuten sperren den Namen kurzzeitig (`Config.Login`). Das ist ein **IC-Passwort**, keine Sicherheitsgrenze gegen Serveradmins: wer Datenbankzugriff hat, kann jeden Zugang zurücksetzen. Es verhindert, dass Mitspieler fremde Seiten ändern — nicht mehr. ## Installation ### 1. Ordner Der Ordner `ic-web` gehört in dein Resource-Verzeichnis, zum Beispiel: ``` resources/[haleoe]/ic-web/ ``` ### 2. Datenbank ```bash mysql -u BENUTZER -p DATENBANK < sql/ic_web.sql ``` Legt an: `ic_web_sites`, `ic_web_pages`, `ic_web_accounts`, `ic_web_login_fails`, `ic_web_reports`. Wiederholt ausführbar. ### 3. server.cfg ``` ensure oxmysql ensure es_extended ensure ic-mail ensure ic-web # nach ic-mail ``` Liegt alles in einem Ordner mit eckigen Klammern, genügt `ensure [haleoe]` — dann startet ic-web automatisch mit. Die Reihenfolge stimmt dabei von selbst, weil ic-mail alphabetisch vorher kommt. ### 4. Anbindung an pc-live ic-web bringt keine eigene Oberfläche mit — sie sitzt im PC. Diese Dateien gehören nach `pc-live`: | Datei | Ziel in pc-live | Zweck | |---|---|---| | `client/webhosting.lua` | `client/` | Brücke zwischen NUI und Server | | `nui/js/apps/webhosting.js` | `nui/js/apps/` | die App selbst | Dazu in pc-live eintragen: **`fxmanifest.lua`** ```lua client_scripts { …, 'client/webhosting.lua' } files { …, 'nui/js/apps/webhosting.js' } ``` **`nui/index.html`** — vor `desktop.js`, weil andere Apps den Dialogbaukasten daraus benutzen: ```html ``` **`nui/js/desktop.js`** — App eintragen und den Antwortkanal anschließen: ```js 'pc.webhosting': { name: 'Webhosting', icon: '🌍', open: () => WebhostingApp.open() }, … case 'icweb_response': ICWebNet.onResponse(data); break; ``` **`server/apps.lua`** — damit die App auf dem Schreibtisch erscheint: ```lua AppRegistry.Register({ app_id = "pc.webhosting", name = "Webhosting", icon = "🌍", version = "1.0.0", category = "system", default = true, permissions = {}, dependencies = {}, price = 0, description = "IC-Domänen und Webseiten verwalten. Anmeldung erforderlich.", }) ``` **`nui/js/apps/browser.js`** — damit `ic://domäne.ls` im Browser aufgeht: der Zweig, der Adressen der Form `name.endung` an `ICWebNet.call('getSite', …)` gibt und mit `ICWebRender.renderPage` zeichnet. ### 5. Erste Anmeldung Beim ersten Start legt ic-web den Anbieterzugang an und schreibt ihn in die Serverkonsole. Vorgabe: `admin@liveinvader.ls` / `liveinvader`. Beim ersten Anmelden wird ein eigenes Passwort verlangt. ## Voraussetzungen | | | |---|---| | `oxmysql` | Datenbankzugriff | | `es_extended` | Spielername, Jobliste, Benachrichtigungen | | `ic-mail` | Domänen und Postfächer. Fehlt es, laufen die Webseiten trotzdem – nur ohne Mailadressen | | `pc-live` | die Oberfläche | | `fmsdk` | Protokoll. Fehlt es, wird verworfen statt zu scheitern | ic-mail braucht dafür sechs Exports, die dieses Projekt voraussetzt: `GetRealmByName`, `CreateRealm`, `EnsureSharedAddress`, `GrantAddressAccess`, `RevokeAddressAccess`, `RevokeAccessGrantedBy`. ## Selbstanmeldung (optional, aus) `Config.SelfRegistration.enabled = true` erlaubt Behörden und Unternehmen, sich ohne Anbieter selbst eine Domäne zu holen — Grundlage ist dann der ESX-Job ab `minGrade`. Der Zugang wird dabei automatisch erzeugt und einmalig angezeigt. Standardmäßig aus, weil die Vergabe über den Anbieter läuft. ## Konsolenbefehle Notbremse des Serverbetreibers, kein IC-Weg. Erlaubt sind Serverkonsole sowie ESX-Gruppe `admin`/`superadmin` bzw. ACE `ic_web.admin` (`Config.Console`). | Befehl | Wirkung | |---|---| | `webpasswd ` | Passwort setzen | | `webaccounts` | alle Zugänge auflisten | | `webreports` | offene Meldungen | | `webblock ` | Seite sperren bzw. freigeben | | `webreport done\|dismiss ` | Meldung abschließen | ## Exports ```lua exports['ic-web']:GetSite(domain, slug) -- (seite, nil) oder (nil, fehlertext) exports['ic-web']:DomainExists(domain) exports['ic-web']:GetSitesForJob(job) exports['ic-web']:GetSession(src) -- wer ist an diesem PC angemeldet ``` `ic-mail` wurde um vier Exports ergänzt, die ic-web benutzt: `EnsureSharedAddress`, `GrantAddressAccess`, `RevokeAddressAccess`, `DeleteAddress` — sowie `GetRealmByName` und `CreateRealm` für die Domänen. ## Netzwerkereignisse Jedes Ereignis bekommt eine Anfrage-Id und antwortet über `ic-web:client:response` mit `{ ok, data, error }`. Alle sind gedrosselt; Rechte werden ausnahmslos serverseitig aus der Anmeldung abgeleitet. Anmeldung: `login`, `logout`, `session`, `changePassword`. Seiten: `getSite`, `listMine`, `getEditable`, `savePage`, `deletePage`, `updateSite`, `report`. Zugänge: `listAccounts`, `createAccount`, `setAccountPassword`, `setAccountActive`, `setAccountRole`, `deleteAccount`. Anbieter: `adminListSites`, `adminListAccounts`, `adminListJobs`, `adminRegister`, `adminSetBlocked`, `adminSetOwner`, `adminDeleteSite`, `adminListReports`, `adminSetReportStatus`. ## Tabellen `ic_web_sites`, `ic_web_pages`, `ic_web_accounts`, `ic_web_login_fails`, `ic_web_reports`. Die Domäne wird über den **Namen** referenziert, nicht über einen Fremdschlüssel auf `ic_mail_realms` — Fremdschlüssel über Resource-Grenzen hinweg koppeln zwei Lebenszyklen aneinander, die unabhängig voneinander installiert werden.