# 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 - 🎯 **Vue Focus** — tout ce qui est En cours / En révision, tous projets confondus (`/focus`) - 📝 **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 | ## 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 | | POST | `/api/move` | Déplacer une issue (ordre persisté, synchro Gitea) | | POST | `/api/issues` | Créer une issue Gitea depuis le board | | 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).