diff --git a/app/routers/api.py b/app/routers/api.py index 5ae7184..a411515 100644 --- a/app/routers/api.py +++ b/app/routers/api.py @@ -13,6 +13,7 @@ from app.config import settings from app.db import get_conn from app.routers.board import _issue_column, _map_issue_to_card, STATUS_COLORS, STATUS_LABELS from app.services.gitea_client import gitea +from app.auth.session import SessionManager logger = logging.getLogger(__name__) router = APIRouter(tags=["api"], prefix="/api") @@ -538,6 +539,40 @@ async def delete_checklist(checklist_id: int): return {"status": "ok"} +# ── v1.0.0: User management ── + +@router.get("/users/me") +async def get_my_profile(request: Request): + """Get current user profile.""" + user = SessionManager.decode_session(request.cookies.get("flowdeck_session", "")) + if not user: + return {"login": "guest", "full_name": "Guest", "email": ""} + with get_conn() as conn: + row = conn.execute( + "SELECT * FROM users WHERE login=?", (user.get("login", ""),) + ).fetchone() + if row: + return dict(row) + return {"login": user.get("login", ""), "full_name": "", "email": ""} + + +@router.put("/users/me") +async def update_my_profile(request: Request, full_name: str = Query(default=""), + email: str = Query(default="")): + """Update current user's local profile.""" + user = SessionManager.decode_session(request.cookies.get("flowdeck_session", "")) + if not user: + raise HTTPException(401, "Not authenticated") + login = user.get("login", "") + with get_conn() as conn: + conn.execute( + "UPDATE users SET full_name=?, email=? WHERE login=?", + (full_name, email, login), + ) + conn.commit() + return {"status": "ok"} + + # ── v0.5.0: Card priority & due date ── @router.post("/card/{owner}/{repo}/{issue_id}") diff --git a/app/routers/dashboard.py b/app/routers/dashboard.py index 10f45f5..cb6b296 100644 --- a/app/routers/dashboard.py +++ b/app/routers/dashboard.py @@ -82,6 +82,20 @@ async def view_page_root(request: Request, page_id: int): return template.render(**ctx) +@router.get("/accounts", response_class=HTMLResponse) +async def accounts_page(request: Request): + """Account management panel.""" + from jinja2 import Environment, FileSystemLoader + env = Environment(loader=FileSystemLoader("app/templates")) + sidebar = _sidebar_data(request, []) + user = SessionManager.decode_session(request.cookies.get("flowdeck_session", "")) + with get_conn() as conn: + users = conn.execute("SELECT * FROM users ORDER BY created_at DESC").fetchall() + ctx = {**sidebar, "user": user, "users": [dict(u) for u in users]} + template = env.get_template("accounts.html") + return template.render(**ctx) + + @router.get("/", response_class=HTMLResponse) async def dashboard( request: Request, diff --git a/app/templates/accounts.html b/app/templates/accounts.html new file mode 100644 index 0000000..b76fd1b --- /dev/null +++ b/app/templates/accounts.html @@ -0,0 +1,131 @@ +{% extends "base.html" %} +{% block page_icon %}👤{% endblock %} +{% block page_title %}Accounts{% endblock %} + +{% block content %} +
+
+ 👤 +

Account Management

+
+
+ +
+ +
+

Your Profile

+
+
+ {{ user.login[0] if user else 'B' }} +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+ + ✓ Saved +
+
+
+ + +
+

Connected Accounts

+
+ 🔗 +
+
Gitea
+
+ {% if user %}Connected as {{ user.login }}{% else %}Not connected{% endif %} +
+
+ {% if user %} + Disconnect + {% else %} + Connect Gitea + {% endif %} +
+
+ + +
+

Workspace Members

+
+ Members are managed through Gitea. New users connect via OAuth and appear here automatically. +
+ + + + + + + + + + + {% for u in users %} + + + + + + + {% endfor %} + {% if not users %} + + {% endif %} + +
UserEmailJoinedRole
+
+
{{ u.login[0] }}
+ {{ u.login }} +
+
{{ u.email or '—' }}{{ u.created_at[:10] if u.created_at else '—' }}{% if u.is_admin %}Admin{% else %}Member{% endif %}
No members yet
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %}