98af48e81a3164d6b45b9656b04a4152f89db27b
SCHEMA.md
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +# Wiki Schema |
|
| 2 | + |
|
| 3 | +## Domain |
|
| 4 | +Hermes VPS — configuration, skills, conteneurs, sécurité |
|
| 5 | + |
|
| 6 | +## Conventions |
|
| 7 | +- File names: lowercase, hyphens, no spaces |
|
| 8 | +- Every page starts with YAML frontmatter |
|
| 9 | +- Use [[wikilinks]] to link between pages (minimum 2 outbound links) |
|
| 10 | +- Update `updated` date on every change |
|
| 11 | +- Every new page must be added to index.md |
|
| 12 | + |
|
| 13 | +## Frontmatter |
|
| 14 | +```yaml |
|
| 15 | +--- |
|
| 16 | +title: Page Title |
|
| 17 | +created: YYYY-MM-DD |
|
| 18 | +updated: YYYY-MM-DD |
|
| 19 | +type: entity | concept | comparison | query |
|
| 20 | +tags: [] |
|
| 21 | +sources: [] |
|
| 22 | +--- |
|
| 23 | +``` |
|
| 24 | + |
|
| 25 | +## Tag Taxonomy |
|
| 26 | +- infra: Infrastructure, serveurs, réseaux |
|
| 27 | +- security: Sécurité, firewall, hardening |
|
| 28 | +- container: Docker, conteneurs, Traefik |
|
| 29 | +- hermes: Hermes Agent, config, outils |
|
| 30 | +- ai: IA, LLM, Claude Code |
|
| 31 | +- skill: Skills, automatisation |
concepts/billetweb-api.md
| ... | ... | @@ -0,0 +1,364 @@ |
| 1 | +# API BilletWeb — Référence complète |
|
| 2 | + |
|
| 3 | +> **Plateforme :** [BilletWeb.fr](https://www.billetweb.fr) — billetterie en ligne |
|
| 4 | +> **Compte :** Parc de la Luge (user=268537) |
|
| 5 | +> **Dernière mise à jour :** 29/05/2026 |
|
| 6 | + |
|
| 7 | +--- |
|
| 8 | + |
|
| 9 | +## 1. Authentification |
|
| 10 | + |
|
| 11 | +L'API BilletWeb utilise une authentification par **query parameters** (pas de token Bearer). |
|
| 12 | + |
|
| 13 | +``` |
|
| 14 | +user=<user_id> |
|
| 15 | +key=<api_key> |
|
| 16 | +version=1 |
|
| 17 | +``` |
|
| 18 | + |
|
| 19 | +L'API key se trouve dans le compte BilletWeb : **Réglages → Outils → API**. |
|
| 20 | + |
|
| 21 | +> ⚠️ **Pas de JSON-RPC, pas de REST auth standard** — clé en query string. |
|
| 22 | + |
|
| 23 | +--- |
|
| 24 | + |
|
| 25 | +## 2. Endpoints |
|
| 26 | + |
|
| 27 | +### 2.1 Lister les événements |
|
| 28 | + |
|
| 29 | +``` |
|
| 30 | +GET https://www.billetweb.fr/api/user/events |
|
| 31 | + ?user=<user_id> |
|
| 32 | + &key=<api_key> |
|
| 33 | + &version=1 |
|
| 34 | +``` |
|
| 35 | + |
|
| 36 | +Retourne la liste des événements du compte. |
|
| 37 | + |
|
| 38 | +**Réponse :** |
|
| 39 | +```json |
|
| 40 | +[ |
|
| 41 | + { |
|
| 42 | + "id": 1350897, |
|
| 43 | + "name": "Parc de la Luge - Billetterie", |
|
| 44 | + "date_start": "2026-01-01", |
|
| 45 | + "date_end": "2026-12-31", |
|
| 46 | + "status": "active" |
|
| 47 | + } |
|
| 48 | +] |
|
| 49 | +``` |
|
| 50 | + |
|
| 51 | +### 2.2 Lister les participants |
|
| 52 | + |
|
| 53 | +``` |
|
| 54 | +GET https://www.billetweb.fr/api/event/{event_id}/attendees |
|
| 55 | + ?user=<user_id> |
|
| 56 | + &key=<api_key> |
|
| 57 | + &version=1 |
|
| 58 | + &since=<YYYY-MM-DD HH:MM:SS> |
|
| 59 | +``` |
|
| 60 | + |
|
| 61 | +**Paramètres :** |
|
| 62 | + |
|
| 63 | +| Paramètre | Description | |
|
| 64 | +|-----------|-------------| |
|
| 65 | +| `event_id` | ID de l'événement BilletWeb | |
|
| 66 | +| `since` | Optionnel — filtre les ventes après cette date (format `YYYY-MM-DD HH:MM:SS`) | |
|
| 67 | +| `ticket_id` | Optionnel — filtre par type de billet | |
|
| 68 | + |
|
| 69 | +**Réponse (tableau de participants) :** |
|
| 70 | +```json |
|
| 71 | +[ |
|
| 72 | + { |
|
| 73 | + "id": "12345678", |
|
| 74 | + "barcode": "6743097169", |
|
| 75 | + "ref": "BW-XXXXXX", |
|
| 76 | + "ticket": "Luge - Forfait 4 tours", |
|
| 77 | + "ticket_id": 12345, |
|
| 78 | + "name": "Jean Dupont", |
|
| 79 | + "email": "jean@email.com", |
|
| 80 | + "used": "0", |
|
| 81 | + "date": "2026-05-15 10:30:00", |
|
| 82 | + "price": "11.00", |
|
| 83 | + "event_id": "1350897" |
|
| 84 | + } |
|
| 85 | +] |
|
| 86 | +``` |
|
| 87 | + |
|
| 88 | +**Champs importants :** |
|
| 89 | + |
|
| 90 | +| Champ | Type | Description | |
|
| 91 | +|-------|------|-------------| |
|
| 92 | +| `id` | string | ID unique de la commande (référence de commande) | |
|
| 93 | +| `barcode` | string | **Code barre unique** (servira de lien entre BilletWeb et Odoo) | |
|
| 94 | +| `ref` | string | Référence BilletWeb (format `BW-XXXXXX`) | |
|
| 95 | +| `ticket` | string | Nom du billet | |
|
| 96 | +| `ticket_id` | int | ID du type de billet | |
|
| 97 | +| `name` | string | Nom du participant | |
|
| 98 | +| `email` | string | Email du participant | |
|
| 99 | +| `used` | string | `"0"` = pas encore composté, `"1"` = déjà utilisé | |
|
| 100 | +| `date` | string | Date d'achat | |
|
| 101 | +| `price` | string | Prix payé | |
|
| 102 | +| `event_id` | string | ID de l'événement | |
|
| 103 | + |
|
| 104 | +### 2.3 Composter un billet (check) |
|
| 105 | + |
|
| 106 | +``` |
|
| 107 | +POST https://www.billetweb.fr/api/event/{event_id}/check |
|
| 108 | + ?user=<user_id> |
|
| 109 | + &key=<api_key> |
|
| 110 | + &version=1 |
|
| 111 | + |
|
| 112 | +Body: {"code": "6743097169"} |
|
| 113 | +``` |
|
| 114 | + |
|
| 115 | +**Comportement :** |
|
| 116 | +- Valide que le billet existe et n'a pas déjà été utilisé |
|
| 117 | +- **Composte** le billet (le marque comme `used=1`) |
|
| 118 | +- **Idempotent ?** Non — une fois composté, un deuxième appel échoue |
|
| 119 | + |
|
| 120 | +**Réponse succès :** |
|
| 121 | +```json |
|
| 122 | +{"success": true, "message": "Billet composté avec succès"} |
|
| 123 | +``` |
|
| 124 | + |
|
| 125 | +**Réponse erreur (billet déjà utilisé) :** |
|
| 126 | +```json |
|
| 127 | +{"error": "Ce billet a déjà été utilisé"} |
|
| 128 | +``` |
|
| 129 | + |
|
| 130 | +--- |
|
| 131 | + |
|
| 132 | +## 3. Événements du Parc de la Luge |
|
| 133 | + |
|
| 134 | +| ID BilletWeb | Nom | Événement Odoo | |
|
| 135 | +|:-----------:|------|:--------------:| |
|
| 136 | +| `1350897` | Parc de la Luge - Billetterie | Event ID 1 | |
|
| 137 | +| `1353693` | BON CADEAU | Event ID 2 | |
|
| 138 | + |
|
| 139 | +--- |
|
| 140 | + |
|
| 141 | +## 4. Mapping produits (18 billets) |
|
| 142 | + |
|
| 143 | +| Intitulé BilletWeb | Code barre Odoo | |
|
| 144 | +|-------------------|-----------------| |
|
| 145 | +| Journée Luge illimitée | `BW-LUGE-JOUR` | |
|
| 146 | +| Luge - Forfait 4 tours | `BW-LUGE-4T` | |
|
| 147 | +| Luge - Forfait 6 tours | `BW-LUGE-6T` | |
|
| 148 | +| Luge - Forfait 10 tours | `BW-LUGE-10T` | |
|
| 149 | +| Luge - Forfait 12 tours | `BW-LUGE-12T` | |
|
| 150 | +| Poney | `BW-PONEY` | |
|
| 151 | +| Mon ticket Promo | `BW-PROMO` | |
|
| 152 | +| OFFRE LIMITÉE - SPÉCIALE TRIBU | `BW-LUGE-TRIBU` | |
|
| 153 | +| Tir à l'arc - 20 min | `BW-TIR-20` | |
|
| 154 | +| Tir à l'arc - 40 min | `BW-TIR-40` | |
|
| 155 | +| Kids électrique - 10 min | `BW-KIDS-E10` | |
|
| 156 | +| Trottinettes ETT - 20 min | `BW-TROTT-20` | |
|
| 157 | +| Bon cadeau Luge 4 tours | `BW-CADEAU-LUGE4` | |
|
| 158 | +| Bon cadeau Luge 3 tours | `BW-CADEAU-LUGE3` | |
|
| 159 | +| Bon cadeau Poney | `BW-CADEAU-PONEY` | |
|
| 160 | +| Bon cadeau Kids électrique | `BW-CADEAU-KIDS` | |
|
| 161 | +| Bon cadeau Tir à l'arc 20 min | `BW-CADEAU-TIR20` | |
|
| 162 | +| Bon cadeau Trottinettes ETT | `BW-CADEAU-TROTT` | |
|
| 163 | + |
|
| 164 | +--- |
|
| 165 | + |
|
| 166 | +## 5. Architecture de synchronisation |
|
| 167 | + |
|
| 168 | +``` |
|
| 169 | +┌─────────────┐ ┌──────────────┐ ┌──────────────┐ |
|
| 170 | +│ BilletWeb │◄────│ n8n.n8n-1 │────►│ Sync Server │ |
|
| 171 | +│ (API REST) │ │ (orchestre) │ │ (Python) │ |
|
| 172 | +└─────────────┘ └──────┬───────┘ └──────┬────────┘ |
|
| 173 | + │ │ |
|
| 174 | + │ Schedule: 15 min │ POST /sync |
|
| 175 | + │ ▼ |
|
| 176 | + │ ┌────────────────┐ |
|
| 177 | + │ │ sync_billetweb │ |
|
| 178 | + │ │ (Python) │ |
|
| 179 | + │ └───────┬────────┘ |
|
| 180 | + │ │ |
|
| 181 | + │ ▼ |
|
| 182 | + │ ┌────────────────┐ |
|
| 183 | + └──────────►│ Odoo Online │ |
|
| 184 | + │ (JSON-RPC) │ |
|
| 185 | + └────────────────┘ |
|
| 186 | +``` |
|
| 187 | + |
|
| 188 | +### Composants |
|
| 189 | + |
|
| 190 | +| Service | Technologie | Port | Accès | |
|
| 191 | +|---------|------------|------|-------| |
|
| 192 | +| n8n | n8n v2.21.7 Docker | 5678 | `https://n8n.delgard.cloud` | |
|
| 193 | +| Sync Server | Python 3.12-slim | 8855 | Interne (Docker network) | |
|
| 194 | +| Traefik | Reverse proxy | 80/443 | Let's Encrypt | |
|
| 195 | + |
|
| 196 | +### Flux de sync (script `sync_billetweb.py`) |
|
| 197 | + |
|
| 198 | +``` |
|
| 199 | +1. 🔒 Charger tous les barcodes Odoo existants (anti-doublon) |
|
| 200 | +2. 🌐 GET BilletWeb /api/event/{id}/attendees (2 events) |
|
| 201 | +3. Pour chaque attendee nouveau : |
|
| 202 | + a. Trouver/créer client (res.partner) |
|
| 203 | + b. Trouver produit (product.product par barcode BW-*) |
|
| 204 | + c. Trouver/créer commande (sale.order) |
|
| 205 | + d. Créer ligne (sale.order.line) |
|
| 206 | + e. Créer inscription (event.registration) |
|
| 207 | + f. Lier ligne → inscription |
|
| 208 | +4. 📊 Résultat : "X nouveaux importés" |
|
| 209 | +``` |
|
| 210 | + |
|
| 211 | +--- |
|
| 212 | + |
|
| 213 | +## 6. Webhook de compostage (Odoo → BilletWeb) |
|
| 214 | + |
|
| 215 | +Quand un billet est scanné au PDV Odoo (`state: open → done`), un webhook composte le billet BilletWeb. |
|
| 216 | + |
|
| 217 | +``` |
|
| 218 | +Odoo (Automated Action) |
|
| 219 | + │ POST webhook |
|
| 220 | + ▼ |
|
| 221 | +n8n Webhook Trigger |
|
| 222 | + │ POST /webhook/billetweb-compost |
|
| 223 | + ▼ |
|
| 224 | +BilletWeb API |
|
| 225 | + │ POST /api/event/{id}/check |
|
| 226 | + ▼ |
|
| 227 | +Billet composté ✅ |
|
| 228 | +``` |
|
| 229 | + |
|
| 230 | +### Webhook URL |
|
| 231 | + |
|
| 232 | +``` |
|
| 233 | +https://n8n.delgard.cloud/webhook/billetweb-compost |
|
| 234 | +``` |
|
| 235 | + |
|
| 236 | +### Payload attendu |
|
| 237 | + |
|
| 238 | +```json |
|
| 239 | +{ |
|
| 240 | + "event_id": 1350897, |
|
| 241 | + "barcode": "6743097169", |
|
| 242 | + "name": "Jean Dupont", |
|
| 243 | + "state": "done" |
|
| 244 | +} |
|
| 245 | +``` |
|
| 246 | + |
|
| 247 | +### Configuration Odoo (Automated Action) |
|
| 248 | + |
|
| 249 | +1. Activer le mode développeur |
|
| 250 | +2. Technique → Automatisation → Règles d'automatisation → Créer |
|
| 251 | +3. **Modèle :** `event.registration` |
|
| 252 | +4. **Déclencheur :** « Lors de la mise à jour » + champ `state` |
|
| 253 | +5. **Action :** « Envoyer une notification webhook » |
|
| 254 | +6. **URL :** `https://n8n.delgard.cloud/webhook/billetweb-compost` |
|
| 255 | +7. **Headers :** `Content-Type: application/json` |
|
| 256 | +8. **Champs :** `barcode`, `name`, `state`, `event_id` |
|
| 257 | + |
|
| 258 | +### Test manuel |
|
| 259 | + |
|
| 260 | +```bash |
|
| 261 | +curl -X POST https://n8n.delgard.cloud/webhook/billetweb-compost \ |
|
| 262 | + -H "Content-Type: application/json" \ |
|
| 263 | + -d '{"event_id": 1350897, "barcode": "6743097169", "name": "Test", "state": "done"}' |
|
| 264 | +# → {"message":"Workflow was started"} |
|
| 265 | +``` |
|
| 266 | + |
|
| 267 | +--- |
|
| 268 | + |
|
| 269 | +## 7. Pièges et bonnes pratiques |
|
| 270 | + |
|
| 271 | +### Import initial : respecter le champ `used` |
|
| 272 | + |
|
| 273 | +| BilletWeb `used` | État réel | Odoo `state` correct | |
|
| 274 | +|:----------------:|-----------|:--------------------:| |
|
| 275 | +| `"0"` | Pas encore venu | `open` | |
|
| 276 | +| `"1"` | Déjà composté | `done` | |
|
| 277 | + |
|
| 278 | +> ⚠️ Ne pas tout mettre en `done` par défaut — l'import initial du 26/05 avait ce bug. |
|
| 279 | + |
|
| 280 | +**Correction post-import :** |
|
| 281 | +```python |
|
| 282 | +# Repasser en "open" les billets used=0 |
|
| 283 | +bw_unused = {a['barcode'] for a in bw_attendees if a['used'] == '0'} |
|
| 284 | +to_open = odoo_call("event.registration", "search_read", |
|
| 285 | + [["barcode", "in", list(bw_unused)], ["state", "=", "done"]]) |
|
| 286 | +odoo_call("event.registration", "write", |
|
| 287 | + [[r['id'] for r in to_open], {"state": "open"}]) |
|
| 288 | + |
|
| 289 | +# Repasser en "done" les billets used=1 |
|
| 290 | +bw_used = {a['barcode'] for a in bw_attendees if a['used'] == '1'} |
|
| 291 | +to_done = odoo_call("event.registration", "search_read", |
|
| 292 | + [["barcode", "in", list(bw_used)], ["state", "=", "open"]]) |
|
| 293 | +odoo_call("event.registration", "write", |
|
| 294 | + [[r['id'] for r in to_done], {"state": "done"}]) |
|
| 295 | +``` |
|
| 296 | + |
|
| 297 | +### Anti-doublon : cache vs Odoo direct |
|
| 298 | + |
|
| 299 | +- **Toujours requêter Odoo directement** pour la liste des barcodes existants |
|
| 300 | +- Ne pas se fier uniquement au fichier cache (`sync_cache.json`) — il peut être vidé |
|
| 301 | +- Le cache est une optimisation, pas une source de vérité |
|
| 302 | + |
|
| 303 | +### Commandes multi-tickets |
|
| 304 | + |
|
| 305 | +Ne pas bloquer la création d'une commande si `client_order_ref` existe déjà : |
|
| 306 | + |
|
| 307 | +```python |
|
| 308 | +# ✅ Réutiliser la commande existante |
|
| 309 | +existing_so = odoo_call("sale.order", "search_read", |
|
| 310 | + [[["client_order_ref", "=", ref]]], {"fields": ["id"]}) |
|
| 311 | +if existing_so: |
|
| 312 | + so = existing_so[0]["id"] # Ajouter des lignes à la commande existante |
|
| 313 | +else: |
|
| 314 | + so = odoo_call("sale.order", "create", [{...}]) # Créer nouvelle commande |
|
| 315 | +``` |
|
| 316 | + |
|
| 317 | +### Rate limiting BilletWeb |
|
| 318 | + |
|
| 319 | +L'API BilletWeb n'a pas de limite documentée, mais espacer les appels d'au moins 1 seconde en cas de bulk. |
|
| 320 | + |
|
| 321 | +--- |
|
| 322 | + |
|
| 323 | +## 8. Maintenance |
|
| 324 | + |
|
| 325 | +### Sync manuelle |
|
| 326 | + |
|
| 327 | +```bash |
|
| 328 | +docker exec n8n-sync-server-1 python3 /app/sync_billetweb.py |
|
| 329 | +``` |
|
| 330 | + |
|
| 331 | +### Réinitialiser le cache anti-doublon |
|
| 332 | + |
|
| 333 | +```bash |
|
| 334 | +docker exec n8n-sync-server-1 rm -f /root/.hermes/scripts/sync_cache.json |
|
| 335 | +# Prochaine sync : recharge TOUS les barcodes depuis Odoo |
|
| 336 | +``` |
|
| 337 | + |
|
| 338 | +### Logs |
|
| 339 | + |
|
| 340 | +```bash |
|
| 341 | +docker logs n8n-n8n-1 --tail 50 |
|
| 342 | +docker logs n8n-sync-server-1 --tail 50 |
|
| 343 | +docker exec n8n-sync-server-1 cat /tmp/sync_result.json |
|
| 344 | +``` |
|
| 345 | + |
|
| 346 | +### Workflows n8n |
|
| 347 | + |
|
| 348 | +| Nom | ID | Status | |
|
| 349 | +|-----|-----|--------| |
|
| 350 | +| BilletWeb → Odoo Sync | `n4r0JG3BvHKi9nmC` | ✅ Actif (toutes les 15 min) | |
|
| 351 | +| Compost BilletWeb (webhook) | `V1TCykwcoSWlBHyX` | ✅ Actif | |
|
| 352 | + |
|
| 353 | +--- |
|
| 354 | + |
|
| 355 | +## 9. Fichiers associés |
|
| 356 | + |
|
| 357 | +| Fichier | Emplacement | |
|
| 358 | +|---------|------------| |
|
| 359 | +| sync_billetweb.py (Docker) | `/docker/n8n-gdkj/sync-server/sync_billetweb.py` | |
|
| 360 | +| sync_billetweb.py (host) | `/root/.hermes/scripts/sync_billetweb.py` | |
|
| 361 | +| server.py (sync-server) | `/docker/n8n-gdkj/sync-server/server.py` | |
|
| 362 | +| docker-compose n8n | `/docker/n8n-gdkj/docker-compose.yml` | |
|
| 363 | +| Workflow n8n (export) | `/root/workspace/n8n/workflow-billetweb-odoo.json` | |
|
| 364 | +| Architecture doc | `/root/workspace/n8n/ARCHITECTURE.md` | |
concepts/odoo-api.md
| ... | ... | @@ -0,0 +1,367 @@ |
| 1 | +# API Odoo Online (SaaS) — Référence complète |
|
| 2 | + |
|
| 3 | +> **Instance :** Parc de la Luge — Odoo Online Standard |
|
| 4 | +> **Dernière mise à jour :** 29/05/2026 |
|
| 5 | + |
|
| 6 | +--- |
|
| 7 | + |
|
| 8 | +## 1. Authentification |
|
| 9 | + |
|
| 10 | +### Endpoint |
|
| 11 | + |
|
| 12 | +``` |
|
| 13 | +POST https://<subdomain>.odoo.com/jsonrpc |
|
| 14 | +``` |
|
| 15 | + |
|
| 16 | +### Login (JSON-RPC) |
|
| 17 | + |
|
| 18 | +```python |
|
| 19 | +payload = { |
|
| 20 | + "jsonrpc": "2.0", "method": "call", |
|
| 21 | + "params": { |
|
| 22 | + "service": "common", "method": "login", |
|
| 23 | + "args": [DB, "login@email.com", "api_key_or_password"] |
|
| 24 | + }, "id": 1 |
|
| 25 | +} |
|
| 26 | +uid = requests.post(ODOO_URL, json=payload).json()["result"] |
|
| 27 | +``` |
|
| 28 | + |
|
| 29 | +> **⚠️ ERREUR CLASSIQUE :** `/web/session/authenticate` → `AccessDenied` sur SaaS. |
|
| 30 | +> Toujours utiliser `common.login` sur `/jsonrpc`. |
|
| 31 | + |
|
| 32 | +### API Key |
|
| 33 | + |
|
| 34 | +Générée dans Odoo : **Profil → Preferences → Account Security → API Keys**. |
|
| 35 | + |
|
| 36 | +--- |
|
| 37 | + |
|
| 38 | +## 2. Helper générique |
|
| 39 | + |
|
| 40 | +```python |
|
| 41 | +def odoo_call(model, method, *args): |
|
| 42 | + payload = { |
|
| 43 | + "jsonrpc": "2.0", "method": "call", |
|
| 44 | + "params": { |
|
| 45 | + "service": "object", |
|
| 46 | + "method": "execute_kw", |
|
| 47 | + "args": [DB, UID, PWD, model, method] + list(args) |
|
| 48 | + }, "id": 1 |
|
| 49 | + } |
|
| 50 | + r = requests.post(ODOO_URL, json=payload, timeout=30) |
|
| 51 | + data = r.json() |
|
| 52 | + if "error" in data: |
|
| 53 | + msg = data['error']['data']['message'][:200] |
|
| 54 | + raise Exception(f"Odoo error: {msg}") |
|
| 55 | + return data.get("result") |
|
| 56 | +``` |
|
| 57 | + |
|
| 58 | +--- |
|
| 59 | + |
|
| 60 | +## 3. Opérations CRUD |
|
| 61 | + |
|
| 62 | +### search_read — Rechercher + lire |
|
| 63 | + |
|
| 64 | +```python |
|
| 65 | +records = odoo_call("res.partner", "search_read", |
|
| 66 | + [[["email", "=", "x@y.com"]]], # domain (list of tuples) |
|
| 67 | + {"fields": ["id", "name", "email"]}) # fields dict |
|
| 68 | +``` |
|
| 69 | + |
|
| 70 | +### search_count — Compter |
|
| 71 | + |
|
| 72 | +```python |
|
| 73 | +count = odoo_call("sale.order", "search_count", |
|
| 74 | + [[["state", "=", "sale"]]]) |
|
| 75 | +``` |
|
| 76 | + |
|
| 77 | +### create — Créer (⚠️ un seul dict) |
|
| 78 | + |
|
| 79 | +```python |
|
| 80 | +new_id = odoo_call("sale.order", "create", |
|
| 81 | + [{"name": "ORDER-REF", "partner_id": 42, ...}]) # ONE dict, PAS une liste |
|
| 82 | +``` |
|
| 83 | + |
|
| 84 | +### write — Modifier |
|
| 85 | + |
|
| 86 | +```python |
|
| 87 | +odoo_call("sale.order", "write", |
|
| 88 | + [[42], {"state": "sale"}]) # [[id], {values}] |
|
| 89 | +``` |
|
| 90 | + |
|
| 91 | +### unlink — Supprimer |
|
| 92 | + |
|
| 93 | +```python |
|
| 94 | +odoo_call("sale.order", "unlink", [[42]]) |
|
| 95 | +``` |
|
| 96 | + |
|
| 97 | +### fields_get — Découvrir les champs |
|
| 98 | + |
|
| 99 | +```python |
|
| 100 | +fields = odoo_call("sale.order", "fields_get", [], |
|
| 101 | + {"attributes": ["string", "type", "required"]}) |
|
| 102 | +required = [(k, v) for k, v in fields.items() if v.get("required")] |
|
| 103 | +``` |
|
| 104 | + |
|
| 105 | +--- |
|
| 106 | + |
|
| 107 | +## 4. Modèles clés |
|
| 108 | + |
|
| 109 | +### 4.1 sale.order — Commande |
|
| 110 | + |
|
| 111 | +| Champ | Type | Obligatoire | Notes | |
|
| 112 | +|-------|------|:----------:|-------| |
|
| 113 | +| `name` | char | ✅ | Référence commande | |
|
| 114 | +| `partner_id` | m2o → res.partner | ✅ | Client | |
|
| 115 | +| `partner_invoice_id` | m2o → res.partner | ✅ | Adresse facturation | |
|
| 116 | +| `partner_shipping_id` | m2o → res.partner | ✅ | Adresse livraison | |
|
| 117 | +| `company_id` | m2o → res.company | ✅ | Généralement `1` | |
|
| 118 | +| `date_order` | datetime | ✅ | Format `"2026-01-01 00:00:00"` | |
|
| 119 | +| `picking_policy` | selection | ✅ | `"direct"` | |
|
| 120 | +| `client_order_ref` | char | | Référence externe (ex: BilletWeb) | |
|
| 121 | +| `state` | selection | | `draft` → `sent` → `sale` → `done` → `cancel` | |
|
| 122 | + |
|
| 123 | +Création minimale : |
|
| 124 | + |
|
| 125 | +```python |
|
| 126 | +odoo_call("sale.order", "create", [{ |
|
| 127 | + "name": "ORDER-REF", |
|
| 128 | + "partner_id": partner_id, |
|
| 129 | + "partner_invoice_id": partner_id, |
|
| 130 | + "partner_shipping_id": partner_id, |
|
| 131 | + "company_id": 1, |
|
| 132 | + "date_order": "2026-01-01 00:00:00", |
|
| 133 | + "picking_policy": "direct", |
|
| 134 | +}]) |
|
| 135 | +``` |
|
| 136 | + |
|
| 137 | +### 4.2 sale.order.line — Ligne de commande |
|
| 138 | + |
|
| 139 | +| Champ | Type | Notes | |
|
| 140 | +|-------|------|-------| |
|
| 141 | +| `order_id` | m2o → sale.order | ID commande | |
|
| 142 | +| `product_id` | m2o → product.product | ID produit | |
|
| 143 | +| `product_uom_qty` | float | Quantité (défaut: 1) | |
|
| 144 | +| `price_unit` | float | Prix unitaire | |
|
| 145 | +| `registration_ids` | o2m → event.registration | Lie les inscriptions | |
|
| 146 | + |
|
| 147 | +### 4.3 res.partner — Client |
|
| 148 | + |
|
| 149 | +| Champ | Type | Notes | |
|
| 150 | +|-------|------|-------| |
|
| 151 | +| `name` | char | Nom complet | |
|
| 152 | +| `email` | char | Email (unique par client) | |
|
| 153 | +| `phone` | char | Téléphone | |
|
| 154 | +| `barcode` | char | Code barre | |
|
| 155 | + |
|
| 156 | +### 4.4 product.product — Produit |
|
| 157 | + |
|
| 158 | +| Champ | Type | Notes | |
|
| 159 | +|-------|------|-------| |
|
| 160 | +| `name` | char | Nom | |
|
| 161 | +| `barcode` | char | Code barre (utilisé pour mapper BilletWeb) | |
|
| 162 | +| `list_price` | float | Prix de vente | |
|
| 163 | +| `active` | bool | `False` pour désactiver sans supprimer | |
|
| 164 | + |
|
| 165 | +### 4.5 event.registration — Inscription |
|
| 166 | + |
|
| 167 | +| Champ | Type | Notes | |
|
| 168 | +|-------|------|-------| |
|
| 169 | +| `event_id` | m2o → event.event | Événement | |
|
| 170 | +| `name` | char | Nom participant | |
|
| 171 | +| `email` | char | Email | |
|
| 172 | +| `barcode` | char | **Code barre BilletWeb** | |
|
| 173 | +| `state` | selection | `draft` → `open` → `done` → `cancel` | |
|
| 174 | +| `event_ticket_id` | m2o → event.event.ticket | Type de billet | |
|
| 175 | + |
|
| 176 | +Création : |
|
| 177 | + |
|
| 178 | +```python |
|
| 179 | +reg_id = odoo_call("event.registration", "create", [{ |
|
| 180 | + "event_id": event_id, |
|
| 181 | + "name": "Jean Dupont", |
|
| 182 | + "email": "jean@email.com", |
|
| 183 | + "barcode": "1234567890", |
|
| 184 | + "state": "open", |
|
| 185 | + "event_ticket_id": ticket_id, |
|
| 186 | +}]) |
|
| 187 | +``` |
|
| 188 | + |
|
| 189 | +Lier à une ligne de commande : |
|
| 190 | + |
|
| 191 | +```python |
|
| 192 | +odoo_call("sale.order.line", "write", |
|
| 193 | + [[line_id], {"registration_ids": [[6, 0, [reg_id]]]}]) |
|
| 194 | +``` |
|
| 195 | + |
|
| 196 | +### 4.6 event.event.ticket — Type de billet |
|
| 197 | + |
|
| 198 | +```python |
|
| 199 | +odoo_call("event.event.ticket", "create", [{ |
|
| 200 | + "name": "Luge - Forfait 4 tours", |
|
| 201 | + "event_id": event_id, |
|
| 202 | + "product_id": product_id, |
|
| 203 | + "price": 11.0, |
|
| 204 | + "seats_limited": False, |
|
| 205 | +}]) |
|
| 206 | +``` |
|
| 207 | + |
|
| 208 | +### 4.7 pos.order — Commande PDV |
|
| 209 | + |
|
| 210 | +| Champ | Type | Notes | |
|
| 211 | +|-------|------|-------| |
|
| 212 | +| `name` | char | Référence session | |
|
| 213 | +| `date_order` | datetime | Date commande | |
|
| 214 | +| `amount_total` | float | Total TTC | |
|
| 215 | +| `amount_tax` | float | Total TVA | |
|
| 216 | +| `state` | selection | `draft` → `paid` → `done` | |
|
| 217 | +| `lines` | o2m → pos.order.line | Lignes | |
|
| 218 | + |
|
| 219 | +--- |
|
| 220 | + |
|
| 221 | +## 5. Flux BilletWeb → Odoo |
|
| 222 | + |
|
| 223 | +Ordre correct pour importer un participant : |
|
| 224 | + |
|
| 225 | +``` |
|
| 226 | +1. res.partner — find by email, or create |
|
| 227 | +2. product.product — find by barcode (BW-*) |
|
| 228 | +3. event.event.ticket — find by product_id |
|
| 229 | +4. sale.order — create with partner_id |
|
| 230 | +5. sale.order.line — create with order_id + product_id |
|
| 231 | +6. event.registration — create with event_id + barcode + event_ticket_id |
|
| 232 | +7. sale.order.line — write registration_ids to link |
|
| 233 | +``` |
|
| 234 | + |
|
| 235 | +--- |
|
| 236 | + |
|
| 237 | +## 6. TVA — Réunion (DOM) |
|
| 238 | + |
|
| 239 | +| Taux | Usage | |
|
| 240 | +|------|-------| |
|
| 241 | +| **8,5%** | Standard DOM | |
|
| 242 | +| **2,1%** | Réduit | |
|
| 243 | +| 0% | Exonéré | |
|
| 244 | + |
|
| 245 | +```python |
|
| 246 | +odoo_call("account.tax", "create", [{ |
|
| 247 | + "name": "TVA 8.5% (Réunion)", |
|
| 248 | + "amount": 8.5, |
|
| 249 | + "amount_type": "percent", |
|
| 250 | + "type_tax_use": "sale", |
|
| 251 | + "tax_group_id": group_id, |
|
| 252 | + "price_include": False, |
|
| 253 | +}]) |
|
| 254 | +``` |
|
| 255 | + |
|
| 256 | +> ⚠️ `l10n_fr_account` installe les taux métropole (20%, 10%, 5.5%). |
|
| 257 | +> Désactiver avec `write([[id], {"active": False}])`. |
|
| 258 | + |
|
| 259 | +--- |
|
| 260 | + |
|
| 261 | +## 7. Modules de localisation française |
|
| 262 | + |
|
| 263 | +| Module | Rôle | |
|
| 264 | +|--------|------| |
|
| 265 | +| `l10n_fr` | Localisation France | |
|
| 266 | +| `l10n_fr_account` | Plan comptable + taxes | |
|
| 267 | +| `l10n_fr_pos_cert` | Certification **NF 525** (obligatoire en France) | |
|
| 268 | +| `l10n_fr_reports` | Rapports comptables | |
|
| 269 | + |
|
| 270 | +Installation via API : |
|
| 271 | + |
|
| 272 | +```python |
|
| 273 | +mid = odoo_call("ir.module.module", "search_read", |
|
| 274 | + [[["name", "=", "l10n_fr"]]], {"fields": ["id"]})[0]["id"] |
|
| 275 | +odoo_call("ir.module.module", "button_immediate_install", [[mid]]) |
|
| 276 | +``` |
|
| 277 | + |
|
| 278 | +--- |
|
| 279 | + |
|
| 280 | +## 8. Pièges et bonnes pratiques |
|
| 281 | + |
|
| 282 | +### Emails (limite 5/jour sur SaaS) |
|
| 283 | + |
|
| 284 | +```python |
|
| 285 | +# Désactiver TOUS les emails après import |
|
| 286 | +odoo_call("ir.mail_server", "write", [all_ids], {"active": False}) |
|
| 287 | +odoo_call("fetchmail.server", "write", [all_ids], {"active": False}) |
|
| 288 | +odoo_call("mail.template", "write", [all_ids], {"auto_delete": True}) |
|
| 289 | +odoo_call("ir.cron", "write", [ids], {"active": False}) # jobs mail/email/send |
|
| 290 | +odoo_call("event.event", "write", [[1,2]], {"auto_confirm": False}) |
|
| 291 | +``` |
|
| 292 | + |
|
| 293 | +### Produits de démo |
|
| 294 | + |
|
| 295 | +Ne pas `unlink` (référencés par les moyens de paiement PDV). Utiliser : |
|
| 296 | + |
|
| 297 | +```python |
|
| 298 | +odoo_call("product.product", "write", [[pid], {"active": False}]) |
|
| 299 | +``` |
|
| 300 | + |
|
| 301 | +### Groupes de taxes — country_id |
|
| 302 | + |
|
| 303 | +Ne PAS mettre `country_id` sauf s'il correspond au pays de la société : |
|
| 304 | + |
|
| 305 | +```python |
|
| 306 | +# ✅ OK |
|
| 307 | +odoo_call("account.tax.group", "create", [{"name": "TVA 8.5%"}]) |
|
| 308 | + |
|
| 309 | +# ❌ Erreur si société = Réunion (187) |
|
| 310 | +odoo_call("account.tax.group", "create", [{"name": "TVA", "country_id": 75}]) |
|
| 311 | +``` |
|
| 312 | + |
|
| 313 | +### create() — un seul dict |
|
| 314 | + |
|
| 315 | +L'API JSON-RPC Odoo Online attend **un dict** pour `create`, pas une liste de dicts : |
|
| 316 | + |
|
| 317 | +```python |
|
| 318 | +# ✅ OK |
|
| 319 | +odoo_call("sale.order", "create", [{"name": "X", ...}]) |
|
| 320 | + |
|
| 321 | +# ❌ Erreur silencieuse |
|
| 322 | +odoo_call("sale.order", "create", [[{"name": "X", ...}]]) |
|
| 323 | +``` |
|
| 324 | + |
|
| 325 | +### Anti-doublon |
|
| 326 | + |
|
| 327 | +Trois niveaux de protection : |
|
| 328 | + |
|
| 329 | +```python |
|
| 330 | +# 1. Charger tous les barcodes existants au démarrage |
|
| 331 | +existing = odoo_call("event.registration", "search_read", [[]], |
|
| 332 | + {"fields": ["barcode"]}) |
|
| 333 | +done = {r["barcode"] for r in existing if r.get("barcode")} |
|
| 334 | + |
|
| 335 | +# 2. Vérifier le barcode avant création |
|
| 336 | +if barcode in done: |
|
| 337 | + continue |
|
| 338 | + |
|
| 339 | +# 3. Réutiliser les commandes existantes (ne pas bloquer) |
|
| 340 | +existing_so = odoo_call("sale.order", "search_read", |
|
| 341 | + [[["client_order_ref", "=", ref]]], {"fields": ["id"]}) |
|
| 342 | +if existing_so: |
|
| 343 | + so = existing_so[0]["id"] # RÉUTILISER |
|
| 344 | +else: |
|
| 345 | + so = odoo_call("sale.order", "create", [{...}]) |
|
| 346 | +``` |
|
| 347 | + |
|
| 348 | +--- |
|
| 349 | + |
|
| 350 | +## 9. Limites Odoo Online (SaaS) |
|
| 351 | + |
|
| 352 | +| Possible | Impossible | |
|
| 353 | +|----------|-----------| |
|
| 354 | +| API JSON-RPC (`/jsonrpc`) | Accès SSH au serveur | |
|
| 355 | +| CRUD sur tous les modèles standards | Modules customs (pas de `__manifest__.py`) | |
|
| 356 | +| Modules Studio (custom fields, vues) | Accès direct PostgreSQL | |
|
| 357 | +| Imports CSV/XML | Modifications du core Odoo | |
|
| 358 | +| Webhooks sortants (Automatisations) | Installation de packages Python | |
|
| 359 | + |
|
| 360 | +--- |
|
| 361 | + |
|
| 362 | +## 10. Événements Odoo du Parc de la Luge |
|
| 363 | + |
|
| 364 | +| ID | Nom | |
|
| 365 | +|----|-----| |
|
| 366 | +| 1 | Parc de la Luge — Billetterie | |
|
| 367 | +| 2 | BON CADEAU | |
concepts/odoo-saas-limites.md
| ... | ... | @@ -0,0 +1,32 @@ |
| 1 | +# Contraintes Odoo Online (SaaS) |
|
| 2 | + |
|
| 3 | +> **Dernière mise à jour** : 2026-05-27 |
|
| 4 | +> **Périmètre** : Parc de la Luge — Odoo Online Standard |
|
| 5 | + |
|
| 6 | +## Ce qui est possible |
|
| 7 | + |
|
| 8 | +- API JSON-RPC : `https://<instance>.odoo.com/jsonrpc` |
|
| 9 | +- CRUD sur tous les modèles standards (sale.order, product.product, res.partner...) |
|
| 10 | +- Modules Studio (custom fields, vues) |
|
| 11 | +- Imports CSV/XML |
|
| 12 | +- Webhooks sortants (via Automatisations) |
|
| 13 | + |
|
| 14 | +## Ce qui est IMPOSSIBLE |
|
| 15 | + |
|
| 16 | +- **Pas d'accès au serveur** — pas de SSH, pas de fichiers Python custom |
|
| 17 | +- **Pas de modules customs** au sens classique (pas de `__manifest__.py` sur le serveur) |
|
| 18 | +- **Pas d'accès direct à la base PostgreSQL** |
|
| 19 | +- **Pas de modifications du core Odoo** |
|
| 20 | + |
|
| 21 | +## Conséquences pour les projets |
|
| 22 | + |
|
| 23 | +- Toute customisation passe par l'API JSON-RPC ou le Studio |
|
| 24 | +- Les extensions Chrome interagissent avec le DOM, pas avec le backend |
|
| 25 | +- Les automatismes passent par n8n (sur le VPS) qui appelle l'API |
|
| 26 | +- Les modules customs Odoo ne sont pas possibles sans Odoo.sh ou on-premise |
|
| 27 | + |
|
| 28 | +## Authentification API |
|
| 29 | + |
|
| 30 | +- API key : générée dans le profil utilisateur Odoo |
|
| 31 | +- Session : login via `/web/session/authenticate` |
|
| 32 | +- Pas d'OAuth2 natif |
concepts/security-vps.md
| ... | ... | @@ -0,0 +1,59 @@ |
| 1 | +--- |
|
| 2 | +title: Sécurité VPS |
|
| 3 | +created: 2026-05-19 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: concept |
|
| 6 | +tags: [security, infra] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Sécurité VPS |
|
| 12 | + |
|
| 13 | +## Firewall (UFW) |
|
| 14 | +- **Statut** : Actif |
|
| 15 | +- **Politique** : Deny incoming, Allow outgoing |
|
| 16 | +- **Ports autorisés** : 22 (SSH), 80 (HTTP), 443 (HTTPS) |
|
| 17 | +- **Logging** : Low |
|
| 18 | + |
|
| 19 | +## Fail2ban |
|
| 20 | +- **Jail active** : sshd |
|
| 21 | +- **Règles** : 3 tentatives max en 10 min → ban 1 heure |
|
| 22 | +- **Bilan actuel** : 36 échecs totaux, 8 bans totaux, 0 actif |
|
| 23 | + |
|
| 24 | +## Hardening SSH |
|
| 25 | +| Paramètre | Valeur | |
|
| 26 | +|---|---| |
|
| 27 | +| PermitRootLogin | yes (terminaux Hostinger) | |
|
| 28 | +| PasswordAuthentication | yes (terminaux Hostinger) | |
|
| 29 | +| MaxAuthTries | 3 | |
|
| 30 | +| LoginGraceTime | 30s | |
|
| 31 | +| X11Forwarding | no | |
|
| 32 | +| TCPForwarding | no | |
|
| 33 | +| HostbasedAuth | no | |
|
| 34 | +| PermitEmptyPasswords | no | |
|
| 35 | + |
|
| 36 | +## Kernel Hardening |
|
| 37 | +- `net.ipv4.conf.all.send_redirects = 0` |
|
| 38 | +- `net.ipv6.conf.all.accept_redirects = 0` |
|
| 39 | +- `net.ipv4.conf.all.accept_source_route = 0` |
|
| 40 | +- `net.ipv4.tcp_syncookies = 1` |
|
| 41 | +- `net.ipv4.conf.all.log_martians = 1` |
|
| 42 | + |
|
| 43 | +## Isolation Docker |
|
| 44 | +Tous les conteneurs bindés sur `127.0.0.1` uniquement : |
|
| 45 | + |
|
| 46 | +| Service | Port | |
|
| 47 | +|---------|------| |
|
| 48 | +| [[searxng]] | `127.0.0.1:62761` | |
|
| 49 | +| [[n8n]] | `127.0.0.1:5678` | |
|
| 50 | +| [[crawl4ai]] | `127.0.0.1:32771` | |
|
| 51 | +| [[qdrant]] | `127.0.0.1:32770` | |
|
| 52 | +| [[hermes-webui]] | `127.0.0.1:8787` | |
|
| 53 | + |
|
| 54 | +Seul [[traefik]] est en host network (ports 80/443) — unique point d'entrée externe. |
|
| 55 | +Docker bypass UFW → le binding `127.0.0.1` est la seule protection efficace. |
|
| 56 | + |
|
| 57 | +## Mises à jour |
|
| 58 | +- **Unattended-upgrades** : Actif (sécurité uniquement) |
|
| 59 | +- Dernière mise à jour : 2026-05-29 |
decisions/dashboard-public-api-patch.md
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +# Dashboard API public patch |
|
| 2 | + |
|
| 3 | +## Contexte |
|
| 4 | +Hermes Workspace a besoin d'accéder aux APIs du dashboard Hermes (sessions, skills, config, mcp, memory, jobs) sans authentification. |
|
| 5 | +Par défaut, le dashboard génère un token éphémère à chaque démarrage et protège toutes les APIs sauf `/api/status` et quelques autres. |
|
| 6 | + |
|
| 7 | +## Solution appliquée |
|
| 8 | +Patch dans `/usr/local/lib/hermes-agent/hermes_cli/web_server.py` — ajout des routes à `_PUBLIC_API_PATHS` : |
|
| 9 | + |
|
| 10 | +```python |
|
| 11 | +"/api/config", |
|
| 12 | +"/api/sessions", |
|
| 13 | +"/api/skills", |
|
| 14 | +"/api/mcp", |
|
| 15 | +"/api/memory", |
|
| 16 | +"/api/jobs", |
|
| 17 | +``` |
|
| 18 | + |
|
| 19 | +## Risques |
|
| 20 | +- Le dashboard est bindé sur `127.0.0.1:9119` → seuls les processus locaux peuvent y accéder |
|
| 21 | +- Les APIs restent protégées des accès externes (le dashboard n'est pas exposé sur internet) |
|
| 22 | +- Le patch sera perdu après une mise à jour de Hermes → réappliquer si nécessaire |
|
| 23 | + |
|
| 24 | +## Résultat |
|
| 25 | +Workspace passe de `mode=portable` à `mode=zero-fork` avec sessions, skills, config, memory, jobs disponibles. |
entities/conteneurs-docker.md
| ... | ... | @@ -0,0 +1,50 @@ |
| 1 | +--- |
|
| 2 | +title: Conteneurs Docker |
|
| 3 | +created: 2026-05-19 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: concept |
|
| 6 | +tags: [container, infra] |
|
| 7 | +sources: [CONTAINERS.md] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Conteneurs Docker |
|
| 12 | + |
|
| 13 | +## Inventaire complet |
|
| 14 | + |
|
| 15 | +| Stack | Conteneur | Image | Rôle | Port local | |
|
| 16 | +|-------|-----------|-------|------|------------| |
|
| 17 | +| traefik | traefik-traefik-1 | traefik:latest | Reverse proxy + HTTPS | `:80`, `:443` | |
|
| 18 | +| searxng-dm6i | searxng-dm6i-searxng-1 | ghcr.io/searxng/searxng:latest | Recherche web privée | `127.0.0.1:62761→8080` | |
|
| 19 | +| n8n-gdkj | n8n-n8n-1 | docker.n8n.io/n8nio/n8n | Automatisation | `127.0.0.1:5678` | |
|
| 20 | +| n8n-gdkj | n8n-sync-server-1 | n8n-sync-server | Serveur Python sync | `8855` (interne) | |
|
| 21 | +| crawl4ai-fhxz | crawl-crawl4ai-1 | unclecode/crawl4ai:latest | Extraction web LLM | `127.0.0.1:32771→11235` | |
|
| 22 | +| qdrant-5hvs | qdrant-qdrant-1 | qdrant/qdrant:latest | Base vectorielle | `127.0.0.1:32770→6333` | |
|
| 23 | +| hermes-webui | hermes-webui-proxy | nginx:alpine | Proxy WebUI | `127.0.0.1:8787` | |
|
| 24 | +| wiki | wiki-wiki-1 | nginx:alpine (custom) | Wiki web Docsify | `127.0.0.1:4570→80` | |
|
| 25 | + |
|
| 26 | +## Réseau |
|
| 27 | + |
|
| 28 | +- **Traefik** : Host network — seul point d'entrée externe |
|
| 29 | +- **Tous les autres** : Bridge, ports bindés sur `127.0.0.1` uniquement |
|
| 30 | +- **n8n-sync-server** : Réseau interne n8n, port 8855 non exposé |
|
| 31 | + |
|
| 32 | +## Sécurité |
|
| 33 | + |
|
| 34 | +Aucune exposition directe sur internet. Docker bypass UFW → tous les ports bindés |
|
| 35 | +sur `127.0.0.1`. Seul Traefik (host network) est joignable de l'extérieur sur 80/443. |
|
| 36 | + |
|
| 37 | +## Gestion |
|
| 38 | + |
|
| 39 | +```bash |
|
| 40 | +# Déploiement complet |
|
| 41 | +for dir in docker/*/; do (cd "$dir" && docker compose up -d); done |
|
| 42 | + |
|
| 43 | +# Stack individuelle |
|
| 44 | +cd docker/searxng-dm6i && docker compose up -d |
|
| 45 | +``` |
|
| 46 | + |
|
| 47 | +## Configuration |
|
| 48 | + |
|
| 49 | +Chaque stack a son `docker-compose.yml` dans `/root/hermes-config/docker/<nom-stack>/`. |
|
| 50 | +Les backups des volumes sont dans `/root/hermes-config/docker-volumes/` (n8n_data, searxng-config, sync_cache). |
entities/crawl4ai.md
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +--- |
|
| 2 | +title: Crawl4AI |
|
| 3 | +created: 2026-05-19 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [container, hermes, ai] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Crawl4AI |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Extracteur web optimisé LLM, sortie Markdown propre. Version 0.8.6. |
|
| 15 | + |
|
| 16 | +## Setup |
|
| 17 | +- **Conteneur** : `crawl-crawl4ai-1` |
|
| 18 | +- **Stack** : `docker/crawl4ai-fhxz/` |
|
| 19 | +- **Image** : unclecode/crawl4ai:latest |
|
| 20 | +- **Port** : `127.0.0.1:32771→11235` |
|
| 21 | +- **Endpoints** : `/crawl`, `/md`, `/llm/{url}` |
|
| 22 | + |
|
| 23 | +## Pipeline type |
|
| 24 | +[[searxng]] (cherche) → Crawl4AI (extrait en markdown) → [[hermes-agent]] (analyse) |
|
| 25 | + |
|
| 26 | +## Utilisation par Hermes |
|
| 27 | +Intégré nativement via `web_extract()` — aucune config nécessaire. |
|
| 28 | +L'agent enchaîne automatiquement `web_search()` → `web_extract()`. |
entities/hermes-agent.md
| ... | ... | @@ -0,0 +1,76 @@ |
| 1 | +--- |
|
| 2 | +title: Hermes Agent |
|
| 3 | +created: 2026-05-19 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [hermes, ai] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Hermes Agent |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Agent IA autonome open-source par Nous Research, installé sur [[vps-srv1672918]]. |
|
| 15 | +Point d'entrée : interface WebUI, [[telegram-bot]], ou CLI. |
|
| 16 | + |
|
| 17 | +## Configuration |
|
| 18 | +- **Provider** : DeepSeek (api.deepseek.com/v1) |
|
| 19 | +- **Modèle principal** : `deepseek-v4-pro` (orchestrateur, chef de projet) |
|
| 20 | +- **Modèle rapide** : `deepseek-v4-flash` (chercheur, dev, reviewer) |
|
| 21 | +- **Vision** : Google Gemini 2.5 Flash (via Google AI Studio) |
|
| 22 | +- **Fallback** : `nvidia/nemotron-3-super-120b-a12b:free` via OpenRouter |
|
| 23 | +- **Python** : 3.11.15, venv `/usr/local/lib/hermes-agent/venv/` |
|
| 24 | +- **Binaire** : `~/.local/bin/hermes` |
|
| 25 | +- **Config** : `~/.hermes/config.yaml` |
|
| 26 | +- **Env** : `~/.hermes/.env` |
|
| 27 | + |
|
| 28 | +## Architecture Kanban |
|
| 29 | + |
|
| 30 | +5 profils collaborant via un board Kanban : |
|
| 31 | + |
|
| 32 | +``` |
|
| 33 | +default (V4 Pro) → Orchestrateur — définit projets, crée tâches |
|
| 34 | +chef-de-projet (V4 Pro) → Coordonne un projet, décompose en sous-tâches |
|
| 35 | +chercheur (V4 Flash) → Recherche web/docs |
|
| 36 | +dev (V4 Flash) → Code, scripts, implémentation |
|
| 37 | +reviewer (V4 Flash) → Tests, validation, quality gates |
|
| 38 | +``` |
|
| 39 | + |
|
| 40 | +- **Board** : `kanban.db` (SQLite) |
|
| 41 | +- **Dispatcher** : intégré à la gateway |
|
| 42 | +- **Nesting** : max_spawn_depth=2 |
|
| 43 | +- **Wiki partagé** : `/root/wiki/` — accessible à tous les profils |
|
| 44 | + |
|
| 45 | +## Gateway |
|
| 46 | + |
|
| 47 | +- **Telegram** : [[telegram-bot]] @HeermesJod974Bot — 6 topics |
|
| 48 | +- **Webhooks** : supportés |
|
| 49 | +- **Cron** : jobs programmés (backup toutes les 6h, script `auto-backup.sh`) |
|
| 50 | + |
|
| 51 | +## Services intégrés |
|
| 52 | + |
|
| 53 | +| Service | Rôle | Accès | |
|
| 54 | +|---------|------|-------| |
|
| 55 | +| [[searxng]] | Recherche web (web_search) | `127.0.0.1:62761` | |
|
| 56 | +| [[crawl4ai]] | Extraction contenu (web_extract) | `127.0.0.1:32771` | |
|
| 57 | +| [[qdrant]] | Base vectorielle (RAG) | `127.0.0.1:32770` | |
|
| 58 | +| [[n8n]] | Automatisations externes | `127.0.0.1:5678` | |
|
| 59 | + |
|
| 60 | +## Outils actifs |
|
| 61 | + |
|
| 62 | +Terminal, File, Memory, Skills, Kanban, Cron, Web Search/Extract, |
|
| 63 | +Code Execution, Vision, Session Search, Delegation, Telegram |
|
| 64 | + |
|
| 65 | +## Mémoire |
|
| 66 | + |
|
| 67 | +- `~/.hermes/memories/MEMORY.md` — notes techniques, conventions |
|
| 68 | +- `~/.hermes/memories/USER.md` — profil utilisateur, préférences |
|
| 69 | +- Pas de backend externe (tout en fichiers) |
|
| 70 | + |
|
| 71 | +## Backup |
|
| 72 | + |
|
| 73 | +- Repo GitHub privé : `github.com/Jod97426/hermes-config` |
|
| 74 | +- Script `backup.sh` : sauvegarde tout (config, profils, skills, kanban, wiki, docker, cron) |
|
| 75 | +- Script `auto-backup.sh` : cron toutes les 6h + màj README + push |
|
| 76 | +- Script `restore.sh` : restauration complète VPS vierge |
entities/hermes-webui.md
| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | +--- |
|
| 2 | +title: Hermes WebUI |
|
| 3 | +created: 2026-05-19 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [hermes, container] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Hermes WebUI |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Interface web pour [[hermes-agent]], accessible en HTTPS via [[traefik]]. |
|
| 15 | + |
|
| 16 | +## Setup |
|
| 17 | +- **Source** : `/opt/hermes-webui` (GitHub: nesquena/hermes-webui) |
|
| 18 | +- **Service** : systemd `hermes-webui`, auto-restart, démarre au boot |
|
| 19 | +- **Python** : `/usr/local/lib/hermes-agent/venv/bin/python3` |
|
| 20 | +- **Écoute** : `0.0.0.0:8787` (protégé par UFW) |
|
| 21 | +- **Proxy** : Conteneur `hermes-webui-proxy` (nginx:alpine) en host network |
|
| 22 | + |
|
| 23 | +## Accès |
|
| 24 | +- **Public** : `https://hermes.srv1672918.hstgr.cloud` (Traefik → nginx → :8787) |
|
| 25 | +- **Local** : `http://127.0.0.1:8787` |
|
| 26 | + |
|
| 27 | +## Fonctionnalités |
|
| 28 | +- Chat avec streaming SSE |
|
| 29 | +- Gestion des sessions multiples |
|
| 30 | +- Multi-provider LLM |
|
| 31 | +- Workspace file browser |
|
| 32 | +- 8 thèmes + custom CSS |
|
| 33 | +- Mobile responsive |
|
| 34 | +- Panneau Insights (token usage) |
|
| 35 | + |
|
| 36 | +## Stack Docker |
|
| 37 | +```yaml |
|
| 38 | +# docker/hermes-webui/docker-compose.yml |
|
| 39 | +services: |
|
| 40 | + proxy: |
|
| 41 | + image: nginx:alpine |
|
| 42 | + network_mode: host |
|
| 43 | + volumes: |
|
| 44 | + - ./nginx.conf:/etc/nginx/nginx.conf:ro |
|
| 45 | +``` |
|
| 46 | +Le conteneur proxy relaie vers `127.0.0.1:8787` (le service systemd). |
entities/kanban.md
| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | +--- |
|
| 2 | +title: Kanban |
|
| 3 | +created: 2026-05-29 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [hermes, workflow] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Kanban |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Système de gestion de projet interne à [[hermes-agent]], basé sur un board Kanban |
|
| 15 | +SQLite et 5 profils collaboratifs. |
|
| 16 | + |
|
| 17 | +## Profils |
|
| 18 | + |
|
| 19 | +| Profil | Modèle | Rôle | |
|
| 20 | +|--------|--------|------| |
|
| 21 | +| default | deepseek-v4-pro | Orchestrateur — définit projets, crée tâches | |
|
| 22 | +| chef-de-projet | deepseek-v4-pro | Coordonne UN projet, crée sous-tâches | |
|
| 23 | +| chercheur | deepseek-v4-flash | Recherche web/docs (tools: web, file, clarify) | |
|
| 24 | +| dev | deepseek-v4-flash | Code, scripts (tools: terminal, file, code, web) | |
|
| 25 | +| reviewer | deepseek-v4-flash | Tests, validation (tools: terminal, file, code, web) | |
|
| 26 | + |
|
| 27 | +## Flux type |
|
| 28 | + |
|
| 29 | +``` |
|
| 30 | +User → default (définit le projet) |
|
| 31 | + → Kanban: task pour chef-de-projet |
|
| 32 | + → chef-de-projet crée sous-tâches |
|
| 33 | + → chercheur (analyse) |
|
| 34 | + → dev (implémentation) |
|
| 35 | + → reviewer (validation) |
|
| 36 | +``` |
|
| 37 | + |
|
| 38 | +## Configuration |
|
| 39 | +- **Board** : `kanban.db` (SQLite) |
|
| 40 | +- **Dispatcher** : intégré à la gateway Hermes |
|
| 41 | +- **Nesting** : max_spawn_depth=2 (un orchestrateur peut déléguer à un autre) |
|
| 42 | +- **Wiki** : `/root/wiki/` partagé entre tous les profils |
|
| 43 | + |
|
| 44 | +## Fichiers clés |
|
| 45 | +- `~/.hermes/config.yaml` → section `delegation` |
|
| 46 | +- `~/.hermes/profiles/{chef-de-projet,chercheur,dev,reviewer}/` → configs restreintes |
|
| 47 | +- `/root/wiki/` → mémoire collective |
entities/n8n.md
| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | +--- |
|
| 2 | +title: n8n |
|
| 3 | +created: 2026-05-29 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [container, automation] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# n8n |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Plateforme d'automatisation low-code, intégrée avec Odoo Online pour le Parc de la Luge. |
|
| 15 | + |
|
| 16 | +## Setup |
|
| 17 | +- **Stack** : `docker/n8n-gdkj/` |
|
| 18 | +- **Conteneur principal** : `n8n-n8n-1` — image `docker.n8n.io/n8nio/n8n` |
|
| 19 | +- **Conteneur sync** : `n8n-sync-server-1` — serveur Python custom (port 8855 interne) |
|
| 20 | +- **Port** : `127.0.0.1:5678` |
|
| 21 | +- **Accès public** : `https://n8n-gdkj.srv1672918.hstgr.cloud` (via [[traefik]]) |
|
| 22 | +- **Volume** : `n8n_n8n_data` — workflows, credentials, données |
|
| 23 | + |
|
| 24 | +## Workflows principaux |
|
| 25 | +- **BilletWeb → Odoo** : Sync des ventes de billets |
|
| 26 | +- **Odoo → externe** : Webhooks et automatisations |
|
| 27 | +- **Scripts Python** : Serveur sync pour opérations complexes |
|
| 28 | + |
|
| 29 | +## Sécurité |
|
| 30 | +- Port bindé sur `127.0.0.1` |
|
| 31 | +- Accès web via Traefik avec `noindex`, HSTS, Permissions-Policy |
|
| 32 | +- Secure cookie désactivé (cookie same-site car reverse proxy) |
|
| 33 | + |
|
| 34 | +## Backup |
|
| 35 | +- Volume `n8n_n8n_data` sauvegardé dans `/root/hermes-config/docker-volumes/n8n_n8n_data.tar.gz` |
|
| 36 | +- Workflows exportables en JSON depuis l'interface |
entities/qdrant.md
| ... | ... | @@ -0,0 +1,35 @@ |
| 1 | +--- |
|
| 2 | +title: Qdrant |
|
| 3 | +created: 2026-05-29 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [container, ai, search] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Qdrant |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Base de données vectorielle pour la recherche sémantique et RAG (Retrieval-Augmented Generation). |
|
| 15 | + |
|
| 16 | +## Setup |
|
| 17 | +- **Conteneur** : `qdrant-qdrant-1` |
|
| 18 | +- **Stack** : `docker/qdrant-5hvs/` |
|
| 19 | +- **Image** : qdrant/qdrant:latest |
|
| 20 | +- **Port** : `127.0.0.1:32770→6333` (API REST) |
|
| 21 | +- **Port gRPC** : `6334` (interne, non exposé) |
|
| 22 | + |
|
| 23 | +## Utilisation |
|
| 24 | +- Stockage d'embeddings pour la recherche contextuelle |
|
| 25 | +- Utilisé par les pipelines RAG de [[hermes-agent]] |
|
| 26 | +- Intégré nativement dans les flows de recherche |
|
| 27 | + |
|
| 28 | +## API |
|
| 29 | +```bash |
|
| 30 | +# Health check |
|
| 31 | +curl http://127.0.0.1:32770/health |
|
| 32 | + |
|
| 33 | +# List collections |
|
| 34 | +curl http://127.0.0.1:32770/collections |
|
| 35 | +``` |
entities/searxng.md
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +--- |
|
| 2 | +title: SearXNG |
|
| 3 | +created: 2026-05-19 |
|
| 4 | +updated: 2026-05-19 |
|
| 5 | +type: entity |
|
| 6 | +tags: [container, hermes] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# SearXNG |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Métamoteur de recherche privé, utilisé par [[hermes-agent]] comme backend de recherche. |
|
| 15 | + |
|
| 16 | +## Setup |
|
| 17 | +- **Conteneur** : `searxng-dm6i-searxng-1` |
|
| 18 | +- **Image** : ghcr.io/searxng/searxng:latest |
|
| 19 | +- **Port** : `127.0.0.1:62761` |
|
| 20 | +- **Config** : Volume Docker `searxng-dm6i_searxng-config` |
|
| 21 | +- **Formats** : HTML + JSON |
|
| 22 | + |
|
| 23 | +## Utilisation |
|
| 24 | +```bash |
|
| 25 | +curl "http://127.0.0.1:62761/search?format=json&q=requête" |
|
| 26 | +``` |
|
| 27 | + |
|
| 28 | +Remplace Tavily/Exa pour la recherche web. |
entities/telegram-bot.md
| ... | ... | @@ -0,0 +1,42 @@ |
| 1 | +--- |
|
| 2 | +title: Telegram Bot |
|
| 3 | +created: 2026-05-29 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [hermes, telegram] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Telegram Bot |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Bot Telegram connecté à la gateway [[hermes-agent]] pour interagir avec l'agent |
|
| 15 | +via messagerie instantanée. |
|
| 16 | + |
|
| 17 | +## Identifiants |
|
| 18 | +- **Username** : @HeermesJod974Bot |
|
| 19 | +- **User ID** : 7738179065 (Jérôme Delgard) |
|
| 20 | +- **Groupe** : -1003298293378 |
|
| 21 | + |
|
| 22 | +## Topics du groupe |
|
| 23 | + |
|
| 24 | +| # | Nom | Thread ID | Usage | |
|
| 25 | +|---|------|-----------|-------| |
|
| 26 | +| 🏠 | Général | 3 | Discussion générale | |
|
| 27 | +| 📦 | Projets | 6 | Suivi des projets | |
|
| 28 | +| 🚨 | Alertes | 9 | Notifications critiques | |
|
| 29 | +| 🦾 | DevOps | 11 | Infra, déploiements | |
|
| 30 | +| 🚀 | Releases | 12 | Mises en production | |
|
| 31 | +| 💾 | Sauvegardes | 68 | Notifs backup auto | |
|
| 32 | + |
|
| 33 | +## Intégration |
|
| 34 | +- **Gateway** : La gateway Hermes consomme les updates Telegram |
|
| 35 | +- **Notifications cron** : Le job de backup (toutes les 6h) notifie dans le topic 💾 Sauvegardes |
|
| 36 | +- **Config** : `TELEGRAM_BOT_TOKEN` dans `.env`, `platforms.telegram` dans `config.yaml` |
|
| 37 | +- **Fallback** : `python-telegram-bot` v22.6 disponible pour scripts custom |
|
| 38 | + |
|
| 39 | +## Pièges |
|
| 40 | +- La gateway consomme les updates → la stopper avant d'utiliser `getUpdates` directement |
|
| 41 | +- `allowed_users` doit être défini via variable d'environnement (pas `hermes config set`) |
|
| 42 | +- `extra: {}` vide dans la config Telegram → crash gateway TEMPFAIL 75 |
entities/traefik.md
| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | +--- |
|
| 2 | +title: Traefik |
|
| 3 | +created: 2026-05-19 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [container, infra] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# Traefik |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Proxy inverse et équilibreur de charge, gère HTTPS et routage pour tous les services exposés. |
|
| 15 | + |
|
| 16 | +## Setup |
|
| 17 | +- **Conteneur** : `traefik-traefik-1` |
|
| 18 | +- **Stack** : `docker/traefik/` |
|
| 19 | +- **Réseau** : Host network (partage les ports de l'hôte) |
|
| 20 | +- **Entrypoints** : HTTP (80), HTTPS (443) |
|
| 21 | +- **Certificats** : Let's Encrypt (⚠️ rate-limité sur le wildcard `*.hstgr.cloud`) |
|
| 22 | +- **Provider** : Docker (labels sur les conteneurs) |
|
| 23 | + |
|
| 24 | +## Routes actives |
|
| 25 | + |
|
| 26 | +| Domaine | Service | Backend | |
|
| 27 | +|---------|---------|---------| |
|
| 28 | +| `hermes.srv1672918.hstgr.cloud` | [[hermes-webui]] | `127.0.0.1:8787` | |
|
| 29 | +| `n8n-gdkj.srv1672918.hstgr.cloud` | [[n8n]] | `127.0.0.1:5678` | |
|
| 30 | + |
|
| 31 | +## Sécurité |
|
| 32 | + |
|
| 33 | +- Robots tag `noindex, nofollow` sur n8n |
|
| 34 | +- HSTS activé sur n8n |
|
| 35 | +- Permissions-Policy restrictives |
|
| 36 | +- Tous les services backend en `127.0.0.1` |
entities/vps-srv1672918.md
| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | +--- |
|
| 2 | +title: VPS Hostinger srv1672918 |
|
| 3 | +created: 2026-05-19 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [infra, hermes] |
|
| 7 | +sources: [] |
|
| 8 | +confidence: high |
|
| 9 | +--- |
|
| 10 | + |
|
| 11 | +# VPS Hostinger srv1672918 |
|
| 12 | + |
|
| 13 | +## Overview |
|
| 14 | +Serveur VPS Hostinger Ubuntu 24.04 LTS hébergeant [[hermes-agent]], [[telegram-bot]], [[traefik]], et 6 stacks Docker (7 conteneurs). |
|
| 15 | + |
|
| 16 | +## Hardware |
|
| 17 | +- **Hostname** : srv1672918 |
|
| 18 | +- **OS** : Ubuntu 24.04.4 LTS (Noble Numbat) |
|
| 19 | +- **Kernel** : 6.8.0-117-generic |
|
| 20 | +- **CPU** : AMD EPYC 7543P — 2 cores |
|
| 21 | +- **RAM** : 7.8 GiB |
|
| 22 | +- **Disque** : 96 GB — 25 GB utilisé (26%), 72 GB libre |
|
| 23 | +- **IP** : 72.61.195.45 / 2a02:4780:28:6b4c::1 |
|
| 24 | + |
|
| 25 | +## Réseau |
|
| 26 | +- **Fuseau** : Indian/Reunion (UTC+4) |
|
| 27 | +- **Proxy inverse** : [[traefik]] (ports 80, 443) |
|
| 28 | +- **Firewall** : UFW actif, deny incoming par défaut |
|
| 29 | +- **SSH** : Port 22, protégé par Fail2ban |
|
| 30 | +- **Accès** : Terminal web Hostinger + SSH |
|
| 31 | + |
|
| 32 | +## Services déployés |
|
| 33 | + |
|
| 34 | +| Service | Rôle | Type | Accès | |
|
| 35 | +|---------|------|------|-------| |
|
| 36 | +| [[hermes-agent]] | Agent IA autonome | Python/systemd | CLI + [[telegram-bot]] + [[hermes-webui]] | |
|
| 37 | +| [[hermes-webui]] | Interface web | Docker (nginx) | `https://hermes.srv1672918.hstgr.cloud` | |
|
| 38 | +| [[telegram-bot]] | Bot @HeermesJod974Bot | Hermes Gateway | Groupe privé 6 topics | |
|
| 39 | +| [[searxng]] | Recherche web privée | Docker | `127.0.0.1:62761` | |
|
| 40 | +| [[n8n]] | Automatisation | Docker | `127.0.0.1:5678` | |
|
| 41 | +| [[crawl4ai]] | Extraction web LLM | Docker | `127.0.0.1:32771` | |
|
| 42 | +| [[qdrant]] | Base vectorielle | Docker | `127.0.0.1:32770` | |
|
| 43 | +| [[traefik]] | Reverse proxy + HTTPS | Docker | Ports 80/443 | |
|
| 44 | +| [[kanban]] | Gestion de projets Hermes | SQLite | Interne (5 profils) | |
|
| 45 | +| Fail2ban | Protection SSH | systemd | Jail sshd | |
|
| 46 | +| Unattended-upgrades | Mises à jour auto | systemd | Sécurité uniquement | |
entities/wiki-web.md
| ... | ... | @@ -0,0 +1,53 @@ |
| 1 | +--- |
|
| 2 | +title: Wiki Web |
|
| 3 | +created: 2026-05-29 |
|
| 4 | +updated: 2026-05-29 |
|
| 5 | +type: entity |
|
| 6 | +tags: [wiki, container, hermes, infra] |
|
| 7 | +sources: [/docker/wiki/] |
|
| 8 | +--- |
|
| 9 | + |
|
| 10 | +# Wiki Web (Docsify) |
|
| 11 | + |
|
| 12 | +Interface web du wiki partagé Hermes, accessible publiquement (protégé par noindex). |
|
| 13 | + |
|
| 14 | +## URLs |
|
| 15 | + |
|
| 16 | +- **Production** : `https://wiki.delgard.cloud` |
|
| 17 | +- **Locale** : `http://127.0.0.1:4570` |
|
| 18 | + |
|
| 19 | +## Stack |
|
| 20 | + |
|
| 21 | +- **Base** : `nginx:alpine` (image custom dans `/docker/wiki/Dockerfile`) |
|
| 22 | +- **Renderer** : [Docsify](https://docsify.js.org) (markdown côté client, zero build) |
|
| 23 | +- **Source** : `/root/wiki/` monté en read-only (`:ro`) |
|
| 24 | +- **Reverse proxy** : Traefik avec Let's Encrypt |
|
| 25 | + |
|
| 26 | +## Fichiers |
|
| 27 | + |
|
| 28 | +``` |
|
| 29 | +/docker/wiki/ |
|
| 30 | +├── .env # TZ, TRAEFIK_HOST, COMPOSE_PROJECT_NAME |
|
| 31 | +├── Dockerfile # FROM nginx:alpine |
|
| 32 | +├── docker-compose.yml # Port 4570:80, labels Traefik |
|
| 33 | +├── index.html # Docsify SPA avec thème sombre |
|
| 34 | +└── nginx.conf # try_files → SPA fallback |
|
| 35 | +``` |
|
| 36 | + |
|
| 37 | +## Particularités |
|
| 38 | + |
|
| 39 | +- **Read-only** : le volume `/root/wiki` est en `:ro` → pas de modification via le web |
|
| 40 | +- **Recherche** : activée côté client (docsify search plugin) |
|
| 41 | +- **Wikilinks** : les `[[liens]]` sont gérés nativement par Docsify |
|
| 42 | +- **SEO** : `X-Robots-Tag: noindex, nofollow` (wiki privé) |
|
| 43 | +- **Syntax highlighting** : bash, python, yaml, json, docker |
|
| 44 | + |
|
| 45 | +## Commandes |
|
| 46 | + |
|
| 47 | +```bash |
|
| 48 | +# Redémarrer après modif du wiki |
|
| 49 | +docker compose -f /docker/wiki/docker-compose.yml restart |
|
| 50 | + |
|
| 51 | +# Reconstruire l'image |
|
| 52 | +docker compose -f /docker/wiki/docker-compose.yml up -d --build |
|
| 53 | +``` |
index.md
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +# Wiki Index — Hermes Agent System |
|
| 2 | + |
|
| 3 | +> Mémoire collective partagée entre tous les profils. |
|
| 4 | +> Dernière mise à jour : 2026-05-29 |
|
| 5 | + |
|
| 6 | +## concepts/ — Savoir durable et contraintes |
|
| 7 | +- `odoo-saas-limites.md` — Ce qui est possible/impossible avec Odoo Online |
|
| 8 | +- `security-vps.md` — Sécurité VPS complète |
|
| 9 | + |
|
| 10 | +## entities/ — APIs, services, infrastructure |
|
| 11 | +- `vps-srv1672918.md` — Infrastructure VPS Hostinger |
|
| 12 | +- `hermes-agent.md` — Framework d'agents (config, profils, outils) |
|
| 13 | +- `kanban.md` — Système de gestion de projet interne |
|
| 14 | +- `telegram-bot.md` — Bot @HeermesJod974Bot et topics |
|
| 15 | +- `conteneurs-docker.md` — Inventaire des 7 conteneurs Docker |
|
| 16 | +- `traefik.md` — Reverse proxy et certificats HTTPS |
|
| 17 | +- `searxng.md` — Moteur de recherche web privé |
|
| 18 | +- `crawl4ai.md` — Extraction web optimisée LLM |
|
| 19 | +- `qdrant.md` — Base vectorielle pour RAG |
|
| 20 | +- `n8n.md` — Plateforme d'automatisation BilletWeb/Odoo |
|
| 21 | +- `hermes-webui.md` — Interface web de chat |
|
| 22 | +- `wiki-web.md` — Interface web du wiki (Docsify) |
|
| 23 | + |
|
| 24 | +## decisions/ — Choix architecturaux |
|
| 25 | +*(à remplir au fil des projets)* |
|
| 26 | + |
|
| 27 | +## patterns/ — Solutions éprouvées |
|
| 28 | +*(à remplir au fil des projets)* |
log.md
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | +# Wiki Log |
|
| 2 | + |
|
| 3 | +> Chronological record of all wiki actions. Format: `## [YYYY-MM-DD] action | subject` |
|
| 4 | + |
|
| 5 | +## [2026-05-29] update | Mise à jour complète de l'infrastructure |
|
| 6 | + |
|
| 7 | +## [2026-05-29] create | Conteneur wiki-web (Docsify + nginx) |
|
| 8 | +- Créé le conteneur Wiki Web dans `/docker/wiki/` |
|
| 9 | +- Stack : nginx:alpine + Docsify (renderer markdown côté client) |
|
| 10 | +- Exposé sur `wiki.delgard.cloud` via Traefik/Let's Encrypt |
|
| 11 | +- Volume `/root/wiki` monté en read-only |
|
| 12 | +- Updated entities/vps-srv1672918.md — kernel 117, disque 26%, 12 services listés |
|
| 13 | +- Updated entities/conteneurs-docker.md — 7 conteneurs, ports exacts, n8n+Qdrant |
|
| 14 | +- Updated entities/hermes-agent.md — Kanban, profils, Telegram, cron, backup |
|
| 15 | +- Updated entities/traefik.md — routes n8n+webui, HSTS |
|
| 16 | +- Updated entities/crawl4ai.md — port 32771, v0.8.6 |
|
| 17 | +- Updated entities/hermes-webui.md — proxy Docker nginx |
|
| 18 | +- Updated concepts/security-vps.md — ports exacts, bilan Fail2ban |
|
| 19 | +- Created entities/n8n.md — automatisation BilletWeb/Odoo |
|
| 20 | +- Created entities/qdrant.md — base vectorielle RAG |
|
| 21 | +- Created entities/telegram-bot.md — bot, topics, pièges |
|
| 22 | +- Created entities/kanban.md — 5 profils, flux, config |
|
| 23 | +- Updated index.md — 11 entités, 2 concepts |
|
| 24 | + |
|
| 25 | +## [2026-05-19] create | Wiki initialized |
|
| 26 | +- Domain: Hermes VPS — configuration, skills, conteneurs, sécurité |
|
| 27 | +- Structure created with SCHEMA.md, index.md, log.md |
|
| 28 | + |
|
| 29 | +## [2026-05-19] ingest | Initialisation complète du wiki |
|
| 30 | +- Created entities/vps-srv1672918.md — fiche serveur |
|
| 31 | +- Created entities/hermes-agent.md — config agent IA |
|
| 32 | +- Created entities/conteneurs-docker.md — inventaire conteneurs |
|
| 33 | +- Created entities/hermes-webui.md — interface web |
|
| 34 | +- Created entities/searxng.md — recherche privée |
|
| 35 | +- Created entities/crawl4ai.md — extraction web |
|
| 36 | +- Created entities/traefik.md — reverse proxy |
|
| 37 | +- Created concepts/security-vps.md — hardening complet |
|
| 38 | +- Updated index.md — 7 entités, 1 concept |