# CLAUDE.md — Projet API-KA ## Vision du projet **API-KA** est la plateforme centrale légendaire de l'écosystème KA. Elle collecte, sauvegarde et centralise **chaque jour** les données de tous les services KA (lou-ka, immo-ka, food-ka, auto-ka, fabri-ka, resto-ka, sorti-ka, crea-ka) dans une base de données unifiée, puis les expose via une API publique accessible sur **www.api-ka.com** (tunnel ngrok). **Tout le déploiement se fait sur le node `m3u96b`.** Aucun service de production ne tourne ailleurs. ## Auteur — OBLIGATOIRE dans chaque fichier Chaque fichier de code (Python, JS, SQL, shell, config, etc.) DOIT commencer par un en-tête d'auteur. Règle non négociable — aucun fichier sans en-tête. **Python / Shell / SQL / YAML :** ``` # ============================================ # Projet : API-KA # Fichier : # Node : m3u96b # Author : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Date : # ============================================ ``` **JavaScript / TypeScript :** ```javascript /** * ============================================ * Projet : API-KA * Fichier : * Node : m3u96b * Author : Simon-Pierre Boucher * Contact : contact@spboucher.ai * Date : * ============================================ */ ``` ## Node de déploiement : m3u96b - **Node cible unique** : `m3u96b`. Tous les services (API, scheduler, base de données, tunnel ngrok) tournent sur ce node. - Répertoire de déploiement : `/opt/api-ka/` sur m3u96b. - Données et backups : `/opt/api-ka/data/` et `/opt/api-ka/data/backups/`. - Logs centralisés : `/opt/api-ka/logs/`. - Les services sont gérés par **systemd** sur m3u96b (voir section "Services systemd"). - Toute commande de déploiement, cron ou service doit référencer explicitement m3u96b dans sa documentation et ses logs (`hostname` vérifié au démarrage : si le hostname n'est pas `m3u96b`, le service refuse de démarrer en mode production et log un avertissement). ### Vérification du node au démarrage (obligatoire dans chaque service) ```python import socket, os REQUIRED_NODE = "m3u96b" def verify_node(): hostname = socket.gethostname() if os.getenv("APP_ENV") == "production" and hostname != REQUIRED_NODE: raise RuntimeError(f"API-KA doit tourner sur {REQUIRED_NODE}, node actuel : {hostname}") ``` ## Sources de données (les 8 services KA) | Service | Table | Variable source | Fréquence | |-----------|------------------|----------------------|-------------| | lou-ka | `louka_data` | `LOUKA_SOURCE_URL` | Quotidienne | | immo-ka | `immoka_data` | `IMMOKA_SOURCE_URL` | Quotidienne | | food-ka | `foodka_data` | `FOODKA_SOURCE_URL` | Quotidienne | | auto-ka | `autoka_data` | `AUTOKA_SOURCE_URL` | Quotidienne | | fabri-ka | `fabrika_data` | `FABRIKA_SOURCE_URL` | Quotidienne | | resto-ka | `restoka_data` | `RESTOKA_SOURCE_URL` | Quotidienne | | sorti-ka | `sortika_data` | `SORTIKA_SOURCE_URL` | Quotidienne | | crea-ka | `creaka_data` | `CREAKA_SOURCE_URL` | Quotidienne | **RÈGLE CRITIQUE : les données de chaque service DOIVENT être sauvegardées tous les jours, sans exception.** - Chaque exécution quotidienne est journalisée. - Tout échec de collecte est détecté, loggé et relancé automatiquement (3 tentatives, backoff exponentiel : 30s → 2min → 10min). - Si un service échoue après 3 tentatives, une entrée `status=failed` est écrite dans `collection_runs` et une alerte est ajoutée à `logs/alerts.log`. Le lendemain, le job tente automatiquement un rattrapage (backfill) des dates manquées. ## Architecture ``` /opt/api-ka/ # sur le node m3u96b ├── CLAUDE.md ├── README.md ├── .env # jamais commité ├── .env.example ├── requirements.txt ├── src/ │ ├── config.py # chargement .env + vérification node m3u96b │ ├── collectors/ │ │ ├── base_collector.py # classe abstraite : fetch, validate, save, retry │ │ ├── louka_collector.py │ │ ├── immoka_collector.py │ │ ├── foodka_collector.py │ │ ├── autoka_collector.py │ │ ├── fabrika_collector.py │ │ ├── restoka_collector.py │ │ ├── sortika_collector.py │ │ └── creaka_collector.py │ ├── database/ │ │ ├── models.py # SQLAlchemy : 8 tables données + collection_runs │ │ ├── db.py # engine, session, init, healthcheck │ │ └── migrations/ # Alembic │ ├── api/ │ │ ├── main.py # FastAPI, monté derrière ngrok │ │ ├── routes/ │ │ │ ├── services.py # /api/v1/{service}... │ │ │ ├── runs.py # /api/v1/runs │ │ │ └── health.py # /health (inclut node, dernière collecte, db) │ │ └── middleware/ │ │ ├── logging.py # log de chaque requête │ │ └── ratelimit.py # protection basique de l'API publique │ ├── scheduler/ │ │ ├── daily_job.py # orchestre les 8 collecteurs │ │ └── backfill.py # rattrapage des dates manquées │ └── utils/ │ ├── logger.py # logs JSON structurés, rotation quotidienne │ ├── retry.py # décorateur retry avec backoff │ └── backup.py # dump quotidien horodaté ├── scripts/ │ ├── deploy_m3u96b.sh # déploiement complet sur le node │ ├── run_daily_backup.sh │ ├── start_ngrok.sh # tunnel www.api-ka.com avec auto-restart │ └── healthcheck.sh # utilisé par systemd / monitoring ├── systemd/ │ ├── apika-api.service │ ├── apika-scheduler.service │ └── apika-ngrok.service ├── data/ │ └── backups/YYYY-MM-DD/ # dumps quotidiens par service ├── logs/ └── tests/ ``` ## Base de données - **Moteur** : PostgreSQL 16 sur m3u96b (SQLite acceptable uniquement en dev local). - Base : `apika`, utilisateur dédié `apika_user`, accès restreint à localhost sur m3u96b. ### Schéma des tables de données (identique pour les 8 services) | Colonne | Type | Détail | |----------------|-------------|------------------------------------------| | `id` | BIGSERIAL | Clé primaire | | `payload` | JSONB | Données brutes du service | | `source` | TEXT | Nom du service (ex. `louka`) | | `collected_at` | TIMESTAMPTZ | Horodatage exact de la collecte | | `date_key` | DATE | Date logique de la collecte (index) | | `checksum` | TEXT | SHA-256 du payload (déduplication) | Index : `(date_key)`, `(source, date_key)`, unique sur `(source, date_key, checksum)`. ### Table `collection_runs` `id`, `service`, `date_key`, `status` (`success` / `failed` / `retried`), `records_count`, `duration_seconds`, `error_message`, `node` (toujours `m3u96b`), `started_at`, `finished_at`. ### Règles - **Append-only** : aucune suppression destructive, l'historique complet est conservé. - **Backup quotidien** : `pg_dump` horodaté par service dans `data/backups/YYYY-MM-DD/`, compressé (`.sql.gz`), conservé 90 jours minimum. - Vérification d'intégrité hebdomadaire : comparaison du nombre de `date_key` distincts vs jours écoulés — toute journée manquante déclenche un backfill. ## Collecte quotidienne (scheduler) - Job planifié **tous les jours à 02:00 (heure du node m3u96b)** via `apika-scheduler.service` (APScheduler) — cron système en fallback. - Les 8 collecteurs s'exécutent en parallèle mais de façon **indépendante** : l'échec d'un service ne bloque jamais les autres. - Pipeline par collecteur : `fetch → validate → checksum → insert → backup → log run`. - À la fin du run global : résumé dans `collection_runs` + `logs/daily_YYYY-MM-DD.log` + mise à jour de `/health`. - `backfill.py` s'exécute juste après le job quotidien et rattrape automatiquement toute date manquée des 7 derniers jours. ## API publique (www.api-ka.com via ngrok) - Framework : **FastAPI** (docs auto sur `/docs`, OpenAPI sur `/openapi.json`). - Serveur : `uvicorn` sur `127.0.0.1:8000` (jamais exposé directement — seul ngrok est public). - Tunnel : `ngrok http --domain=www.api-ka.com 8000` — le domaine doit être réservé dans le dashboard ngrok (compte payant requis pour un domaine personnalisé) et le `NGROK_AUTHTOKEN` configuré sur m3u96b. - `apika-ngrok.service` redémarre le tunnel automatiquement en cas de coupure (`Restart=always`). ### Endpoints | Méthode | Route | Description | |---------|-----------------------------------------|----------------------------------------------------| | GET | `/` | Statut de la plateforme + version | | GET | `/health` | Node (m3u96b), état DB, dernière collecte par service | | GET | `/api/v1/{service}` | Données paginées d'un service | | GET | `/api/v1/{service}/latest` | Dernière collecte du service | | GET | `/api/v1/{service}/date/{YYYY-MM-DD}` | Données d'une date précise | | GET | `/api/v1/{service}/stats` | Nb d'enregistrements par jour, dernière réussite | | GET | `/api/v1/runs` | Historique des collectes (filtrable par service/statut) | - `{service}` ∈ `louka`, `immoka`, `foodka`, `autoka`, `fabrika`, `restoka`, `sortika`, `creaka` — toute autre valeur → 404. - Versionnement `/api/v1/`, pagination `?page=&limit=` (limit max 500). - Format de réponse uniforme : ```json { "success": true, "data": [...], "meta": { "page": 1, "limit": 100, "total": 4200, "node": "m3u96b" } } ``` - Rate limiting basique (ex. 120 req/min/IP) car l'API est publique via ngrok. ## Services systemd (sur m3u96b) | Service | Rôle | Restart | |--------------------------|-----------------------------------|----------| | `apika-api.service` | uvicorn FastAPI :8000 | always | | `apika-scheduler.service`| Job quotidien 02:00 + backfill | always | | `apika-ngrok.service` | Tunnel www.api-ka.com | always | Commandes : `sudo systemctl enable --now apika-api apika-scheduler apika-ngrok`, statut via `systemctl status apika-*`. ## Configuration (.env sur m3u96b) ``` APP_ENV=production NODE_NAME=m3u96b DATABASE_URL=postgresql://apika_user:***@localhost:5432/apika NGROK_AUTHTOKEN=... NGROK_DOMAIN=www.api-ka.com API_PORT=8000 DAILY_RUN_HOUR=02 BACKUP_RETENTION_DAYS=90 LOUKA_SOURCE_URL=... IMMOKA_SOURCE_URL=... FOODKA_SOURCE_URL=... AUTOKA_SOURCE_URL=... FABRIKA_SOURCE_URL=... RESTOKA_SOURCE_URL=... SORTIKA_SOURCE_URL=... CREAKA_SOURCE_URL=... ``` Ne jamais commiter `.env` — seulement `.env.example` avec des valeurs vides. ## Standards de code - Python 3.11+, type hints partout, `black` + `ruff`, docstrings sur toute fonction publique. - Logging structuré JSON via `utils/logger.py` avec rotation quotidienne — jamais de `print()` en production. - Tests `pytest` dans `tests/` : chaque collecteur, chaque route API, la logique de retry et de backfill. - Gestion d'erreurs explicite : jamais de `except: pass`. - Commits descriptifs ; branche `main` = état déployé sur m3u96b. - **Rappel : en-tête d'auteur (Simon-Pierre Boucher / contact@spboucher.ai / Node m3u96b) dans CHAQUE fichier.** ## Commandes utiles ```bash # Déploiement complet sur m3u96b bash scripts/deploy_m3u96b.sh # Initialiser la base python -m src.database.db --init # Collecte manuelle immédiate (tous les services) python -m src.scheduler.daily_job --now # Backfill d'une date manquée python -m src.scheduler.backfill --date 2026-08-15 # API en local (dev) uvicorn src.api.main:app --reload --port 8000 # Tunnel public bash scripts/start_ngrok.sh # Vérifier la santé curl http://127.0.0.1:8000/health ``` ## Priorités absolues (dans l'ordre) 1. **Fiabilité de la sauvegarde quotidienne** — aucune journée sans données pour aucun des 8 services. 2. Intégrité de la base (append-only, checksums, backups horodatés, rétention 90 jours). 3. Tout tourne sur le node **m3u96b** — vérification du hostname au démarrage. 4. Disponibilité de l'API publique sur www.api-ka.com (auto-restart ngrok + uvicorn). 5. En-tête d'auteur présent dans chaque fichier. 6. Logs, alertes et traçabilité de chaque collecte et chaque requête. --- *Plateforme légendaire API-KA — Node m3u96b — créée par Simon-Pierre Boucher (contact@spboucher.ai)*