fix: self-hosted install — network_mode, cf-core bind mount, install script
This commit is contained in:
parent
663d92fc11
commit
7672dd758a
5 changed files with 152 additions and 16 deletions
44
README.md
44
README.md
|
|
@ -4,6 +4,50 @@
|
|||
|
||||
**Status:** Active — eBay listing intelligence MVP complete (search, trust scoring, affiliate links, feedback FAB, vision task scheduling). Auction sniping engine and multi-platform support are next.
|
||||
|
||||
## Quick install (self-hosted)
|
||||
|
||||
**Requirements:** Docker with Compose plugin, Git. No API keys needed to get started.
|
||||
|
||||
```bash
|
||||
# One-line install — clones to ~/snipe by default
|
||||
bash <(curl -fsSL https://git.opensourcesolarpunk.com/Circuit-Forge/snipe/raw/branch/main/install.sh)
|
||||
|
||||
# Or clone manually and run the script:
|
||||
git clone https://git.opensourcesolarpunk.com/Circuit-Forge/snipe.git
|
||||
bash snipe/install.sh
|
||||
```
|
||||
|
||||
Then open **http://localhost:8509**.
|
||||
|
||||
### Manual setup (if you prefer)
|
||||
|
||||
Snipe's API image is built from a parent context that includes `circuitforge-core`. Both repos must sit as siblings in the same directory:
|
||||
|
||||
```
|
||||
workspace/
|
||||
├── snipe/ ← this repo
|
||||
└── circuitforge-core/ ← required sibling
|
||||
```
|
||||
|
||||
```bash
|
||||
mkdir snipe-workspace && cd snipe-workspace
|
||||
git clone https://git.opensourcesolarpunk.com/Circuit-Forge/snipe.git
|
||||
git clone https://git.opensourcesolarpunk.com/Circuit-Forge/circuitforge-core.git
|
||||
cd snipe
|
||||
cp .env.example .env # edit if you have eBay API credentials (optional)
|
||||
./manage.sh start
|
||||
```
|
||||
|
||||
### Optional: eBay API credentials
|
||||
|
||||
Snipe works without any credentials using its Playwright scraper fallback. Adding eBay API credentials unlocks faster searches and inline seller account age (no extra scrape needed):
|
||||
|
||||
1. Register at [developer.ebay.com](https://developer.ebay.com/my/keys)
|
||||
2. Copy your Production **App ID** and **Cert ID** into `.env`
|
||||
3. Restart: `./manage.sh restart`
|
||||
|
||||
---
|
||||
|
||||
## What it does
|
||||
|
||||
Snipe has two layers that work together:
|
||||
|
|
|
|||
|
|
@ -1,21 +1,17 @@
|
|||
# compose.override.yml — dev-only additions (auto-applied by Docker Compose in dev).
|
||||
# Safe to delete on a self-hosted machine — compose.yml is self-contained.
|
||||
#
|
||||
# What this adds over compose.yml:
|
||||
# - Live source mounts so code changes take effect without rebuilding images
|
||||
# - RELOAD=true to enable uvicorn --reload for the API
|
||||
# - NOTE: circuitforge-core is NOT mounted here — use `./manage.sh build` to
|
||||
# pick up cf-core changes. Mounting it as a bind volume would break self-hosted
|
||||
# installs that don't have the sibling directory.
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: snipe/Dockerfile
|
||||
network_mode: host
|
||||
volumes:
|
||||
- ../circuitforge-core:/app/circuitforge-core
|
||||
- ./api:/app/snipe/api
|
||||
- ./app:/app/snipe/app
|
||||
- ./data:/app/snipe/data
|
||||
- ./tests:/app/snipe/tests
|
||||
environment:
|
||||
- RELOAD=true
|
||||
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/web/Dockerfile
|
||||
volumes:
|
||||
- ./web/src:/app/src # not used at runtime but keeps override valid
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ services:
|
|||
build:
|
||||
context: ..
|
||||
dockerfile: snipe/Dockerfile
|
||||
ports:
|
||||
- "8510:8510"
|
||||
# Host networking lets nginx (in the web container) reach the API at
|
||||
# 172.17.0.1:8510 (the Docker bridge gateway). Required — nginx.conf
|
||||
# is baked into the image and hard-codes that address.
|
||||
network_mode: host
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./data:/app/snipe/data
|
||||
restart: unless-stopped
|
||||
|
||||
web:
|
||||
build:
|
||||
|
|
|
|||
93
install.sh
Executable file
93
install.sh
Executable file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env bash
|
||||
# Snipe — self-hosted install script
|
||||
# Clones Snipe and its shared library (circuitforge-core) into a workspace,
|
||||
# then starts the Docker stack.
|
||||
#
|
||||
# Usage:
|
||||
# bash install.sh # installs to ~/snipe
|
||||
# bash install.sh /opt/snipe # custom install directory
|
||||
#
|
||||
# Requirements: Docker with Compose plugin, Git
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL_DIR="${1:-$HOME/snipe}"
|
||||
FORGEJO="https://git.opensourcesolarpunk.com/Circuit-Forge"
|
||||
|
||||
info() { echo " [snipe] $*"; }
|
||||
ok() { echo "✓ $*"; }
|
||||
fail() { echo "✗ $*" >&2; exit 1; }
|
||||
|
||||
echo ""
|
||||
echo " Snipe — self-hosted installer"
|
||||
echo " Install directory: $INSTALL_DIR"
|
||||
echo ""
|
||||
|
||||
# ── Pre-flight checks ────────────────────────────────────────────────────────
|
||||
|
||||
command -v docker >/dev/null 2>&1 || fail "Docker is required. Install from https://docs.docker.com/get-docker/"
|
||||
docker compose version >/dev/null 2>&1 || fail "Docker Compose plugin is required (docker compose, not docker-compose)."
|
||||
command -v git >/dev/null 2>&1 || fail "Git is required."
|
||||
ok "Docker $(docker --version | awk '{print $3}' | tr -d ,) and Git found."
|
||||
|
||||
# ── Clone repos ──────────────────────────────────────────────────────────────
|
||||
|
||||
# compose.yml builds with context: .. so both repos must be siblings.
|
||||
SNIPE_DIR="$INSTALL_DIR/snipe"
|
||||
CORE_DIR="$INSTALL_DIR/circuitforge-core"
|
||||
|
||||
if [[ -d "$SNIPE_DIR" ]]; then
|
||||
info "Snipe already exists at $SNIPE_DIR — pulling latest..."
|
||||
git -C "$SNIPE_DIR" pull --ff-only
|
||||
else
|
||||
info "Cloning Snipe..."
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
git clone "$FORGEJO/snipe.git" "$SNIPE_DIR"
|
||||
fi
|
||||
ok "Snipe cloned to $SNIPE_DIR"
|
||||
|
||||
if [[ -d "$CORE_DIR" ]]; then
|
||||
info "circuitforge-core already exists — pulling latest..."
|
||||
git -C "$CORE_DIR" pull --ff-only
|
||||
else
|
||||
info "Cloning circuitforge-core (shared library)..."
|
||||
git clone "$FORGEJO/circuitforge-core.git" "$CORE_DIR"
|
||||
fi
|
||||
ok "circuitforge-core cloned to $CORE_DIR"
|
||||
|
||||
# ── Configure environment ────────────────────────────────────────────────────
|
||||
|
||||
ENV_FILE="$SNIPE_DIR/.env"
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
cp "$SNIPE_DIR/.env.example" "$ENV_FILE"
|
||||
ok ".env created from .env.example"
|
||||
echo ""
|
||||
echo " ┌────────────────────────────────────────────────────────┐"
|
||||
echo " │ Next step: edit $ENV_FILE │"
|
||||
echo " │ │"
|
||||
echo " │ Snipe works out of the box with no API keys. │"
|
||||
echo " │ Add EBAY_APP_ID / EBAY_CERT_ID for faster searches │"
|
||||
echo " │ and full seller account age data (optional). │"
|
||||
echo " └────────────────────────────────────────────────────────┘"
|
||||
echo ""
|
||||
else
|
||||
info ".env already exists — skipping (delete it to reset)"
|
||||
fi
|
||||
|
||||
# ── Build and start ──────────────────────────────────────────────────────────
|
||||
|
||||
info "Building Docker images (first run downloads ~1 GB of dependencies)..."
|
||||
cd "$SNIPE_DIR"
|
||||
docker compose build
|
||||
|
||||
info "Starting Snipe..."
|
||||
docker compose up -d
|
||||
|
||||
echo ""
|
||||
ok "Snipe is running!"
|
||||
echo ""
|
||||
echo " Web UI: http://localhost:8509"
|
||||
echo " API: http://localhost:8510/docs"
|
||||
echo ""
|
||||
echo " Manage: cd $SNIPE_DIR && ./manage.sh {start|stop|restart|logs|test}"
|
||||
echo ""
|
||||
|
|
@ -78,7 +78,7 @@ case "$cmd" in
|
|||
test)
|
||||
echo "Running test suite..."
|
||||
docker compose -f "$COMPOSE_FILE" exec api \
|
||||
conda run -n job-seeker python -m pytest /app/snipe/tests/ -v "${@}"
|
||||
python -m pytest /app/snipe/tests/ -v "${@}"
|
||||
;;
|
||||
|
||||
# ── Cloud commands ────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Reference in a new issue