feat: mode application de bureau + packaging Windows autonome

Permet de distribuer l'application en executable Windows double-clic,
sans Python/Node/terminal pour l'utilisateur final.

- `desktop.py` : demarre le serveur sur un port libre dans un thread et
  ouvre une fenetre native (pywebview). `PLESNA_DEBUG=1` active les
  DevTools. Commande `plesna-gerance desktop` (import paresseux).
- `paths.py` : resolution centralisee des chemins selon le contexte
  (dev, executable PyInstaller, surcharge par variables d'env). Les
  donnees vont dans un dossier utilisateur inscriptible (%APPDATA%),
  les ressources dans le bundle. connection.py/storage.py utilisent
  `get_data_dir()`, app.py `resource_path()`.
- app.py : type MIME `.mjs` force (modules ES / worker PDF.js sous
  Windows).
- Groupes de deps optionnels desktop / desktop-linux / build.
- packaging/ : spec PyInstaller, build_windows.ps1, installeur Inno
  Setup. Workflow GitHub build-windows.yml (runner Windows).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 17:21:27 +02:00
parent 7cebc0bb3d
commit a90d1c4f64
15 changed files with 912 additions and 29 deletions

View File

@@ -7,33 +7,23 @@ from pathlib import Path
from sqlalchemy import create_engine, text
from sqlalchemy.orm import Session, sessionmaker
from ..paths import get_data_dir
from .models import Base, Tag
# Default database path - relative to project root
def _get_project_root() -> Path:
"""Find project root by looking for pyproject.toml."""
current = Path(__file__).resolve()
for parent in current.parents:
if (parent / "pyproject.toml").exists():
return parent
# Fallback to home directory if not found
return Path.home() / ".plesna_gerance"
DEFAULT_DB_PATH = _get_project_root() / "data" / "database.sqlite"
# Global engine instance
_engine = None
_SessionLocal = None
def get_db_path() -> Path:
"""Get database path from environment or default."""
"""Get database path from environment or default.
Resolved lazily so a packaged build writes to the per-user data dir.
"""
env_path = os.environ.get("PLESNA_DB_PATH")
if env_path:
return Path(env_path)
return DEFAULT_DB_PATH
return get_data_dir() / "database.sqlite"
def _build_engine(db_path: Path):