gigafibre-fsm/docs/karrio-shipping.md
louispaulb 3a90dafb9f docs: déploiement Karrio (expédition multi-transporteurs self-hosted) + sources
Architecture, accès, Dockerfile.carriers + override compose (sanitisés), transporteurs
(Canada Post/Purolator/Canpar/Nationex/Dicom/BoxKnight + agrégateurs eShipper/Freightcom),
pièges (collision hostname, admin, binaire venv), plan d'intégration hub #54. Sources incluses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 10:25:36 -04:00

151 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Karrio — plateforme d'expédition multi-transporteurs (self-hosted)
Karrio est l'**EasyPost/Shippo open-source** (LGPL), auto-hébergé sur le serveur de prod.
Il fournit, sous une **API unifiée**, le **calcul de tarif d'expédition + étiquettes + suivi**
à travers de nombreux transporteurs (BYOCA : on branche nos propres comptes, **0 frais par étiquette**).
Objectif : calculer les frais de livraison au **checkout de la boutique matériel** (`/store`) et
gérer l'expédition des commandes (#54). Déployé le 2026-06-04.
> ⚠️ Ce document ne contient **aucun secret**. Les identifiants vivent sur le serveur
> (`/opt/karrio/.env`, `/opt/karrio/ADMIN_CREDS.txt`).
---
## Accès
| Quoi | URL |
|---|---|
| **Dashboard** (GUI gestion transporteurs) | https://karrio.gigafibre.ca — connexion `admin@targo.ca` (mdp : `/opt/karrio/ADMIN_CREDS.txt`) |
| **API publique** | https://api.karrio.gigafibre.ca |
| **API interne** (depuis le hub) | `http://karrio.api:5002` (réseau Docker `erpnext_erpnext`) |
| Auth API | `POST /api/token {email,password}` → JWT `access` |
DNS Cloudflare : `karrio` + `api.karrio`.gigafibre.ca → 96.125.196.67, **DNS-only** (proxied=false,
comme `msg`/`erp`, pour le challenge Let's Encrypt). TLS via Traefik (resolver `letsencrypt`).
---
## Architecture
- **Emplacement** : `/opt/karrio/` (compose) ; source clonée dans `/opt/karrio-src/` (Git).
- **Conteneurs** : `karrio.api` (5002) · `karrio.worker` · `karrio.dashboard` (3002) · `karrio.db`
(postgres:16 dédié) · `karrio.redis` · `karrio.mail` (maildev).
- **Réseaux Docker** : `karrio_default` (interne) + `erpnext_erpnext` (pour que le hub joigne l'API)
+ `proxy` (Traefik). L'attache aux réseaux externes est dans `docker-compose.override.yml` (persistante).
- **Images** : `karrio/server` + `karrio/dashboard` **2026.1.31**. L'`api`/`worker` utilisent une
**image custom** `karrio-server-targo:2026.1.31` (transporteurs canadiens supplémentaires, voir plus bas).
### `Dockerfile.carriers` (transporteurs canadiens opt-in)
```dockerfile
FROM karrio.docker.scarf.sh/karrio/server:2026.1.31
# Transporteurs non bundlés dans l'image OSS (paquets PyPI gratuits)
RUN /karrio/venv/bin/pip install --no-cache-dir "karrio==2026.1.31" \
karrio_canpar karrio_nationex karrio_dicom karrio_boxknight
```
Build : `docker build -t karrio-server-targo:2026.1.31 -f Dockerfile.carriers .`
### `docker-compose.override.yml` (réseaux + image custom + labels Traefik)
```yaml
networks:
erpnext_erpnext: { external: true }
proxy: { external: true }
services:
api:
image: karrio-server-targo:2026.1.31
networks: [default, erpnext_erpnext, proxy]
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.karrio-api.rule=Host(`api.karrio.gigafibre.ca`)
- traefik.http.routers.karrio-api.entrypoints=websecure
- traefik.http.routers.karrio-api.tls.certresolver=letsencrypt
- traefik.http.services.karrio-api.loadbalancer.server.port=5002
worker:
image: karrio-server-targo:2026.1.31
dashboard:
networks: [default, proxy]
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.karrio-dash.rule=Host(`karrio.gigafibre.ca`)
- traefik.http.routers.karrio-dash.entrypoints=websecure
- traefik.http.routers.karrio-dash.tls.certresolver=letsencrypt
- traefik.http.services.karrio-dash.loadbalancer.server.port=3002
```
### Variables `.env` clés (valeurs réelles sur le serveur)
```
KARRIO_TAG=2026.1.31
DATABASE_HOST=karrio.db # nom de conteneur, PAS "db" (collision ERPNext)
REDIS_HOST=karrio.redis # idem
USE_HTTPS=True
KARRIO_PUBLIC_URL=https://api.karrio.gigafibre.ca
DASHBOARD_URL=https://karrio.gigafibre.ca
ALLOW_SIGNUP=False
ADMIN_EMAIL=admin@targo.ca
# SECRET_KEY / JWT_SECRET / OIDC_RSA_PRIVATE_KEY / ADMIN_PASSWORD / DATABASE_PASSWORD : générés
```
---
## Transporteurs disponibles
- **Bundlés (image OSS)** : Canada Post, Purolator, UPS, FedEx, DHL (×4), GLS, USPS, DPD, Chronopost,
La Poste, Sendle, Australia Post, **eShipper**, **Freightcom**, generic… (~29).
- **Ajoutés (image custom)** : 🇨🇦 **Canpar**, **Nationex** (QC), **Dicom / GLS Canada**, **BoxKnight** (last-mile Montréal).
- **Agrégateurs canadiens** (1 compte → multi-transporteurs à tarifs négociés, **Canpar inclus**) :
**eShipper**, **Freightcom** (déjà bundlés).
- Dispo sur PyPI si besoin (international) : `karrio_tge, karrio_geodis, karrio_asendia, karrio_aramex`.
Non publiés : shiptime, loomis, intelcom, ics_courier.
> Karrio OSS ne bundle qu'un sous-ensemble ; les autres transporteurs sont des paquets PyPI
> `karrio_<nom>` **gratuits** (pas une limitation de licence — du packaging). On les cuit dans
> l'image custom.
---
## Pièges rencontrés (résolus)
1. **Collision de hostname** : `api` étant sur le réseau partagé `erpnext_erpnext`, `db`/`redis`
résolvaient vers le **Postgres/Redis d'ERPNext** (« password auth failed for user karrio »).
`.env` : `DATABASE_HOST=karrio.db`, `REDIS_HOST=karrio.redis` (noms de conteneurs non ambigus).
2. **Attache réseau** : `docker network connect` est perdu au recreate → réseaux dans l'`override.yml`.
3. **Admin non auto-semé** : créer manuellement —
binaire = `/karrio/venv/bin/karrio` (absent du PATH en `bash -lc` → utiliser `docker exec ... sh -c`).
```
docker exec -e KPW=<pw> karrio.api sh -c "cd /karrio/app && karrio shell < /tmp/mkadmin.py"
# mkadmin.py : get_user_model().objects.create_superuser('admin@targo.ca', os.environ['KPW'])
```
4. Réinit DB si besoin : `docker compose down -v` (sinon vieux creds persistent dans le volume).
---
## Intégration prévue (hub → #54)
`services/targo-hub/lib/shipping.js` exposera `quoteRates({origin, dest, parcel})` :
1. token JWT (`POST /api/token`) → cache,
2. appel API Karrio (rates) avec origine (entrepôt) + destination (client) + colis
(poids = `Item.weight_per_unit` ERPNext),
3. options de tarif → ligne « Frais de livraison » au checkout boutique,
4. `/store/checkout` → Sales Invoice ERPNext (`update_stock`) + Stripe via `lib/payments.js`.
---
## Sources
- Karrio — dépôt : <https://github.com/karrioapi/karrio>
- Karrio — intégrations transporteurs : <https://docs.karrio.io/carriers/>
- Karrio — Canada Post : <https://docs.karrio.io/carriers/integrations/canadapost>
- Karrio — Purolator : <https://www.karrio.io/docs/carriers/purolator>
- Karrio — plateforme / éditions : <https://www.karrio.io/platform>
- Karrio — starter Next.js (dashboard) : <https://docs.karrio.io/resources/nextjs-starter>
- Extension Canpar (PyPI) : <https://pypi.org/project/karrio_canpar/>
- Agrégateurs canadiens — eShipper : <https://www.eshipper.com/>
- Agrégateurs canadiens — Freightcom : <https://www.freightcom.com/>
- ShipTime (transporteurs/rabais, non Karrio-natif) : <https://ca.linkedin.com/company/shiptime>
- Comparatif shipping APIs 2026 : <https://revaddress.com/blog/shipping-api-comparison-2026/>
- Comparatif transporteurs Canada 2026 : <https://www.goodfirms.co/supply-chain-logistics-companies/shipping/canada>
_Mémoire interne liée : `project_karrio_shipping.md`._