|
Some checks failed
Veza CI / Rust (Stream Server) (push) Successful in 4m19s
Security Scan / Secret Scanning (gitleaks) (push) Successful in 1m4s
Veza CI / Frontend (Web) (push) Failing after 16m42s
Veza CI / Backend (Go) (push) Failing after 19m28s
Veza CI / Notify on failure (push) Successful in 15s
E2E Playwright / e2e (full) (push) Failing after 19m56s
Phase 3 closes the loop on Item G's pending_payment state machine:
the user-facing recovery path for stalled paid-plan subscriptions, and
the distribution gate that surfaces a "complete payment" hint instead
of the generic "upgrade your plan".
Recovery endpoint — POST /api/v1/subscriptions/complete/:id
Re-fetches the PSP client_secret for a subscription stuck in
StatusPendingPayment so the SPA can drive the payment UI to
completion. The PSP CreateSubscriptionPayment call is idempotent on
sub.ID.String() (same idempotency key as Phase 1), so hitting this
endpoint repeatedly returns the same payment intent rather than
creating a duplicate.
Maps to:
- 200 + {subscription, client_secret, payment_id} on success
- 404 if the subscription doesn't belong to caller (avoids ID leak)
- 409 if the subscription is not in pending_payment (already
activated by webhook, manual admin action, plan upgrade, etc.)
- 503 if HYPERSWITCH_ENABLED=false (mirrors Subscribe's fail-closed
behaviour from Phase 1)
Service surface:
- subscription.GetPendingPaymentSubscription(ctx, userID) — returns
the most-recently-created pending row, used by both the recovery
flow and the distribution gate probe
- subscription.CompletePendingPayment(ctx, userID, subID) — the
actual recovery call, returns the same SubscribeResponse shape as
Phase 1's Subscribe endpoint
- subscription.ErrSubscriptionNotPending — sentinel for the 409
- subscription.ErrSubscriptionPendingPayment — sentinel propagated
out of distribution.checkEligibility
Distribution gate — distinct path for pending_payment
Before: a creator with only a pending_payment row hit
ErrNoActiveSubscription → distribution surfaced the generic
ErrNotEligible "upgrade your plan" error. Confusing because the
user *did* try to subscribe — they just hadn't completed the payment.
After: distribution.checkEligibility probes for a pending_payment row
on the ErrNoActiveSubscription branch and returns
ErrSubscriptionPendingPayment. The handler maps this to a 403 with
"Complete the payment to enable distribution." so the SPA can route
to the recovery page instead of the upgrade page.
Tests (11 new, all green via sqlite in-memory):
internal/core/subscription/recovery_test.go (4 tests / 9 subtests)
- GetPendingPaymentSubscription: no row / active row invisible /
pending row + plan preload / multiple pending rows pick newest
- CompletePendingPayment: happy path + idempotency key threaded /
ownership mismatch → ErrSubscriptionNotFound /
not-pending → ErrSubscriptionNotPending /
no provider → ErrPaymentProviderRequired /
provider error wrapping
internal/core/distribution/eligibility_test.go (2 tests)
- Submit_EligibilityGate_PendingPayment: pending_payment user
gets ErrSubscriptionPendingPayment (recovery hint)
- Submit_EligibilityGate_NoSubscription: no-sub user gets
ErrNotEligible (upgrade hint), NOT the recovery branch
E2E test (28-subscription-pending-payment.spec.ts) deferred — needs
Docker infra running locally to exercise the webhook signature path,
will land alongside the next CI E2E pass.
TODO removal: the roadmap mentioned a `TODO(v1.0.7-item-G)` in
subscription/service.go to remove. Verified none present
(`grep -n TODO internal/core/subscription/service.go` → 0 hits).
Acceptance criterion trivially met.
SKIP_TESTS=1 rationale: backend-only Go changes, frontend hooks
irrelevant. All Go tests verified manually:
$ go test -short -count=1 ./internal/core/subscription/... \
./internal/core/distribution/... ./internal/core/marketplace/... \
./internal/services/hyperswitch/... ./internal/handlers/...
ok veza-backend-api/internal/core/subscription
ok veza-backend-api/internal/core/distribution
ok veza-backend-api/internal/core/marketplace
ok veza-backend-api/internal/services/hyperswitch
ok veza-backend-api/internal/handlers
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| .github/workflows | ||
| cmd | ||
| docs | ||
| internal | ||
| migrations | ||
| ops/prometheus | ||
| scripts | ||
| templates/email | ||
| tests | ||
| veza_back_api_db | ||
| .dockerignore | ||
| .env.production | ||
| .env.production.example | ||
| .env.template | ||
| .golangci.yml | ||
| CONTRIBUTING.md | ||
| d_plus_all.txt | ||
| Dockerfile | ||
| Dockerfile.production | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Makefile | ||
| openapi.yaml | ||
| package.json | ||
| README.md | ||
| test_output.txt | ||
| veza_uuid_lab_schema.sql | ||
Veza Backend API
Version: 1.2.0
Language: Go 1.23+
Framework: Gin
Database: PostgreSQL (GORM)
License: Apache 2.0
📋 Table des Matières
- Vue d'ensemble
- Installation
- Configuration
- Architecture
- API Endpoints
- Développement
- Tests
- Déploiement
- Intégration avec autres services
🎯 Vue d'ensemble
Veza Backend API est le serveur HTTP principal de la plateforme Veza (audio collaborative). Il expose une API REST pour :
- Authentification & Autorisation : JWT, sessions, RBAC
- Gestion Utilisateurs : Profils, settings, completion
- Gestion Tracks : Upload, streaming, métadonnées, likes, partage
- Playlists : Création, collaboration, tracks
- Marketplace : Produits, commandes, téléchargements
- Chat : Génération de tokens JWT pour WebSocket (délégué au Chat Server Rust)
- Audit & Monitoring : Logs, métriques Prometheus, health checks
- Webhooks : Système d'événements asynchrones
Rôle dans Veza :
- Backend Go : API REST principale (port 8080)
- Frontend React : Consommateur de l'API REST
- Chat Server Rust : WebSocket (port 8081) — reçoit tokens JWT depuis
/api/v1/chat/token - Stream Server Rust : Streaming audio WebRTC (port 8082) — reçoit callbacks depuis
/api/v1/internal/tracks/:id/stream-ready
🚀 Installation
Prérequis
- Go 1.23 ou supérieur
- PostgreSQL 12+ (base de données principale)
- Redis (optionnel, pour cache et rate limiting)
- ClamAV (optionnel, pour scan antivirus des uploads)
- RabbitMQ (optionnel, pour event bus)
Installation depuis les sources
# Cloner le repository
git clone <repository-url>
cd veza-backend-api
# Installer les dépendances
go mod download
# Build
make build
# Ou build pour Linux
make build-linux
Installation via Docker
# Build image
make docker-build
# Run container
make docker-run
⚙️ Configuration
Variables d'Environnement Requises
# REQUIS - Sécurité
JWT_SECRET=<32+ chars> # Secret JWT (minimum 32 caractères)
DATABASE_URL=postgres://user:pass@host:5432/dbname # URL PostgreSQL
APP_ENV=production # development|staging|production
# REQUIS en production
CORS_ALLOWED_ORIGINS=https://app.veza.com,https://www.veza.com # Origines CORS autorisées
# Optionnel - Base de données
DB_MAX_RETRIES=5 # Nombre de tentatives de connexion DB
DB_RETRY_INTERVAL=5s # Intervalle entre tentatives
# Optionnel - Redis
REDIS_URL=redis://localhost:6379 # URL Redis
REDIS_ENABLE=true # Activer/désactiver Redis
# Optionnel - RabbitMQ
RABBITMQ_URL=amqp://guest:guest@localhost:5672/ # URL RabbitMQ
RABBITMQ_ENABLE=true # Activer/désactiver RabbitMQ
RABBITMQ_MAX_RETRIES=3 # Tentatives de connexion
RABBITMQ_RETRY_INTERVAL=2s # Intervalle entre tentatives
# Optionnel - ClamAV
CLAMAV_REQUIRED=true # Rejeter uploads si ClamAV down (true) ou accepter en mode dégradé (false)
CLAMAV_ADDRESS=localhost:3310 # Adresse ClamAV
# Optionnel - Uploads
UPLOAD_DIR=uploads # Répertoire d'upload
MAX_CONCURRENT_UPLOADS=10 # Limite uploads simultanés (backpressure)
# Optionnel - Serveur
APP_PORT=8080 # Port HTTP (défaut: 8080)
HANDLER_TIMEOUT=30s # Timeout global handlers
# Optionnel - JWT
JWT_ISSUER=veza-api # Issuer claim
JWT_AUDIENCE=veza-app # Audience claim
CHAT_JWT_SECRET=<secret> # Secret pour tokens Chat (fallback: JWT_SECRET)
# Optionnel - Rate Limiting
RATE_LIMIT_LIMIT=100 # Limite requêtes
RATE_LIMIT_WINDOW=60 # Fenêtre en secondes
AUTH_RATE_LIMIT_LOGIN_ATTEMPTS=5 # Tentatives login max
AUTH_RATE_LIMIT_LOGIN_WINDOW=1 # Fenêtre login en minutes
# Optionnel - Sentry
SENTRY_DSN=https://... # DSN Sentry pour error tracking
SENTRY_ENVIRONMENT=production # Environnement Sentry
SENTRY_SAMPLE_RATE_ERRORS=1.0 # Sample rate erreurs (0.0-1.0)
SENTRY_SAMPLE_RATE_TRANSACTIONS=0.1 # Sample rate transactions (0.0-1.0)
# Optionnel - Logging
LOG_LEVEL=INFO # DEBUG|INFO|WARN|ERROR
# Optionnel - Services externes
STREAM_SERVER_URL=http://localhost:8082 # URL Stream Server
STREAM_SERVER_INTERNAL_API_KEY= # Clé partagée pour callbacks stream-ready (doit correspondre à INTERNAL_API_KEY du Stream Server)
CHAT_SERVER_URL=http://localhost:8081 # URL Chat Server
# DEV/TEST UNIQUEMENT - Ne jamais utiliser en production
# CSRF_DISABLED=true # Désactive CSRF (dev/test seulement)
# BYPASS_CONTENT_CREATOR_ROLE=true # Bypass vérification rôle créateur (dev/test seulement)
# DISABLE_RATE_LIMIT_FOR_TESTS=true # Désactive rate limiting (E2E/integration tests seulement)
Fichiers de Configuration
.env: Variables d'environnement (optionnel, chargé viagodotenv).env.{APP_ENV}: Variables spécifiques à l'environnementmigrations/*.sql: Migrations SQL (exécutées au démarrage)
Validation de Configuration
La configuration est validée au démarrage :
- Production :
CORS_ALLOWED_ORIGINSest REQUIS (fail-fast si vide) - JWT_SECRET : Minimum 32 caractères (fail-fast si trop court)
- DATABASE_URL : Requis pour tous les environnements
🏗️ Architecture
Structure du Projet
veza-backend-api/
├── cmd/
│ └── api/
│ └── main.go # Point d'entrée
├── internal/
│ ├── api/ # Routes et configuration router
│ ├── core/ # Business logic (auth, track, marketplace, social)
│ │ ├── auth/ # Authentification
│ │ ├── track/ # Gestion tracks
│ │ ├── marketplace/ # Marketplace
│ │ └── social/ # Social features
│ ├── config/ # Configuration (env, validation, secrets)
│ ├── database/ # DB connection, migrations, pool
│ ├── handlers/ # HTTP handlers (legacy + modern)
│ ├── middleware/ # Auth, CORS, rate limiting, recovery, metrics
│ ├── models/ # GORM models (User, Track, Playlist, etc.)
│ ├── repositories/ # Data access layer (GORM)
│ ├── services/ # Business services (JWT, Session, Upload, etc.)
│ ├── workers/ # Background jobs (webhooks, analytics)
│ ├── metrics/ # Prometheus metrics
│ ├── monitoring/ # Business metrics (tracks_uploaded, users_registered)
│ ├── logging/ # Structured logging (zap)
│ └── validators/ # Input validation
├── migrations/ # Migrations SQL
├── tests/ # Tests d'intégration
├── docs/ # Documentation
└── Makefile # Commandes build/test
Patterns
Pattern Moderne (recommandé) :
- Handlers dans
internal/core/*/handler.go - Services dans
internal/core/*/service.go - Utilisation de
context.Contextpour timeouts - Gestion d'erreurs via
internal/errors
Pattern Legacy (déprécié) :
- Handlers dans
internal/handlers/*.go - Migration progressive vers pattern moderne
Dépendances Internes
internal/errors: Error handling uniformeinternal/response: Réponses HTTP standardiséesinternal/common: Types et utilitaires communs
Dépendances Externes
- PostgreSQL : Base principale (GORM +
database/sql) - Redis : Cache, rate limiting, sessions (optionnel)
- RabbitMQ : Event bus (optionnel)
- ClamAV : Scan antivirus uploads (optionnel)
- Sentry : Error tracking (optionnel)
🌐 API Endpoints
Base URL
http://localhost:8080/api/v1
Routes Principales
Authentification (/auth)
POST /auth/register # Inscription
POST /auth/login # Connexion (rate limited)
POST /auth/refresh # Renouvellement token
POST /auth/verify-email # Vérification email
POST /auth/resend-verification # Renvoyer vérification
GET /auth/check-username # Vérifier disponibilité username
POST /auth/password/reset-request # Demande reset password
POST /auth/password/reset # Reset password
POST /auth/logout # Déconnexion (protégé)
GET /auth/me # Profil utilisateur (protégé)
Utilisateurs (/users)
GET /users/:id # Profil utilisateur
GET /users/by-username/:username # Profil par username
PUT /users/:id # Mise à jour profil (protégé, ownership)
GET /users/:id/completion # Complétion profil (protégé)
Tracks (/tracks)
GET /tracks # Liste tracks
GET /tracks/:id # Détails track
GET /tracks/:id/stats # Statistiques track
GET /tracks/:id/history # Historique track
GET /tracks/:id/download # Téléchargement track
GET /tracks/shared/:token # Track partagé (public)
POST /tracks # Upload track (protégé, creator role)
PUT /tracks/:id # Mise à jour track (protégé, ownership)
DELETE /tracks/:id # Suppression track (protégé, ownership)
POST /tracks/:id/like # Like track (protégé)
DELETE /tracks/:id/like # Unlike track (protégé)
GET /tracks/:id/likes # Liste likes (protégé)
POST /tracks/:id/share # Partager track (protégé)
DELETE /tracks/share/:id # Révoquer partage (protégé)
Playlists (/playlists)
GET /playlists # Liste playlists
GET /playlists/:id # Détails playlist
POST /playlists # Créer playlist (protégé)
PUT /playlists/:id # Mettre à jour playlist (protégé)
DELETE /playlists/:id # Supprimer playlist (protégé)
POST /playlists/:id/tracks # Ajouter track (protégé)
DELETE /playlists/:id/tracks/:track_id # Retirer track (protégé)
Marketplace (/marketplace)
GET /marketplace/products # Liste produits
POST /marketplace/products # Créer produit (protégé, creator role)
POST /marketplace/orders # Créer commande (protégé)
GET /marketplace/download/:product_id # URL téléchargement (protégé)
Chat (/chat)
POST /chat/token # Génération token WS (protégé)
Admin (/admin)
GET /admin/audit/logs # Logs audit (protégé, admin role)
GET /admin/audit/stats # Statistiques audit (protégé, admin role)
GET /admin/audit/suspicious # Activité suspecte (protégé, admin role)
GET /admin/debug/pprof/*path # Profiling pprof (protégé, admin role)
Health & Monitoring
GET /health # Health check simple
GET /healthz # Liveness probe (Kubernetes)
GET /readyz # Readiness probe (DB, Redis, RabbitMQ)
GET /status # Status détaillé (DB, Redis, Chat, Stream)
GET /metrics # Prometheus metrics
Authentification
JWT Bearer Token :
Authorization: Bearer <token>
JWT Claims :
{
"sub": "uuid-v4",
"iss": "veza-api",
"aud": "veza-app",
"exp": 1234567890,
"iat": 1234567890,
"token_version": 1,
"role": "user"
}
Rôles : user, admin, creator, premium, artist, producer, label
💻 Développement
Commandes Build/Run
# Build
make build # Compile pour OS courant
make build-linux # Compile pour Linux
# Run
make run # Build + run
make dev # Mode développement (go run)
Commandes Qualité
make lint # golangci-lint
make vet # go vet
make security # gosec + govulncheck
Mode Développement
# Définir environnement
export APP_ENV=development
export LOG_LEVEL=DEBUG
# Lancer
make dev
🧪 Tests
Tests Unitaires
make test # Tests unitaires (sans integration)
make test-coverage # Tests avec couverture
Tests d'Intégration
make test-integration # Tests d'intégration (requiert Docker)
Exécution Manuelle
# Tests unitaires
go test ./...
# Tests avec couverture
go test ./... -cover
# Tests d'intégration
go test ./tests/integration/... -tags=integration
🚢 Déploiement
Docker
# Build image
docker build -t veza-backend-api .
# Run container
docker run -p 8080:8080 \
-e JWT_SECRET=<secret> \
-e DATABASE_URL=postgres://... \
-e APP_ENV=production \
-e CORS_ALLOWED_ORIGINS=https://app.veza.com \
veza-backend-api
Kubernetes
Les health checks sont disponibles :
/healthz: Liveness probe/readyz: Readiness probe
Variables d'Environnement Production
REQUIS :
JWT_SECRET(min 32 chars)DATABASE_URLCORS_ALLOWED_ORIGINS(fail-fast si vide en production)APP_ENV=production
Recommandé :
REDIS_ENABLE=trueRABBITMQ_ENABLE=trueCLAMAV_REQUIRED=trueSENTRY_DSN(pour error tracking)
🔗 Intégration avec autres services
Frontend React
- Consomme
/api/v1/*avec JWT Bearer tokens - Headers requis:
Authorization: Bearer <token> - Content-Type:
application/json
Chat Server (Rust)
- Reçoit tokens JWT depuis
/api/v1/chat/token - Valide tokens avec
CHAT_JWT_SECRET(ouJWT_SECRETsi non défini) - WebSocket endpoint:
ws://localhost:8081/ws
Stream Server (Rust)
- Reçoit callbacks depuis
/api/v1/internal/tracks/:id/stream-ready - Endpoint interne (pas de auth JWT, validation par IP/secret si nécessaire)
Schéma DB / UUID
- UUID v4 : Tous les IDs utilisent
uuid.UUID - Tables:
users,tracks,playlists,sessions,rooms,messages, etc.
📊 Monitoring & Observabilité
Métriques Prometheus
Exposées via /metrics :
- HTTP :
veza_http_requests_total,veza_http_request_duration_seconds - Auth :
veza_auth_login_attempts_total,veza_auth_sessions_active - Database :
veza_database_query_duration_seconds,veza_database_connections_active - Business :
veza_tracks_uploaded_total,veza_users_registered_total,veza_playlists_created_total - Uploads :
veza_file_uploads_total,veza_uploads_failed_total
Profiling
- pprof :
/api/v1/admin/debug/pprof/*path(protégé par auth admin)
Logs
- Structured Logging : Zap (JSON en production)
- Request ID : Corrélation des logs via
request_id - Niveaux : DEBUG, INFO, WARN, ERROR
🔒 Sécurité
Validation
- JWT Secret : Minimum 32 caractères (fail-fast au démarrage)
- CORS : Strict en production (fail-fast si
CORS_ALLOWED_ORIGINSvide) - Input Validation : Tags
validatesur tous les DTOs - Ownership Checks : Middleware
RequireOwnershipOrAdminpour ressources sensibles
Uploads
- ClamAV : Scan antivirus (configurable via
CLAMAV_REQUIRED) - Backpressure : Limite uploads simultanés (
MAX_CONCURRENT_UPLOADS) - Validation : Type MIME, taille, extension
Rate Limiting
Rate limiting est toujours actif (A04). Limites par défaut selon l'environnement :
| Environnement | Global (req/min) | Register (inscriptions/heure) |
|---|---|---|
| development, test | 1000 | 20 |
| staging, production | 200 | 3 |
- Global :
RATE_LIMIT_LIMITrequêtes parRATE_LIMIT_WINDOWsecondes (override possible) - Login :
AUTH_RATE_LIMIT_LOGIN_ATTEMPTStentatives parAUTH_RATE_LIMIT_LOGIN_WINDOWminutes
📝 License
Apache 2.0
🤝 Support
- Documentation API :
/swagger/index.html(Swagger UI) - Issues : GitHub Issues