feat: add fnos storyforge web dev deploy

This commit is contained in:
kris
2026-03-26 21:14:23 +08:00
parent ea2d305a3c
commit d0673d08a5
6 changed files with 147 additions and 1 deletions

View File

@@ -0,0 +1,9 @@
services:
storyforge-web-v4-dev:
image: docker.m.daocloud.io/library/nginx:alpine
container_name: storyforge-web-v4-dev
restart: unless-stopped
ports:
- "${STORYFORGE_WEB_V4_DEV_PORT:-19192}:80"
volumes:
- ../../storyforge/web/storyforge-web-v4:/usr/share/nginx/html:ro

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"
export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
export FNOS_SKILL="${FNOS_SKILL:-$CODEX_HOME/skills/fnos-hyzq-deploy}"
export FNOS_SSH="${FNOS_SSH:-$FNOS_SKILL/scripts/fnos_ssh.sh}"
export FNOS_SCP="${FNOS_SCP:-$FNOS_SKILL/scripts/fnos_scp.sh}"
FNOS_HOST="${FNOS_HOST:-192.168.31.188}"
FNOS_USER="${FNOS_USER:-krisolo}"
REMOTE_ROOT="${STORYFORGE_FNOS_REMOTE_ROOT:-/vol1/docker/hyzq-stack/current/storyforge}"
REMOTE_WEB_PARENT="$REMOTE_ROOT/web"
REMOTE_WEB_DIR="$REMOTE_WEB_PARENT/storyforge-web-v4"
REMOTE_ASSETS_DIR="$REMOTE_WEB_DIR/assets"
REMOTE_COMPOSE_DIR="${STORYFORGE_FNOS_COMPOSE_DIR:-/vol1/docker/hyzq-stack/current/deploy/fnos}"
REMOTE_COMPOSE_FILE="$REMOTE_COMPOSE_DIR/storyforge-fnos-web-v4.compose.yaml"
BACKEND_URL="${STORYFORGE_FNOS_BACKEND_URL:-https://storyforge.hyzq.net}"
WEB_PORT="${STORYFORGE_WEB_V4_DEV_PORT:-19192}"
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "missing required command: $1" >&2
exit 1
fi
}
need_cmd python3
if [ -z "${FNOS_PASSWORD:-}" ]; then
need_cmd security
fi
shell_quote() {
python3 - "$1" <<'PY'
import shlex
import sys
print(shlex.quote(sys.argv[1]))
PY
}
json_quote() {
python3 - "$1" <<'PY'
import json
import sys
print(json.dumps(sys.argv[1], ensure_ascii=False))
PY
}
resolve_password() {
if [ -n "${FNOS_PASSWORD:-}" ]; then
printf '%s' "$FNOS_PASSWORD"
return 0
fi
security find-internet-password -s "$FNOS_HOST" -a "$FNOS_USER" -w
}
run_remote_sudo() {
local password="$1"
shift
local remote_cmd="$*"
local password_quoted
password_quoted="$(shell_quote "$password")"
"$FNOS_SSH" "$(shell_quote "printf '%s\\n' $password_quoted | sudo -S -p '' sh -lc $(shell_quote "$remote_cmd")")"
}
PASSWORD="$(resolve_password)"
TMPDIR_DEPLOY="$(mktemp -d)"
RUNTIME_CONFIG_FILE="$TMPDIR_DEPLOY/storyforge-runtime-config.js"
BACKEND_URL_JSON="$(json_quote "$BACKEND_URL")"
trap 'rm -rf "$TMPDIR_DEPLOY"' EXIT
cat >"$RUNTIME_CONFIG_FILE" <<EOF
(function () {
window.__STORYFORGE_RUNTIME_CONFIG__ = Object.assign(
{},
window.__STORYFORGE_RUNTIME_CONFIG__ || {},
{
backendUrl: $BACKEND_URL_JSON
}
);
})();
EOF
echo "[1/6] prepare remote directories"
"$FNOS_SSH" "$(shell_quote "mkdir -p $(shell_quote "$REMOTE_WEB_PARENT") $(shell_quote "$REMOTE_ASSETS_DIR") $(shell_quote "$REMOTE_COMPOSE_DIR")")"
echo "[2/6] sync web files"
"$FNOS_SCP" "$REMOTE_WEB_PARENT" "$ROOT/web/storyforge-web-v4"
echo "[3/6] sync runtime config override"
"$FNOS_SCP" "$REMOTE_ASSETS_DIR" "$RUNTIME_CONFIG_FILE"
echo "[4/6] sync compose file"
"$FNOS_SCP" "$REMOTE_COMPOSE_DIR" "$ROOT/deploy/storyforge-fnos-web-v4.compose.yaml"
echo "[5/6] restart fnOS web container"
run_remote_sudo "$PASSWORD" "cd $(shell_quote "$REMOTE_COMPOSE_DIR") && STORYFORGE_WEB_V4_DEV_PORT=$(shell_quote "$WEB_PORT") docker compose -f $(shell_quote "$REMOTE_COMPOSE_FILE") up -d --force-recreate storyforge-web-v4-dev && STORYFORGE_WEB_V4_DEV_PORT=$(shell_quote "$WEB_PORT") docker compose -f $(shell_quote "$REMOTE_COMPOSE_FILE") ps"
echo "[6/6] verify lan web"
for _attempt in 1 2 3 4 5 6 7 8 9 10; do
if curl -fsS "http://$FNOS_HOST:$WEB_PORT/" >/dev/null; then
break
fi
sleep 2
done
curl -fsS "http://$FNOS_HOST:$WEB_PORT/" >/dev/null
curl -fsS "http://$FNOS_HOST:$WEB_PORT/assets/storyforge-runtime-config.js" | grep -q "$BACKEND_URL"
echo "fnOS StoryForge Web V4 ready: http://$FNOS_HOST:$WEB_PORT/"

View File

@@ -110,7 +110,7 @@ python3 -m http.server 3918
- [http://127.0.0.1:3918/index.html](http://127.0.0.1:3918/index.html)
首次进入需要手动连接后端,默认地址是
如果本地是 `127.0.0.1 / localhost`,前端默认会连
- `http://127.0.0.1:8081`
@@ -122,6 +122,18 @@ python3 -m http.server 3918
- `https://storyforge.hyzq.net`
如果页面部署在 NAS 之类的局域网静态站点上,推荐通过运行时配置显式指定后端:
- `assets/storyforge-runtime-config.js`
- `window.__STORYFORGE_RUNTIME_CONFIG__.backendUrl = "https://storyforge.hyzq.net"`
仓库内已经提供一键部署脚本,可直接把前端发到飞牛 NAS 上跑开发测试:
```bash
cd /Users/kris/code/StoryForge-gitea
./scripts/deploy_fnos_storyforge_web.sh
```
## 后续建议
- 继续补多平台各自更深的专属采集与解析能力,而不只是一套统一抽象层

View File

@@ -3,6 +3,11 @@
if (typeof window === "undefined") {
return "http://127.0.0.1:8081";
}
const runtimeConfig = window.__STORYFORGE_RUNTIME_CONFIG__ || window.__STORYFORGE_CONFIG__ || {};
const configuredBackendUrl = String(runtimeConfig.backendUrl || runtimeConfig.backendURL || "").trim();
if (configuredBackendUrl) {
return configuredBackendUrl.replace(/\/$/, "");
}
const { origin, hostname, port, pathname } = window.location;
if (/^https?:/i.test(origin) && hostname === "storyforge.hyzq.net") {
return origin;

View File

@@ -0,0 +1,8 @@
(function () {
window.__STORYFORGE_RUNTIME_CONFIG__ = Object.assign(
{
backendUrl: ""
},
window.__STORYFORGE_RUNTIME_CONFIG__ || {}
);
})();

View File

@@ -1912,6 +1912,7 @@
</main>
</div>
<script src="./assets/storyforge-runtime-config.js"></script>
<script src="./assets/storyforge-session-store.js"></script>
<script src="./assets/storyforge-api-client.js"></script>
<script src="./assets/storyforge-platform-runtime.js"></script>