chore: remove local deployment and template artifacts
This commit is contained in:
@@ -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
@@ -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
|
|
||||||
@@ -23,8 +23,8 @@ use std::time::Duration;
|
|||||||
|
|
||||||
const TEMPLATE_GALLERY_PAGE_URL: &str =
|
const TEMPLATE_GALLERY_PAGE_URL: &str =
|
||||||
"https://weiyyin.github.io/linux-do-gpt-image/prompt_gallery.html";
|
"https://weiyyin.github.io/linux-do-gpt-image/prompt_gallery.html";
|
||||||
const LINUX_DO_TEMPLATE_JSON: &str =
|
const TEMPLATE_DATA_URL: &str =
|
||||||
include_str!("../../assets/picture-agent/linux_do_2036927_prompts_images.with_tags.json");
|
"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 TASK_TYPE_PICTURE_GENERATION: &str = "picture-agent-generation";
|
||||||
const PICTURE_GENERATION_TIMEOUT_SECS: u64 = 300;
|
const PICTURE_GENERATION_TIMEOUT_SECS: u64 = 300;
|
||||||
const PICTURE_GENERATION_RETRY_SECS: u64 = 180;
|
const PICTURE_GENERATION_RETRY_SECS: u64 = 180;
|
||||||
@@ -435,10 +435,10 @@ async fn templates_handler(
|
|||||||
) -> AppResult<Json<Value>> {
|
) -> AppResult<Json<Value>> {
|
||||||
let _auth = resolve_picture_agent_auth(&headers, &state).await?;
|
let _auth = resolve_picture_agent_auth(&headers, &state).await?;
|
||||||
let limit = query.limit.unwrap_or(144).clamp(1, 240);
|
let limit = query.limit.unwrap_or(144).clamp(1, 240);
|
||||||
let (templates, local_status) = match parse_gallery_templates(LINUX_DO_TEMPLATE_JSON, limit) {
|
let (templates, remote_status) = match fetch_gallery_templates(&state.http, limit).await {
|
||||||
Ok(templates) => (templates, json!({ "ok": true, "source": "local-json" })),
|
Ok(templates) => (templates, json!({ "ok": true, "source": "remote-json" })),
|
||||||
Err(err) => {
|
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 }))
|
(Vec::new(), json!({ "ok": false, "message": err }))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -446,17 +446,35 @@ async fn templates_handler(
|
|||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
"sourceUrl": TEMPLATE_GALLERY_PAGE_URL,
|
"sourceUrl": TEMPLATE_GALLERY_PAGE_URL,
|
||||||
"source": local_status,
|
"source": remote_status,
|
||||||
"templates": templates,
|
"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> {
|
fn parse_gallery_templates(raw: &str, limit: usize) -> Result<Vec<Value>, String> {
|
||||||
if limit == 0 {
|
if limit == 0 {
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
let items = serde_json::from_str::<Vec<GalleryItem>>(raw)
|
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
|
Ok(items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
Reference in New Issue
Block a user