chore: remove local deployment and template artifacts

This commit is contained in:
boos
2026-07-14 03:34:59 +08:00
parent d68da02d9b
commit bdd26a5e43
4 changed files with 106 additions and 268 deletions
+81
View File
@@ -0,0 +1,81 @@
# BoosAPI Engineering Rules
## Project
BoosAPI is a Rust API gateway. The production server is built with Cargo and
serves the React/Vite frontend from `dist/web/` and the standalone website
from `dist/website/`.
- Backend entry point: `src/main.rs`
- Library entry point: `src/lib.rs`
- HTTP routes: `src/routes/`
- Proxy core: `src/proxy/`
- Background services: `src/services/`
- Database code: `src/db/`
- Protocol transformers: `src/transformers/`
- SQL migrations: `migrations/`, `migrations_pg/`, and `migrations_mysql/`
- Desktop workspace: `desktop/`
Use Rust and the existing project patterns. Do not introduce a Node.js runtime
dependency into the production server.
## Development
```bash
cargo run --release
cargo metadata --no-deps --format-version 1
cargo test
```
The frontend uses Vite during development and is built into `dist/web/` for
the Rust server. Do not commit generated build output, local databases, logs,
or dependency installation directories.
## Data And Migrations
- Keep SQLite, PostgreSQL, and MySQL migration history aligned when a schema
change affects more than one database.
- Use the existing database helpers and query style.
- Do not commit local database files or credentials.
- `vendor/sqlx-*` is currently intentional: `Cargo.toml` patches SQLx core,
MySQL, and SQLite crates to these local sources. Do not remove them without
first removing the patch and validating the complete Cargo build.
## Picture Agent
The Picture Agent routes are under `src/routes/picture_agent.rs`. Template data
is fetched from the configured remote gallery source at request time; do not
add the Linux Do template JSON as a bundled local asset.
Picture generation jobs must continue to use the existing authentication,
background task, workspace asset, and usage tracking services.
## ComfyUI Agent
The ComfyUI Agent defaults to local mode and reuses this service's `/v1/*`
routes with the built-in `comfyui-agent` downstream key. When
`BOOSAPI_UPSTREAM_BASE` is set, it uses fallback remote mode. Preserve the
built-in key protections, idempotent startup provisioning, and SSE marker
handling for `CHARACTER`, `SCENE`, `TTS`, and `WORKFLOW`.
## Frontend Sessions
Use `src/web/authSession.ts` for new session work:
- `commitSession`
- `clearSession`
- `getSessionToken`
Keep admin and user audiences distinct. OAuth slots are placeholders until a
feature explicitly wires their behavior. Centralize login error text in
`src/web/loginError.ts`.
## Editing And Verification
- Keep changes scoped and preserve unrelated user changes.
- Prefer `rg` for searches and structured parsers for JSON/TOML/SQL.
- Use `apply_patch` for manual edits.
- Add focused tests for changed behavior.
- Run the narrowest relevant test or Cargo check before committing.
- Never commit secrets, generated artifacts, local environment files, or
temporary deployment output.
File diff suppressed because one or more lines are too long
-260
View File
@@ -1,260 +0,0 @@
#!/usr/bin/env bash
# Build the BossAI production binary, package Alpine/musl runtime libraries,
# and deploy the runnable bundle to a Debian server.
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"
log() { echo "[$(date '+%H:%M:%S')] $*"; }
ok() { echo "[$(date '+%H:%M:%S')] ✓ $*"; }
fail() { echo "[$(date '+%H:%M:%S')] ✗ $*" >&2; exit 1; }
REMOTE_HOST="${BOSSAI_DEBIAN_HOST:-129.211.30.104}"
REMOTE_USER="${BOSSAI_DEBIAN_USER:-root}"
REMOTE_DIR="${BOSSAI_DEBIAN_DIR:-/root/BossAI}"
SSH_KEY="${BOSSAI_DEBIAN_SSH_KEY:-$HOME/.ssh/id_rsa_mbp}"
BOSSAI_SOURCE_DIR="${BOSSAI_SOURCE_DIR:-/root/BossAI-build/main}"
BOSSAI_REMOTE="${BOSSAI_REMOTE:-bossai}"
BOSSAI_REF="${BOSSAI_REF:-main}"
BOSSAI_WORKFLOW_UI_SOURCE="${BOSSAI_WORKFLOW_UI_SOURCE:-$ROOT_DIR/vendor/ComfyUI_frontend}"
PRODUCTION_DIR="${BOSSAI_PRODUCTION_DIR:-$BOSSAI_SOURCE_DIR/dist/bossai-production}"
BUNDLE_DIR="${BOSSAI_DEBIAN_BUNDLE_DIR:-$BOSSAI_SOURCE_DIR/dist/bossai-debian-bundle}"
BUNDLE_ARCHIVE="${BOSSAI_DEBIAN_BUNDLE_ARCHIVE:-$BOSSAI_SOURCE_DIR/dist/bossai-debian-bundle.tar.gz}"
FRONTEND_PM="${BOOSAPI_FRONTEND_PM:-auto}"
SKIP_BUILD=0
SKIP_VERIFY=0
SKIP_SOURCE_UPDATE=0
usage() {
cat <<'EOF'
Usage: scripts/deploy-bossai-debian.sh [options]
Build BossAI from the BossAI source tree, bundle the musl runtime libraries
needed by the Alpine-built binary, upload it to a Debian host, extract it,
and run a config check.
Options:
--host <host> Debian host/IP. Default: 129.211.30.104
--user <user> SSH user. Default: root
--dir <remote-dir> Remote deploy directory. Default: /root/BossAI
--key <ssh-key> SSH private key. Default: ~/.ssh/id_rsa_mbp
--source-dir <dir> BossAI source worktree. Default: /root/BossAI-build/main
--remote <name> Git remote for BossAI. Default: bossai
--ref <ref> BossAI branch/ref to build. Default: main
--skip-build Reuse dist/bossai-production from a previous build
--skip-source-update Do not fetch/reset the BossAI source worktree
--skip-verify Skip remote ./run-boosapi.sh --check-config
-h, --help Show this help
Environment overrides:
BOSSAI_DEBIAN_HOST
BOSSAI_DEBIAN_USER
BOSSAI_DEBIAN_DIR
BOSSAI_DEBIAN_SSH_KEY
BOSSAI_SOURCE_DIR
BOSSAI_REMOTE
BOSSAI_REF
BOSSAI_WORKFLOW_UI_SOURCE
BOSSAI_PRODUCTION_DIR
BOSSAI_DEBIAN_BUNDLE_DIR
BOSSAI_DEBIAN_BUNDLE_ARCHIVE
BOOSAPI_FRONTEND_PM auto / bun / npm, forwarded to build-and-run.sh
Remote start command after deploy:
cd /root/BossAI && ./run-boosapi.sh
EOF
}
while [ $# -gt 0 ]; do
case "$1" in
--host)
shift
[ $# -gt 0 ] || fail "--host requires a value"
REMOTE_HOST="$1"
;;
--user)
shift
[ $# -gt 0 ] || fail "--user requires a value"
REMOTE_USER="$1"
;;
--dir)
shift
[ $# -gt 0 ] || fail "--dir requires a value"
REMOTE_DIR="$1"
;;
--key)
shift
[ $# -gt 0 ] || fail "--key requires a value"
SSH_KEY="$1"
;;
--source-dir)
shift
[ $# -gt 0 ] || fail "--source-dir requires a value"
BOSSAI_SOURCE_DIR="$1"
PRODUCTION_DIR="${BOSSAI_PRODUCTION_DIR:-$BOSSAI_SOURCE_DIR/dist/bossai-production}"
BUNDLE_DIR="${BOSSAI_DEBIAN_BUNDLE_DIR:-$BOSSAI_SOURCE_DIR/dist/bossai-debian-bundle}"
BUNDLE_ARCHIVE="${BOSSAI_DEBIAN_BUNDLE_ARCHIVE:-$BOSSAI_SOURCE_DIR/dist/bossai-debian-bundle.tar.gz}"
;;
--remote)
shift
[ $# -gt 0 ] || fail "--remote requires a value"
BOSSAI_REMOTE="$1"
;;
--ref)
shift
[ $# -gt 0 ] || fail "--ref requires a value"
BOSSAI_REF="$1"
;;
--skip-build)
SKIP_BUILD=1
;;
--skip-source-update)
SKIP_SOURCE_UPDATE=1
;;
--skip-verify)
SKIP_VERIFY=1
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage >&2
exit 1
;;
esac
shift
done
require_file() {
local path="$1"
[ -f "$path" ] || fail "Missing required file: $path"
}
require_command() {
command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"
}
ensure_bossai_source() {
require_command git
if [ "$SKIP_SOURCE_UPDATE" -eq 1 ]; then
log "Skipping BossAI source update; using $BOSSAI_SOURCE_DIR"
[ -d "$BOSSAI_SOURCE_DIR" ] || fail "BossAI source directory does not exist: $BOSSAI_SOURCE_DIR"
return 0
fi
log "Preparing BossAI source from $BOSSAI_REMOTE/$BOSSAI_REF → $BOSSAI_SOURCE_DIR"
git fetch "$BOSSAI_REMOTE" "$BOSSAI_REF"
if [ -d "$BOSSAI_SOURCE_DIR" ] && git -C "$BOSSAI_SOURCE_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
git -C "$BOSSAI_SOURCE_DIR" reset --hard "$BOSSAI_REMOTE/$BOSSAI_REF"
git -C "$BOSSAI_SOURCE_DIR" clean -fd
else
rm -rf "$BOSSAI_SOURCE_DIR"
mkdir -p "$(dirname "$BOSSAI_SOURCE_DIR")"
git worktree add "$BOSSAI_SOURCE_DIR" "$BOSSAI_REMOTE/$BOSSAI_REF"
fi
require_file "$BOSSAI_SOURCE_DIR/build-and-run.sh"
ok "BossAI source ready: $(git -C "$BOSSAI_SOURCE_DIR" rev-parse --short HEAD)"
}
prepare_workflow_ui_source() {
if [ -n "${BOOSAPI_WORKFLOW_UI_REPO:-}" ]; then
log "Using BOOSAPI_WORKFLOW_UI_REPO=$BOOSAPI_WORKFLOW_UI_REPO"
return 0
fi
if [ ! -d "$BOSSAI_WORKFLOW_UI_SOURCE/.git" ] && [ ! -f "$BOSSAI_WORKFLOW_UI_SOURCE/.git" ]; then
log "No local Workflow UI source at $BOSSAI_WORKFLOW_UI_SOURCE; install script may fetch from GitHub"
return 0
fi
log "Using local ComfyUI_frontend source: $BOSSAI_WORKFLOW_UI_SOURCE"
export BOOSAPI_WORKFLOW_UI_REPO="$BOSSAI_WORKFLOW_UI_SOURCE"
}
runtime_lib() {
local path="$1"
require_file "$path"
cp "$path" "$BUNDLE_DIR/lib/"
}
write_runner() {
cat > "$BUNDLE_DIR/run-boosapi.sh" <<'EOF'
#!/usr/bin/env sh
set -eu
DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
exec "$DIR/lib/ld-musl-x86_64.so.1" --library-path "$DIR/lib" "$DIR/boosapi" "$@"
EOF
chmod +x "$BUNDLE_DIR/run-boosapi.sh"
}
package_bundle() {
require_file "$PRODUCTION_DIR/boosapi"
require_file "$PRODUCTION_DIR/config.toml"
rm -rf "$BUNDLE_DIR" "$BUNDLE_ARCHIVE"
mkdir -p "$BUNDLE_DIR/lib"
cp "$PRODUCTION_DIR/boosapi" "$BUNDLE_DIR/boosapi"
cp "$PRODUCTION_DIR/config.toml" "$BUNDLE_DIR/config.toml"
chmod +x "$BUNDLE_DIR/boosapi"
runtime_lib /lib/ld-musl-x86_64.so.1
runtime_lib /usr/lib/libssl.so.3
runtime_lib /usr/lib/libcrypto.so.3
runtime_lib /usr/lib/libgcc_s.so.1
write_runner
tar -C "$BUNDLE_DIR" -czf "$BUNDLE_ARCHIVE" .
ok "Bundle ready: $BUNDLE_ARCHIVE"
sha256sum "$BUNDLE_ARCHIVE" "$BUNDLE_DIR/boosapi"
ls -lh "$BUNDLE_ARCHIVE"
}
deploy_bundle() {
require_file "$SSH_KEY"
require_command ssh
require_command scp
local remote="${REMOTE_USER}@${REMOTE_HOST}"
local ssh_opts=(-i "$SSH_KEY" -o StrictHostKeyChecking=accept-new)
log "Preparing remote directory: $remote:$REMOTE_DIR"
ssh "${ssh_opts[@]}" "$remote" "mkdir -p '$REMOTE_DIR'"
log "Uploading bundle..."
scp "${ssh_opts[@]}" "$BUNDLE_ARCHIVE" "$remote:$REMOTE_DIR/bossai-debian-bundle.tar.gz"
log "Extracting bundle on Debian host..."
ssh "${ssh_opts[@]}" "$remote" "cd '$REMOTE_DIR' && tar -xzf bossai-debian-bundle.tar.gz && chmod +x boosapi run-boosapi.sh && sha256sum bossai-debian-bundle.tar.gz boosapi"
if [ "$SKIP_VERIFY" -eq 0 ]; then
log "Checking remote config..."
ssh "${ssh_opts[@]}" "$remote" "cd '$REMOTE_DIR' && ./run-boosapi.sh --check-config"
fi
ok "Deployed BossAI to $remote:$REMOTE_DIR"
log "Start with: cd $REMOTE_DIR && ./run-boosapi.sh"
}
if [ "$SKIP_BUILD" -eq 0 ]; then
ensure_bossai_source
prepare_workflow_ui_source
log "Building BossAI production bundle..."
BOOSAPI_FRONTEND_PM="$FRONTEND_PM" "$BOSSAI_SOURCE_DIR/build-and-run.sh" \
--production \
--production-dir "$PRODUCTION_DIR"
else
[ -d "$BOSSAI_SOURCE_DIR" ] || fail "BossAI source directory does not exist: $BOSSAI_SOURCE_DIR"
log "Skipping build; using existing $PRODUCTION_DIR"
fi
package_bundle
deploy_bundle
+25 -7
View File
@@ -23,8 +23,8 @@ use std::time::Duration;
const TEMPLATE_GALLERY_PAGE_URL: &str =
"https://weiyyin.github.io/linux-do-gpt-image/prompt_gallery.html";
const LINUX_DO_TEMPLATE_JSON: &str =
include_str!("../../assets/picture-agent/linux_do_2036927_prompts_images.with_tags.json");
const TEMPLATE_DATA_URL: &str =
"https://weiyyin.github.io/linux-do-gpt-image/linux_do_2036927_prompts_images.with_tags.json";
const TASK_TYPE_PICTURE_GENERATION: &str = "picture-agent-generation";
const PICTURE_GENERATION_TIMEOUT_SECS: u64 = 300;
const PICTURE_GENERATION_RETRY_SECS: u64 = 180;
@@ -435,10 +435,10 @@ async fn templates_handler(
) -> AppResult<Json<Value>> {
let _auth = resolve_picture_agent_auth(&headers, &state).await?;
let limit = query.limit.unwrap_or(144).clamp(1, 240);
let (templates, local_status) = match parse_gallery_templates(LINUX_DO_TEMPLATE_JSON, limit) {
Ok(templates) => (templates, json!({ "ok": true, "source": "local-json" })),
let (templates, remote_status) = match fetch_gallery_templates(&state.http, limit).await {
Ok(templates) => (templates, json!({ "ok": true, "source": "remote-json" })),
Err(err) => {
tracing::warn!(%err, "Picture Agent local Linux Do template gallery unavailable");
tracing::warn!(%err, "Picture Agent remote Linux Do template gallery unavailable");
(Vec::new(), json!({ "ok": false, "message": err }))
}
};
@@ -446,17 +446,35 @@ async fn templates_handler(
Ok(Json(json!({
"success": true,
"sourceUrl": TEMPLATE_GALLERY_PAGE_URL,
"source": local_status,
"source": remote_status,
"templates": templates,
})))
}
async fn fetch_gallery_templates(
client: &reqwest::Client,
limit: usize,
) -> Result<Vec<Value>, String> {
let raw = client
.get(TEMPLATE_DATA_URL)
.send()
.await
.map_err(|err| format!("failed to fetch remote Linux Do templates: {err}"))?
.error_for_status()
.map_err(|err| format!("remote Linux Do templates returned an error: {err}"))?
.text()
.await
.map_err(|err| format!("failed to read remote Linux Do templates: {err}"))?;
parse_gallery_templates(&raw, limit)
}
fn parse_gallery_templates(raw: &str, limit: usize) -> Result<Vec<Value>, String> {
if limit == 0 {
return Ok(Vec::new());
}
let items = serde_json::from_str::<Vec<GalleryItem>>(raw)
.map_err(|err| format!("failed to parse local Linux Do templates: {err}"))?;
.map_err(|err| format!("failed to parse remote Linux Do templates: {err}"))?;
Ok(items
.into_iter()