# retoor <retoor@molodetz.nl>
|
|
|
|
from .._shared import endpoint, field
|
|
|
|
GROUP = {
|
|
"slug": "admin",
|
|
"title": "Admin API",
|
|
"admin": True,
|
|
"intro": """
|
|
# Admin API
|
|
|
|
Site administration endpoints for users, news curation, and settings. Every call requires an
|
|
**admin** account. Background service management lives on the
|
|
[Background Services](/docs/services.html) page.
|
|
|
|
Every endpoint follows the shared [Conventions & Errors](/docs/conventions.html) (auth, content
|
|
negotiation, pagination, status codes); see [Authentication](/docs/authentication.html) for the
|
|
four ways to sign requests.
|
|
""",
|
|
"endpoints": [
|
|
endpoint(
|
|
id="admin-users",
|
|
method="GET",
|
|
path="/admin/users",
|
|
title="List users",
|
|
summary="Paginated user management page. Returns HTML.",
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[field("page", "query", "int", False, "1", "Page number.")],
|
|
),
|
|
endpoint(
|
|
id="admin-media",
|
|
method="GET",
|
|
path="/admin/media",
|
|
title="Media trash",
|
|
summary=(
|
|
"Moderation view of deleted media. When a member or admin deletes an "
|
|
"attachment it is soft-deleted: hidden from the gallery and from its parent "
|
|
"object, but the row and file are kept and the relation to the parent is "
|
|
"preserved. This page lists every soft-deleted attachment, newest first, with "
|
|
"its uploader, so an admin can restore or permanently purge it. Returns HTML."
|
|
),
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[field("page", "query", "int", False, "1", "Page number.")],
|
|
),
|
|
endpoint(
|
|
id="media-restore",
|
|
method="POST",
|
|
path="/media/{uid}/restore",
|
|
title="Restore media",
|
|
summary=(
|
|
"Restore a soft-deleted attachment. Because the parent relation is never "
|
|
"cleared, it reappears in the owner's Media tab and on its original post, "
|
|
"project, or gist immediately."
|
|
),
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"uid", "path", "string", True, "", "Soft-deleted attachment uid."
|
|
),
|
|
],
|
|
sample_response={"ok": True, "redirect": "/admin/media"},
|
|
),
|
|
endpoint(
|
|
id="admin-revoke-award",
|
|
method="POST",
|
|
path="/admin/awards/{uid}/revoke",
|
|
title="Revoke award",
|
|
summary=(
|
|
"Soft-delete a published award and its linked attachments, then recompute "
|
|
"receiver stats. Restorable from admin trash."
|
|
),
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "", "Award uid to revoke."),
|
|
],
|
|
sample_response={"ok": True, "redirect": "/profile/receiver?tab=awards"},
|
|
),
|
|
endpoint(
|
|
id="admin-media-purge",
|
|
method="POST",
|
|
path="/admin/media/{uid}/purge",
|
|
title="Purge media",
|
|
summary=(
|
|
"Permanently delete a soft-deleted attachment: removes the database row and "
|
|
"deletes the file from disk. This cannot be undone and is the only way media "
|
|
"is hard-deleted from the UI."
|
|
),
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "", "Attachment uid to purge."),
|
|
],
|
|
sample_response={"ok": True, "redirect": "/admin/media"},
|
|
),
|
|
endpoint(
|
|
id="admin-audit-log",
|
|
method="GET",
|
|
path="/admin/audit-log",
|
|
title="Audit log",
|
|
summary=(
|
|
"Paginated, filterable audit event list (newest first, 25 per page). "
|
|
"Negotiates HTML or JSON. Every state-changing action on the platform "
|
|
"is recorded here with actor, origin, target, change, and result."
|
|
),
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[
|
|
field("page", "query", "int", False, "1", "Page number."),
|
|
field("event_key", "query", "string", False, "post.create", "Filter by exact event key."),
|
|
field("category", "query", "string", False, "admin", "Filter by category (auth, content, admin, container, ...)."),
|
|
field("actor_role", "query", "string", False, "admin", "Filter by actor role at action time."),
|
|
field("actor_uid", "query", "string", False, "", "Filter by acting user uid."),
|
|
field("origin", "query", "string", False, "web", "Filter by origin (web, api, devii, cli, service, scheduler)."),
|
|
field("result", "query", "string", False, "denied", "Filter by result (success, failure, denied)."),
|
|
field("q", "query", "string", False, "", "Free-text search over summary, event key, and target."),
|
|
field("date_from", "query", "string", False, "", "ISO date lower bound (inclusive)."),
|
|
field("date_to", "query", "string", False, "", "ISO date upper bound (inclusive)."),
|
|
],
|
|
sample_response={
|
|
"entries": [
|
|
{
|
|
"uid": "AUDIT_UID",
|
|
"created_at": "2026-06-11T20:00:00+00:00",
|
|
"event_key": "admin.setting.update",
|
|
"category": "admin",
|
|
"actor_username": "ADMIN",
|
|
"actor_role": "admin",
|
|
"origin": "web",
|
|
"via_agent": 0,
|
|
"target_type": "setting",
|
|
"old_value": "0",
|
|
"new_value": "1",
|
|
"result": "success",
|
|
}
|
|
],
|
|
"pagination": {"page": 1, "total": 1, "total_pages": 1},
|
|
"filters": {},
|
|
"options": {"category": ["admin", "auth", "content"]},
|
|
},
|
|
),
|
|
endpoint(
|
|
id="admin-audit-event",
|
|
method="GET",
|
|
path="/admin/audit-log/{uid}",
|
|
title="Audit event",
|
|
summary="A single audit event with its full row and every related-object link.",
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[field("uid", "path", "string", True, "AUDIT_UID", "Audit event uid.")],
|
|
sample_response={
|
|
"event": {"uid": "AUDIT_UID", "event_key": "container.instance.start", "result": "success"},
|
|
"links": [
|
|
{"relation": "actor", "object_type": "user", "object_uid": "USER_UID"},
|
|
{"relation": "instance", "object_type": "instance", "object_uid": "INSTANCE_UID"},
|
|
],
|
|
},
|
|
),
|
|
endpoint(
|
|
id="admin-analytics",
|
|
method="GET",
|
|
path="/admin/analytics",
|
|
title="Site analytics",
|
|
summary=(
|
|
"One-call aggregate analytics, returned as JSON: total members, active users "
|
|
"in the last 24h/7d/30d, signed_in_now, new signups (24h/7d/30d), content "
|
|
"totals (posts, comments, gists, projects, news), and top authors. Use this "
|
|
"instead of paging the user list to count or measure activity."
|
|
),
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"top_n",
|
|
"query",
|
|
"int",
|
|
False,
|
|
"10",
|
|
"How many top authors to include (1-50).",
|
|
)
|
|
],
|
|
notes=[
|
|
"`signed_in_now` counts members holding an unexpired session (logged in within the session lifetime, default 7-30 days), not a real-time online/presence count; the response carries a `signed_in_definition` explaining this. `active_*` means created content in the window (`active_definition`).",
|
|
"This is the endpoint the Devii assistant calls as `site_analytics`; see [Devii internals](/docs/devii-internals.html).",
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-statistics",
|
|
method="GET",
|
|
path="/admin/statistics/data",
|
|
title="Platform statistics",
|
|
summary=(
|
|
"Tabbed platform statistics with KPI cards, period-over-period deltas, "
|
|
"time-series data for charts, and breakdown tables. Covers visitors, members, "
|
|
"content, engagement, social, AI, Devii, services, containers, game, awards, "
|
|
"moderation, tools, and storage."
|
|
),
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"tab",
|
|
"query",
|
|
"string",
|
|
False,
|
|
"overview",
|
|
"Tab key (overview, visitors, members, content, ...).",
|
|
),
|
|
field(
|
|
"hours",
|
|
"query",
|
|
"int",
|
|
False,
|
|
"168",
|
|
"Lookback window in hours (24, 168, 720, 2160, or 0 for all time).",
|
|
),
|
|
field(
|
|
"compare",
|
|
"query",
|
|
"int",
|
|
False,
|
|
"1",
|
|
"Include previous-period comparison (1 or 0).",
|
|
),
|
|
field(
|
|
"top_n",
|
|
"query",
|
|
"int",
|
|
False,
|
|
"10",
|
|
"Rows in breakdown tables (1-50).",
|
|
),
|
|
],
|
|
notes=[
|
|
"The HTML dashboard lives at `/admin/statistics`. Visitor metrics require the statistics tracking middleware (hourly aggregation, 90-day retention).",
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-statistics-page",
|
|
method="GET",
|
|
path="/admin/statistics",
|
|
title="Statistics dashboard",
|
|
summary="Admin HTML dashboard for platform statistics with charts and tabs.",
|
|
auth="admin",
|
|
interactive=False,
|
|
params=[
|
|
field(
|
|
"tab",
|
|
"query",
|
|
"string",
|
|
False,
|
|
"overview",
|
|
"Initial tab to render.",
|
|
),
|
|
field(
|
|
"hours",
|
|
"query",
|
|
"int",
|
|
False,
|
|
"168",
|
|
"Initial time window in hours.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-ai-usage",
|
|
method="GET",
|
|
path="/admin/ai-usage/data",
|
|
title="AI gateway usage analytics",
|
|
summary=(
|
|
"One-call AI gateway metrics, returned as JSON for a bounded time window: request "
|
|
"volume and throughput, token usage with averages and percentiles (p50/p90/p95/p99), "
|
|
"latency (upstream, gateway overhead, queue wait, connection establishment), error "
|
|
"rates by category, cost in USD (per model, per caller, input vs output, projected "
|
|
"monthly burn, caching savings), caller behavior, and an hourly breakdown. Cost is "
|
|
"taken from the upstream native cost when present (OpenRouter) and computed from the "
|
|
"configured per-million pricing otherwise (DeepSeek)."
|
|
),
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"hours",
|
|
"query",
|
|
"int",
|
|
False,
|
|
"48",
|
|
"Lookback window in hours (1-168).",
|
|
),
|
|
field(
|
|
"top_n",
|
|
"query",
|
|
"int",
|
|
False,
|
|
"10",
|
|
"How many rows in each top-N breakdown.",
|
|
),
|
|
],
|
|
notes=[
|
|
"TTFT and inter-token latency are not reported: the gateway forwards non-streaming to the upstream."
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-user-ai-usage",
|
|
method="GET",
|
|
path="/admin/users/{uid}/ai-usage",
|
|
title="Per-user AI usage",
|
|
summary=(
|
|
"One user's AI gateway usage over the last 24 hours, returned as JSON: request "
|
|
"volume, success and error rates, token totals, cost (window, per hour, per request, "
|
|
"and a 30-day projection from the full 24h spend), average latency and throughput, a "
|
|
"per-model breakdown, and an hourly cost series. Because Devii operates a signed-in "
|
|
"user's account with that user's own API key, this is the user's complete gateway "
|
|
"spend, whether driven through Devii or direct API calls. Shown admin-only on the "
|
|
"user's profile page."
|
|
),
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"uid", "path", "string", True, "USER_UID", "Target user UID."
|
|
),
|
|
field(
|
|
"hours",
|
|
"query",
|
|
"int",
|
|
False,
|
|
"24",
|
|
"Lookback window in hours (1-168).",
|
|
),
|
|
],
|
|
sample_response={
|
|
"owner_id": "USER_UID",
|
|
"window_hours": 24,
|
|
"requests": 42,
|
|
"success": 41,
|
|
"failed": 1,
|
|
"success_pct": 97.6,
|
|
"error_pct": 2.4,
|
|
"tokens": {"prompt": 120000, "completion": 38000, "total": 158000},
|
|
"cost": {
|
|
"window_usd": 0.214,
|
|
"per_hour_usd": 0.0089,
|
|
"per_request_usd": 0.0051,
|
|
"projected_30d_usd": 6.42,
|
|
},
|
|
"latency": {"avg_ms": 1830.0, "avg_tps": 41.2},
|
|
"by_model": [
|
|
{
|
|
"key": "molodetz",
|
|
"requests": 42,
|
|
"total_tokens": 158000,
|
|
"cost_usd": 0.214,
|
|
}
|
|
],
|
|
"hourly": [
|
|
{
|
|
"hour": "2026-06-08T16",
|
|
"requests": 6,
|
|
"cost_usd": 0.031,
|
|
"total_tokens": 22000,
|
|
}
|
|
],
|
|
},
|
|
),
|
|
endpoint(
|
|
id="admin-user-role",
|
|
method="POST",
|
|
path="/admin/users/{uid}/role",
|
|
title="Set a user role",
|
|
summary="Promote or demote a user. You cannot change your own role.",
|
|
auth="admin",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"uid", "path", "string", True, "USER_UID", "Target user UID."
|
|
),
|
|
field(
|
|
"role",
|
|
"form",
|
|
"enum",
|
|
True,
|
|
"member",
|
|
"New role.",
|
|
["member", "admin"],
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-user-password",
|
|
method="POST",
|
|
path="/admin/users/{uid}/password",
|
|
title="Reset a user password",
|
|
summary="Set a new password for a user.",
|
|
auth="admin",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"uid", "path", "string", True, "USER_UID", "Target user UID."
|
|
),
|
|
field(
|
|
"password",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"newpassword",
|
|
"New password, 6+ characters.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-user-toggle",
|
|
method="POST",
|
|
path="/admin/users/{uid}/toggle",
|
|
title="Enable or disable a user",
|
|
summary="Toggle a user's active state. You cannot disable yourself.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "USER_UID", "Target user UID.")
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-news-list",
|
|
method="GET",
|
|
path="/admin/news",
|
|
title="List news articles",
|
|
summary="Paginated news management page. Returns HTML.",
|
|
auth="admin",
|
|
interactive=True,
|
|
params=[field("page", "query", "int", False, "1", "Page number.")],
|
|
),
|
|
endpoint(
|
|
id="admin-news-toggle",
|
|
method="POST",
|
|
path="/admin/news/{uid}/toggle",
|
|
title="Toggle featured",
|
|
summary="Toggle an article's featured flag and lock it from the news service's auto-rotation.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "NEWS_UID", "Article UID.")
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-news-publish",
|
|
method="POST",
|
|
path="/admin/news/{uid}/publish",
|
|
title="Toggle published",
|
|
summary="Switch an article between draft and published.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "NEWS_UID", "Article UID.")
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-news-landing",
|
|
method="POST",
|
|
path="/admin/news/{uid}/landing",
|
|
title="Toggle landing",
|
|
summary="Toggle whether an article shows on the landing page and lock it from the news service's auto-rotation.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "NEWS_UID", "Article UID.")
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-news-delete",
|
|
method="POST",
|
|
path="/admin/news/{uid}/delete",
|
|
title="Delete a news article",
|
|
summary="Delete an article and its images.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "NEWS_UID", "Article UID.")
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-settings-get",
|
|
method="GET",
|
|
path="/admin/settings",
|
|
title="Read site settings",
|
|
summary="Return the current site and operational settings. Negotiates HTML or JSON.",
|
|
auth="admin",
|
|
interactive=True,
|
|
),
|
|
endpoint(
|
|
id="admin-settings",
|
|
method="POST",
|
|
path="/admin/settings",
|
|
title="Save site settings",
|
|
summary="Update operational and site settings. Empty fields are skipped.",
|
|
auth="admin",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"site_name", "form", "string", False, "DevPlace", "Site name."
|
|
),
|
|
field(
|
|
"rate_limit_per_minute",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"60",
|
|
"Requests per window.",
|
|
),
|
|
field(
|
|
"registration_open",
|
|
"form",
|
|
"enum",
|
|
False,
|
|
"1",
|
|
"Allow signups.",
|
|
["1", "0"],
|
|
),
|
|
field(
|
|
"maintenance_mode",
|
|
"form",
|
|
"enum",
|
|
False,
|
|
"0",
|
|
"Maintenance gate.",
|
|
["1", "0"],
|
|
),
|
|
],
|
|
notes=[
|
|
"Accepts every field on the admin settings form; only non-empty values are written."
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-notification-default",
|
|
method="POST",
|
|
path="/admin/notifications",
|
|
title="Set a notification default",
|
|
summary="Set the platform-wide default for one notification type on one channel. Applies to users who have not customized that notification; explicit user choices are unaffected.",
|
|
auth="admin",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"notification_type",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"vote",
|
|
"One of: comment, reply, mention, vote, follow, message, badge, level, issue, reminder, harvest_stolen.",
|
|
),
|
|
field(
|
|
"channel",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"push",
|
|
"One of in_app, push or telegram (telegram is off by default and requires a paired Telegram account).",
|
|
),
|
|
field(
|
|
"value",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"1",
|
|
"1 to enable this notification by default, 0 to disable.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"redirect": "/admin/notifications",
|
|
"data": {
|
|
"notification_type": "vote",
|
|
"channel": "push",
|
|
"value": False,
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
id="admin-user-reset-ai-quota",
|
|
method="POST",
|
|
path="/admin/users/{uid}/reset-ai-quota",
|
|
title="Reset user AI quota",
|
|
summary="Delete a specific user's AI gateway ledger rows, resetting their quota.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"uid", "path", "string", True, "USER_UID", "Target user UID."
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-ai-quota-reset-guests",
|
|
method="POST",
|
|
path="/admin/ai-quota/reset-guests",
|
|
title="Reset guest AI quotas",
|
|
summary="Reset AI quota for all anonymous guest users.",
|
|
auth="admin",
|
|
destructive=True,
|
|
),
|
|
endpoint(
|
|
id="admin-ai-quota-reset-all",
|
|
method="POST",
|
|
path="/admin/ai-quota/reset-all",
|
|
title="Reset all AI quotas",
|
|
summary="Reset AI quota for every user (members and guests).",
|
|
auth="admin",
|
|
destructive=True,
|
|
),
|
|
endpoint(
|
|
id="admin-gateway-quota-rules",
|
|
method="GET",
|
|
path="/admin/gateway/quota-rules",
|
|
title="List AI gateway quota rules",
|
|
summary=(
|
|
"List every rolling-24h USD quota rule on /openai/v1/*, each scoped by any "
|
|
"combination of role, specific user uid, and app_reference label, plus the "
|
|
"global per-role default caps that apply when no rule matches."
|
|
),
|
|
auth="admin",
|
|
interactive=True,
|
|
),
|
|
endpoint(
|
|
id="admin-gateway-quota-rule-set",
|
|
method="POST",
|
|
path="/admin/gateway/quota-rules",
|
|
title="Create or update an AI gateway quota rule",
|
|
summary=(
|
|
"Caps rolling-24h USD spend on /openai/v1/*. At least one of owner_kind, "
|
|
"owner_id, app_reference must be set; leaving a dimension blank makes it a "
|
|
"wildcard, and the most specific active match wins over other rules and over "
|
|
"the global default. Pass uid to update an existing rule."
|
|
),
|
|
auth="admin",
|
|
params=[
|
|
field("uid", "json", "string", False, "", "Existing rule uid to update; omit to create a new rule."),
|
|
field("owner_kind", "json", "string", False, "user", "internal, key, user, admin, or anonymous. Blank = any role."),
|
|
field("owner_id", "json", "string", False, "", "Specific user uid. Blank = any caller of the matched role."),
|
|
field("app_reference", "json", "string", False, "devplace-bots-v-1-0-0", "App label (the X-App-Reference header). Blank = any app."),
|
|
field("limit_usd", "json", "number", True, "2.5", "Rolling 24h USD cap. 0 = unlimited."),
|
|
field("is_active", "json", "boolean", False, "true", "Whether the rule is enforced."),
|
|
field("label", "json", "string", False, "", "Optional admin-facing note."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-gateway-quota-rule-delete",
|
|
method="DELETE",
|
|
path="/admin/gateway/quota-rules/{uid}",
|
|
title="Delete an AI gateway quota rule",
|
|
summary="Delete a quota rule; callers it covered fall back to the next most specific rule or the global default.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "RULE_UID", "Quota rule uid."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-bots-monitor",
|
|
method="GET",
|
|
path="/admin/bots",
|
|
title="Bot monitor page",
|
|
summary="Live low-quality screenshot grid of every running bot persona, one frame per bot.",
|
|
auth="admin",
|
|
interactive=True,
|
|
),
|
|
endpoint(
|
|
id="admin-bots-data",
|
|
method="GET",
|
|
path="/admin/bots/data",
|
|
title="Bot monitor data",
|
|
summary="JSON of every running bot slot with its label, persona, current action/status, and latest frame URL for polling.",
|
|
auth="admin",
|
|
sample_response={
|
|
"enabled": True,
|
|
"service_status": "running",
|
|
"frames": [
|
|
{
|
|
"slot": 0,
|
|
"username": "bytewren",
|
|
"persona": "grumpy_senior",
|
|
"action": "POST: rant [3]",
|
|
"status": "page load",
|
|
"url": "https://example.com/feed",
|
|
"label": "bytewren",
|
|
"captured_at": 1718366400,
|
|
"age_seconds": 2,
|
|
"has_image": True,
|
|
"active": True,
|
|
"frame_url": "/admin/bots/0/frame.jpg?t=1718366400",
|
|
}
|
|
],
|
|
},
|
|
),
|
|
endpoint(
|
|
id="admin-bots-frame",
|
|
method="GET",
|
|
path="/admin/bots/{slot}/frame.jpg",
|
|
title="Bot frame image",
|
|
summary="The latest low-quality JPEG screenshot for one bot slot (no-store).",
|
|
auth="admin",
|
|
encoding="none",
|
|
interactive=False,
|
|
params=[
|
|
field(
|
|
"slot",
|
|
"path",
|
|
"integer",
|
|
True,
|
|
"0",
|
|
"Bot fleet slot index.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="admin-backups",
|
|
method="GET",
|
|
path="/admin/backups",
|
|
title="Backups dashboard",
|
|
summary=(
|
|
"Storage usage, every backup archive, and every backup schedule. Returns HTML "
|
|
"(or JSON with Accept: application/json)."
|
|
),
|
|
auth="admin",
|
|
interactive=True,
|
|
),
|
|
endpoint(
|
|
id="admin-backups-data",
|
|
method="GET",
|
|
path="/admin/backups/data",
|
|
title="Backups data",
|
|
summary=(
|
|
"JSON dashboard payload: per-path storage usage, total data and backup size, "
|
|
"disk usage, the backup list, and the schedule list."
|
|
),
|
|
auth="admin",
|
|
),
|
|
endpoint(
|
|
id="admin-backups-run",
|
|
method="POST",
|
|
path="/admin/backups/run",
|
|
title="Create backup",
|
|
summary=(
|
|
"Enqueue an async backup job for a target (database, uploads, keys, full). "
|
|
"Returns the job uid and a status_url."
|
|
),
|
|
auth="admin",
|
|
params=[
|
|
field(
|
|
"target",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"full",
|
|
"One of database, uploads, keys, full.",
|
|
)
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"uid": "JOB_UID",
|
|
"backup_uid": "BACKUP_UID",
|
|
"status_url": "/admin/backups/jobs/JOB_UID",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="admin-backups-job",
|
|
method="GET",
|
|
path="/admin/backups/jobs/{uid}",
|
|
title="Backup job status",
|
|
summary="Status of one backup job (pending, running, done, failed) with archive stats.",
|
|
auth="admin",
|
|
params=[field("uid", "path", "string", True, "", "Backup job uid.")],
|
|
sample_response={
|
|
"uid": "JOB_UID",
|
|
"kind": "backup",
|
|
"status": "done",
|
|
"target": "full",
|
|
"backup_uid": "BACKUP_UID",
|
|
"download_url": "/admin/backups/BACKUP_UID/download",
|
|
"bytes_out": 10485760,
|
|
"file_count": 1240,
|
|
"sha256": "…",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="admin-backups-download",
|
|
method="GET",
|
|
path="/admin/backups/{uid}/download",
|
|
title="Download backup",
|
|
summary="Stream a completed backup archive as a tar.gz file. Restricted to the primary administrator (the first user created with the Admin role); every other administrator receives 403 Forbidden.",
|
|
auth="admin",
|
|
encoding="none",
|
|
params=[field("uid", "path", "string", True, "", "Backup uid.")],
|
|
),
|
|
endpoint(
|
|
id="admin-backups-delete",
|
|
method="POST",
|
|
path="/admin/backups/{uid}/delete",
|
|
title="Delete backup",
|
|
summary="Permanently delete a backup archive and reclaim its disk space.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[field("uid", "path", "string", True, "", "Backup uid.")],
|
|
sample_response={"ok": True, "redirect": "/admin/backups"},
|
|
),
|
|
endpoint(
|
|
id="admin-backups-schedule-create",
|
|
method="POST",
|
|
path="/admin/backups/schedules/create",
|
|
title="Create backup schedule",
|
|
summary=(
|
|
"Create a recurring backup. kind=interval uses every_seconds; kind=cron uses a "
|
|
"5-field cron expression. keep_last rotates older backups of the schedule."
|
|
),
|
|
auth="admin",
|
|
params=[
|
|
field("name", "form", "string", True, "Nightly full", "Schedule name."),
|
|
field("target", "form", "string", True, "full", "Backup target."),
|
|
field("kind", "form", "string", True, "interval", "interval or cron."),
|
|
field("every_seconds", "form", "int", False, "86400", "Seconds between runs (kind=interval)."),
|
|
field("cron", "form", "string", False, "0 3 * * *", "Cron expression (kind=cron)."),
|
|
field("keep_last", "form", "int", False, "7", "Keep only the newest N (0 = all)."),
|
|
],
|
|
sample_response={"ok": True, "redirect": "/admin/backups"},
|
|
),
|
|
endpoint(
|
|
id="admin-backups-schedule-edit",
|
|
method="POST",
|
|
path="/admin/backups/schedules/{uid}/edit",
|
|
title="Edit backup schedule",
|
|
summary="Update a backup schedule's target, trigger, and retention.",
|
|
auth="admin",
|
|
params=[
|
|
field("uid", "path", "string", True, "", "Schedule uid."),
|
|
field("name", "form", "string", True, "Nightly full", "Schedule name."),
|
|
field("target", "form", "string", True, "full", "Backup target."),
|
|
field("kind", "form", "string", True, "interval", "interval or cron."),
|
|
field("every_seconds", "form", "int", False, "86400", "Seconds between runs (kind=interval)."),
|
|
field("cron", "form", "string", False, "0 3 * * *", "Cron expression (kind=cron)."),
|
|
field("keep_last", "form", "int", False, "7", "Keep only the newest N (0 = all)."),
|
|
],
|
|
sample_response={"ok": True, "redirect": "/admin/backups"},
|
|
),
|
|
endpoint(
|
|
id="admin-backups-schedule-toggle",
|
|
method="POST",
|
|
path="/admin/backups/schedules/{uid}/toggle",
|
|
title="Toggle backup schedule",
|
|
summary="Enable or disable a backup schedule.",
|
|
auth="admin",
|
|
params=[field("uid", "path", "string", True, "", "Schedule uid.")],
|
|
sample_response={"ok": True, "redirect": "/admin/backups"},
|
|
),
|
|
endpoint(
|
|
id="admin-backups-schedule-run",
|
|
method="POST",
|
|
path="/admin/backups/schedules/{uid}/run",
|
|
title="Run backup schedule now",
|
|
summary="Immediately enqueue a backup for a schedule without waiting for its next run.",
|
|
auth="admin",
|
|
params=[field("uid", "path", "string", True, "", "Schedule uid.")],
|
|
sample_response={"ok": True, "redirect": "/admin/backups"},
|
|
),
|
|
endpoint(
|
|
id="admin-backups-schedule-delete",
|
|
method="POST",
|
|
path="/admin/backups/schedules/{uid}/delete",
|
|
title="Delete backup schedule",
|
|
summary="Delete a backup schedule. Existing archives are kept.",
|
|
auth="admin",
|
|
destructive=True,
|
|
params=[field("uid", "path", "string", True, "", "Schedule uid.")],
|
|
sample_response={"ok": True, "redirect": "/admin/backups"},
|
|
),
|
|
endpoint(
|
|
id="admin-game",
|
|
method="GET",
|
|
path="/admin/game",
|
|
title="Code Farm Era management",
|
|
summary="View the current Code Farm Era status.",
|
|
auth="admin",
|
|
sample_response={"era_active": False, "era_name": ""},
|
|
),
|
|
endpoint(
|
|
id="admin-game-era-start",
|
|
method="POST",
|
|
path="/admin/game/era/start",
|
|
title="Start an Era",
|
|
summary="Start a new Code Farm Era: resets every farm's visible Era coins/harvests counters to zero. Real coins, prestige, stars, Legacy, and Mastery are never touched.",
|
|
auth="admin",
|
|
params=[
|
|
field("name", "form", "string", True, "Genesis", "Era name."),
|
|
field("duration_days", "form", "int", False, "28", "Planned Era length in days."),
|
|
],
|
|
sample_response={"ok": True, "redirect": "/admin/game"},
|
|
),
|
|
endpoint(
|
|
id="admin-game-era-end",
|
|
method="POST",
|
|
path="/admin/game/era/end",
|
|
title="End the running Era",
|
|
summary="End the current Era: ranks every participating farm by Era score, awards Stars to the top 10 (and an Era-exclusive cosmetic when available), and permanently records the results.",
|
|
auth="admin",
|
|
destructive=True,
|
|
sample_response={"ok": True, "redirect": "/admin/game"},
|
|
),
|
|
],
|
|
}
|