Tech Context — Paper Surplus Marketplace
Tech Stack
Frontend
- Framework: Angular 18+ (latest stable)
- Styling: Tailwind CSS (primary), component SCSS only when Tailwind can't handle it
- UI Library: Angular Material (form fields, dialogs, tables)
- Charts: ApexCharts (
ngx-apexcharts) — never Highcharts - Forms: Reactive Forms exclusively — never template-driven
- Components: Standalone, OnPush change detection, ViewEncapsulation.None
- State: BehaviorSubject in services, exposed via observables
- Build: Angular CLI (
ng buildfor verification)
Backend
- Framework: Django 5.x + Django REST Framework 5.x
- Database: PostgreSQL (always — never SQLite or MySQL)
- Auth: Django auth + JWT tokens (djangorestframework-simplejwt)
- Filtering: django-filter
- API Docs: drf-spectacular (OpenAPI/Swagger)
- Task Queue: Celery + Redis (for async processing — email ingestion, matching)
- Validation:
python manage.py checkfor verification
Infrastructure
- Server: Contabo VPS
- Hostname: vmi2998435.contaboserver.net
- Public IP: 77.237.235.106
- User: claude
- Process Manager: PM2 (process name:
marketplace-api) - Database: PostgreSQL on same server (db:
marketplace, user:marketplace) - Reverse Proxy: Nginx — CONFIGURED
- Config:
/etc/nginx/sites-available/b2bpaper.xdvu.com(also copy insites-enabled/) - Marketplace mounted at
/mvp/onb2bpaper.xdvu.com - Django
FORCE_SCRIPT_NAME = '/mvp' - Django
STATIC_URL = '/mvp/static/' - Static files served by nginx from
backend/staticfiles/
- Config:
- SSL: Shares b2bpaper.xdvu.com cert (Let's Encrypt via tracker.xdvu.com wildcard)
Deployment URLs
| URL | What |
|---|---|
| https://b2bpaper.xdvu.com/mvp/ | Marketplace root (redirects to Swagger) |
| https://b2bpaper.xdvu.com/mvp/api/docs/ | Swagger UI |
| https://b2bpaper.xdvu.com/mvp/admin/ | Django Admin |
| https://b2bpaper.xdvu.com/mvp/static/ | Static files |
| http://127.0.0.1:8910 | Direct gunicorn (internal only) |
Development Setup
Critical Rules
- NEVER run
ng serve— frontend dev server runs on a PM2-managed screen - NEVER run
python manage.py runserver— backend dev server runs on a PM2-managed screen - Always use
ng buildto verify frontend changes - Always use
python manage.py checkto verify backend changes
Server Access
- All URLs use public IP:
http://77.237.235.106:<PORT> - Never use
localhostin URLs shared with users - Always bind to
0.0.0.0when starting servers
Key Integrations (Planned)
Email Ingestion
- Receive Excel attachments from mills via email
- Parse with AI to extract structured surplus data
- Technology: Celery task + email client library
Excel Parsing
- Parse mill surplus spreadsheets (various formats)
- Extract: paper type, GSM, width, quantity, grade, pricing
- Technology: openpyxl or pandas
Newsletter Generation
- Automated buyer newsletters with matched surplus
- HTML email templates
- Technology: Django email + Celery scheduling
Matching Algorithm
- Spec-based matching (GSM, width, grade, geography)
- Scoring and ranking of matches
- Technology: Custom Django service + future ML integration
Container Fill Optimization (IMPLEMENTED)
- FreightRate model for LCL vs FCL cost comparison per route/container type
- Service layer at
apps/containers/services.py(first services.py in the project) - Product compatibility: paper group check, grade proximity (≤1), food contact
- Scoring: 0-100 (paper type match +20, GSM closeness +15, price advantage +15)
- Fallback freight rates from PRD Section 8.6 when no DB rate exists
- POST
/api/container-proposals/fill-suggestions/endpoint - GET/POST
/api/freight-rates/(admin CRUD, buyer read-only)
Constraints
- Single-server deployment for MVP (no Kubernetes/cloud complexity)
- Must support Excel files of varying formats (mills don't standardize)
- Geographic visibility rules at regional level (not per-buyer)
- 48-hour exclusivity windows need reliable scheduling
- Commission tracking must be accurate to the cent