mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
Document secure runtime configuration
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
# Local Development Setup with `yarn start`
|
||||
|
||||
This guide explains how to run Nitro locally with Vite, using:
|
||||
|
||||
- local UI on `http://localhost:5173`;
|
||||
- local API/emulator on `http://localhost:2096`;
|
||||
- local WebSocket on `ws://localhost:2096`;
|
||||
- remote plain assets and gamedata, so you do not need to copy the full `client/nitro` folder locally.
|
||||
|
||||
## 1. Start the emulator
|
||||
|
||||
Inside `Arcturus-Morningstar-Extended/Emulator`, start the emulator with WebSocket enabled.
|
||||
|
||||
Recommended local `config.ini` values:
|
||||
|
||||
```ini
|
||||
ws.enabled=true
|
||||
ws.host=0.0.0.0
|
||||
ws.port=2096
|
||||
ws.whitelist=*
|
||||
ws.ip.header=
|
||||
|
||||
crypto.ws.enabled=0
|
||||
|
||||
nitro.secure.assets.enabled=false
|
||||
nitro.secure.api.enabled=false
|
||||
```
|
||||
|
||||
For local development, it is easier to disable:
|
||||
|
||||
- `crypto.ws.enabled`;
|
||||
- `nitro.secure.assets.enabled`;
|
||||
- `nitro.secure.api.enabled`.
|
||||
|
||||
This keeps debugging simple and avoids the secure runtime layer.
|
||||
|
||||
## 2. `public/configuration/client-mode.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/public/configuration/client-mode.json
|
||||
```
|
||||
|
||||
Recommended local config:
|
||||
|
||||
```json
|
||||
{
|
||||
"distObfuscationEnabled": true,
|
||||
"secureAssetsEnabled": false,
|
||||
"secureApiEnabled": false,
|
||||
"apiBaseUrl": "http://localhost:2096",
|
||||
"plainConfigBaseUrl": "http://localhost:5173/configuration/",
|
||||
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `secureAssetsEnabled=false` avoids `/nitro-sec/file`.
|
||||
- `secureApiEnabled=false` avoids encrypted `/api/*` requests.
|
||||
- `apiBaseUrl` must point to your local emulator.
|
||||
- `plainGamedataBaseUrl` can stay remote if you do not have gamedata copied locally.
|
||||
|
||||
If you want everything local, use:
|
||||
|
||||
```json
|
||||
"plainGamedataBaseUrl": "http://localhost:5173/client/nitro/gamedata/"
|
||||
```
|
||||
|
||||
but the files must really exist under:
|
||||
|
||||
```txt
|
||||
Nitro-V3/public/client/nitro/gamedata/
|
||||
```
|
||||
|
||||
## 3. `public/configuration/renderer-config.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/public/configuration/renderer-config.json
|
||||
```
|
||||
|
||||
Minimum local values:
|
||||
|
||||
```json
|
||||
{
|
||||
"socket.url": "ws://localhost:2096",
|
||||
"api.url": "http://localhost:2096",
|
||||
"crypto.ws.enabled": false,
|
||||
"gamedata.url": "https://hotel.example.com/client/nitro/gamedata",
|
||||
"external.texts.url": [
|
||||
"${gamedata.url}/ExternalTexts.json",
|
||||
"${gamedata.url}/UITexts.json"
|
||||
],
|
||||
"furnidata.url": "${gamedata.url}/FurnitureData.json?t=%timestamp%",
|
||||
"productdata.url": "${gamedata.url}/ProductData.json?t=%timestamp%",
|
||||
"avatar.actions.url": "${gamedata.url}/HabboAvatarActions.json?t=%timestamp%",
|
||||
"avatar.figuredata.url": "${gamedata.url}/FigureData.json?t=%timestamp%",
|
||||
"avatar.figuremap.url": "${gamedata.url}/FigureMap.json?t=%timestamp%",
|
||||
"avatar.effectmap.url": "${gamedata.url}/EffectMap.json?t=%timestamp%",
|
||||
"login.endpoint": "${api.url}/api/auth/login",
|
||||
"login.register.endpoint": "${api.url}/api/auth/register",
|
||||
"login.forgot.endpoint": "${api.url}/api/auth/forgot-password",
|
||||
"login.logout.endpoint": "${api.url}/api/auth/logout",
|
||||
"login.remember.endpoint": "${api.url}/api/auth/remember",
|
||||
"login.health.endpoint": "${api.url}/api/health",
|
||||
"login.health.method": "GET",
|
||||
"login.check-email.endpoint": "${api.url}/api/auth/check-email",
|
||||
"login.check-username.endpoint": "${api.url}/api/auth/check-username",
|
||||
"login.register.imaging.url": "${api.url}/api/avatar/imaging",
|
||||
"login.news.url": "${api.url}/api/auth/news",
|
||||
"badges.custom.list.endpoint": "${api.url}/api/badges/custom",
|
||||
"badges.custom.create.endpoint": "${api.url}/api/badges/custom",
|
||||
"badges.custom.update.endpoint": "${api.url}/api/badges/custom/%badgeId%",
|
||||
"badges.custom.delete.endpoint": "${api.url}/api/badges/custom/%badgeId%",
|
||||
"badges.custom.texts.endpoint": "${api.url}/api/badges/custom/texts"
|
||||
}
|
||||
```
|
||||
|
||||
Important:
|
||||
|
||||
- Do not use `https://localhost:2096/nitro-sec/file` locally if `secureAssetsEnabled=false`.
|
||||
- Do not use `ws://192.168.x.x/:2096`; it is malformed. Use `ws://localhost:2096` or `ws://192.168.x.x:2096`.
|
||||
|
||||
## 4. `public/configuration/ui-config.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/public/configuration/ui-config.json
|
||||
```
|
||||
|
||||
For the login view, you can use remote plain images:
|
||||
|
||||
```json
|
||||
{
|
||||
"loginview": {
|
||||
"images": {
|
||||
"background": "https://hotel.example.com/client/nitro/images/reception/background_gradient_apr25.png",
|
||||
"background.colour": "#6eadc8",
|
||||
"drape": "https://hotel.example.com/client/nitro/images/reception/drape.png",
|
||||
"left": "https://hotel.example.com/client/nitro/images/reception/mute_reception_backdrop_left.png",
|
||||
"right": "https://hotel.example.com/client/nitro/images/reception/background_right.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you see `ERR_NAME_NOT_RESOLVED`, the configured domain does not exist or is not reachable.
|
||||
|
||||
## 5. Database-backed news
|
||||
|
||||
Login news should come from the database through the emulator.
|
||||
|
||||
In renderer config use:
|
||||
|
||||
```json
|
||||
"login.news.url": "${api.url}/api/auth/news"
|
||||
```
|
||||
|
||||
The emulator reads from:
|
||||
|
||||
```txt
|
||||
ui_news
|
||||
```
|
||||
|
||||
Reference SQL:
|
||||
|
||||
```txt
|
||||
Arcturus-Morningstar-Extended/Database Updates/013_UI_Client_News.sql
|
||||
```
|
||||
|
||||
Main columns:
|
||||
|
||||
- `title`
|
||||
- `body`
|
||||
- `image`
|
||||
- `link_text`
|
||||
- `link_url`
|
||||
- `enabled`
|
||||
- `sort_order`
|
||||
|
||||
`public/configuration/news.json` can stay as a mock/fallback only, but it is not the correct production flow.
|
||||
|
||||
## 6. Start Nitro
|
||||
|
||||
Inside `Nitro-V3`:
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
Open:
|
||||
|
||||
```txt
|
||||
http://localhost:5173
|
||||
```
|
||||
|
||||
Recommendation: use `localhost`, not `192.168.x.x`, because cookies and API sessions are host-based and can otherwise cause `401 Unauthorized`.
|
||||
|
||||
## 7. Common errors
|
||||
|
||||
### `Unable to load renderer-config.json`
|
||||
|
||||
Check:
|
||||
|
||||
```txt
|
||||
public/configuration/client-mode.json
|
||||
```
|
||||
|
||||
It must contain:
|
||||
|
||||
```json
|
||||
"secureAssetsEnabled": false
|
||||
```
|
||||
|
||||
### `Invalid JSON ... Unexpected token '<'`
|
||||
|
||||
The client requested JSON, but Vite returned HTML.
|
||||
|
||||
This happens when a URL points to a file that does not exist, for example:
|
||||
|
||||
```txt
|
||||
http://localhost:5173/client/nitro/gamedata/ExternalTexts.json
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
- use remote plain gamedata;
|
||||
- or copy the gamedata files into `public/client/nitro/gamedata`.
|
||||
|
||||
### WebSocket `1006`
|
||||
|
||||
Check:
|
||||
|
||||
```json
|
||||
"socket.url": "ws://localhost:2096"
|
||||
```
|
||||
|
||||
and emulator config:
|
||||
|
||||
```ini
|
||||
ws.enabled=true
|
||||
ws.port=2096
|
||||
```
|
||||
|
||||
### Custom badges `401 Unauthorized`
|
||||
|
||||
This is normal if you are not logged in or if you open Nitro from a different host.
|
||||
|
||||
Use:
|
||||
|
||||
```txt
|
||||
http://localhost:5173
|
||||
```
|
||||
|
||||
and API:
|
||||
|
||||
```txt
|
||||
http://localhost:2096
|
||||
```
|
||||
|
||||
## 8. Difference from production
|
||||
|
||||
Local `yarn start`:
|
||||
|
||||
```html
|
||||
<script type="module" src="/src/bootstrap.ts"></script>
|
||||
```
|
||||
|
||||
Production build:
|
||||
|
||||
```html
|
||||
<script src="/configuration/bootstrap.js"></script>
|
||||
```
|
||||
|
||||
Do not mix the two flows.
|
||||
@@ -0,0 +1,279 @@
|
||||
# Setup locale con `yarn start`
|
||||
|
||||
Questa guida serve per avviare Nitro in locale con Vite, usando:
|
||||
|
||||
- UI locale su `http://localhost:5173`;
|
||||
- API/emulatore locale su `http://localhost:2096`;
|
||||
- WebSocket locale su `ws://localhost:2096`;
|
||||
- asset e gamedata remoti plain, così non devi copiare tutta la cartella `client/nitro`.
|
||||
|
||||
## 1. Avvia l'emulatore
|
||||
|
||||
Nel repo `Arcturus-Morningstar-Extended/Emulator`, avvia l'emulatore con WebSocket attivo.
|
||||
|
||||
Nel tuo `config.ini` locale usa valori tipo:
|
||||
|
||||
```ini
|
||||
ws.enabled=true
|
||||
ws.host=0.0.0.0
|
||||
ws.port=2096
|
||||
ws.whitelist=*
|
||||
ws.ip.header=
|
||||
|
||||
crypto.ws.enabled=0
|
||||
|
||||
nitro.secure.assets.enabled=false
|
||||
nitro.secure.api.enabled=false
|
||||
```
|
||||
|
||||
Per il locale è meglio tenere spenti:
|
||||
|
||||
- `crypto.ws.enabled`;
|
||||
- `nitro.secure.assets.enabled`;
|
||||
- `nitro.secure.api.enabled`.
|
||||
|
||||
Così puoi debuggare senza layer secure in mezzo.
|
||||
|
||||
## 2. `public/configuration/client-mode.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/public/configuration/client-mode.json
|
||||
```
|
||||
|
||||
Config locale consigliato:
|
||||
|
||||
```json
|
||||
{
|
||||
"distObfuscationEnabled": true,
|
||||
"secureAssetsEnabled": false,
|
||||
"secureApiEnabled": false,
|
||||
"apiBaseUrl": "http://localhost:2096",
|
||||
"plainConfigBaseUrl": "http://localhost:5173/configuration/",
|
||||
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
|
||||
}
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- `secureAssetsEnabled=false` evita `/nitro-sec/file`.
|
||||
- `secureApiEnabled=false` evita cifratura `/api/*`.
|
||||
- `apiBaseUrl` deve puntare all'emulatore locale.
|
||||
- `plainGamedataBaseUrl` può rimanere remoto se non hai gamedata copiato in locale.
|
||||
|
||||
Se vuoi tutto locale, usa:
|
||||
|
||||
```json
|
||||
"plainGamedataBaseUrl": "http://localhost:5173/client/nitro/gamedata/"
|
||||
```
|
||||
|
||||
ma devi avere davvero i file sotto:
|
||||
|
||||
```txt
|
||||
Nitro-V3/public/client/nitro/gamedata/
|
||||
```
|
||||
|
||||
## 3. `public/configuration/renderer-config.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/public/configuration/renderer-config.json
|
||||
```
|
||||
|
||||
Valori minimi locali:
|
||||
|
||||
```json
|
||||
{
|
||||
"socket.url": "ws://localhost:2096",
|
||||
"api.url": "http://localhost:2096",
|
||||
"crypto.ws.enabled": false,
|
||||
"gamedata.url": "https://hotel.example.com/client/nitro/gamedata",
|
||||
"external.texts.url": [
|
||||
"${gamedata.url}/ExternalTexts.json",
|
||||
"${gamedata.url}/UITexts.json"
|
||||
],
|
||||
"furnidata.url": "${gamedata.url}/FurnitureData.json?t=%timestamp%",
|
||||
"productdata.url": "${gamedata.url}/ProductData.json?t=%timestamp%",
|
||||
"avatar.actions.url": "${gamedata.url}/HabboAvatarActions.json?t=%timestamp%",
|
||||
"avatar.figuredata.url": "${gamedata.url}/FigureData.json?t=%timestamp%",
|
||||
"avatar.figuremap.url": "${gamedata.url}/FigureMap.json?t=%timestamp%",
|
||||
"avatar.effectmap.url": "${gamedata.url}/EffectMap.json?t=%timestamp%",
|
||||
"login.endpoint": "${api.url}/api/auth/login",
|
||||
"login.register.endpoint": "${api.url}/api/auth/register",
|
||||
"login.forgot.endpoint": "${api.url}/api/auth/forgot-password",
|
||||
"login.logout.endpoint": "${api.url}/api/auth/logout",
|
||||
"login.remember.endpoint": "${api.url}/api/auth/remember",
|
||||
"login.health.endpoint": "${api.url}/api/health",
|
||||
"login.health.method": "GET",
|
||||
"login.check-email.endpoint": "${api.url}/api/auth/check-email",
|
||||
"login.check-username.endpoint": "${api.url}/api/auth/check-username",
|
||||
"login.register.imaging.url": "${api.url}/api/avatar/imaging",
|
||||
"login.news.url": "${api.url}/api/auth/news",
|
||||
"badges.custom.list.endpoint": "${api.url}/api/badges/custom",
|
||||
"badges.custom.create.endpoint": "${api.url}/api/badges/custom",
|
||||
"badges.custom.update.endpoint": "${api.url}/api/badges/custom/%badgeId%",
|
||||
"badges.custom.delete.endpoint": "${api.url}/api/badges/custom/%badgeId%",
|
||||
"badges.custom.texts.endpoint": "${api.url}/api/badges/custom/texts"
|
||||
}
|
||||
```
|
||||
|
||||
Importante:
|
||||
|
||||
- Non usare `https://localhost:2096/nitro-sec/file` in locale se `secureAssetsEnabled=false`.
|
||||
- Non usare `ws://192.168.x.x/:2096`: è malformato. Usa `ws://localhost:2096` oppure `ws://192.168.x.x:2096`.
|
||||
|
||||
## 4. `public/configuration/ui-config.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/public/configuration/ui-config.json
|
||||
```
|
||||
|
||||
Per la login view puoi usare immagini remote plain:
|
||||
|
||||
```json
|
||||
{
|
||||
"loginview": {
|
||||
"images": {
|
||||
"background": "https://hotel.example.com/client/nitro/images/reception/background_gradient_apr25.png",
|
||||
"background.colour": "#6eadc8",
|
||||
"drape": "https://hotel.example.com/client/nitro/images/reception/drape.png",
|
||||
"left": "https://hotel.example.com/client/nitro/images/reception/mute_reception_backdrop_left.png",
|
||||
"right": "https://hotel.example.com/client/nitro/images/reception/background_right.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Se vedi `ERR_NAME_NOT_RESOLVED`, il dominio configurato non esiste o non è raggiungibile.
|
||||
|
||||
## 5. News dal database
|
||||
|
||||
Le news della login devono arrivare dal database tramite l'emulatore.
|
||||
|
||||
Nel renderer config usa:
|
||||
|
||||
```json
|
||||
"login.news.url": "${api.url}/api/auth/news"
|
||||
```
|
||||
|
||||
L'emulatore legge dalla tabella:
|
||||
|
||||
```txt
|
||||
ui_news
|
||||
```
|
||||
|
||||
SQL di riferimento:
|
||||
|
||||
```txt
|
||||
Arcturus-Morningstar-Extended/Database Updates/013_UI_Client_News.sql
|
||||
```
|
||||
|
||||
Colonne principali:
|
||||
|
||||
- `title`
|
||||
- `body`
|
||||
- `image`
|
||||
- `link_text`
|
||||
- `link_url`
|
||||
- `enabled`
|
||||
- `sort_order`
|
||||
|
||||
`public/configuration/news.json` può rimanere solo come mock/fallback, ma non è il flow corretto.
|
||||
|
||||
## 6. Avvio Nitro
|
||||
|
||||
Nel repo `Nitro-V3`:
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
Apri:
|
||||
|
||||
```txt
|
||||
http://localhost:5173
|
||||
```
|
||||
|
||||
Consiglio: usa `localhost`, non `192.168.x.x`, perché cookie e sessioni API possono cambiare host e causare `401 Unauthorized`.
|
||||
|
||||
## 7. Errori comuni
|
||||
|
||||
### `Unable to load renderer-config.json`
|
||||
|
||||
Controlla:
|
||||
|
||||
```txt
|
||||
public/configuration/client-mode.json
|
||||
```
|
||||
|
||||
Deve avere:
|
||||
|
||||
```json
|
||||
"secureAssetsEnabled": false
|
||||
```
|
||||
|
||||
### `Invalid JSON ... Unexpected token '<'`
|
||||
|
||||
Vuol dire che il client ha chiesto un JSON, ma Vite ha risposto HTML.
|
||||
|
||||
Succede quando un URL punta a un file che non esiste, per esempio:
|
||||
|
||||
```txt
|
||||
http://localhost:5173/client/nitro/gamedata/ExternalTexts.json
|
||||
```
|
||||
|
||||
Soluzione:
|
||||
|
||||
- usa gamedata remoto plain;
|
||||
- oppure copia davvero i gamedata in `public/client/nitro/gamedata`.
|
||||
|
||||
### WebSocket `1006`
|
||||
|
||||
Controlla:
|
||||
|
||||
```json
|
||||
"socket.url": "ws://localhost:2096"
|
||||
```
|
||||
|
||||
e nel config emulator:
|
||||
|
||||
```ini
|
||||
ws.enabled=true
|
||||
ws.port=2096
|
||||
```
|
||||
|
||||
### Custom badges `401 Unauthorized`
|
||||
|
||||
È normale se non sei loggato o se apri Nitro da un host diverso.
|
||||
|
||||
Usa:
|
||||
|
||||
```txt
|
||||
http://localhost:5173
|
||||
```
|
||||
|
||||
e API:
|
||||
|
||||
```txt
|
||||
http://localhost:2096
|
||||
```
|
||||
|
||||
## 8. Differenza con produzione
|
||||
|
||||
Locale con `yarn start`:
|
||||
|
||||
```html
|
||||
<script type="module" src="/src/bootstrap.ts"></script>
|
||||
```
|
||||
|
||||
Produzione buildata:
|
||||
|
||||
```html
|
||||
<script src="/configuration/bootstrap.js"></script>
|
||||
```
|
||||
|
||||
Non mischiare i due flow.
|
||||
@@ -0,0 +1,365 @@
|
||||
# Secure Runtime Production Setup
|
||||
|
||||
Quick setup guide for running Nitro with:
|
||||
|
||||
- configuration and gamedata served through `/nitro-sec/file`;
|
||||
- encrypted runtime `/api/*` calls;
|
||||
- obfuscated production bundles loaded as `.dat`.
|
||||
|
||||
Replace the example domains with your real domains:
|
||||
|
||||
- `https://hotel.example.com`
|
||||
- `https://nitro.example.com:2096`
|
||||
|
||||
## 1. Build Nitro
|
||||
|
||||
Inside the `Nitro-V3` repository:
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
```
|
||||
|
||||
Then publish the `dist` folder to your web server, for example:
|
||||
|
||||
```txt
|
||||
C:/inetpub/wwwroot/hotel/nitro
|
||||
```
|
||||
|
||||
The deployed folder should contain at least:
|
||||
|
||||
```txt
|
||||
configuration/
|
||||
assets/
|
||||
asset-loader.js
|
||||
index.html
|
||||
src/
|
||||
```
|
||||
|
||||
## 2. `configuration/client-mode.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/client-mode.json
|
||||
```
|
||||
|
||||
Secure production configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"distObfuscationEnabled": true,
|
||||
"secureAssetsEnabled": true,
|
||||
"secureApiEnabled": true,
|
||||
"apiBaseUrl": "https://nitro.example.com:2096",
|
||||
"plainConfigBaseUrl": "https://hotel.example.com/configuration/",
|
||||
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
|
||||
}
|
||||
```
|
||||
|
||||
Meaning:
|
||||
|
||||
- `distObfuscationEnabled: true` loads `app.js.dat` and `app.css.dat`.
|
||||
- `secureAssetsEnabled: true` loads `renderer-config.json`, `ui-config.json`, and gamedata through `/nitro-sec/file`.
|
||||
- `secureApiEnabled: true` automatically encrypts `/api/*` requests.
|
||||
- `apiBaseUrl` must point to the emulator/API.
|
||||
- `plainConfigBaseUrl` and `plainGamedataBaseUrl` are fallbacks when secure assets are disabled.
|
||||
|
||||
## 3. `configuration/renderer-config.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/renderer-config.json
|
||||
```
|
||||
|
||||
Important values:
|
||||
|
||||
```json
|
||||
{
|
||||
"socket.url": "wss://nitro.example.com:2096",
|
||||
"api.url": "https://nitro.example.com:2096",
|
||||
"gamedata.url": "https://nitro.example.com:2096/nitro-sec/file?kind=gamedata&file=",
|
||||
"external.texts.url": [
|
||||
"${gamedata.url}/ExternalTexts.json",
|
||||
"${gamedata.url}/UITexts.json"
|
||||
],
|
||||
"furnidata.url": "${gamedata.url}/FurnitureData.json?t=%timestamp%",
|
||||
"productdata.url": "${gamedata.url}/ProductData.json?t=%timestamp%",
|
||||
"avatar.actions.url": "${gamedata.url}/HabboAvatarActions.json?t=%timestamp%",
|
||||
"avatar.figuredata.url": "${gamedata.url}/FigureData.json?t=%timestamp%",
|
||||
"avatar.figuremap.url": "${gamedata.url}/FigureMap.json?t=%timestamp%",
|
||||
"avatar.effectmap.url": "${gamedata.url}/EffectMap.json?t=%timestamp%",
|
||||
"crypto.ws.enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
If you are not using WebSocket crypto yet, use:
|
||||
|
||||
```json
|
||||
"crypto.ws.enabled": false
|
||||
```
|
||||
|
||||
## 4. `configuration/ui-config.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/ui-config.json
|
||||
```
|
||||
|
||||
Static image and camera URLs can remain plain:
|
||||
|
||||
```json
|
||||
{
|
||||
"camera.url": "https://hotel.example.com/client/camera/",
|
||||
"thumbnails.url": "https://hotel.example.com/client/camera/thumbnail/%thumbnail%.png"
|
||||
}
|
||||
```
|
||||
|
||||
Non-sensitive images can stay static. JSON configuration and gamedata should go through the secure endpoint.
|
||||
|
||||
## 5. Emulator `config.ini`
|
||||
|
||||
Inside `Arcturus-Morningstar-Extended`, edit the emulator config:
|
||||
|
||||
```txt
|
||||
Emulator/config.ini
|
||||
```
|
||||
|
||||
Production example:
|
||||
|
||||
```ini
|
||||
ws.enabled=true
|
||||
ws.host=0.0.0.0
|
||||
ws.port=2096
|
||||
ws.whitelist=https://hotel.example.com
|
||||
ws.ip.header=CF-Connecting-IP
|
||||
|
||||
crypto.ws.enabled=1
|
||||
|
||||
nitro.secure.assets.enabled=true
|
||||
nitro.secure.api.enabled=true
|
||||
nitro.secure.config.root=C:/inetpub/wwwroot/hotel/nitro/configuration
|
||||
nitro.secure.gamedata.root=C:/inetpub/wwwroot/hotel/nitro/client/nitro/gamedata
|
||||
nitro.secure.master_key=change-this-to-a-long-random-secret
|
||||
|
||||
login.remember.enabled=true
|
||||
login.remember.duration.days=30
|
||||
login.remember.jwt.secret=change-this-too-if-you-use-remember-me
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `nitro.secure.config.root` must point to the folder containing `renderer-config.json`, `ui-config.json`, and `client-mode.json`.
|
||||
- `nitro.secure.gamedata.root` must point to the live gamedata folder.
|
||||
- Files are read live from disk: if you update a JSON file, a new browser refresh reads the new version.
|
||||
- `nitro.secure.master_key` must be secret and stable. Never put it in public files.
|
||||
|
||||
## 6. Cloudflare
|
||||
|
||||
If you use Cloudflare:
|
||||
|
||||
1. Keep the proxy enabled for the website domain `hotel.example.com`.
|
||||
2. Make sure Cloudflare supports/proxies the port used by `nitro.example.com:2096`.
|
||||
3. Always use HTTPS/WSS in the browser:
|
||||
|
||||
```json
|
||||
"api.url": "https://nitro.example.com:2096",
|
||||
"socket.url": "wss://nitro.example.com:2096"
|
||||
```
|
||||
|
||||
If you get CORS errors, check:
|
||||
|
||||
```ini
|
||||
ws.whitelist=https://hotel.example.com
|
||||
```
|
||||
|
||||
## 7. IIS / `.dat` MIME type
|
||||
|
||||
If obfuscated `.dat` assets are enabled, IIS must serve them correctly.
|
||||
|
||||
Add this MIME type:
|
||||
|
||||
```txt
|
||||
Extension: .dat
|
||||
MIME type: application/octet-stream
|
||||
```
|
||||
|
||||
Without it, the browser can receive 404 even when the file exists.
|
||||
|
||||
## 8. Final checklist
|
||||
|
||||
- `client-mode.json` has `secureAssetsEnabled=true`.
|
||||
- `client-mode.json` has `secureApiEnabled=true`.
|
||||
- `renderer-config.json` uses `/nitro-sec/file?kind=gamedata&file=`.
|
||||
- `api.url` points to `https://nitro.example.com:2096`.
|
||||
- `socket.url` points to `wss://nitro.example.com:2096`.
|
||||
- `config.ini` has the correct `nitro.secure.config.root`.
|
||||
- `config.ini` has the correct `nitro.secure.gamedata.root`.
|
||||
- `config.ini` has a stable `nitro.secure.master_key`.
|
||||
- IIS knows the `.dat` MIME type.
|
||||
- Restart the emulator after changing `config.ini`.
|
||||
- Refresh the browser after changing JSON files in `configuration` or `gamedata`.
|
||||
|
||||
## 9. Temporarily disable secure mode
|
||||
|
||||
For quick debugging, only change `client-mode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"distObfuscationEnabled": false,
|
||||
"secureAssetsEnabled": false,
|
||||
"secureApiEnabled": false,
|
||||
"apiBaseUrl": "https://nitro.example.com:2096",
|
||||
"plainConfigBaseUrl": "https://hotel.example.com/configuration/",
|
||||
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
|
||||
}
|
||||
```
|
||||
|
||||
Then hard refresh the browser.
|
||||
|
||||
## 10. `configuration/bootstrap.js`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/bootstrap.js
|
||||
```
|
||||
|
||||
This is the first loader when you use the external secure mode.
|
||||
|
||||
It does three things:
|
||||
|
||||
1. opens an ECDH session with the emulator through `/nitro-sec/bootstrap`;
|
||||
2. downloads encrypted `client-mode.json` through `/nitro-sec/file?kind=config`;
|
||||
3. downloads encrypted `asset-loader.js` and imports it as a JavaScript module.
|
||||
|
||||
### Value to check
|
||||
|
||||
Inside `bootstrap.js` there is:
|
||||
|
||||
```js
|
||||
const API_BASE = "https://nitro.example.com:2096";
|
||||
```
|
||||
|
||||
It must point to your public emulator/API URL.
|
||||
|
||||
In production:
|
||||
|
||||
```js
|
||||
const API_BASE = "https://nitro.example.com:2096";
|
||||
```
|
||||
|
||||
In local development:
|
||||
|
||||
```js
|
||||
const API_BASE = "http://localhost:2096";
|
||||
```
|
||||
|
||||
If `bootstrap.js` fails, it automatically falls back to the plain loader:
|
||||
|
||||
```txt
|
||||
configuration/asset-loader.js
|
||||
```
|
||||
|
||||
So `asset-loader.js` must always exist inside the `configuration` folder.
|
||||
|
||||
## 11. `configuration/asset-loader.js`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/asset-loader.js
|
||||
```
|
||||
|
||||
This loader loads the actual bundle:
|
||||
|
||||
- if `distObfuscationEnabled=true`
|
||||
- it loads `app.css.dat`;
|
||||
- it loads `app.js.dat`;
|
||||
- it decodes, decompresses, and imports the bundle from a blob.
|
||||
|
||||
- if `distObfuscationEnabled=false`
|
||||
- it loads `assets/app.css`;
|
||||
- it loads `assets/app.js`.
|
||||
|
||||
### Required files in production
|
||||
|
||||
With obfuscation enabled, these files must exist:
|
||||
|
||||
```txt
|
||||
assets/app.css.dat
|
||||
assets/app.js.dat
|
||||
configuration/asset-loader.js
|
||||
configuration/bootstrap.js
|
||||
configuration/client-mode.json
|
||||
```
|
||||
|
||||
With obfuscation disabled, these files must exist:
|
||||
|
||||
```txt
|
||||
assets/app.css
|
||||
assets/app.js
|
||||
configuration/asset-loader.js
|
||||
configuration/client-mode.json
|
||||
```
|
||||
|
||||
## 12. `index.html`
|
||||
|
||||
`index.html` should stay minimal.
|
||||
|
||||
Secure production example:
|
||||
|
||||
```html
|
||||
<div id="root"></div>
|
||||
<script src="/configuration/bootstrap.js"></script>
|
||||
```
|
||||
|
||||
Vite development example:
|
||||
|
||||
```html
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/bootstrap.ts"></script>
|
||||
```
|
||||
|
||||
Do not mix the two flows:
|
||||
|
||||
- production build: use `configuration/bootstrap.js`;
|
||||
- `yarn start` development: use `/src/bootstrap.ts`.
|
||||
|
||||
## 13. Files inside `/configuration`
|
||||
|
||||
The `configuration` folder should contain:
|
||||
|
||||
```txt
|
||||
asset-loader.js
|
||||
bootstrap.js
|
||||
client-mode.json
|
||||
renderer-config.json
|
||||
ui-config.json
|
||||
adsense.json optional
|
||||
hotlooks.json if register hot looks are enabled
|
||||
UITexts.json if separate UI texts are enabled
|
||||
```
|
||||
|
||||
Login news should not live in `news.json` in production. They come from the database through:
|
||||
|
||||
```json
|
||||
"login.news.url": "${api.url}/api/auth/news"
|
||||
```
|
||||
|
||||
The emulator reads from the `ui_news` table.
|
||||
|
||||
With `secureAssetsEnabled=true`, client-loaded files go through:
|
||||
|
||||
```txt
|
||||
https://nitro.example.com:2096/nitro-sec/file?kind=config&file=...
|
||||
```
|
||||
|
||||
The emulator reads them from:
|
||||
|
||||
```ini
|
||||
nitro.secure.config.root=C:/inetpub/wwwroot/hotel/nitro/configuration
|
||||
```
|
||||
|
||||
If you add new JSON/JS files inside `configuration` and want to protect them, they must be requested through the secure endpoint or loaded through `bootstrap.js`.
|
||||
@@ -0,0 +1,365 @@
|
||||
# Setup Secure Runtime in produzione
|
||||
|
||||
Guida rapida per avviare Nitro con:
|
||||
|
||||
- configurazioni e gamedata serviti da `/nitro-sec/file`;
|
||||
- API `/api/*` cifrate dal wrapper runtime;
|
||||
- bundle buildati offuscati come `.dat`.
|
||||
|
||||
Negli esempi usa i tuoi domini reali al posto di:
|
||||
|
||||
- `https://hotel.example.com`
|
||||
- `https://nitro.example.com:2096`
|
||||
|
||||
## 1. Build Nitro
|
||||
|
||||
Nel repo `Nitro-V3`:
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
```
|
||||
|
||||
Poi pubblica la cartella `dist` nel web server del sito, ad esempio:
|
||||
|
||||
```txt
|
||||
C:/inetpub/wwwroot/hotel/nitro
|
||||
```
|
||||
|
||||
La struttura pubblicata deve contenere almeno:
|
||||
|
||||
```txt
|
||||
configuration/
|
||||
assets/
|
||||
asset-loader.js
|
||||
index.html
|
||||
src/
|
||||
```
|
||||
|
||||
## 2. `configuration/client-mode.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/client-mode.json
|
||||
```
|
||||
|
||||
Configurazione produzione secure:
|
||||
|
||||
```json
|
||||
{
|
||||
"distObfuscationEnabled": true,
|
||||
"secureAssetsEnabled": true,
|
||||
"secureApiEnabled": true,
|
||||
"apiBaseUrl": "https://nitro.example.com:2096",
|
||||
"plainConfigBaseUrl": "https://hotel.example.com/configuration/",
|
||||
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
|
||||
}
|
||||
```
|
||||
|
||||
Significato:
|
||||
|
||||
- `distObfuscationEnabled: true` carica `app.js.dat` e `app.css.dat`.
|
||||
- `secureAssetsEnabled: true` carica `renderer-config.json`, `ui-config.json` e gamedata da `/nitro-sec/file`.
|
||||
- `secureApiEnabled: true` cifra automaticamente le chiamate `/api/*`.
|
||||
- `apiBaseUrl` deve puntare all'emulatore/API.
|
||||
- `plainConfigBaseUrl` e `plainGamedataBaseUrl` restano fallback quando spegni secure assets.
|
||||
|
||||
## 3. `configuration/renderer-config.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/renderer-config.json
|
||||
```
|
||||
|
||||
Valori importanti:
|
||||
|
||||
```json
|
||||
{
|
||||
"socket.url": "wss://nitro.example.com:2096",
|
||||
"api.url": "https://nitro.example.com:2096",
|
||||
"gamedata.url": "https://nitro.example.com:2096/nitro-sec/file?kind=gamedata&file=",
|
||||
"external.texts.url": [
|
||||
"${gamedata.url}/ExternalTexts.json",
|
||||
"${gamedata.url}/UITexts.json"
|
||||
],
|
||||
"furnidata.url": "${gamedata.url}/FurnitureData.json?t=%timestamp%",
|
||||
"productdata.url": "${gamedata.url}/ProductData.json?t=%timestamp%",
|
||||
"avatar.actions.url": "${gamedata.url}/HabboAvatarActions.json?t=%timestamp%",
|
||||
"avatar.figuredata.url": "${gamedata.url}/FigureData.json?t=%timestamp%",
|
||||
"avatar.figuremap.url": "${gamedata.url}/FigureMap.json?t=%timestamp%",
|
||||
"avatar.effectmap.url": "${gamedata.url}/EffectMap.json?t=%timestamp%",
|
||||
"crypto.ws.enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
Se non usi ancora WebSocket crypto, metti:
|
||||
|
||||
```json
|
||||
"crypto.ws.enabled": false
|
||||
```
|
||||
|
||||
## 4. `configuration/ui-config.json`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/ui-config.json
|
||||
```
|
||||
|
||||
Qui puoi lasciare immagini e camera su URL statici normali:
|
||||
|
||||
```json
|
||||
{
|
||||
"camera.url": "https://hotel.example.com/client/camera/",
|
||||
"thumbnails.url": "https://hotel.example.com/client/camera/thumbnail/%thumbnail%.png"
|
||||
}
|
||||
```
|
||||
|
||||
Le immagini non sensibili possono rimanere statiche. I JSON/gamedata invece passano dal secure endpoint.
|
||||
|
||||
## 5. `config.ini` dell'emulatore
|
||||
|
||||
Nel repo `Arcturus-Morningstar-Extended`, file usato dall'emulatore:
|
||||
|
||||
```txt
|
||||
Emulator/config.ini
|
||||
```
|
||||
|
||||
Esempio produzione:
|
||||
|
||||
```ini
|
||||
ws.enabled=true
|
||||
ws.host=0.0.0.0
|
||||
ws.port=2096
|
||||
ws.whitelist=https://hotel.example.com
|
||||
ws.ip.header=CF-Connecting-IP
|
||||
|
||||
crypto.ws.enabled=1
|
||||
|
||||
nitro.secure.assets.enabled=true
|
||||
nitro.secure.api.enabled=true
|
||||
nitro.secure.config.root=C:/inetpub/wwwroot/hotel/nitro/configuration
|
||||
nitro.secure.gamedata.root=C:/inetpub/wwwroot/hotel/nitro/client/nitro/gamedata
|
||||
nitro.secure.master_key=change-this-to-a-long-random-secret
|
||||
|
||||
login.remember.enabled=true
|
||||
login.remember.duration.days=30
|
||||
login.remember.jwt.secret=change-this-too-if-you-use-remember-me
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- `nitro.secure.config.root` deve puntare alla cartella dove ci sono `renderer-config.json`, `ui-config.json`, `client-mode.json`.
|
||||
- `nitro.secure.gamedata.root` deve puntare alla cartella live dei gamedata.
|
||||
- I file vengono letti live da disco: se cambi un JSON, un nuovo refresh pagina legge la nuova versione.
|
||||
- `nitro.secure.master_key` deve restare segreta e stabile. Non metterla nei file pubblici.
|
||||
|
||||
## 6. Cloudflare
|
||||
|
||||
Se usi Cloudflare:
|
||||
|
||||
1. Lascia la nuvoletta attiva sul dominio web `hotel.example.com`.
|
||||
2. Per `nitro.example.com:2096`, assicurati che Cloudflare supporti/proxy il traffico sulla porta usata.
|
||||
3. Usa sempre HTTPS/WSS lato browser:
|
||||
|
||||
```json
|
||||
"api.url": "https://nitro.example.com:2096",
|
||||
"socket.url": "wss://nitro.example.com:2096"
|
||||
```
|
||||
|
||||
Se vedi errori CORS, controlla:
|
||||
|
||||
```ini
|
||||
ws.whitelist=https://hotel.example.com
|
||||
```
|
||||
|
||||
## 7. IIS / MIME `.dat`
|
||||
|
||||
Se usi gli asset offuscati `.dat`, IIS deve servirli.
|
||||
|
||||
Aggiungi MIME type:
|
||||
|
||||
```txt
|
||||
Extension: .dat
|
||||
MIME type: application/octet-stream
|
||||
```
|
||||
|
||||
Senza questo, il browser può dare 404 anche se il file esiste davvero.
|
||||
|
||||
## 8. Checklist finale
|
||||
|
||||
- `client-mode.json` ha `secureAssetsEnabled=true`.
|
||||
- `client-mode.json` ha `secureApiEnabled=true`.
|
||||
- `renderer-config.json` usa `/nitro-sec/file?kind=gamedata&file=`.
|
||||
- `api.url` punta a `https://nitro.example.com:2096`.
|
||||
- `socket.url` punta a `wss://nitro.example.com:2096`.
|
||||
- `config.ini` ha `nitro.secure.config.root` corretto.
|
||||
- `config.ini` ha `nitro.secure.gamedata.root` corretto.
|
||||
- `config.ini` ha `nitro.secure.master_key` stabile.
|
||||
- IIS conosce il MIME `.dat`.
|
||||
- Dopo modifiche a `config.ini`, riavvia l'emulatore.
|
||||
- Dopo modifiche ai JSON in `configuration` o `gamedata`, basta refresh pagina.
|
||||
|
||||
## 9. Spegnere temporaneamente secure
|
||||
|
||||
Per debug rapido, cambia solo `client-mode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"distObfuscationEnabled": false,
|
||||
"secureAssetsEnabled": false,
|
||||
"secureApiEnabled": false,
|
||||
"apiBaseUrl": "https://nitro.example.com:2096",
|
||||
"plainConfigBaseUrl": "https://hotel.example.com/configuration/",
|
||||
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
|
||||
}
|
||||
```
|
||||
|
||||
Poi fai hard refresh.
|
||||
|
||||
## 10. `configuration/bootstrap.js`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/bootstrap.js
|
||||
```
|
||||
|
||||
Questo è il primo loader quando usi la modalità secure esterna.
|
||||
|
||||
Fa tre cose:
|
||||
|
||||
1. apre una sessione ECDH con l'emulatore tramite `/nitro-sec/bootstrap`;
|
||||
2. scarica `client-mode.json` cifrato da `/nitro-sec/file?kind=config`;
|
||||
3. scarica `asset-loader.js` cifrato e lo importa come modulo JavaScript.
|
||||
|
||||
### Valore da controllare
|
||||
|
||||
Dentro `bootstrap.js` esiste:
|
||||
|
||||
```js
|
||||
const API_BASE = "https://nitro.example.com:2096";
|
||||
```
|
||||
|
||||
Deve puntare all'emulatore/API pubblico.
|
||||
|
||||
In produzione:
|
||||
|
||||
```js
|
||||
const API_BASE = "https://nitro.example.com:2096";
|
||||
```
|
||||
|
||||
In locale:
|
||||
|
||||
```js
|
||||
const API_BASE = "http://localhost:2096";
|
||||
```
|
||||
|
||||
Se `bootstrap.js` fallisce, prova automaticamente fallback plain su:
|
||||
|
||||
```txt
|
||||
configuration/asset-loader.js
|
||||
```
|
||||
|
||||
Quindi `asset-loader.js` deve esistere sempre nella cartella `configuration`.
|
||||
|
||||
## 11. `configuration/asset-loader.js`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
Nitro-V3/dist/configuration/asset-loader.js
|
||||
```
|
||||
|
||||
Questo loader carica il bundle vero:
|
||||
|
||||
- se `distObfuscationEnabled=true`
|
||||
- carica `app.css.dat`;
|
||||
- carica `app.js.dat`;
|
||||
- decodifica, decomprime e importa il bundle da blob.
|
||||
|
||||
- se `distObfuscationEnabled=false`
|
||||
- carica `assets/app.css`;
|
||||
- carica `assets/app.js`.
|
||||
|
||||
### File richiesti in produzione
|
||||
|
||||
Con offuscamento attivo devono esistere:
|
||||
|
||||
```txt
|
||||
assets/app.css.dat
|
||||
assets/app.js.dat
|
||||
configuration/asset-loader.js
|
||||
configuration/bootstrap.js
|
||||
configuration/client-mode.json
|
||||
```
|
||||
|
||||
Con offuscamento spento devono esistere:
|
||||
|
||||
```txt
|
||||
assets/app.css
|
||||
assets/app.js
|
||||
configuration/asset-loader.js
|
||||
configuration/client-mode.json
|
||||
```
|
||||
|
||||
## 12. `index.html`
|
||||
|
||||
Il file `index.html` deve rimanere minimale.
|
||||
|
||||
Esempio secure:
|
||||
|
||||
```html
|
||||
<div id="root"></div>
|
||||
<script src="/configuration/bootstrap.js"></script>
|
||||
```
|
||||
|
||||
Esempio dev Vite:
|
||||
|
||||
```html
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/bootstrap.ts"></script>
|
||||
```
|
||||
|
||||
Non mischiare i due flow:
|
||||
|
||||
- produzione buildata: usa `configuration/bootstrap.js`;
|
||||
- sviluppo con `yarn start`: usa `/src/bootstrap.ts`.
|
||||
|
||||
## 13. File dentro `/configuration`
|
||||
|
||||
La cartella `configuration` deve contenere:
|
||||
|
||||
```txt
|
||||
asset-loader.js
|
||||
bootstrap.js
|
||||
client-mode.json
|
||||
renderer-config.json
|
||||
ui-config.json
|
||||
adsense.json opzionale
|
||||
hotlooks.json se usi register hot looks
|
||||
UITexts.json se usi testi UI separati
|
||||
```
|
||||
|
||||
Le news login non devono stare in `news.json` in produzione: arrivano dal database tramite:
|
||||
|
||||
```json
|
||||
"login.news.url": "${api.url}/api/auth/news"
|
||||
```
|
||||
|
||||
L'emulatore legge dalla tabella `ui_news`.
|
||||
|
||||
Con `secureAssetsEnabled=true`, i file letti dal client passano da:
|
||||
|
||||
```txt
|
||||
https://nitro.example.com:2096/nitro-sec/file?kind=config&file=...
|
||||
```
|
||||
|
||||
Quindi l'emulatore li legge da:
|
||||
|
||||
```ini
|
||||
nitro.secure.config.root=C:/inetpub/wwwroot/hotel/nitro/configuration
|
||||
```
|
||||
|
||||
Se aggiungi nuovi file JSON/JS in `configuration` e vuoi proteggerli, devono essere richiesti passando dal secure endpoint o caricati tramite `bootstrap.js`.
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"news": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Welcome to Nitro",
|
||||
"body": "This news entry is loaded from configuration/news.json.",
|
||||
"image": "${image.library.url}web_promo_small/spromo_Canal_Bundle.png",
|
||||
"link": "",
|
||||
"linkText": "Read more"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -49,5 +49,43 @@
|
||||
"login.logout.endpoint": "${api.url}/api/auth/logout",
|
||||
"login.remember.endpoint": "${api.url}/api/auth/remember",
|
||||
"login.turnstile.enabled": false,
|
||||
"login.turnstile.sitekey": ""
|
||||
"login.turnstile.sitekey": "",
|
||||
"avatar.mandatory.libraries": [
|
||||
"bd:1",
|
||||
"li:0"
|
||||
],
|
||||
"avatar.mandatory.effect.libraries": [
|
||||
"dance.1",
|
||||
"dance.2",
|
||||
"dance.3",
|
||||
"dance.4"
|
||||
],
|
||||
"avatar.default.figuredata": {
|
||||
"palettes": [],
|
||||
"setTypes": []
|
||||
},
|
||||
"avatar.default.actions": {
|
||||
"actions": []
|
||||
},
|
||||
"pet.types": [],
|
||||
"preload.assets.urls": [
|
||||
"${asset.url}/generic/avatar_additions.nitro",
|
||||
"${asset.url}/generic/group_badge.nitro",
|
||||
"${asset.url}/generic/floor_editor.nitro",
|
||||
"${images.url}/loading_icon.png",
|
||||
"${images.url}/clear_icon.png",
|
||||
"${images.url}/big_arrow.png"
|
||||
],
|
||||
"login.health.endpoint": "${api.url}/api/health",
|
||||
"login.health.method": "GET",
|
||||
"login.check-email.endpoint": "${api.url}/api/auth/check-email",
|
||||
"login.check-username.endpoint": "${api.url}/api/auth/check-username",
|
||||
"login.register.imaging.url": "${api.url}/api/avatar/imaging",
|
||||
"crypto.ws.enabled": true,
|
||||
"login.news.url": "${api.url}/api/auth/news",
|
||||
"badges.custom.list.endpoint": "${api.url}/api/badges/custom",
|
||||
"badges.custom.create.endpoint": "${api.url}/api/badges/custom",
|
||||
"badges.custom.update.endpoint": "${api.url}/api/badges/custom/%badgeId%",
|
||||
"badges.custom.delete.endpoint": "${api.url}/api/badges/custom/%badgeId%",
|
||||
"badges.custom.texts.endpoint": "${api.url}/api/badges/custom/texts"
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,30 @@ const setBootDebug = (message: string) =>
|
||||
|
||||
setBootDebug('boot: secure fetch installed');
|
||||
|
||||
const loadClientMode = async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if((window as any).__nitroClientMode) return;
|
||||
|
||||
const url = new URL('configuration/client-mode.json', `${ window.location.origin }/`);
|
||||
url.searchParams.set('v', Date.now().toString(36));
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
|
||||
if(!response.ok) throw new Error(`HTTP ${ response.status }`);
|
||||
|
||||
(window as any).__nitroClientMode = await response.json();
|
||||
setBootDebug('boot: client-mode loaded');
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
setBootDebug(`boot: client-mode fallback ${ error?.message || error }`);
|
||||
}
|
||||
};
|
||||
|
||||
await loadClientMode();
|
||||
|
||||
const search = new URLSearchParams(window.location.search);
|
||||
const clientMode = getClientMode();
|
||||
const cacheBustUrl = (path: string): string =>
|
||||
|
||||
@@ -13,6 +13,7 @@ import flagNl from '../../assets/images/flag_icon/flag_icon_nl.png';
|
||||
import flagSelected from '../../assets/images/flag_icon/flag_icon_selected.png';
|
||||
import flagTr from '../../assets/images/flag_icon/flag_icon_tr.png';
|
||||
import { applyTextTranslationLocale } from '../../hooks/translation/useTranslation';
|
||||
import { NewsWindow } from './components/NewsWindow';
|
||||
import { TurnstileWidget } from './TurnstileWidget';
|
||||
|
||||
type DialogMode = 'login' | 'register' | 'forgot';
|
||||
@@ -229,6 +230,7 @@ export const LoginView: FC<LoginViewProps> = ({ onAuthenticated, isEntering = fa
|
||||
const loginUrl = GetConfigurationValue<string>('login.endpoint', '/api/auth/login');
|
||||
const registerUrl = GetConfigurationValue<string>('login.register.endpoint', '/api/auth/register');
|
||||
const forgotUrl = GetConfigurationValue<string>('login.forgot.endpoint', '/api/auth/forgot-password');
|
||||
const newsUrl = interpolate(GetConfigurationValue<string>('login.news.url', ''));
|
||||
const turnstileSiteKey = GetConfigurationValue<string>('login.turnstile.sitekey', '');
|
||||
const rawTurnstileEnabled = GetConfigurationValue<unknown>('login.turnstile.enabled', false);
|
||||
const turnstileEnabled = (rawTurnstileEnabled === true
|
||||
@@ -678,6 +680,8 @@ export const LoginView: FC<LoginViewProps> = ({ onAuthenticated, isEntering = fa
|
||||
}) }
|
||||
</div> }
|
||||
|
||||
{ newsUrl && <NewsWindow newsUrl={ newsUrl } /> }
|
||||
|
||||
<div className="login-stack">
|
||||
<div className="nitro-login-card login-language-card">
|
||||
<div className="card-title">Choose your language</div>
|
||||
|
||||
Reference in New Issue
Block a user