Member invites, gallery resync, content and operator docs
- 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
This commit is contained in:
@@ -6,7 +6,7 @@ GROUP = {
|
||||
"title": "Admin",
|
||||
"description": "Administrators only. Writing and publishing posts, join requests, services, trash, settings.",
|
||||
"endpoints": [
|
||||
endpoint("GET", "/admin", "Overview", auth="admin", sample={"post_count": 5, "open_joins": 0, "user_count": 1, "deleted_count": 0}),
|
||||
endpoint("GET", "/admin", "Overview", auth="admin", sample={"post_count": 5, "open_joins": 0, "user_count": 1, "deleted_count": 0, "flyer_count": 4, "meme_count": 38}),
|
||||
endpoint("GET", "/admin/posts", "Posts", auth="admin", sample={"posts": []}),
|
||||
endpoint("GET", "/admin/posts/new", "New post", auth="admin", sample={}),
|
||||
endpoint(
|
||||
@@ -32,6 +32,10 @@ GROUP = {
|
||||
endpoint("GET", "/admin/joins", "Join requests", auth="admin", sample={"requests": []}),
|
||||
endpoint("POST", "/admin/joins/{uid}/status", "Join request status", auth="admin", fields=[field("uid", required=True, location="path"), field("status", enum=["open", "contacted", "accepted", "declined"])], probe_path="/admin/joins/unknown/status", probe_body={"status": "open"}),
|
||||
endpoint("POST", "/admin/joins/{uid}/check", "Check repo link", "Fetches the title through the guarded outbound client.", auth="admin", fields=[field("uid", required=True, location="path")], probe_path="/admin/joins/unknown/check"),
|
||||
endpoint("POST", "/admin/joins/{uid}/invite", "Issue invite", "Issues a single-use claim link, shown once. Revokes the previous open invite.", auth="admin", fields=[field("uid", required=True, location="path")], sample={"ok": True, "redirect": "/admin/joins", "data": {"claim_url": "https://example.com/invite/...", "expires_at": "..."}}, probe_path="/admin/joins/unknown/invite"),
|
||||
endpoint("POST", "/admin/joins/{uid}/invite/revoke", "Revoke invite", "Revokes the open invite without touching the request.", auth="admin", fields=[field("uid", required=True, location="path")], sample={"ok": True, "redirect": "/admin/joins", "data": {"revoked": 1}}, probe_path="/admin/joins/unknown/invite/revoke"),
|
||||
endpoint("GET", "/admin/gallery", "Gallery status", "Live flyer and meme counts plus catalogued files missing from disk.", auth="admin", sample={"flyers": 4, "memes": 38, "missing": []}),
|
||||
endpoint("POST", "/admin/gallery/resync", "Resync gallery", "Re-reads sources, refreshes changed thumbnails, retires removed entries.", auth="admin", sample={"ok": True, "redirect": "/admin/gallery", "data": {"synced": 42, "flyers": 4, "memes": 38, "missing": []}}),
|
||||
endpoint("GET", "/admin/services", "Services", auth="admin", sample={"services": []}),
|
||||
endpoint("GET", "/admin/services/{name}", "Service", auth="admin", fields=[field("name", required=True, location="path", example="backup")], probe_path="/admin/services/backup"),
|
||||
endpoint("POST", "/admin/services/{name}/command", "Service command", auth="admin", fields=[field("name", required=True, location="path"), field("verb", enum=["start", "stop", "run", "clear"])], probe_path="/admin/services/backup/command", probe_body={"verb": "noop"}),
|
||||
|
||||
@@ -21,5 +21,31 @@ GROUP = {
|
||||
sample={"ok": True, "redirect": "/join?ok=1", "data": {"uid": "0190..."}},
|
||||
probe_body={"name": "Probe", "contact": "probe@example.com"},
|
||||
),
|
||||
endpoint(
|
||||
"GET",
|
||||
"/invite/{token}",
|
||||
"Invite claim form",
|
||||
"Shows the claim form for a live invite; unknown, used and expired links answer 404 with the same message.",
|
||||
fields=[field("token", required=True, location="path", example="unknown-token")],
|
||||
sample={"valid": True, "username": "ada", "email": "ada@example.com", "email_locked": True},
|
||||
probe_path="/invite/unknown-token",
|
||||
),
|
||||
endpoint(
|
||||
"POST",
|
||||
"/invite/{token}",
|
||||
"Claim invite",
|
||||
"Creates the Member account, logs in and marks the join request accepted. Single use.",
|
||||
fields=[
|
||||
field("token", required=True, location="path", example="unknown-token"),
|
||||
field("username", required=True, example="ada"),
|
||||
field("email", required=True, example="ada@example.com"),
|
||||
field("password", required=True, example="correct horse battery staple"),
|
||||
field("password_confirm", required=True, example="correct horse battery staple"),
|
||||
field("terms", required=True, type="boolean", example=True),
|
||||
],
|
||||
sample={"ok": True, "redirect": "/", "data": {"username": "ada"}},
|
||||
probe_path="/invite/unknown-token",
|
||||
probe_body={"username": "Probe", "email": "probe@example.com", "password": "probe-pass-1", "password_confirm": "probe-pass-1", "terms": True},
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user