feat: vault-aligned server v1 + UX polish
All checks were successful
CI / Windows build (push) Successful in 7m47s
All checks were successful
CI / Windows build (push) Successful in 7m47s
Redesign the optional FastAPI companion around vault files (manifest / PUT/GET/DELETE + OCR jobs) instead of legacy strokes_json notes. Wire a client Server settings panel for health/login. Polish shell UX: l10n for settings/home/board, sticky-board empty state, and a narrow-screen diagnostics FAB. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,12 @@
|
||||
"""BadNote FastAPI server — main application."""
|
||||
"""BadNote FastAPI server — main application.
|
||||
|
||||
Architecture (v1):
|
||||
- Vault files are the source of truth (same layout as the Flutter vault).
|
||||
- ``/api/v1/vault/*`` assists multi-device sync (manifest + PUT/GET/DELETE).
|
||||
- ``/api/v1/ocr/*`` accepts ink rasters for deferred server OCR.
|
||||
- Legacy ``/api/notes`` etc. remain mounted under ``/api/legacy/*`` for
|
||||
compatibility with old experiments; new clients must not use them.
|
||||
"""
|
||||
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
@@ -13,12 +21,15 @@ from .routers.notes_router import router as notes_router
|
||||
from .routers.documents_router import router as documents_router
|
||||
from .routers.ocr_router import router as ocr_router
|
||||
from .routers.sync_router import router as sync_router
|
||||
from .routers.v1_vault_router import router as v1_vault_router
|
||||
from .routers.v1_ocr_router import router as v1_ocr_router
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Startup: create directories and init DB. Shutdown: close DB."""
|
||||
os.makedirs(settings.storage_path, exist_ok=True)
|
||||
os.makedirs(settings.vault_path, exist_ok=True)
|
||||
for subdir in ("pending", "processing", "done", "failed"):
|
||||
os.makedirs(os.path.join(settings.queue_path, subdir), exist_ok=True)
|
||||
await init_db()
|
||||
@@ -26,12 +37,17 @@ async def lifespan(app: FastAPI):
|
||||
await close_db()
|
||||
|
||||
|
||||
app = FastAPI(title="BadNote Server", version="1.0.0", lifespan=lifespan)
|
||||
app = FastAPI(
|
||||
title="BadNote Server",
|
||||
version="2.0.0",
|
||||
description=(
|
||||
"Self-hosted companion for BadNote. "
|
||||
"Primary API is /api/v1 (vault + OCR). "
|
||||
"Legacy notes CRUD lives under /api/legacy."
|
||||
),
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# Authentication is Bearer-token based, so cookies/credentials are not needed.
|
||||
# `allow_origins=["*"]` together with `allow_credentials=True` is an invalid and
|
||||
# insecure combination, so we keep credentials disabled. Set BADNOTE_CORS_ORIGINS
|
||||
# (comma-separated) to lock the API down to specific front-end origins.
|
||||
_cors_origins = settings.cors_origins or ["*"]
|
||||
|
||||
app.add_middleware(
|
||||
@@ -42,14 +58,34 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# --- Auth (shared by v1 + legacy) ---
|
||||
app.include_router(auth_router, prefix="/api/auth", tags=["auth"])
|
||||
app.include_router(notes_router, prefix="/api/notes", tags=["notes"])
|
||||
app.include_router(documents_router, prefix="/api/documents", tags=["documents"])
|
||||
app.include_router(ocr_router, prefix="/api/ocr", tags=["ocr"])
|
||||
app.include_router(sync_router, prefix="/api/sync", tags=["sync"])
|
||||
app.include_router(auth_router, prefix="/api/v1/auth", tags=["auth-v1"])
|
||||
|
||||
# --- Primary v1 API ---
|
||||
app.include_router(v1_vault_router, prefix="/api/v1/vault", tags=["vault-v1"])
|
||||
app.include_router(v1_ocr_router, prefix="/api/v1/ocr", tags=["ocr-v1"])
|
||||
|
||||
# --- Legacy (deprecated) ---
|
||||
app.include_router(notes_router, prefix="/api/legacy/notes", tags=["legacy-notes"])
|
||||
app.include_router(documents_router, prefix="/api/legacy/documents", tags=["legacy-documents"])
|
||||
app.include_router(ocr_router, prefix="/api/legacy/ocr", tags=["legacy-ocr"])
|
||||
app.include_router(sync_router, prefix="/api/legacy/sync", tags=["legacy-sync"])
|
||||
# Keep old paths temporarily so existing bookmarks to /docs experiments still work,
|
||||
# but they are the same legacy routers.
|
||||
app.include_router(notes_router, prefix="/api/notes", tags=["legacy-notes"], include_in_schema=False)
|
||||
app.include_router(documents_router, prefix="/api/documents", tags=["legacy-documents"], include_in_schema=False)
|
||||
app.include_router(ocr_router, prefix="/api/ocr", tags=["legacy-ocr"], include_in_schema=False)
|
||||
app.include_router(sync_router, prefix="/api/sync", tags=["legacy-sync"], include_in_schema=False)
|
||||
|
||||
|
||||
@app.get("/api/ping")
|
||||
async def ping() -> dict:
|
||||
"""Health check endpoint."""
|
||||
return {"status": "ok"}
|
||||
@app.get("/api/v1/health")
|
||||
async def health() -> dict:
|
||||
"""Liveness probe for clients and reverse proxies."""
|
||||
return {
|
||||
"status": "ok",
|
||||
"version": "2.0.0",
|
||||
"api": "v1",
|
||||
"features": ["vault", "ocr", "auth"],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user