Files
kanbante/README.md
T
Spark 7d2faf027d
CI / test (push) Successful in 1m45s
chore(release): Kanbante 0.5.0
Version, CHANGELOG (palier 3 points 7-9 + board) et README
(fonctionnalités, variables ntfy/briefing, nouvelles routes,
branchement d'un client MCP).
2026-10-11 11:00:53 -04:00

231 lines
11 KiB
Markdown
Raw 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.
# Kanbante
**Kanban project manager integrated with Gitea** — lightweight, self-hosted, dark theme.
## Features
- 📋 **Dashboard** — tous tes projets Gitea en un coup d'œil (actifs / inactifs / archivés — le vrai drapeau Gitea)
- 🗂️ **Kanban Board** — colonnes Backlog → À faire → En cours → Révision → Terminé, ordre des cartes persisté
- 🎯 **Drag & Drop** — glisse les issues entre colonnes ; en cas d'échec, la carte est replacée
- 🔗 **Gitea Sync bidirectionnelle** — « Terminé » ferme l'issue dans Gitea, la ressortir la rouvre ; l'état local est réconcilié à chaque chargement
- ➕ **Création d'issues** depuis le board, filtres texte + étiquettes, cartes liées à Gitea
- ⚡ **Temps réel** — un webhook Gitea invalide le cache et pousse un événement SSE : le board ouvert se met à jour tout seul (en douceur, sans interrompre une saisie ni un drag)
- 🏷️ **Visible dans Gitea** — les colonnes intermédiaires sont reflétées par des étiquettes `kanban:*` sur les issues, et un board peut se filtrer **par milestone** (échéance + progression façon sprint)
- 🗂️ **Panneau détail** — clic sur une carte : corps markdown, commentaires, et réponse postée dans Gitea sans quitter le board
- 📰 **Journal d'événements** — chaque mouvement est horodaté : fil d'activité (`/activity`), métriques de débit et de temps de cycle (`/api/metrics`)
- ⏳ **Vieillissement + limites WIP** — badge d'ancienneté par colonne et limites souples configurables
- 🎯 **Vue Focus** — tout ce qui est En cours / En révision, tous projets confondus (`/focus`)
- 🔍 **Recherche globale + Mes issues** — chercher dans toutes les issues de tous les projets (`/search`), et voir ce qui vous est assigné (`/my`)
- 📊 **Briefing quotidien** — encours, vieillissement, milestones proches : page `/briefing` et envoi **ntfy** planifié
- 🤖 **Serveur MCP** — 7 outils en lecture seule (boards, focus, recherche, métriques) exposés en JSON-RPC sur `/mcp` pour vos agents
- 🏷️ **Étiquettes gérables depuis le board** — créer / supprimer les étiquettes du dépôt, les appliquer aux issues depuis le panneau détail
- 📝 **Notes de projet** — panneau repliable, largeur ajustable, sauvegarde automatique
- 📝 **Notes** — bloc-notes par projet
- 🔐 **Authentification** Basic Auth intégrée + protection CSRF (voir Sécurité)
- 🌙 **Dark theme** — conçu pour les longues sessions
- 🐳 **Docker** — un seul conteneur, utilisateur non-root, healthcheck
- 📴 **Aucun CDN** — les bibliothèques JS sont vendorées dans `static/vendor/`, l'app fonctionne hors-ligne
## Quick Start
```bash
# 1. Installer les dépendances
cd kanbante
pip install -e .
# 2. Configurer les variables d'environnement
export GITEA_URL=https://git.dracodev.net
export GITEA_TOKEN=ton_token_gitea
# Recommandé dès que le service dépasse localhost :
export KANBANTE_AUTH_USER=ton_nom
export KANBANTE_AUTH_PASSWORD=ton_mot_de_passe
# 3. Lancer
uvicorn app.main:app --reload --port 8080
```
Ouvrir http://localhost:8080
## Docker
### Avec docker-compose (recommandé)
```bash
cat > .env <<'EOF'
GITEA_TOKEN=ton_token
KANBANTE_AUTH_USER=ton_nom
KANBANTE_AUTH_PASSWORD=ton_mot_de_passe
EOF
docker compose up -d
```
La base SQLite vit dans le volume `kanbante_data` (monté sur `/data`,
chemin `KANBANTE_DB_PATH`) : les positions de cartes et les notes
survivent aux recréations du conteneur.
### Avec docker uniquement
```bash
docker build -t kanbante:latest .
docker volume create kanbante_data
docker run -d \
--name kanbante \
-p 8080:8080 \
-v kanbante_data:/data \
-e GITEA_URL=https://git.dracodev.net \
-e GITEA_TOKEN=ton_token \
-e KANBANTE_AUTH_USER=ton_nom \
-e KANBANTE_AUTH_PASSWORD=ton_mot_de_passe \
--restart unless-stopped \
kanbante:latest
```
### Déploiement sur Deepin 25 (Debian Bookworm)
```bash
sudo apt install -y docker.io docker-compose docker-buildx
sudo usermod -aG docker $USER
sudo systemctl enable --now docker
# Builder et lancer (voir section "Avec docker uniquement")
```
## Configuration
| Variable | Défaut | Rôle |
|---|---|---|
| `GITEA_URL` | `https://git.dracodev.net` | Instance Gitea |
| `GITEA_TOKEN` | *(vide)* | Jeton API Gitea — **portée conseillée : lecture + écriture des issues, sur un compte dédié, pas un jeton admin**. Sans jeton : contenu public seulement |
| `KANBANTE_DB_PATH` | `~/.kanbante/kanbante.db` | Base SQLite locale (en Docker : `/data/kanbante.db`) |
| `KANBANTE_AUTH_USER` / `KANBANTE_AUTH_PASSWORD` | *(vide)* | Active la Basic Auth dès que les deux sont définis |
| `KANBANTE_REQUIRE_AUTH` | `0` | `1` = l'app refuse de servir (503) si l'auth n'est pas configurée |
| `KANBANTE_ACTIVE_WINDOW_DAYS` | `365` | Un dépôt non archivé mis à jour depuis moins de N jours est « actif » |
| `KANBANTE_CACHE_TTL` | `30` | Secondes de cache des listes Gitea (dépôts, issues) |
| `KANBANTE_MAX_NOTE_CHARS` | `20000` | Taille maximale d'une note de projet |
| `KANBANTE_SYNC_GITEA` | `1` | `0` = les déplacements ne ferment/rouvrent plus les issues Gitea |
| `KANBANTE_WIP_LIMITS` | *(vide)* | Limites souples par colonne, ex. `in_progress:5,review:3` (affichage `n/limite`, rouge en dépassement, jamais bloquant) |
| `KANBANTE_AGING_DAYS` | `7` | Jours dans la même colonne avant le badge « ⏳ » sur une carte (rouge au double) |
| `KANBANTE_WEBHOOK_SECRET` | *(vide)* | Secret partagé du webhook Gitea ; si défini, la signature HMAC `X-Gitea-Signature` est exigée |
| `KANBANTE_SYNC_LABELS` | `1` | `0` = ne plus refléter les colonnes intermédiaires en étiquettes `kanban:*` dans Gitea |
| `KANBANTE_NTFY_URL` | *(vide)* | Base du serveur ntfy (ex. `https://ntfy.sh` ou auto-hébergé) pour le briefing |
| `KANBANTE_NTFY_TOPIC` | *(vide)* | Topic ntfy du briefing (URL + topic requis pour activer l'envoi) |
| `KANBANTE_NTFY_TOKEN` | *(vide)* | Jeton Bearer optionnel pour un topic protégé |
| `KANBANTE_BRIEFING_TIME` | *(vide)* | Heure d'envoi quotidien du briefing, `HH:MM` heure du serveur ; vide = pas d'envoi automatique |
## Sécurité
- Sans `KANBANTE_AUTH_*`, **quiconque atteint le port 8080 voit tous les
dépôts visibles par le jeton et peut modifier les boards** — l'app
l'annonce par un avertissement au démarrage. Définis les identifiants,
ou mets Kanbante derrière un reverse proxy authentifiant, et pose
`KANBANTE_REQUIRE_AUTH=1` pour échouer fermé.
- Tous les POST exigent un jeton CSRF (cookie double-submit posé par les
pages) : les appels API directs doivent d'abord charger une page pour
obtenir le cookie, puis envoyer le jeton (en-tête `X-CSRF-Token` ou
champ `csrf_token`).
- Aucun secret n'est versionné : le jeton passe uniquement par
l'environnement / le fichier `.env` local (ignoré par git).
## Architecture
```
kanbante/
├── app/
│ ├── main.py # FastAPI app (routes, auth, CSRF, lifespan)
│ ├── config.py # Réglages par variables d'environnement
│ ├── gitea_client.py # Client API Gitea asynchrone, paginé, avec cache
│ ├── models.py # SQLite (état du board local + migrations)
│ └── templates/ # Jinja2 (dashboard, board, focus, error)
├── static/
│ ├── css/style.css # Design system dark theme
│ ├── js/board.js # Drag & drop, filtres, création d'issues
│ └── vendor/ # HTMX, Alpine, SortableJS (vendorés, aucun CDN)
├── tests/ # Suite pytest
├── Dockerfile
├── docker-compose.yml
└── README.md
```
## API
| Méthode | Route | Description |
|---------|-------|-------------|
| GET | `/` | Dashboard tous les projets |
| GET | `/board/{owner}/{repo}` | Kanban board |
| GET | `/focus` | En cours + En révision, tous projets actifs |
| GET | `/activity` | Fil d'activité (journal d'événements, tous projets) |
| POST | `/api/move` | Déplacer une issue (ordre persisté, synchro Gitea) |
| POST | `/api/issues` | Créer une issue Gitea depuis le board |
| GET | `/api/issues/{owner}/{repo}/{n}` | Détail d'une issue + commentaires (panneau) |
| POST | `/api/issues/{owner}/{repo}/{n}/comments` | Commenter l'issue dans Gitea |
| GET | `/api/events` | Derniers événements du journal (`?repo=`, `?limit=`) |
| GET | `/api/metrics` | Débit + temps de cycle moyen (`?repo=`, `?window=`) |
| POST | `/webhooks/gitea` | Réception des webhooks Gitea (HMAC si secret défini) |
| GET | `/api/stream` | Flux SSE des événements Gitea (rafraîchissement live) |
| GET | `/board/{o}/{r}?milestone=` | Board filtré par milestone (titre ou id) |
| GET | `/search?q=` | Recherche globale d'issues (tous projets, tous états) |
| GET | `/my` | Issues ouvertes assignées à l'utilisateur du jeton |
| GET | `/briefing` | Briefing quotidien (page lisible + statut ntfy) |
| GET | `/api/briefing` | Briefing en JSON + texte formaté |
| POST | `/api/briefing/send` | Envoyer le briefing sur ntfy maintenant |
| GET/POST | `/mcp` | Serveur MCP lecture seule (JSON-RPC 2.0) |
| GET/POST | `/api/repos/{o}/{r}/labels` | Lister / créer les étiquettes du dépôt |
| DELETE | `/api/repos/{o}/{r}/labels/{id}` | Supprimer une étiquette du dépôt |
| POST | `/api/issues/{o}/{r}/{n}/labels` | Ajouter / retirer une étiquette sur une issue |
## Brancher un client MCP
Kanbante expose un serveur MCP **en lecture seule** (aucune écriture :
les lectures d'abord). Configuration type (transport HTTP) :
```json
{
"mcpServers": {
"kanbante": {
"url": "https://<votre-hôte-kanbante>/mcp"
}
}
}
```
Outils : `list_projects`, `get_board`, `get_issue`, `get_focus`,
`search_issues`, `get_activity`, `get_metrics`. Si la Basic Auth
Kanbante est active, le client doit envoyer l'en-tête
`Authorization` correspondant.
## Activer le temps réel (webhook Gitea)
Dans chaque dépôt (ou au niveau d'une organisation) : **Réglages →
Webhooks → Ajouter un webhook → Gitea**, avec :
- **URL cible** : `https://<votre-hôte-kanbante>/webhooks/gitea`
- **Type de contenu** : `application/json`
- **Secret** : la même valeur que `KANBANTE_WEBHOOK_SECRET`
- **Déclencheur** : « Événements d'issue » (et « Commentaire d'issue »
si vous voulez aussi le rafraîchissement sur commentaires externes)
Dès lors, fermer ou rouvrir une issue dans Gitea met à jour les boards
Kanbante ouverts, sans rechargement manuel.
| POST | `/api/notes` | Sauvegarder des notes |
| GET | `/api/stats` | Statistiques JSON |
| GET | `/api/projects` | Projets JSON (actifs / inactifs / archivés) |
| GET | `/api/health` | Liveness (aucune dépendance) |
| GET | `/api/readiness` | Readiness : teste SQLite + Gitea (503 si KO) |
Les POST répondent avec des codes explicites : `400` entrée invalide,
`403` jeton CSRF manquant/invalide, `413` note trop longue, `502` Gitea
injoignable.
## Tests
```bash
pip install -e ".[dev]"
pytest
```
La CI (`.gitea/workflows/ci.yml`) lance la même suite à chaque push.
## Licence
MIT — voir [LICENSE](LICENSE). Historique des versions :
[CHANGELOG.md](CHANGELOG.md).