Browse Source

bug-fix build

imwald
Silberengel 2 weeks ago
parent
commit
f7eeeead7b
  1. 2
      PROXY_SETUP.md
  2. 30
      docker-compose.prod.yml
  3. 4
      package-lock.json
  4. 2
      package.json
  5. 28
      scripts/README-deploy.md
  6. 20
      scripts/build-and-push-prod.sh
  7. 12
      scripts/stack-remote.sh
  8. 2
      src/lib/read-aloud.ts

2
PROXY_SETUP.md

@ -185,7 +185,7 @@ VITE_LANGUAGE_TOOL_URL=/api/languagetool
VITE_TRANSLATE_URL=/api/translate VITE_TRANSLATE_URL=/api/translate
``` ```
**Production:** `docker compose -f docker-compose.prod.yml --profile editor-tools up -d languagetool libretranslate` publishes **127.0.0.1:8010** and **127.0.0.1:5000** (loopback-only). Proxy those paths from Apache/nginx to the SPA origin, and bake the client with `LANGUAGE_TOOL_URL=/api/languagetool` and `TRANSLATE_URL=/api/translate` when running `./scripts/build-and-push-prod.sh`. **Production:** `docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d` starts the full stack (including LanguageTool on **127.0.0.1:8010** and LibreTranslate on **127.0.0.1:5000**). Run `bash scripts/ensure-libretranslate-dirs.sh` once on the server for LibreTranslate volume permissions. Proxy `/api/languagetool` and `/api/translate` from Apache/nginx to those ports, and bake the client with `LANGUAGE_TOOL_URL=/api/languagetool` and `TRANSLATE_URL=/api/translate` when running `./scripts/build-and-push-prod.sh`.
**Notes:** LanguageTool’s JVM image often needs **~1–2GiB** RAM. LibreTranslate **does not listen on port 5000 until models are ready**; without **`LT_LOAD_ONLY`** it may pull **many gigabytes** first, so the Vite proxy can show **`ECONNRESET` on `/translate`** while booting. Compose defaults **`LT_LOAD_ONLY`** to **ten** widely used codes (**en, de, es, fr, it, pt, ru, zh, ja, ar** — see `libretranslate` in `docker-compose.dev.yml`). Override with **`LT_LOAD_ONLY`** to add or remove codes; first start downloads packs for every listed code. **`LT_UPDATE_MODELS`** defaults to **`true`** so if you **expand** `LT_LOAD_ONLY` later, a **recreated** container still **installs missing** Argos packages into the bind-mounted `.local-libretranslate` tree (otherwise an older en/de-only cache sticks). Set **`LT_UPDATE_MODELS=false`** after everything is installed if you want faster routine restarts. Models are stored under **`.local-libretranslate/share`** and **`.local-libretranslate/cache`** (gitignored) with **bind mounts** so they survive **`docker compose down`**, image updates, and container recreate. **`scripts/ensure-libretranslate-dirs.sh`** (run automatically by **`npm run dev:all`**, **`npm run stack:remote`**, **`npm run docker:editor-tools`**, etc.) creates those dirs and **`chown`s them to UID 1032** via a short **Alpine** container so the LibreTranslate user can write. If you start **`libretranslate` by hand**, run **`npm run docker:prep-libretranslate`** once first. First download can still take **several minutes**; use **`docker logs -f jumble-libretranslate`** until **`curl http://127.0.0.1:5000/languages`** returns JSON. If logs show **`Cannot update models`** / **`Unavailable language codes: …`**, one bad token in **`LT_LOAD_ONLY` aborts the whole install** (you stay on whatever was already on disk, often en/de only). **Norwegian** must be **`nb`** (Bokmål), not ISO **`no`**. After you shrink **`LT_LOAD_ONLY`**, run **`npm run docker:prune-libretranslate-packages`** to remove leftover Argos package dirs under **`.local-libretranslate/share/argos-translate/packages`** (and unused **MiniSBD** `.onnx` files); the script briefly stops **`jumble-libretranslate`** or **`imwald-libretranslate`**. Override codes with **`LT_LOAD_ONLY=…`** on the same command if they differ from the compose default. **Notes:** LanguageTool’s JVM image often needs **~1–2GiB** RAM. LibreTranslate **does not listen on port 5000 until models are ready**; without **`LT_LOAD_ONLY`** it may pull **many gigabytes** first, so the Vite proxy can show **`ECONNRESET` on `/translate`** while booting. Compose defaults **`LT_LOAD_ONLY`** to **ten** widely used codes (**en, de, es, fr, it, pt, ru, zh, ja, ar** — see `libretranslate` in `docker-compose.dev.yml`). Override with **`LT_LOAD_ONLY`** to add or remove codes; first start downloads packs for every listed code. **`LT_UPDATE_MODELS`** defaults to **`true`** so if you **expand** `LT_LOAD_ONLY` later, a **recreated** container still **installs missing** Argos packages into the bind-mounted `.local-libretranslate` tree (otherwise an older en/de-only cache sticks). Set **`LT_UPDATE_MODELS=false`** after everything is installed if you want faster routine restarts. Models are stored under **`.local-libretranslate/share`** and **`.local-libretranslate/cache`** (gitignored) with **bind mounts** so they survive **`docker compose down`**, image updates, and container recreate. **`scripts/ensure-libretranslate-dirs.sh`** (run automatically by **`npm run dev:all`**, **`npm run stack:remote`**, **`npm run docker:editor-tools`**, etc.) creates those dirs and **`chown`s them to UID 1032** via a short **Alpine** container so the LibreTranslate user can write. If you start **`libretranslate` by hand**, run **`npm run docker:prep-libretranslate`** once first. First download can still take **several minutes**; use **`docker logs -f jumble-libretranslate`** until **`curl http://127.0.0.1:5000/languages`** returns JSON. If logs show **`Cannot update models`** / **`Unavailable language codes: …`**, one bad token in **`LT_LOAD_ONLY` aborts the whole install** (you stay on whatever was already on disk, often en/de only). **Norwegian** must be **`nb`** (Bokmål), not ISO **`no`**. After you shrink **`LT_LOAD_ONLY`**, run **`npm run docker:prune-libretranslate-packages`** to remove leftover Argos package dirs under **`.local-libretranslate/share/argos-translate/packages`** (and unused **MiniSBD** `.onnx` files); the script briefly stops **`jumble-libretranslate`** or **`imwald-libretranslate`**. Override codes with **`LT_LOAD_ONLY=…`** on the same command if they differ from the compose default.

30
docker-compose.prod.yml

@ -1,6 +1,7 @@
# Minimal compose for running the published image (e.g. on remote server). # Production stack: jumble + NIP-66 monitor + og-proxy + Wyoming Piper + Piper HTTP proxy + LanguageTool + LibreTranslate.
# Full stack (jumble + NIP-66 + og-proxy + Piper + LanguageTool + LibreTranslate): npm run stack:remote # Remote: docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d
# Usage (app only): docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d # First-time LibreTranslate bind mounts: bash scripts/ensure-libretranslate-dirs.sh (see scripts/README-deploy.md).
# Images built/pushed by ./scripts/build-and-push-prod.sh (app, monitor, piper-tts-proxy); others pull from Hub.
# #
# Apache (unchanged on your host) should keep (order: specific paths before catch-all /): # Apache (unchanged on your host) should keep (order: specific paths before catch-all /):
# ProxyPass /sites/ http://127.0.0.1:8090/sites/ # ProxyPass /sites/ http://127.0.0.1:8090/sites/
@ -14,10 +15,8 @@
# - Cron service `jumble-nip66-monitor` (Imwald NIP-66 monitor image) uses NIP66_MONITOR_NSEC to publish 30166/10166; nsec never goes to the client. # - Cron service `jumble-nip66-monitor` (Imwald NIP-66 monitor image) uses NIP66_MONITOR_NSEC to publish 30166/10166; nsec never goes to the client.
# - Set NIP66_MONITOR_NPUB (npub1... derived from the same key) so the relay info page shows the monitor's avatar and handle in the NIP-66 liveliness section. # - Set NIP66_MONITOR_NPUB (npub1... derived from the same key) so the relay info page shows the monitor's avatar and handle in the NIP-66 liveliness section.
# #
# Optional editor tools (LanguageTool + LibreTranslate): profile `editor-tools` # Apache (or nginx) must proxy same-origin paths baked into the SPA, e.g. /api/languagetool → http://127.0.0.1:8010
# docker compose -f docker-compose.prod.yml --profile editor-tools up -d languagetool libretranslate # and /api/translate → http://127.0.0.1:5000. Build the app with:
# Then Apache (or nginx) must proxy same-origin paths baked into the SPA, e.g. /api/languagetool → http://127.0.0.1:8010
# and /api/translate → http://127.0.0.1:5000. Build the app image with:
# LANGUAGE_TOOL_URL=/api/languagetool TRANSLATE_URL=/api/translate ./scripts/build-and-push-prod.sh # LANGUAGE_TOOL_URL=/api/languagetool TRANSLATE_URL=/api/translate ./scripts/build-and-push-prod.sh
services: services:
@ -68,7 +67,6 @@ services:
languagetool: languagetool:
image: silviof/docker-languagetool:latest image: silviof/docker-languagetool:latest
profiles: ['editor-tools']
container_name: imwald-languagetool container_name: imwald-languagetool
ports: ports:
- '127.0.0.1:8010:8010' - '127.0.0.1:8010:8010'
@ -80,7 +78,6 @@ services:
libretranslate: libretranslate:
image: libretranslate/libretranslate:latest image: libretranslate/libretranslate:latest
profiles: ['editor-tools']
container_name: imwald-libretranslate container_name: imwald-libretranslate
ports: ports:
- '127.0.0.1:5000:5000' - '127.0.0.1:5000:5000'
@ -97,12 +94,11 @@ services:
limits: limits:
memory: 2048M memory: 2048M
# --- profile `stack`: OG proxy + Piper (Apache → 8090 / 9876). One command: npm run stack:remote --- # OG proxy + Piper (Apache → 8090 / 9876). Piper HTTP image: silberengel/imwald-piper-tts-proxy (see build-and-push-prod.sh).
# First-party images: silberengel/* (override with OG_PROXY_IMAGE / WYOMING_PIPER_IMAGE / PIPER_HTTP_PROXY_IMAGE).
og-proxy: og-proxy:
image: ${OG_PROXY_IMAGE:-silberengel/wikistr:latest-og-proxy} image: ${OG_PROXY_IMAGE:-silberengel/wikistr:latest-og-proxy}
profiles: ['stack'] # Distinct name so `docker compose` does not assume an unrelated `og-proxy` container belongs to this project.
container_name: og-proxy container_name: imwald-og-proxy
dns: dns:
- 1.1.1.1 - 1.1.1.1
- 8.8.8.8 - 8.8.8.8
@ -119,8 +115,8 @@ services:
piper-wyoming: piper-wyoming:
image: ${WYOMING_PIPER_IMAGE:-silberengel/wyoming-piper:latest} image: ${WYOMING_PIPER_IMAGE:-silberengel/wyoming-piper:latest}
profiles: ['stack'] # Distinct from ad-hoc stacks named `piper-tts`; Piper HTTP proxy reaches this service as `piper-wyoming`.
container_name: piper-tts container_name: imwald-piper-wyoming
command: command:
- --voice - --voice
- en_US-lessac-medium - en_US-lessac-medium
@ -135,11 +131,7 @@ services:
restart: unless-stopped restart: unless-stopped
piper-tts-proxy: piper-tts-proxy:
profiles: ['stack']
container_name: imwald-piper-tts-proxy container_name: imwald-piper-tts-proxy
build:
context: .
dockerfile: services/piper-tts-proxy/Dockerfile
image: ${PIPER_HTTP_PROXY_IMAGE:-silberengel/imwald-piper-tts-proxy:latest} image: ${PIPER_HTTP_PROXY_IMAGE:-silberengel/imwald-piper-tts-proxy:latest}
environment: environment:
NODE_ENV: production NODE_ENV: production

4
package-lock.json generated

@ -1,12 +1,12 @@
{ {
"name": "imwald", "name": "imwald",
"version": "23.0.0", "version": "23.0.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "imwald", "name": "imwald",
"version": "23.0.0", "version": "23.0.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@asciidoctor/core": "^3.0.4", "@asciidoctor/core": "^3.0.4",

2
package.json

@ -1,6 +1,6 @@
{ {
"name": "imwald", "name": "imwald",
"version": "23.0.0", "version": "23.0.1",
"description": "Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery", "description": "Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery",
"private": true, "private": true,
"type": "module", "type": "module",

28
scripts/README-deploy.md

@ -11,12 +11,13 @@ docker login # once, if needed
./scripts/build-and-push-prod.sh ./scripts/build-and-push-prod.sh
``` ```
This builds both images and pushes two tags each (`latest` and the version from `package.json`, e.g. `17.0.0`): This builds and pushes **three** images, each with `latest` and the version from `package.json` (e.g. `17.0.0`):
- **Main app (Imwald):** `silberengel/imwald-jumble` - **Main app (Imwald):** `silberengel/imwald-jumble`
- **NIP-66 monitor:** `silberengel/imwald-jumble-nip66-monitor` - **NIP-66 monitor:** `silberengel/imwald-jumble-nip66-monitor`
- **Piper HTTP proxy:** `silberengel/imwald-piper-tts-proxy` (Wyoming Piper still pulls `silberengel/wyoming-piper` and `silberengel/wikistr` on the server.)
Registry paths keep the historical `imwald-jumble` name; retagging to e.g. `silberengel/imwald` is optional and requires updating `docker-compose.prod.yml` and pull scripts. Registry paths keep the historical `imwald-jumble` name; retagging is optional and requires updating `docker-compose.prod.yml`.
## Remote server: one-time setup ## Remote server: one-time setup
@ -36,17 +37,38 @@ This builds both images and pushes two tags each (`latest` and the version from
NIP66_MONITOR_NPUB=npub1... NIP66_MONITOR_NPUB=npub1...
``` ```
4. **Once per machine** (LibreTranslate bind mounts need UID 1032 on the host dirs):
```bash
bash scripts/ensure-libretranslate-dirs.sh
```
## Remote server: pull and run ## Remote server: pull and run
Use a **current** `docker-compose.prod.yml` from this repo (`git pull` in the clone). If `docker compose pull` only lists two images, the file is outdated and **LanguageTool / LibreTranslate will never start**.
After you’ve pushed from local: After you’ve pushed from local:
```bash ```bash
cd jumble cd jumble
git pull
bash scripts/ensure-libretranslate-dirs.sh # once per host
docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d docker compose -f docker-compose.prod.yml up -d
``` ```
The app is on **port 8089**. Both services use `:latest`; to pin a version, set the image in `docker-compose.prod.yml` to e.g. `silberengel/imwald-jumble:17.0.0` and `silberengel/imwald-jumble-nip66-monitor:17.0.0`. That starts the **full** stack in `docker-compose.prod.yml` (app, NIP-66 monitor, OG proxy, Piper, LanguageTool, LibreTranslate). The SPA is on **port 8089**; see `docker-compose.prod.yml` header for Apache paths.
**Grammar + translate in the browser** also require the SPA to be built with API paths baked in, for example:
`LANGUAGE_TOOL_URL=/api/languagetool TRANSLATE_URL=/api/translate ./scripts/build-and-push-prod.sh`
and Apache (or nginx) must proxy `/api/languagetool``127.0.0.1:8010` and `/api/translate``127.0.0.1:5000`.
**Shared host:** if you already run another `og-proxy` on `127.0.0.0.1:8090` or another Wyoming Piper on the same ports, `docker compose up -d` can fail with a port or name conflict. Either stop the duplicates or start only the pieces you need, e.g. `docker compose up -d jumble jumble-nip66-monitor languagetool libretranslate` (and point `PIPER_TTS_HOST` / Apache at your existing Piper stack if applicable).
Equivalent one-liner: `npm run stack:remote`
Images default to `:latest`; to pin a version, set image tags in `docker-compose.prod.yml`.
## Useful commands (server) ## Useful commands (server)

20
scripts/build-and-push-prod.sh

@ -1,6 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Build main app and NIP-66 monitor images locally; push to silberengel/imwald-jumble and silberengel/imwald-jumble-nip66-monitor as :latest and :<version from package.json>. # Build and push first-party production images (same tags :latest and :<version from package.json>):
# - silberengel/imwald-jumble (SPA)
# - silberengel/imwald-jumble-nip66-monitor
# - silberengel/imwald-piper-tts-proxy (HTTP → Wyoming Piper; matches docker-compose.prod.yml)
# Then create git tag v<version> and push it. # Then create git tag v<version> and push it.
# Other compose services (og-proxy, wyoming-piper, LanguageTool, LibreTranslate) pull from Hub on the server.
# Run from repo root. Requires: docker, docker login, git. # Run from repo root. Requires: docker, docker login, git.
# #
# Optional env: # Optional env:
@ -19,6 +23,7 @@ VERSION="$(node -p "require('./package.json').version")"
GIT_TAG="v${VERSION}" GIT_TAG="v${VERSION}"
IMAGE_APP="silberengel/imwald-jumble" IMAGE_APP="silberengel/imwald-jumble"
IMAGE_MONITOR="silberengel/imwald-jumble-nip66-monitor" IMAGE_MONITOR="silberengel/imwald-jumble-nip66-monitor"
IMAGE_PIPER_PROXY="silberengel/imwald-piper-tts-proxy"
# OG / link-preview HTML: VITE_PROXY_SERVER is baked into the client bundle at image build time (not runtime). # OG / link-preview HTML: VITE_PROXY_SERVER is baked into the client bundle at image build time (not runtime).
# Use public origin only (no /proxy path): web.service builds <origin>/sites/?url=… # Use public origin only (no /proxy path): web.service builds <origin>/sites/?url=…
@ -39,11 +44,18 @@ docker build \
echo "Building NIP-66 monitor (version: $VERSION)" echo "Building NIP-66 monitor (version: $VERSION)"
docker build -t "$IMAGE_MONITOR:latest" -t "$IMAGE_MONITOR:$VERSION" ./nip66-cron docker build -t "$IMAGE_MONITOR:latest" -t "$IMAGE_MONITOR:$VERSION" ./nip66-cron
echo "Pushing $IMAGE_APP and $IMAGE_MONITOR" echo "Building Piper HTTP TTS proxy (version: $VERSION)"
docker build \
-f services/piper-tts-proxy/Dockerfile \
-t "$IMAGE_PIPER_PROXY:latest" -t "$IMAGE_PIPER_PROXY:$VERSION" .
echo "Pushing $IMAGE_APP, $IMAGE_MONITOR, and $IMAGE_PIPER_PROXY"
docker push "$IMAGE_APP:latest" docker push "$IMAGE_APP:latest"
docker push "$IMAGE_APP:$VERSION" docker push "$IMAGE_APP:$VERSION"
docker push "$IMAGE_MONITOR:latest" docker push "$IMAGE_MONITOR:latest"
docker push "$IMAGE_MONITOR:$VERSION" docker push "$IMAGE_MONITOR:$VERSION"
docker push "$IMAGE_PIPER_PROXY:latest"
docker push "$IMAGE_PIPER_PROXY:$VERSION"
# --- Git tag (matches package.json version) --- # --- Git tag (matches package.json version) ---
if git rev-parse "$GIT_TAG" >/dev/null 2>&1; then if git rev-parse "$GIT_TAG" >/dev/null 2>&1; then
@ -62,5 +74,5 @@ git tag -a "$GIT_TAG" -m "Release $GIT_TAG"
echo "Pushing tag $GIT_TAG to origin" echo "Pushing tag $GIT_TAG to origin"
git push origin "$GIT_TAG" git push origin "$GIT_TAG"
echo "Done. On the server: docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d" echo "Done. On the server (repo clone): bash scripts/ensure-libretranslate-dirs.sh # once, for LibreTranslate volume permissions"
echo "Optional LanguageTool + LibreTranslate: docker compose -f docker-compose.prod.yml --profile editor-tools up -d languagetool libretranslate (see PROXY_SETUP.md)" echo " docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d"

12
scripts/stack-remote.sh

@ -1,14 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# One remote command from repo clone: silberengel/imwald-jumble + nip66 + wikistr og-proxy + wyoming-piper + # One remote command from repo clone: full docker-compose.prod.yml stack (pull + up).
# imwald-piper-tts-proxy (built here; push silberengel/imwald-piper-tts-proxy:latest to Hub for pull-only hosts) + # Same as: ensure-libretranslate-dirs.sh && docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d
# LanguageTool + LibreTranslate. Apache → 8089 / 8090 / 9876. # First-party images must be pushed from ./scripts/build-and-push-prod.sh before pull will get new app/monitor/piper-proxy revisions.
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)" ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT" cd "$ROOT"
bash "$ROOT/scripts/ensure-libretranslate-dirs.sh" bash "$ROOT/scripts/ensure-libretranslate-dirs.sh"
COMPOSE=(docker compose -f docker-compose.prod.yml --profile stack --profile editor-tools) COMPOSE=(docker compose -f docker-compose.prod.yml)
# Pull Hub images only; piper-tts-proxy is built from this repo (push silberengel/imwald-piper-tts-proxy:latest when ready). "${COMPOSE[@]}" pull
"${COMPOSE[@]}" pull jumble jumble-nip66-monitor og-proxy piper-wyoming languagetool libretranslate
"${COMPOSE[@]}" build piper-tts-proxy
"${COMPOSE[@]}" up -d "${COMPOSE[@]}" up -d
echo "[stack:remote] jumble :8089 | og-proxy :8090 | piper HTTP :9876 | LT :8010 | translate :5000" echo "[stack:remote] jumble :8089 | og-proxy :8090 | piper HTTP :9876 | LT :8010 | translate :5000"

2
src/lib/read-aloud.ts

@ -1,5 +1,5 @@
import { ExtendedKind, READ_ALOUD_TTS_URL } from '@/constants' import { ExtendedKind, READ_ALOUD_TTS_URL } from '@/constants'
import i18n, { LocalizedLanguageNames, normalizeToSupportedAppLanguage, type TLanguage } from '@/i18n' import i18n, { LocalizedLanguageNames, normalizeToSupportedAppLanguage } from '@/i18n'
import { getNoteTranslation } from '@/lib/note-translation-display' import { getNoteTranslation } from '@/lib/note-translation-display'
import { import {
getPiperVoiceForChosenLanguage, getPiperVoiceForChosenLanguage,

Loading…
Cancel
Save