v0.3.0: OAuth2, CSRF, rate limiting, issue CRUD, webhooks, card detail, checklists
CI / test (push) Failing after 5s
CI / lint (push) Failing after 4s

- OAuth2 Gitea (login/callback/logout) with fallback admin mode
- Session management with signed cookies (itsdangerous, 7 days)
- CSRF protection middleware on all POST/PUT/PATCH/DELETE
- Rate limiting in-memory (60 req/min per IP)
- Issue creation from FlowDeck (POST /api/issues/{owner}/{repo})
- Inline issue editing (PATCH /api/issues/{owner}/{repo}/{issue_id})
- Card detail modal (double-click): description, labels, comments
- Checklists with toggleable items (CRUD endpoints + UI)
- Priority + due date editable on cards
- Webhook receiver (POST /api/webhook) with HMAC-SHA256
- Auto-register/status webhooks for repos
- GiteaClient: create_issue, update_issue, get_comments, webhooks, collaborators
- New DB tables: users, user_tokens, checklists, checklist_items
- CSS: modal, forms, checklists, comments, PR section
This commit is contained in:
2026-07-08 09:23:57 -04:00
parent 87862b64b8
commit 5a124d1328
17 changed files with 1390 additions and 91 deletions
+28 -4
View File
@@ -6,10 +6,13 @@ import logging
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.sessions import SessionMiddleware
from app.config import settings
from app.db import init_db
from app.routers import dashboard, board, notes, api
from app.middleware.csrf import CSRFMiddleware
from app.routers import dashboard, board, notes, api, auth, webhooks
from app.services.gitea_client import gitea
# ── logging ──
@@ -23,12 +26,24 @@ logger = logging.getLogger(__name__)
app = FastAPI(
title="FlowDeck",
version="0.2.0",
version="0.3.0",
docs_url="/docs" if settings.log_level == "DEBUG" else None,
redoc_url=None,
)
# ponytail: CORS for LAN access
# ── middleware (order matters) ──
# Session (for OAuth2 state)
app.add_middleware(
SessionMiddleware,
secret_key=settings.app_secret_key,
max_age=3600, # 1 hour for OAuth state
)
# CSRF protection
app.add_middleware(CSRFMiddleware)
# CORS for LAN access
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
@@ -38,10 +53,12 @@ app.add_middleware(
# ── routers ──
app.include_router(auth.router)
app.include_router(dashboard.router)
app.include_router(board.router)
app.include_router(notes.router)
app.include_router(api.router)
app.include_router(webhooks.router)
# ── static ──
@@ -51,4 +68,11 @@ app.mount("/static", StaticFiles(directory="static"), name="static")
@app.on_event("startup")
async def startup():
init_db()
logger.info("FlowDeck started on port %d", settings.app_port)
# Seed admin user for non-OAuth mode
from app.db import get_conn
with get_conn() as conn:
conn.execute(
"INSERT OR IGNORE INTO users (login, full_name, email, is_admin) VALUES ('admin', 'Admin', '', 1)"
)
conn.commit()
logger.info("FlowDeck v0.3.0 started on port %d", settings.app_port)