- Invite flow: admin issue/revoke on join requests, public single-use claim links (hash-only tokens, 7-day expiry, no state reveal), claim creates the Member account and marks the request accepted - Gallery: admin status page plus resync endpoint; sync refreshes thumbnails whose content changed; tools/gallery contract and checker - Docs: public content page, admin-only operator runbook, api.md invite/gallery sections, all routes in the live API docs, docs reachability gate test - Screenshots cover the new pages; version 1.0.18
39 lines
1.1 KiB
Python
39 lines
1.1 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
from fastapi import APIRouter, Request
|
|
|
|
from molodetz.database import count_media
|
|
from molodetz.gallery import missing_sources, sync_gallery
|
|
from molodetz.responses import action_result, respond
|
|
from molodetz.routers.admin._shared import admin_seo
|
|
from molodetz.schemas.admin import GalleryStatusOut
|
|
from molodetz.utils.audit import record
|
|
from molodetz.utils.auth import require_admin
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _status():
|
|
return {
|
|
"flyers": count_media("flyer"),
|
|
"memes": count_media("meme"),
|
|
"missing": missing_sources(),
|
|
}
|
|
|
|
|
|
@router.get("/gallery")
|
|
async def gallery_status(request: Request):
|
|
require_admin(request)
|
|
ctx = dict(_status())
|
|
ctx["seo"] = admin_seo(request, "Gallery")
|
|
return respond(request, "admin/gallery.html", ctx, model=GalleryStatusOut)
|
|
|
|
|
|
@router.post("/gallery/resync")
|
|
async def gallery_resync(request: Request):
|
|
user = require_admin(request)
|
|
synced = sync_gallery()
|
|
data = dict(_status())
|
|
data["synced"] = synced
|
|
record(request, "gallery.resync", payload=data, user=user)
|
|
return action_result(request, "/admin/gallery", data=data)
|