ic-web/README.md

336 lines
13 KiB
Markdown
Raw Permalink Normal View History

# 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`) | `<name>@<domäne>` | Seiten der eigenen Domäne pflegen, dort Zugänge anlegen und verwalten |
| **Redakteur** (`editor`) | `<name>@<domäne>` | 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, 330
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
<script src="js/apps/webhosting.js"></script>
```
**`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 <benutzer@domäne> <passwort>` | Passwort setzen |
| `webaccounts` | alle Zugänge auflisten |
| `webreports` | offene Meldungen |
| `webblock <domäne>` | Seite sperren bzw. freigeben |
| `webreport done\|dismiss <id>` | 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.