Plain redirectToRoute() bypasses EasyAdmin's AdminRouterSubscriber, so the admin context (ea.i18n etc.) was never set and the EasyAdmin layout threw "Impossible to access i18n on null". Using AdminUrlGenerator wraps the redirect URL in the EasyAdmin routing layer, keeping context alive. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .gitea/workflows | ||
| bin | ||
| config | ||
| docker | ||
| docs/superpowers | ||
| migrations | ||
| public | ||
| src | ||
| templates | ||
| tests | ||
| translations | ||
| .editorconfig | ||
| .env | ||
| .env.dev | ||
| .env.test | ||
| .gitignore | ||
| .php-cs-fixer.dist.php | ||
| .php-cs-fixer.php | ||
| composer.json | ||
| composer.lock | ||
| docker-compose.override.yml | ||
| docker-compose.yml | ||
| phpstan.neon | ||
| phpunit.dist.xml | ||
| phpunit.xml.dist | ||
| README.md | ||
| symfony.lock | ||
SuperSeller3000
Admin middleware for managing and selling refurbished IT hardware. Handles article ingestion via AI pipeline (photo → vision → specs → eBay text), multi-channel publishing (eBay), and order processing with Frappe ERP invoicing.
Live: https://ss3k.schaunwama.de/admin
Full design doc: docs/superpowers/specs/2026-05-13-superseller3000-design.md
Quick Start
cp .env .env.local # fill in secrets (DB, Redis, AI keys, eBay, Frappe)
docker compose up -d
docker compose exec app php bin/console doctrine:migrations:migrate --env=prod
docker compose exec app php bin/console app:users:create --env=prod
Workers start automatically via Docker Compose (worker-ai, worker-orders, worker-channel).
Tech Stack
| Layer | Technology |
|---|---|
| Language / Framework | PHP 8.4 · Symfony 8.4 |
| ORM | Doctrine ORM |
| Database | PostgreSQL 17 — schemas: app, logs, logs_archive |
| Queue | Symfony Messenger + Redis Streams |
| AI | Ollama (local, SSH-tunnel) or Mistral Cloud API — alias-switchable in services.yaml |
| Web Search | Tavily API (TAVILY_API_KEY) |
| Admin | EasyAdmin 5 |
| Auth | Symfony Security + TOTP 2FA (scheb/2fa-totp) |
| Proxy | Caddy 2 (Auto-HTTPS) |
Running Tests
# Unit tests (fast, no external deps)
docker compose exec app php vendor/bin/phpunit tests/Unit/ --testdox
# Integration tests (requires staging credentials in .env.local)
bin/test-integration
# Single integration file
bin/test-integration tests/Integration/path/to/Test.php
# Static analysis + code style
docker compose exec app composer phpstan
docker compose exec app composer cs-check
AI Backend
Switch between Ollama and Mistral in config/services.yaml:
# Ollama (default — local via SSH tunnel)
App\Infrastructure\AI\OllamaClientInterface:
alias: App\Infrastructure\AI\OllamaClient
# Mistral Cloud
App\Infrastructure\AI\OllamaClientInterface:
alias: App\Infrastructure\AI\MistralClient
Set models via env vars: AI_TEXT_MODEL, AI_VISION_MODEL. After switching: docker compose exec app php bin/console cache:clear.
Admin Panel Features
- Articles — list (click row → detail view, Edit as action), ingest form with stock quantity, attribute validation with required-field highlighting
- AI Pipeline — per-job progress view with step tracking; failed jobs show the real error; re-run AI from article detail
- Article Types — configurable attribute schemas (name, type, unit, options, required flag); attributes drive specs-research field list
- Users — permission checkboxes per user (
ARTICLES_MANAGE,PIPELINE_RUN,ORDERS_MANAGE, etc.) - Orders / Customers / Invoices — full read/manage view
- Prompt Templates — editable via admin (DB-backed,
{{variable}}substitution)
Manual Article Ingest
Admin → New Article (or via API):
curl -X POST https://ss3k.schaunwama.de/api/pipeline/photo-upload \
-H "X-Api-Key: <key>" \
-F "articleTypeId=<uuid>" \
-F "condition=good" \
-F "stock=1" \
-F "photo=@/path/to/photo.jpg"
Pipeline: Photo → OllamaVision (model number) → DB model cache check → (cache hit: copy + done) / (cache miss: Tavily search → JsonCoding → Validation → Draft → eBay text)
API Keys
Generated via console only (raw key shown once):
docker compose exec app php bin/console app:api-keys:create --env=prod
Use as X-Api-Key: <rawKey> header.
Architecture
Hexagonal (Domain / Application / Infrastructure). See design doc for full details.
src/
Domain/ # Pure PHP — Article, ArticleType, Order, Customer, AIPipelineJob …
Application/ # Use cases, orchestration via interfaces
Infrastructure/
AI/ # OllamaClient, MistralClient, 4 AI agents
Channel/ # EbayAdapter, FrappeErpAdapter
Search/ # TavilyWebSearch
Persistence/ # Doctrine repositories (PostgreSQL)
Messenger/ # Message classes + handlers (3 queues: ai_pipeline, orders, channel_sync)
Http/ # Symfony controllers, EasyAdmin CRUD, webhooks
All plans (1–6) complete. Active development tracked via Claude Code sessions.