v2.8.0 WIP: create/upload files + commit history + labels sync
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

- 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
This commit is contained in:
2026-07-13 22:33:47 -04:00
parent f25c8ebaf0
commit 6f15ad3ad4
9 changed files with 689 additions and 27 deletions
+70
View File
@@ -106,6 +106,31 @@ async def save_file(request: Request, owner: str, repo: str):
return JSONResponse({"error": str(e)}, status_code=502)
# ── Upload file (binary) ──
@router.post("/projects/{owner}/{repo}/upload")
async def upload_file(request: Request, owner: str, repo: str):
"""Upload a binary file to the repo."""
import base64
gitea = _require_gitea(request)
try:
form = await request.form()
except Exception:
return JSONResponse({"error": "Invalid form data"}, status_code=400)
file = form.get("file")
folder = form.get("folder", "")
message = form.get("message", "Upload via FlowDeck")
if not file:
return JSONResponse({"error": "No file provided"}, status_code=400)
try:
content = await file.read()
encoded = base64.b64encode(content).decode("utf-8")
path = f"{folder.strip('/')}/{file.filename}".strip("/") if folder.strip("/") else file.filename
result = await gitea.create_or_update_file(owner, repo, path, encoded, message, sha=None)
return {"status": "ok", "path": path, "commit": result.get("commit", {})}
except Exception as e:
return JSONResponse({"error": str(e)}, status_code=502)
# ── Delete file ──
@router.delete("/projects/{owner}/{repo}/file")
async def delete_file(request: Request, owner: str, repo: str):
@@ -266,3 +291,48 @@ async def delete_private_page(owner: str, repo: str, page_id: int, request: Requ
)
conn.commit()
return {"status": "ok"}
# ── Commit history for a file ──
@router.get("/projects/{owner}/{repo}/commits")
async def file_commits(request: Request, owner: str, repo: str, path: str = ""):
"""Get recent commits for a specific file."""
gitea = _require_gitea(request)
try:
commits = await gitea.get_file_commits(owner, repo, path)
return {"commits": commits}
except Exception as e:
return JSONResponse({"error": str(e)}, status_code=502)
# ── Sync labels to tags ──
@router.post("/projects/{owner}/{repo}/sync-labels")
async def sync_labels(request: Request, owner: str, repo: str):
"""Sync Gitea labels to FlowDeck tags for the current user."""
from app.auth.session import get_current_user as gcu
from app.db import get_conn
user = await gcu(request)
if not user:
return JSONResponse({"error": "Not authenticated"}, status_code=401)
gitea = _require_gitea(request)
try:
labels = await gitea.get_labels(owner, repo)
except Exception:
labels = []
imported = 0
with get_conn() as conn:
for label in labels:
name = label.get("name", "")
color = label.get("color", "#787774")
if not name: continue
existing = conn.execute(
"SELECT id FROM tags WHERE name=? AND user_id=?", (name, user["id"])
).fetchone()
if not existing:
conn.execute(
"INSERT INTO tags (name, color, user_id) VALUES (?, ?, ?)",
(name, f"#{color}", user["id"]),
)
imported += 1
conn.commit()
return {"status": "ok", "imported": imported, "total": len(labels)}