diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 90e0de3..733cd4c 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -43,40 +43,208 @@ Keep the game server bound to `127.0.0.1`. Set `TRUST_PROXY=1` only when the server can be reached solely through your local reverse proxy. This lets account limits use the visitor's public IP instead of the proxy's address. -## Separate auth server +## TrueNAS single-container hosting -The auth routes can run as their own Node process. This is useful when you want -`auth.phenomrom.com` to stay available while the game server is being rebuilt or -changed. +### TrueNAS SCALE runbook -On the TrueNAS host, run the auth process against the same project data folder: +This is the simplest TrueNAS setup. One container serves the browser game, +auth routes, game API routes, and one SQLite database. Use this when you want +`iwanttoheal.phenomrom.com` to host the playable browser version and you want +code updates to be a Git pull plus app restart. -```sh -npm ci -npm run db:init -AUTH_HOST=127.0.0.1 AUTH_PORT=4174 TRUST_PROXY=1 COOKIE_SECURE=1 AUTH_CORS_ORIGINS=https://phenomrom.com npm run auth:start +Portainer is not required. Use TrueNAS **Apps > Discover > Install via YAML**. + +Repository: + +```text +https://git.whoagland.com/phenom/i-want-to-heal.git ``` -Point `auth.phenomrom.com` at that process through HTTPS: +TrueNAS paths: + +```text +/mnt/usbssds/apps/iwanttoheal/app +/mnt/usbssds/apps/iwanttoheal/data +``` + +Create the app directory and clone the repo: + +```sh +sudo mkdir -p /mnt/usbssds/apps/iwanttoheal +cd /mnt/usbssds/apps/iwanttoheal +sudo git clone https://git.whoagland.com/phenom/i-want-to-heal.git app +``` + +Because the clone was run with `sudo`, give the normal TrueNAS user ownership: + +```sh +sudo chown -R truenas_admin:truenas_admin /mnt/usbssds/apps/iwanttoheal +``` + +Create the persistent data folder: + +```sh +mkdir -p /mnt/usbssds/apps/iwanttoheal/data +``` + +Check that the production server file exists: + +```sh +ls /mnt/usbssds/apps/iwanttoheal/app/server/production.mjs +``` + +If that file is missing, push the latest code to `git.whoagland.com` from the +development machine, then pull on TrueNAS: + +```sh +cd /mnt/usbssds/apps/iwanttoheal/app +git pull +``` + +If Git fails with `chmod ... Operation not permitted`, do not use a media or SMB +dataset for the repo. Git needs normal file locking and chmod behavior. Create or +use a dedicated apps dataset and clone under `/mnt/usbssds/apps/...`. + +### TrueNAS app YAML + +In TrueNAS: + +1. Open **Apps**. +2. Open **Discover**. +3. Click the three-dot menu. +4. Choose **Install via YAML**. +5. Name the app `iwanttoheal`. +6. Paste this YAML: + +```yaml +services: + iwanttoheal: + image: node:24-bookworm-slim + working_dir: /app + command: sh -lc "npm ci && npm run db:init && npm run build && npm start" + environment: + HOST: 0.0.0.0 + PORT: "4173" + TRUST_PROXY: "1" + COOKIE_SECURE: "1" + CORS_ORIGINS: "http://localhost,https://localhost,capacitor://localhost,https://iwanttoheal.phenomrom.com,https://auth.phenomrom.com" + ports: + - "4173:4173" + volumes: + - /mnt/usbssds/apps/iwanttoheal/app:/app + - /mnt/usbssds/apps/iwanttoheal/data:/app/data + restart: unless-stopped +``` + +The app listens inside Docker on port `4173`. The database lives at +`/mnt/usbssds/apps/iwanttoheal/data/game.db` because that host directory is +mounted into the container as `/app/data`. The startup command installs +dependencies, applies schema migrations, builds the web app, and starts the +production server. + +Test the local TrueNAS service: + +```sh +curl http://TRUENAS-IP:4173/api/auth/session +``` + +Expected response: + +```json +{"account":null,"profile":null} +``` + +### Reverse proxy + +Point `iwanttoheal.phenomrom.com` at the TrueNAS app through HTTPS. Do not expose +port `4173` directly to the internet. Put Caddy or another reverse proxy in +front: ```caddyfile +iwanttoheal.phenomrom.com { + reverse_proxy TRUENAS-IP:4173 +} + auth.phenomrom.com { - reverse_proxy 127.0.0.1:4174 + reverse_proxy TRUENAS-IP:4173 } ``` -Build the web or mobile app with the auth base URL set separately from the game -API: +Both hostnames can point at the same container. `iwanttoheal.phenomrom.com` +serves the browser game. `auth.phenomrom.com` stays available as an auth URL for +Android or other clients that need a dedicated auth hostname. + +DNS should point both hostnames at the public IP or dynamic DNS name that reaches +the reverse proxy. Forward public ports `80` and `443` to the reverse proxy host. + +Test the public game and auth URLs: ```sh -VITE_AUTH_API_BASE_URL=https://auth.phenomrom.com npm run build +curl https://iwanttoheal.phenomrom.com +curl https://auth.phenomrom.com/api/auth/session ``` -For a Capacitor wrapper, set `window.CAPACITOR_AUTH_API_BASE_URL` to -`https://auth.phenomrom.com` the same way `window.CAPACITOR_API_BASE_URL` is set. -The app stores the returned bearer token locally and sends it with later API -requests, so auth works across subdomains and inside the mobile WebView. Existing -same-origin cookie sessions still work when auth is served by the game server. +Expected auth response: + +```json +{"account":null,"profile":null} +``` + +### App build config + +For the hosted browser game, no separate auth build setting is needed. The web +app can call same-origin routes like `/api/auth/login` and `/api/profile`. + +For an Android build that should use the TrueNAS-hosted game API, build with: + +```sh +npm run android:apk:truenas +``` + +If you intentionally want Android auth calls to use `auth.phenomrom.com`, also +set `VITE_AUTH_API_BASE_URL=https://auth.phenomrom.com`. Otherwise, leave it +unset and auth uses the same base URL as the game API. + +Android runs the bundled web app from a local Capacitor origin, not from +`iwanttoheal.phenomrom.com`. The hosted server must allow that origin through +CORS, which is why the TrueNAS YAML includes `http://localhost`, +`https://localhost`, and `capacitor://localhost`. + +### Updating the TrueNAS game app + +Push changes from the development machine to `git.whoagland.com`, then pull them +on TrueNAS: + +```sh +cd /mnt/usbssds/apps/iwanttoheal/app +git pull +``` + +Restart the `iwanttoheal` app in the TrueNAS Apps UI after pulling. The app +command runs `npm ci`, `npm run db:init`, `npm run build`, and `npm start` on +startup, so dependency, schema, and browser bundle changes are applied each time +the container restarts. + +Normal update workflow: + +```sh +# development machine +git add . +git commit -m "Update game" +git push origin main + +# TrueNAS shell +cd /mnt/usbssds/apps/iwanttoheal/app +git pull +``` + +Then restart the TrueNAS app. + +### Existing auth-only app + +If `iwanttoheal-auth` was already created during earlier testing, the simplest +path is to stop that app and use the single `iwanttoheal` app above. The single +container serves both domains and avoids two processes sharing one SQLite file. ## Account limits diff --git a/IWantToHeal-Thor-v1.0.23.apk b/IWantToHeal-Thor-v1.0.23.apk new file mode 100644 index 0000000..e85cb75 Binary files /dev/null and b/IWantToHeal-Thor-v1.0.23.apk differ diff --git a/IWantToHeal-Thor-v1.0.24.apk b/IWantToHeal-Thor-v1.0.24.apk new file mode 100644 index 0000000..c39c830 Binary files /dev/null and b/IWantToHeal-Thor-v1.0.24.apk differ diff --git a/android/app/build.gradle b/android/app/build.gradle index dc5f52e..720a83e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.warren.iwanttoheal" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 39 - versionName "1.0.23" + versionCode 41 + versionName "1.0.24" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/package.json b/package.json index da501ea..ae8883c 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,10 @@ "dev": "vite", "build": "npm run offline:export && npx tsc -b --noEmit && npx vite build && node scripts/generate-service-worker.mjs", "android:sync": "npm run build && cap sync android", + "android:sync:truenas": "VITE_API_BASE_URL=https://iwanttoheal.phenomrom.com npm run android:sync", "android:open": "cap open android", "android:apk": "npm run android:sync && cd android && ./gradlew clean assembleDebug", + "android:apk:truenas": "VITE_API_BASE_URL=https://iwanttoheal.phenomrom.com npm run android:apk", "accounts:ip": "node scripts/manage-ip-allowance.mjs", "db:backup": "node scripts/backup-db.mjs", "db:init": "node scripts/init-db.mjs",