Erreur: block 'page_title' defined twice → TemplateAssertionError
Fix: title_prefix (nouveau bloc) pour <title>, page_title (existant) pour Alpine
10 templates mis à jour avec title_prefix.
- Le titre de page (onglet) : 'bruno/flowdeck' au lieu de 'Gitea Workspace'
- Le topbar breadcrumb : owner/repo correctement peuplé
- workspace_name + workspace_initial passés au template
Téléchargés et servis depuis /static/js/:
- alpine.min.js (44 KB) — remplace unpkg.com
- htmx.min.js (51 KB) — remplace unpkg.com
- sortable.min.js (45 KB) — remplace cdn.jsdelivr.net
- prism.min.js (19 KB) + prism.css (1.3 KB) — remplace cdnjs
Corrige le bug 'rien ne fonctionne' quand le réseau est lent/absent:
sans CDN, Alpine.js/HTMX/SortableJS ne chargeaient pas →
tous les @click, x-show, x-data, drag-drop inopérants.
Si Alpine échoue à s'initialiser, x-show n'était pas traité
et les modals apparaissaient par défaut. display:none en fallback
garantit qu'ils sont cachés, Alpine les montre via x-show.
Alpine 3.x a renommé .away en .outside. Toutes les occurrences dans:
- base.html (3: user menu, context menu, ws ctx)
- page_editor.html (5: share, perm, access, more, gsMore)
- workspaces.html (1: dialog overlay)
- workspace.html (1: modal overlay)
- local_workspace.html (6: create, rename, delete modals, context menus)
Ces .away cassées empêchaient tous les @click Alpine de fonctionner
sur ces pages (Cancel, Delete, et tous les autres boutons modaux).
- @click.away n'existe pas dans Alpine 3.x (renommé @click.outside)
6 occurrences corrigées dans local_workspace.html
- Ajout Alpine.data('_wsInitData') pour un binding fiable des @click
(le IIFE seul ne garantissait pas une reconnaissance Alpine propre)
- POST /api/gitea/projects/{owner}/{repo}/upload (binary upload)
- get_file_commits() in GiteaClient
- GET /api/gitea/projects/{owner}/{repo}/commits (per-file history)
- POST .../sync-labels (Gitea labels → FlowDeck tags)
- New file form + upload button in gitea_workspace.html topbar
Ajout d'un paramètre _force avec timestamp dans l'URL d'autorisation
quand mode=link, pour forcer Gitea à ne pas utiliser le cache.
NOTE: Gitea ne supporte pas prompt=login. Si auto-approve persiste,
il faudra ajouter un champ token manuel en fallback.
Problème: window.history.back() restaurait la page depuis bfcache
→ le IIFE initTheme() ne se ré-exécutait pas
→ le thème dark s'affichait même après avoir sélectionné light
Fix:
- applySavedTheme() → fonction nommée, pas IIFE
- window.addEventListener('pageshow', e.persisted → reapply)
- Proper else branch: removeProperty pour le dark mode
1. _require_gitea: retrait fallback admin token → 401 si pas connecté
2. user-menu-dropdown: background var(--bg-primary) au lieu de #1E1E1E
3. applyTheme() déjà immédiat via style.setProperty sur documentElement
settings.html:773: '} async' → '}, async'
Cette SyntaxError empêchait Alpine.data('settingsInit') d'être parsé
→ toutes les variables Alpine étaient undefined
- Sidebar: retrait du bouton 🌓 (dark/light) + bouton 🚪 logout
- Settings → Account → Preferences: nouveau sélecteur de thème (Dark/Light)
- Persistance localStorage 'fd_theme' — appliqué au chargement de chaque page
- initTheme() dans base.html + applyTheme() dans settings.html
- toggleTheme() conservé dans le JS mais plus utilisé dans l'UI
1. _require_gitea: fallback token admin pour les GET (orgs, projects, tree, file)
_require_user_gitea: token OAuth requis pour les write (save, delete)
2. Private Pages: table gitea_private_pages (user_id, owner, repo, title, content)
API CRUD: GET/POST/PUT/DELETE /api/gitea/projects/{o}/{r}/private-pages
UI: liste + éditeur dans le workspace Gitea, lien 🔒 Private Pages dans sidebar
Lié logiquement au projet (owner+repo), conservé entre sessions
Avant: _sidebar_data() utilisait asyncio.run() → RuntimeWarning + 302 redirects
Après: gitea_workspace.html appelle loadSidebarTree() → fetch API → injecte dans #sidebar-workspace-items
- base.html: ajout id='sidebar-workspace-items' sur le <ul> workspace
- dashboard.py: _sidebar_data() ne fait plus de fetch serveur, juste passe owner/repo
1. Tab user repos: fetch username via /auth/user → affiche 'bruno' au lieu de 'Personal'
2. Sidebar: ws_cookie='gitea:owner:repo' → _sidebar_data() fetch le tree depuis Gitea API
3. base.html: data-gitea-path/type/sha sur les items workspace pour les projets Gitea
4. gitea_workspace.html: refonte sans tree panel → navigation via sidebar
- Clic fichier → ouvre dans le contenu (lecture + edit + commit)
- Clic dossier → lazy-load les enfants dans le sidebar
5. openGitea(): set cookie flowdeck_workspace avant navigation
Avant: oauth_redirect_uri codé en dur à localhost:8080
→ impossible d'utiliser l'OAuth depuis une autre machine du réseau
Après: le redirect_uri est construit à partir du header Host de la requête
→ localhost:8080 → http://localhost:8080/auth/callback
→ 192.168.30.101:8080 → http://192.168.30.101:8080/auth/callback⚠️ L'utilisateur doit ajouter les 2 URIs dans Gitea OAuth App settings.
Avant: get_provider('gitea') → None (pas de client OAuth configuré)
→ fallback créait automatiquement une session admin
→ le bouton 'Register with Gitea' connectait l'utilisateur en admin !
Après: affiche une page d'erreur propre:
'⚠ Gitea OAuth not configured — The Gitea integration has not
been set up by the server administrator. ↩ Use local login'
1. Pas de Gitea sans config: _require_gitea() → 401 si pas de token OAuth
2. Register Gitea: boutons "Login/Register with Gitea" dynamiques selon l'onglet
3. Settings → Integrations: ✅ Active / 🔗 Connect / Disconnect + API status/disconnect
4. Workspaces: tabs par org (All | org1 | org2) + barre recherche 🔍 filtrage live
5. API: GET /api/gitea/status, DELETE /api/gitea/disconnect
GiteaClient:
- __init__(user_token=None) — per-user OAuth token ou admin token
- get_repo_contents(owner, repo, path) — lazy: un niveau à la fois
- get_file_content(owner, repo, path) — base64 décodé
- create_or_update_file(...) — PUT avec sha pour update
- delete_file(owner, repo, path, sha, message)
- _invalidate_tree_cache() — invalidation après write
Routes (/api/gitea):
- GET /orgs — liste des organisations
- GET /projects?org=x — repos user ou org
- GET /projects/{owner}/{repo}/tree?path=x — arborescence lazy
- GET /projects/{owner}/{repo}/file?path=x — contenu fichier
- PUT /projects/{owner}/{repo}/file — save + commit
- DELETE /projects/{owner}/{repo}/file?path=x&sha=x — delete
- GET /projects/{owner}/{repo}/labels — labels → tags
Helper: get_user_gitea_client(request) → GiteaClient ou None
DB:
- tags.user_id REFERENCES users(id)
- UNIQUE(name, user_id) au lieu de UNIQUE(name)
- Migration v2.6: alter + recreate table
API (tous les endpoints tags):
- POST/PUT/DELETE /api/settings/tags: filtré par user_id
- GET /api/settings/tags/all: retourne uniquement les tags du user
- GET/POST /api/local-workspace/*/tags: scope user
→ Les tags de l'utilisateur 1 ne sont pas visibles par l'utilisateur 2
Le cookie de session n'était pas mis à jour après modification du compte
→ le template re-rendait avec les anciennes données au rechargement.
Maintenant le serveur émet un nouveau cookie avec les données fraîches.
Backend:
- PUT /api/settings/account (full_name, login, email, password)
- Vérifie que le username n'est pas déjà pris
- Password min 6 caractères
Frontend (Settings → My Account):
- Champs éditables: Username, Full name, Email
- Section Change password avec toggle 👁/🙈
- Bouton Save changes + Update password
- Message de confirmation ✓ / ✗ avec timeout 3s
- Bouton 👁/🙈 dans le champ password (login + register)
- Label changé de 'Email' à 'Email or username' (les users créés via admin ont un login, pas forcément un email)
- Type email → type text pour accepter les usernames