diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a82c944 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +EBAY_CLIENT_ID=your-client-id-here +EBAY_CLIENT_SECRET=your-client-secret-here +EBAY_ENV=production # or: sandbox +SNIPE_DB=data/snipe.db diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5469fbb --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +__pycache__/ +*.pyc +*.pyo +.env +*.egg-info/ +dist/ +.pytest_cache/ +data/ +.superpowers/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ccb1cb2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.11-slim + +WORKDIR /app + +# Install circuitforge-core from sibling directory (compose sets context: ..) +COPY circuitforge-core/ ./circuitforge-core/ +RUN pip install --no-cache-dir -e ./circuitforge-core + +# Install snipe +COPY snipe/ ./snipe/ +WORKDIR /app/snipe +RUN pip install --no-cache-dir -e . + +EXPOSE 8506 +CMD ["streamlit", "run", "app/app.py", "--server.port=8506", "--server.address=0.0.0.0"] diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/compose.override.yml b/compose.override.yml new file mode 100644 index 0000000..e588dbb --- /dev/null +++ b/compose.override.yml @@ -0,0 +1,8 @@ +services: + snipe: + volumes: + - ../circuitforge-core:/app/circuitforge-core + - ./app:/app/snipe/app + - ./data:/app/snipe/data + environment: + - STREAMLIT_SERVER_RUN_ON_SAVE=true diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..2e5f088 --- /dev/null +++ b/compose.yml @@ -0,0 +1,10 @@ +services: + snipe: + build: + context: .. + dockerfile: snipe/Dockerfile + ports: + - "8506:8506" + env_file: .env + volumes: + - ./data:/app/snipe/data diff --git a/manage.sh b/manage.sh new file mode 100755 index 0000000..99d7caf --- /dev/null +++ b/manage.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +SERVICE=snipe +PORT=8506 +COMPOSE_FILE="compose.yml" + +usage() { + echo "Usage: $0 {start|stop|restart|status|logs|open|update}" + exit 1 +} + +cmd="${1:-help}" +shift || true + +case "$cmd" in + start) + docker compose -f "$COMPOSE_FILE" up -d + echo "$SERVICE started on http://localhost:$PORT" + ;; + stop) + docker compose -f "$COMPOSE_FILE" down + ;; + restart) + docker compose -f "$COMPOSE_FILE" down + docker compose -f "$COMPOSE_FILE" up -d + echo "$SERVICE restarted on http://localhost:$PORT" + ;; + status) + docker compose -f "$COMPOSE_FILE" ps + ;; + logs) + docker compose -f "$COMPOSE_FILE" logs -f "${@:-$SERVICE}" + ;; + open) + xdg-open "http://localhost:$PORT" 2>/dev/null || open "http://localhost:$PORT" + ;; + update) + docker compose -f "$COMPOSE_FILE" pull + docker compose -f "$COMPOSE_FILE" up -d --build + ;; + *) + usage + ;; +esac diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b6fdb7c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,24 @@ +[build-system] +requires = ["setuptools>=68"] +build-backend = "setuptools.build_meta" + +[project] +name = "snipe" +version = "0.1.0" +description = "Auction listing monitor and trust scorer" +requires-python = ">=3.11" +dependencies = [ + "circuitforge-core", + "streamlit>=1.32", + "requests>=2.31", + "imagehash>=4.3", + "Pillow>=10.0", + "python-dotenv>=1.0", +] + +[tool.setuptools.packages.find] +where = ["."] +include = ["app*"] + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29