When users table is empty (fresh install/test), auth check returns
a default admin user instead of redirecting. This allows the application
to bootstrap and tests to run without mocking sessions.
Dashboard now calls _get_user_or_redirect() before rendering.
Unauthenticated users get redirected to /auth/login?provider=local.
All sensitive routes now protected: /, /workspace, /accounts/settings
- Logout now redirects to /auth/login?provider=local instead of /
- Dashboard / and /workspace check auth and redirect to login if no session
- _get_user_or_redirect() helper added for reusable auth checks
- No more fallback admin user on dashboard — explicit login required
- Routes without session redirect to login page
- GET /workspace — unified workspace page
- GET /api/workspace/projects — JSON API for built-in + Gitea projects
- POST /api/workspace/projects — create built-in project
- Added RedirectResponse to imports
- GET /accounts/settings — settings page with profile, forges, tokens, sessions
- PUT /api/user/profile — update display name
- PUT /api/user/password — change password
- POST /api/user/token — generate API token
- DELETE /api/user/forge/{provider} — disconnect forge
- Redirects to /auth/login?provider=local if not logged in
Same pattern as f7d9d91: dashboard.router is registered before board.router
in main.py, so its /pages/{id} takes priority. Added page_data dict to match
the new JSON script tag approach.
Both board.py and dashboard.py page endpoints now return
Cache-Control: no-store, max-age=0 headers to prevent the browser
from caching the page HTML with broken x-data attributes.
dashboard.router is registered before board.router in main.py, so its
/pages/{page_id} takes priority. It was missing page_share_mode and
page_published template variables, causing Jinja2 TypeError.
Replicates Notion's page layout from reference images:
- Top bar: Private badge, Share button, Link copy, Star/favorite toggle, ⋮ menu
- Share modal: Publish toggle, Copy link, Invite people with permission select
- Empty page toolbar: Get started with H1/H2/H3/bullet/todo/callout/quote
- Star toggle integrated with favorites API (POST/DELETE /board/api/favorites)
- board.py view_page now passes workspace context + page_favorited
- dashboard.py view_page_root also passes page_favorited
- CSS: .page-topbar, .share-modal, .empty-page-toolbar, toggle switch, more menu
Bug: lib_private filtered on p.source == '🔒 Private' which only matched pages
without a workspace. All pages have parent_section='Private' by default, so
filtering by source excluded them all.
Fix: add 'section' field to page dict from parent_section DB column,
filter lib_private by p.section == 'Private'
6 files changed:
- db.py: migrate favorites table FK from collection_pages(id) to pages(id)
- board.py: add favorites API (POST/DELETE /board/api/favorites/{id}, GET list)
- board.py: _sidebar_data() now loads favorite_pages from DB via JOIN
- dashboard.py: library_page loads lib_favorites from DB (not parent_section)
- csrf.py: exclude /board/api/favorites from CSRF checks
- base.html: context menu toggles Add/Remove Favorites based on state
- base.html: favoriteIds Alpine set initialized from server-rendered favorites
- test_app.py: test_favorites_crud rewritten for new page-based favorites API
Favorites now work end-to-end:
- Right-click → Add to Favorites (or Remove if already favorited)
- Sidebar Favorites section shows favorited pages
- Library Favorites tab shows the same pages
- API: POST/DELETE /board/api/favorites/{page_id}, GET /board/api/favorites
- Sidebar data (board._sidebar_data()) no longer overwritten by library content
- New variables: lib_recent, lib_favorites, lib_shared, lib_private
- Pages categorized by parent_section column: Private/Favorites/Shared
- Recents tab shows all non-trashed pages ordered by updated_at
- library.html updated to use lib_* variables instead of sidebar data
- Favorites/Shared tabs hidden when empty (lib_has_favorites/lib_has_shared)
3 bugs fixed:
1. dashboard.py /library was registered before board.router and had no workspace context → removed old endpoint, re-added with owner/repo params using board._sidebar_data()
2. base.html navigateTo('/library') lost workspace → now auto-appends ?owner=X&repo=Y from workspaceKey
3. openLibrary(id) used bare window.location.href → now uses this.navigateTo()
Now the Library page:
- Shows pages filtered by workspace when coming from a project
- Preserves the sidebar context (no more Admin Dashboard fallback)
- Tabs (Recents, Favorites, Shared, Private) display project pages from DB
- Ajout des 3 nouvelles tables pour le Database Concept
- collections: base de données abstraite, indépendante de Gitea
- collection_pages: pages génériques avec parent_id et property_values_json
- collection_views: vues configurables par collection
- Mise à jour test_db_tables_exist pour inclure les nouvelles tables
- 28/28 tests passent
- Add sort_order column to pages table for drag & drop reordering
- Rewrite _sidebar_data with recursive _build_page_tree for nested hierarchy
- Add tree toggle buttons (▶ collapsed / ▼ expanded) on parent items
- Alpine.js expandedNodes state with toggleTreeNode() for expand/collapse
- Jinja2 recursive macro render_tree_item for proper nesting + indentation
- SortableJS integration on .sidebar-items for drag & drop repositioning
- New PUT /board/api/pages/{id}/move API for persisting moves
- Auto-set sort_order on page creation (max+1 for same parent)
- CSS: .tree-toggle, .tree-toggle-placeholder, grab cursor on page-icon
- 28/28 tests pass, all endpoints healthy
- Page /accounts: profil utilisateur éditable, comptes connectés, workspace members
- API GET/PUT /api/users/me: lecture/écriture profil local (full_name, email)
- Users list from local DB, auth via Gitea OAuth2
- Imports SessionManager clean dans api.py
- pages.workspace = owner/repo (ex: bruno/flowdeck) au lieu du nom user
- Sidebar: Kanban board + 4 sous-vues avec URLs distinctes (?view=kanban|table|...)
- Cliquer sur 'Table view' dans sidebar → change la vue à droite
- Dashboard (/): sidebar montre uniquement les projets Gitea
- Board (/board/{o}/{r}): sidebar montre les pages de ce projet
- Changement de projet = changement de contexte (pages isolées)
- newPage/newSubPage passent workspace_key à l'API
- Auto-switch view depuis paramètre URL dans board.html
- Ajouté /pages/{page_id} dans dashboard.py (route racine, pas /board/pages/...)
- La redirection après création de page fonctionne maintenant
- Les pages Markdown s'affichent correctement
- dashboard.py: decode session cookie and pass user to template
- notes.py: pass user to template (both GET and POST)
- CSS: .header-right flex layout, #user-info styles, min-height instead of fixed height
- Removed duplicate #user-info CSS block
- Login now properly shows admin/Deconnexion after authentication
Gitea's /api/v1/user/repos already includes all org repos the user
has access to. Fetching org repos separately added duplicates.
Removed the redundant org-repo loop from dashboard and /api/projects.
Also fixed template to use repo.owner.login instead of _org hack.