Self-hosted P&L tracking app with component-level pricing. Offers: Atlas/Atlas+/Rif/Rif+ with granular cost breakdown. API + MCP + multi-user auth.
122 lines
4.2 KiB
Markdown
122 lines
4.2 KiB
Markdown
# Vela Platform 🏔️
|
|
|
|
A lightweight self-hosted P&L tracking app for a C2C server/storage business.
|
|
|
|
## Stack
|
|
- Python 3.11+ with FastAPI + uvicorn
|
|
- SQLite via SQLAlchemy (single file, zero setup)
|
|
- Jinja2 templates + Bootstrap 5 dark frontend
|
|
- Chart.js for trend charts
|
|
- JWT auth (cookie + Bearer header)
|
|
- stdio MCP server for Hermes integration
|
|
|
|
## Data Model
|
|
|
|
### User
|
|
- id, username, password_hash, display_name, role (admin|viewer)
|
|
|
|
### Service (Offer)
|
|
- id, name (Atlas/Atlas+/Rif/Rif+), description
|
|
- sell_price (computed from components), cost_price (computed from components)
|
|
- active
|
|
|
|
### ServiceComponent
|
|
- id, service_id (FK), name (RAM/HDD/SSD/etc.), unit_cost, unit_sell, quantity, notes
|
|
- `recompute_prices()` method sums cost/sell from components
|
|
|
|
### Transaction
|
|
- id, service_id (FK), quantity, revenue (calculated: qty * service.sell_price), cost (calculated: qty * service.cost_price), month (YYYY-MM), notes, created_by (FK user), created_at
|
|
|
|
### Monthly P&L (computed)
|
|
- month, total_revenue, total_cost, gross_profit, gross_margin%, opex (800 MAD), net_profit, net_margin%
|
|
|
|
## Preseed Data
|
|
- Users: admin (password: admin123), viewer (password: viewer123)
|
|
- Services with components (see COMPONENT_TEMPLATES in main.py)
|
|
- Opex: 800 MAD/month hardcoded
|
|
|
|
## Key Files
|
|
|
|
| Path | What |
|
|
|------|------|
|
|
| `backend/main.py` | FastAPI app (API + frontend routes) |
|
|
| `backend/models.py` | SQLAlchemy models |
|
|
| `backend/auth.py` | JWT auth, cookie support |
|
|
| `backend/database.py` | SQLite setup |
|
|
| `backend/templates/` | Jinja2 templates |
|
|
| `mcp/server.py` | MCP stdio server |
|
|
| `CLAUDE.md` | This file |
|
|
|
|
## API Endpoints
|
|
|
|
### Auth
|
|
- POST /api/auth/login -> {token, user} + set cookie
|
|
- GET /api/auth/me -> current user
|
|
|
|
### Services
|
|
- GET /api/services -> list with components
|
|
- GET /api/services/{id} -> single with components
|
|
- POST /api/services -> create (admin)
|
|
- PUT /api/services/{id} -> update (admin)
|
|
- DELETE /api/services/{id} -> deactivate (admin)
|
|
|
|
### Components
|
|
- GET /api/services/{id}/components -> list
|
|
- POST /api/services/{id}/components -> create (admin, JSON body)
|
|
- PUT /api/components/{id} -> update (admin, JSON body) — auto-recomputes prices
|
|
- DELETE /api/components/{id} -> delete (admin) — auto-recomputes prices
|
|
|
|
### Transactions
|
|
- GET /api/transactions?month=YYYY-MM -> list
|
|
- POST /api/transactions -> create {service_id, quantity, month, notes}
|
|
- DELETE /api/transactions/{id} -> (admin)
|
|
|
|
### P&L
|
|
- GET /api/pnl?month=YYYY-MM -> computed P&L
|
|
- GET /api/dashboard -> current + 12-month trend
|
|
|
|
## MCP Server (stdio)
|
|
|
|
Located at `mcp/server.py`. Run as a stdio MCP server via `python3 mcp/server.py`.
|
|
|
|
Tools: get_pnl, get_services, get_service, add_component, update_component, delete_component, add_transaction, get_dashboard
|
|
Resources: vela://pnl/current, vela://services, vela://services/{id}
|
|
|
|
## URLs
|
|
- Web app: http://192.168.1.30:8788
|
|
- MCP: registered as `vela-platform` in Hermes config
|
|
|
|
## Component Pricing Templates
|
|
|
|
Each offer has its own set of components. The total sell/cost is auto-computed:
|
|
|
|
### Atlas (1TB Storage)
|
|
HDD(1TB) 600/1000, RAM(16GB) 300/500, CPU alloc 200/400, Casing 200/300, Bandwidth 200/500, Setup 300/600, Support 200/700
|
|
→ Total: cost 2000, sell 4000
|
|
|
|
### Atlas+ (1TB + Backup)
|
|
HDD(1TB) 600/1000, SSD(256GB) 400/700, RAM(32GB) 600/1000, CPU alloc 300/600, Casing 200/300, Bandwidth 200/500, Sauvegarde 300/500, Setup 200/400, Support 200/700
|
|
→ Total: cost 2800, sell 5000
|
|
|
|
### Rif (500GB Server)
|
|
SSD(500GB) 300/600, RAM(16GB) 300/500, CPU alloc 200/400, Casing 200/300, Bandwidth 200/500, Setup 300/600, Support 200/700
|
|
→ Total: cost 1700, sell 3500
|
|
|
|
### Rif+ (500GB + Backup)
|
|
SSD(500GB) 300/600, HDD(1TB) 600/1000, RAM(32GB) 600/1000, CPU alloc 300/600, Casing 200/300, Bandwidth 200/500, Sauvegarde 300/500, Setup 200/400, Support 200/700
|
|
→ Total: cost 2200, sell 4500
|
|
|
|
## Deployment
|
|
```bash
|
|
cd ~/bestof-manager/backend && uvicorn main:app --host 0.0.0.0 --port 8788
|
|
# Or via systemd:
|
|
systemctl --user restart bestof-manager.service
|
|
```
|
|
|
|
## Auth Flow
|
|
- Page routes use cookie auth (httpOnly 'token' cookie set on login)
|
|
- API routes use Bearer token from Authorization header
|
|
- Login sets both cookie AND returns JSON token
|
|
- Token expires after 24h
|
|
- Pages without valid cookie redirect to /login
|