snotes updates, actions, attachments, easy user space
This commit is contained in:
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
import structlog
|
||||
from fastapi import Depends, FastAPI, HTTPException, Request, Response
|
||||
from fastapi import Depends, FastAPI, File, Form, HTTPException, Request, Response, UploadFile
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
@@ -49,6 +49,12 @@ def current_user(request: Request) -> str:
|
||||
return username or settings.user_list[0]
|
||||
|
||||
|
||||
def valid_username(username: str) -> str:
|
||||
if username not in settings.user_list and username != settings.common_space:
|
||||
raise HTTPException(status_code=404, detail="unknown user")
|
||||
return username
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SPA + static
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -61,7 +67,7 @@ async def index() -> FileResponse:
|
||||
|
||||
@app.get("/favicon.ico", include_in_schema=False)
|
||||
async def favicon() -> FileResponse:
|
||||
return FileResponse("static/snotes-main.png")
|
||||
return FileResponse("static/snotes-new.png")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -114,6 +120,8 @@ async def login(body: LoginRequest) -> Response:
|
||||
@app.post("/api/notes")
|
||||
async def create_note(body: NoteCreate, request: Request, _: None = Depends(require_access)):
|
||||
username = current_user(request)
|
||||
if body.username:
|
||||
username = valid_username(body.username)
|
||||
if not body.body.strip():
|
||||
raise HTTPException(status_code=400, detail="empty note")
|
||||
try:
|
||||
@@ -148,7 +156,13 @@ async def get_note(note_id: str, _: None = Depends(require_access)):
|
||||
async def update_note(
|
||||
note_id: str, body: NoteUpdate, request: Request, _: None = Depends(require_access)
|
||||
):
|
||||
note = await notes.update_note(note_id, body.body, ip=client_ip(request), ua=client_ua(request))
|
||||
note = await notes.update_note(
|
||||
note_id,
|
||||
body.body,
|
||||
ip=client_ip(request),
|
||||
ua=client_ua(request),
|
||||
username=valid_username(body.username) if body.username else None,
|
||||
)
|
||||
if note is None:
|
||||
raise HTTPException(status_code=404, detail="note not found")
|
||||
return note
|
||||
@@ -168,10 +182,45 @@ async def note_history(note_id: str, _: None = Depends(require_access)):
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tags / search
|
||||
# Attachments
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@app.post("/api/attachments")
|
||||
async def upload_attachment(
|
||||
request: Request,
|
||||
file: UploadFile = File(...),
|
||||
username: str | None = Form(None),
|
||||
_: None = Depends(require_access),
|
||||
):
|
||||
owner = current_user(request)
|
||||
if username:
|
||||
owner = valid_username(username)
|
||||
data = await file.read()
|
||||
if not data:
|
||||
raise HTTPException(status_code=400, detail="empty file")
|
||||
filename = await notes.store_attachment(owner, file.filename or "file", data)
|
||||
return {
|
||||
"url": f"/api/attachments/{owner}/{filename}",
|
||||
"name": file.filename or filename,
|
||||
"path": f"{owner}/attachments/{filename}",
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/attachments/{username}/{filename}")
|
||||
async def get_attachment(username: str, filename: str, _: None = Depends(require_access)):
|
||||
from pathlib import Path
|
||||
|
||||
full = Path(settings.data_dir) / username / "attachments" / filename
|
||||
if not full.is_file():
|
||||
raise HTTPException(status_code=404, detail="not found")
|
||||
return FileResponse(full)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tags / search
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@app.get("/api/tags")
|
||||
async def tags(_: None = Depends(require_access)):
|
||||
return await notes.tag_counts()
|
||||
|
||||
Reference in New Issue
Block a user