Spark 71881514c9 fix(board): les issues réelles Gitea n'étaient pas des PR — boards vides
Gitea 1.27.3 renvoie "pull_request": null sur les issues ordinaires:
_is_issue testait la présence de la clé et classait chaque vraie
issue comme un pull request. Board, Focus et briefing affichaient
zéro carte contre un vrai Gitea (le dashboard, basé sur les
compteurs de dépôts, semblait normal, d'où le signalement
« la nouvelle issue n'apparaît pas »). Le filtre teste maintenant
la valeur. Vérifié en réel: board bruno/flowdeck 0 -> 6 cartes.
2026-10-11 11:42:23 -04:00
2026-10-11 11:00:53 -04:00
2026-10-11 11:00:53 -04:00
2026-10-11 11:00:53 -04:00

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

# 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é)

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

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)

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) :

{
  "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

pip install -e ".[dev]"
pytest

La CI (.gitea/workflows/ci.yml) lance la même suite à chaque push.

Licence

MIT — voir LICENSE. Historique des versions : CHANGELOG.md.

S
Description
📋 Kanban project manager integrated with Gitea — lightweight, self-hosted, dark theme
Readme MIT
1.7 MiB
2026-10-23 09:48:17 -04:00
Languages
Python 59.4%
HTML 16.4%
JavaScript 14.5%
CSS 9.6%