Deploiement Scell.io
Version: 1.0 Date: 2026-03-03
Table des matieres
- Infrastructure
- Prerequis
- Deploiement Backend (api.scell.io)
- Deploiement Frontend (scell.io)
- Variables d'Environnement
- Monitoring
- Troubleshooting
1. Infrastructure
Architecture de deploiement
graph TB
subgraph "Internet"
U["Utilisateurs"]
AI["Agents IA (MCP)"]
SPDP["SuperPDP"]
OA["OpenAPI.com"]
end
subgraph "Ploi.io (Scaleway)"
subgraph "scell.io"
FE["Frontend React<br/>Static (Vite build)<br/>dist/"]
end
subgraph "api.scell.io"
BE["Laravel 12<br/>PHP 8.2+<br/>backend/public/"]
HZ["Horizon<br/>(Queue Worker)"]
end
subgraph "Services Manages (Scaleway)"
PG["PostgreSQL 17<br/>(base principale)"]
PGS["PostgreSQL 17<br/>(base sandbox)"]
RD["Redis 7.4<br/>(cache + sessions + queue)"]
S3["Scaleway S3<br/>(stockage factures/docs)"]
end
end
U --> FE
U --> BE
AI -->|"MCP JSON-RPC"| BE
SPDP -->|"Webhooks"| BE
OA -->|"Webhooks"| BE
BE --> PG
BE --> PGS
BE --> RD
BE --> S3
BE -->|"API"| SPDP
BE -->|"API"| OA
HZ --> PG
HZ --> RDComposants
| Composant | Domaine | Type | Stack |
|---|---|---|---|
| Frontend | scell.io | Site statique | React 19 + Vite (dist/) |
| Backend | api.scell.io | Application PHP | Laravel 12 + PHP 8.2 |
| Base de donnees | - | Service manage | PostgreSQL 17 (Scaleway) |
| Cache/Queue | - | Service manage | Redis 7.4 (Scaleway) |
| Stockage | - | Service manage | Scaleway S3 (fr-par) |
Hebergement Ploi.io
Ploi.io gere :
- Le provisionning des serveurs Scaleway
- Les certificats SSL Let's Encrypt (renouvellement automatique)
- Le deploiement automatise via webhook Git
- La supervision des processus (PHP-FPM, Horizon)
Serveur : Scaleway DEV1-M (3 vCPU, 4GB RAM) SSH : ssh ploi@51.15.234.246Chemins :
| Site | Chemin sur le serveur |
|---|---|
| Backend | /home/ploi/api.scell.io/backend |
| Frontend | /home/ploi/scell.io/frontend |
2. Prerequis
Backend
| Composant | Version minimale | Verification |
|---|---|---|
| PHP | 8.2+ | php -v |
| Composer | 2.x | composer --version |
| PostgreSQL | 17 | psql --version |
| Redis | 7.4 | redis-cli --version |
| wkhtmltopdf | 0.12+ | wkhtmltopdf --version |
Extensions PHP requises :
bcmath, ctype, curl, dom, fileinfo, gd, intl, json, mbstring,
openssl, pdo, pdo_pgsql, redis, tokenizer, xml, zipFrontend
| Composant | Version minimale | Verification |
|---|---|---|
| Node.js | 20+ | node --version |
| npm | 10+ | npm --version |
Certificats SSL
Les certificats Let's Encrypt sont geres automatiquement par Ploi.io :
/etc/letsencrypt/live/api.scell.io/fullchain.pem
/etc/letsencrypt/live/api.scell.io/privkey.pem3. Deploiement Backend (api.scell.io)
Script de deploiement
#!/bin/bash
# deploy-backend.sh
set -e
cd /home/ploi/api.scell.io/backend
# 1. Mettre le site en maintenance
php artisan down --render="errors::503" --retry=60
# 2. Recuperer les derniers changements
git pull origin main
# 3. Installer les dependances (production)
composer install --no-dev --optimize-autoloader --no-interaction
# 4. Executer les migrations
php artisan migrate --force
# 5. Mettre en cache la configuration
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
# 6. Redemarrer les workers de queue
php artisan horizon:terminate
# Horizon sera redemarre automatiquement par le superviseur
# 7. Nettoyer les caches obsoletes
php artisan cache:clear
# 8. Remettre le site en ligne
php artisan up
echo "Deploiement backend termine avec succes."Commandes post-deploiement
# Verifier que Horizon tourne
php artisan horizon:status
# Verifier la sante de l'API
curl -s https://api.scell.io/health | jq .
# Verifier les migrations
php artisan migrate:statusConfiguration Nginx (reference)
Le fichier nginx.conf.example a la racine du projet contient la configuration de reference :
server {
listen 443 ssl http2;
server_name api.scell.io;
root /home/ploi/api.scell.io/backend/public;
index index.php;
ssl_certificate /etc/letsencrypt/live/api.scell.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.scell.io/privkey.pem;
# CORS headers
add_header Access-Control-Allow-Origin "https://scell.io" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-API-Key, X-Tenant-Key, X-API-Key, X-Scell-API-Key, mcp-session-id" always;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# SSE pour MCP streaming
location /mcp/stream {
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
try_files $uri /index.php?$query_string;
}
}Superviseur Horizon
Ploi.io configure automatiquement le superviseur pour Horizon :
[program:horizon]
process_name=%(program_name)s
command=php /home/ploi/api.scell.io/backend/artisan horizon
autostart=true
autorestart=true
user=ploi
redirect_stderr=true
stdout_logfile=/home/ploi/api.scell.io/backend/storage/logs/horizon.log
stopwaitsecs=3600Taches planifiees (Cron)
# Ajouter au crontab de l'utilisateur ploi
* * * * * cd /home/ploi/api.scell.io/backend && php artisan schedule:run >> /dev/null 2>&1Taches planifiees automatiques :
| Commande | Frequence | Description |
|---|---|---|
fiscal:daily-closing | Quotidien 00:05 UTC | Cloture fiscale journaliere |
fiscal:monthly-closing | 1er du mois | Cloture fiscale mensuelle |
fiscal:integrity-check | Quotidien 02:00 UTC | Verification d'integrite |
fiscal:check-version-change | Quotidien | Detection changement de version |
billing:generate-monthly-invoices | 1er du mois | Factures tenants |
superpdp:sync-statuses | Toutes les 5 min | Synchronisation statuts |
horizon:snapshot | Toutes les 5 min | Metriques Horizon |
4. Deploiement Frontend (scell.io)
Script de deploiement
#!/bin/bash
# deploy-frontend.sh
set -e
cd /home/ploi/scell.io/frontend
# 1. Recuperer les derniers changements
git pull origin main
# 2. Installer les dependances
npm ci
# 3. Build de production
npm run build
echo "Deploiement frontend termine. Le dossier dist/ est pret."Le dossier dist/ genere par Vite est servi directement par Nginx comme site statique.
Configuration Nginx
server {
listen 443 ssl http2;
server_name scell.io www.scell.io;
root /home/ploi/scell.io/frontend/dist;
index index.html;
ssl_certificate /etc/letsencrypt/live/scell.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/scell.io/privkey.pem;
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# Cache des assets statiques
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}Variables d'environnement frontend
Le fichier .env.production :
VITE_API_URL=https://api.scell.io/api/v1
VITE_APP_NAME=Scell.io
VITE_REVERB_APP_KEY=scell-key
VITE_REVERB_HOST=api.scell.io
VITE_REVERB_PORT=443
VITE_REVERB_SCHEME=httpsCes variables sont integrees au build Vite (prefixe VITE_).
5. Variables d'Environnement
Backend (backend/.env)
Application
| Variable | Description | Exemple |
|---|---|---|
APP_NAME | Nom de l'application | Scell.io |
APP_ENV | Environnement | production |
APP_KEY | Cle de chiffrement (generer avec php artisan key:generate) | base64:... |
APP_DEBUG | Mode debug (toujours false en production) | false |
APP_TIMEZONE | Fuseau horaire | Europe/Paris |
APP_URL | URL de base de l'API | https://api.scell.io |
APP_LOCALE | Langue par defaut | fr |
Base de donnees
| Variable | Description | Exemple |
|---|---|---|
DB_CONNECTION | Driver de BDD | pgsql |
DB_HOST | Hote PostgreSQL | rdb-xxx.scw.cloud |
DB_PORT | Port | 5432 |
DB_DATABASE | Nom de la base | scell_production |
DB_USERNAME | Utilisateur | scell |
DB_PASSWORD | Mot de passe | *** |
DB_HOST_SANDBOX | Hote BDD sandbox (optionnel) | 127.0.0.1 |
DB_DATABASE_SANDBOX | Base sandbox | scell_sandbox |
Cache et Queue
| Variable | Description | Exemple |
|---|---|---|
CACHE_STORE | Driver de cache | redis |
CACHE_PREFIX | Prefixe des cles cache | scell_ |
QUEUE_CONNECTION | Driver de queue | redis |
SESSION_DRIVER | Driver de session | redis |
REDIS_HOST | Hote Redis | redis-xxx.scw.cloud |
REDIS_PORT | Port Redis | 6379 |
REDIS_PASSWORD | Mot de passe Redis | *** |
HORIZON_PREFIX | Prefixe Horizon | scell_horizon: |
Mail
| Variable | Description | Exemple |
|---|---|---|
MAIL_MAILER | Driver mail | smtp |
MAIL_HOST | Serveur SMTP | smtp.tem.scw.cloud |
MAIL_PORT | Port SMTP | 587 |
MAIL_USERNAME | Utilisateur SMTP | *** |
MAIL_PASSWORD | Mot de passe SMTP | *** |
MAIL_ENCRYPTION | Chiffrement | tls |
MAIL_FROM_ADDRESS | Adresse d'envoi | noreply@scell.io |
Services externes
| Variable | Description | Exemple |
|---|---|---|
GOOGLE_CLIENT_ID | OAuth Google | xxx.apps.googleusercontent.com |
GOOGLE_CLIENT_SECRET | Secret Google OAuth | *** |
OPENAPI_BASE_URL | URL OpenAPI.com | https://esignature.openapi.com |
OPENAPI_SANDBOX_URL | URL sandbox OpenAPI | https://test.esignature.openapi.com |
OPENAPI_BEARER_TOKEN | Token OpenAPI.com | *** |
SUPERPDP_API_URL | URL SuperPDP | https://api.superpdp.tech |
SUPERPDP_API_KEY | Cle API SuperPDP | *** |
SUPERPDP_WEBHOOK_SECRET | Secret webhooks SuperPDP | *** |
SUPERPDP_SANDBOX | Mode sandbox SuperPDP | false |
BULKGATE_APPLICATION_ID | ID app BulkGate (SMS) | *** |
BULKGATE_APPLICATION_TOKEN | Token BulkGate | *** |
INSEE (verification SIRET)
| Variable | Description | Exemple |
|---|---|---|
INSEE_API_URL | URL API Sirene | https://api.insee.fr/entreprises/sirene/V3.11 |
INSEE_CLIENT_ID | ID client INSEE | *** |
INSEE_CERT_PATH | Certificat mTLS | storage/certs/insee-client.pem |
INSEE_KEY_PATH | Cle privee mTLS | storage/certs/insee-client.key |
Stockage S3
| Variable | Description | Exemple |
|---|---|---|
AWS_ACCESS_KEY_ID | Access key Scaleway | SCW... |
AWS_SECRET_ACCESS_KEY | Secret key | *** |
AWS_DEFAULT_REGION | Region | fr-par |
AWS_BUCKET | Bucket | scell-storage |
AWS_ENDPOINT | Endpoint S3 | https://s3.fr-par.scw.cloud |
AWS_USE_PATH_STYLE_ENDPOINT | Path style | true |
MCP
| Variable | Description | Exemple |
|---|---|---|
MCP_ENABLED | Activer le serveur MCP | true |
MCP_SESSION_TTL | Duree de session (secondes) | 3600 |
MCP_SESSION_DRIVER | Driver de session MCP | redis |
MCP_RATE_LIMIT | Requetes par minute | 60 |
Conformite fiscale
| Variable | Description | Exemple |
|---|---|---|
FISCAL_COMPLIANCE_ENABLED | Activer la conformite fiscale | true |
FISCAL_EDITOR_NAME | Nom de l'editeur | QR Communication SAS |
FISCAL_EDITOR_SIRET | SIRET de l'editeur | 12345678900001 |
FISCAL_LICENSE_NUMBER | Numero de licence NF525 | NF525-2026-001 |
FISCAL_ANCHOR_PROVIDER | Provider d'ancrage | rfc3161_tsa |
FISCAL_TSA_URL | URL du TSA | https://freetsa.org/tsr |
FISCAL_RETENTION_YEARS | Duree de retention (annees) | 6 |
PDF
| Variable | Description | Exemple |
|---|---|---|
SNAPPY_PDF_BINARY | Chemin wkhtmltopdf | /usr/bin/wkhtmltopdf |
SNAPPY_IMAGE_BINARY | Chemin wkhtmltoimage | /usr/bin/wkhtmltoimage |
6. Monitoring
Horizon Dashboard
Laravel Horizon fournit un dashboard web pour superviser les queues :
URL : https://api.scell.io/horizonAcces : Restreint aux administrateurs (authentification Sanctum + role admin).
Le dashboard affiche :
- Etat des workers (actifs, en pause, termines)
- Metriques de throughput (jobs/minute)
- Jobs en attente, en cours, echoues
- Temps d'execution moyen
- Historique des jobs
Logs Laravel
Les logs sont stockes dans backend/storage/logs/ :
| Fichier | Description |
|---|---|
laravel.log | Log general de l'application |
horizon.log | Log du worker Horizon |
scheduler.log | Log du planificateur |
Configuration dans config/logging.php :
'channels' => [
'stack' => ['driver' => 'stack', 'channels' => ['single']],
'single' => ['driver' => 'single', 'path' => storage_path('logs/laravel.log'), 'level' => 'info'],
],En production, le niveau de log est info (pas de debug).
Health Checks
Le endpoint /health retourne l'etat de l'API :
curl -s https://api.scell.io/health | jq .{
"status": "ok",
"timestamp": "2026-03-03T14:30:00+00:00"
}Le MCP health_check tool fournit un diagnostic plus detaille (database, cache).
Metriques recommandees
| Metrique | Source | Seuil d'alerte |
|---|---|---|
| Temps de reponse API (p95) | Nginx access log | > 500ms |
| Queue pending jobs | Horizon | > 1000 |
| Queue failed jobs | Horizon | > 0 |
| Espace disque | Serveur | < 20% libre |
| Memoire PHP | PHP-FPM | > 80% utilise |
| Connexions PostgreSQL | PG | > 80% du max |
| Redis memoire | Redis | > 80% du max |
7. Troubleshooting
Erreurs communes
"Connection refused" sur l'API
Symptome : L'API retourne une erreur 502 Bad Gateway.
Causes possibles :
- PHP-FPM n'est pas demarre
- Le socket Unix n'existe pas
Solution :
# Verifier le statut de PHP-FPM
sudo systemctl status php8.2-fpm
# Redemarrer si necessaire
sudo systemctl restart php8.2-fpm
# Verifier le socket
ls -la /var/run/php/php8.2-fpm.sockLes jobs de queue ne s'executent pas
Symptome : Les factures restent au statut "pending", les webhooks ne sont pas envoyes.
Solution :
# Verifier Horizon
cd /home/ploi/api.scell.io/backend
php artisan horizon:status
# Si termine, redemarrer
php artisan horizon:terminate
# Le superviseur le relancera automatiquement
# Verifier les jobs echoues
php artisan queue:failed
# Relancer les jobs echoues
php artisan queue:retry allErreur de migration
Symptome : SQLSTATE[42P07]: Duplicate table ou SQLSTATE[42P01]: Undefined table.
Solution :
# Verifier l'etat des migrations
php artisan migrate:status
# Si une migration est bloquee, ne JAMAIS la modifier en production
# Creer une nouvelle migration corrective a la placeProbleme de certificat SSL
Symptome : cURL error 60: SSL certificate problem
Solution :
# Verifier les certificats
sudo certbot certificates
# Renouveler manuellement si necessaire
sudo certbot renew --force-renewal
# Verifier la configuration Nginx
sudo nginx -t
sudo systemctl reload nginxSolde insuffisant pour un tenant
Symptome : Erreur 402 "Solde insuffisant" lors de la creation de facture.
Solution :
# Verifier le solde du tenant (via Tinker)
php artisan tinker
>>> Tenant::find('uuid-du-tenant')->balance
# 0.00
# Crediter manuellement (admin)
curl -X POST https://api.scell.io/api/v1/admin/users/{userId}/credit \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{ "amount": 100.00, "reason": "Recharge manuelle" }'Redis saturee
Symptome : MISCONF Redis is configured to save RDB snapshots
Solution :
# Verifier l'utilisation memoire
redis-cli info memory
# Nettoyer les sessions MCP expirees
php artisan tinker
>>> App\Models\McpSession::where('expires_at', '<', now())->delete()
# Vider le cache si necessaire
php artisan cache:clearPerformance degradee
Symptome : Temps de reponse > 1 seconde.
Diagnostic :
# Verifier que les caches sont actifs
php artisan config:cache
php artisan route:cache
# Verifier les requetes lentes PostgreSQL
# Dans psql :
SELECT query, calls, mean_time, total_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
# Verifier les index manquants
php artisan tinker
>>> DB::select("SELECT schemaname, tablename, indexname FROM pg_indexes WHERE schemaname = 'public' ORDER BY tablename")FAQ technique
Q : Comment regenerer la cle APP_KEY ?
php artisan key:generate
# ATTENTION : invalide toutes les sessions et tokens existantsQ : Comment ajouter un nouveau worker Horizon ?
Editer config/horizon.php et ajouter un nouveau superviseur dans la section environments.
Q : Comment debugger un webhook SuperPDP ?
Les webhooks sont traces dans la table processed_webhooks. Consulter les logs :
grep "SuperPDP" /home/ploi/api.scell.io/backend/storage/logs/laravel.log | tail -20Q : Comment forcer une cloture fiscale journaliere ?
php artisan fiscal:daily-closing --forceQ : Ou sont stockes les fichiers (factures PDF, documents KYB) ?
En production, les fichiers sont sur Scaleway S3 (bucket scell-storage). En local, ils sont dans backend/storage/app/.
Q : Comment acceder a la base de donnees en production ?
ssh ploi@51.15.234.246
psql -h rdb-xxx.scw.cloud -U scell -d scell_production