diff --git a/edit/server.py b/edit/server.py index c606156..fa9568d 100644 --- a/edit/server.py +++ b/edit/server.py @@ -49,14 +49,34 @@ _GIT_DIR = os.environ.get("GIT_DIR") _git_lock = asyncio.Lock() -async def _git_commit(file_path: Path, handle: str, verb: str) -> None: - rel = str(file_path.relative_to(_ROOT)) +def _git_env() -> dict: env = os.environ.copy() if _GIT_DIR: env["GIT_DIR"] = _GIT_DIR env["GIT_WORK_TREE"] = str(_ROOT) env.setdefault("GIT_COMMITTER_NAME", "bincio-wiki") env.setdefault("GIT_COMMITTER_EMAIL", "wiki@bincio.wiki") + return env + + +async def _git_file_hash(file_path: Path) -> str: + rel = str(file_path.relative_to(_ROOT)) + try: + proc = await asyncio.create_subprocess_exec( + "git", "log", "-1", "--format=%H", "--", rel, + cwd=str(_ROOT), env=_git_env(), + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.DEVNULL, + ) + stdout, _ = await proc.communicate() + return stdout.decode().strip() + except Exception: + return "" + + +async def _git_commit(file_path: Path, handle: str, verb: str) -> None: + rel = str(file_path.relative_to(_ROOT)) + env = _git_env() try: async with _git_lock: add = await asyncio.create_subprocess_exec( @@ -256,6 +276,7 @@ def _slug_to_path(slug: str, base: Path) -> Path: class PageBody(BaseModel): content: str + base_hash: str | None = None def _list(base: Path) -> list[str]: @@ -263,11 +284,13 @@ def _list(base: Path) -> list[str]: return [] return [str(p.relative_to(base).with_suffix("")) for p in sorted(base.rglob("*.md"))] -def _get(slug: str, base: Path) -> JSONResponse: +async def _get(slug: str, base: Path) -> JSONResponse: path = _slug_to_path(slug, base) if not path.exists(): raise HTTPException(404, "Not found") - return JSONResponse({"slug": slug, "content": path.read_text(encoding="utf-8")}) + content = path.read_text(encoding="utf-8") + base_hash = await _git_file_hash(path) + return JSONResponse({"slug": slug, "content": content, "base_hash": base_hash}) def _save(slug: str, body: PageBody, base: Path) -> JSONResponse: path = _slug_to_path(slug, base) @@ -295,8 +318,13 @@ async def get_page(slug: str, user: User = Depends(require_auth)) -> JSONRespons @app.post("/pages/{slug:path}") async def save_page(slug: str, body: PageBody, user: User = Depends(require_auth)) -> JSONResponse: + path = _slug_to_path(slug, pages_dir) + if body.base_hash is not None: + current = await _git_file_hash(path) + if current and current != body.base_hash: + raise HTTPException(409, "Pagina modificata da qualcun altro — ricarica prima di salvare") result = _save(slug, body, pages_dir) - await _git_commit(_slug_to_path(slug, pages_dir), user.handle, "edited") + await _git_commit(path, user.handle, "edited") return result @app.delete("/pages/{slug:path}") @@ -319,8 +347,13 @@ async def get_story(slug: str, user: User = Depends(require_auth)) -> JSONRespon @app.post("/stories/{slug:path}") async def save_story(slug: str, body: PageBody, user: User = Depends(require_auth)) -> JSONResponse: + path = _slug_to_path(slug, stories_dir) + if body.base_hash is not None: + current = await _git_file_hash(path) + if current and current != body.base_hash: + raise HTTPException(409, "Pagina modificata da qualcun altro — ricarica prima di salvare") result = _save(slug, body, stories_dir) - await _git_commit(_slug_to_path(slug, stories_dir), user.handle, "edited") + await _git_commit(path, user.handle, "edited") return result @app.delete("/stories/{slug:path}") diff --git a/site b/site index d6eda09..1539bdb 160000 --- a/site +++ b/site @@ -1 +1 @@ -Subproject commit d6eda096d93063ad840de2f70a37de42997ffc39 +Subproject commit 1539bdb3a78146685af9e5d369b325c2119b29ec