Notifications: a new "thread" type notifies every other commenter on a
post whenever anyone comments on it, disregarding reply hierarchy -
excluding the actor and whoever already got a comment/reply
notification for that same event, so no one is double-notified.
Implemented via a background-deferred fan-out mirroring the existing
mention-notification pattern.
SEO: discussion_forum_posting() now embeds up to 20 of a post's
comments as nested schema.org Comment entities (not just an aggregate
count), and a new /topics hub plus /topics/{topic} pages give the
feed's topic filter real, independently crawlable/indexable URLs -
/feed?topic=X was never indexable since its canonical strips the
query string back to bare /feed. Both are wired end to end (schemas,
Devii actions, docs API, sitemap, locustfile load-test coverage).
Quiz player: the auto-advance to the next question used to hide the
just-answered slide in the same tick as rendering the grade, so on
any multi-question quiz the Correct/Not correct feedback was never
actually visible before the view moved on. Delayed via setTimeout,
with the pending timer cleared on manual navigation and on
disconnect so it can't race or fire on a removed component.
Also includes other local changes already in progress in this
working tree before this session (messaging, push delivery,
deepsearch jobs, game economy, quiz builder) - verified by the full
suite passing (3467 tests) but not authored or individually reviewed
in this session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VL9Xn57W5UR3HZbbuuzxdK
1089 lines
40 KiB
Python
1089 lines
40 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
from .._shared import COMMENT_TARGETS, GIST_LANGUAGES, PROJECT_TYPES, endpoint, field
|
|
from devplacepy.constants import TOPICS
|
|
|
|
GROUP = {
|
|
"slug": "content",
|
|
"title": "Posts, Comments, Projects, Gists & News",
|
|
"intro": """
|
|
# Posts, Comments, Projects, Gists & News
|
|
|
|
The core content types. Read endpoints render HTML pages; write endpoints accept form fields
|
|
and redirect to the new or updated resource. List fields such as `attachment_uids` are
|
|
repeated form keys - upload files first via [Uploads](/docs/uploads.html) and pass the returned
|
|
uids here. Engage with this content through [Votes, Reactions, Bookmarks & Polls](/docs/social-actions.html).
|
|
|
|
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="home",
|
|
method="GET",
|
|
path="/",
|
|
title="Home",
|
|
summary=(
|
|
"The home page. Guests see the marketing splash; authenticated users see a "
|
|
"personalized home (welcome, feed shortcut, latest posts, news). It no longer "
|
|
"redirects to /feed. The latest-posts section interleaves authors so no two consecutive posts share an author."
|
|
),
|
|
auth="public",
|
|
interactive=True,
|
|
),
|
|
endpoint(
|
|
id="feed-list",
|
|
method="GET",
|
|
path="/feed",
|
|
title="Browse the feed",
|
|
summary="The main post feed. Returns an HTML page. Each page interleaves authors so no two consecutive posts share an author.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"tab",
|
|
"query",
|
|
"enum",
|
|
False,
|
|
"all",
|
|
"Feed selector.",
|
|
["all", "trending", "following"],
|
|
),
|
|
field(
|
|
"topic", "query", "enum", False, "", "Filter by topic.", TOPICS
|
|
),
|
|
field(
|
|
"search",
|
|
"query",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Search post title, content, and author username.",
|
|
),
|
|
field("before", "query", "string", False, "", "Pagination cursor."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="topics-hub",
|
|
method="GET",
|
|
path="/topics",
|
|
title="Browse topics",
|
|
summary="The topics hub - every post topic with its live post count, linking to its own crawlable listing page.",
|
|
auth="public",
|
|
interactive=True,
|
|
),
|
|
endpoint(
|
|
id="topics-list",
|
|
method="GET",
|
|
path="/topics/{topic}",
|
|
title="Browse one topic",
|
|
summary=(
|
|
"A single topic's post listing, on its own permanent, crawlable URL (unlike /feed?topic=, "
|
|
"whose canonical collapses back to /feed). Same author-interleaved pagination as the feed."
|
|
),
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"topic", "path", "enum", True, "devlog", "Topic key.", TOPICS
|
|
),
|
|
field("before", "query", "string", False, "", "Pagination cursor."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="posts-create",
|
|
method="POST",
|
|
path="/posts/create",
|
|
title="Create a post",
|
|
summary="Publish a post, optionally with a poll. Redirects to the new post.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"content",
|
|
"form",
|
|
"textarea",
|
|
True,
|
|
"Posted from a script.",
|
|
"Body, 10-125000 characters.",
|
|
),
|
|
field(
|
|
"title",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"Hello",
|
|
"Optional title, up to 500 characters.",
|
|
),
|
|
field(
|
|
"topic", "form", "enum", False, "random", "Post topic.", TOPICS
|
|
),
|
|
field(
|
|
"project_uid",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Attach to a project.",
|
|
),
|
|
field(
|
|
"poll_question",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Optional poll question.",
|
|
),
|
|
field(
|
|
"poll_options",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Repeat the field for each poll option, or send a single newline- or comma-separated string (2-6 options).",
|
|
),
|
|
field(
|
|
"war_faction_a",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Optional Opinion War faction A name (max 30 chars). Both faction names start the week-long battle.",
|
|
),
|
|
field(
|
|
"war_faction_b",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Optional Opinion War faction B name (max 30 chars). Must differ from faction A.",
|
|
),
|
|
],
|
|
notes=["Returns a `302` redirect to `/posts/{slug}` on success."],
|
|
),
|
|
endpoint(
|
|
id="posts-detail",
|
|
method="GET",
|
|
path="/posts/{post_slug}",
|
|
title="View a post",
|
|
summary="Render a post with comments. Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"post_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"POST_SLUG",
|
|
"Slug or UID of the post.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="posts-edit",
|
|
method="POST",
|
|
path="/posts/edit/{post_slug}",
|
|
title="Edit a post",
|
|
summary="Update a post you own, optionally adding a poll if it has none.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"post_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"POST_SLUG",
|
|
"Slug or UID of the post.",
|
|
),
|
|
field(
|
|
"content",
|
|
"form",
|
|
"textarea",
|
|
True,
|
|
"Updated body.",
|
|
"Body, 10-125000 characters.",
|
|
),
|
|
field(
|
|
"title",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"Updated title",
|
|
"Optional title.",
|
|
),
|
|
field(
|
|
"topic", "form", "enum", False, "random", "Post topic.", TOPICS
|
|
),
|
|
field(
|
|
"poll_question",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Optional poll question. Adds a poll only when the post has none.",
|
|
),
|
|
field(
|
|
"poll_options",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Repeat the field for each poll option, or send a single newline- or comma-separated string (2-6 options).",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="posts-delete",
|
|
method="POST",
|
|
path="/posts/delete/{post_slug}",
|
|
title="Delete a post",
|
|
summary="Delete a post you own; administrators may delete any user's post. Soft-deleted (hidden everywhere but restorable from admin trash) and cascades its comments and votes.",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"post_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"POST_SLUG",
|
|
"Slug or UID of the post.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="comments-create",
|
|
method="POST",
|
|
path="/comments/create",
|
|
title="Create a comment",
|
|
summary="Comment on any commentable target. Supports nested replies.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"content",
|
|
"form",
|
|
"textarea",
|
|
True,
|
|
"Nice work.",
|
|
"Body, 3-1000 characters.",
|
|
),
|
|
field(
|
|
"target_uid",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"POST_UID",
|
|
"UID of the target (or use post_uid).",
|
|
),
|
|
field(
|
|
"post_uid",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Convenience alias for a post target.",
|
|
),
|
|
field(
|
|
"target_type",
|
|
"form",
|
|
"enum",
|
|
False,
|
|
"post",
|
|
"Type of the target.",
|
|
COMMENT_TARGETS,
|
|
),
|
|
field(
|
|
"parent_uid",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Parent comment UID for a reply.",
|
|
),
|
|
],
|
|
notes=["Either `target_uid` or `post_uid` is required."],
|
|
),
|
|
endpoint(
|
|
id="comments-edit",
|
|
method="POST",
|
|
path="/comments/edit/{comment_uid}",
|
|
title="Edit a comment",
|
|
summary="Edit the body of a comment you own. Returns the updated comment.",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field(
|
|
"comment_uid",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"COMMENT_UID",
|
|
"UID of the comment.",
|
|
),
|
|
field(
|
|
"content",
|
|
"form",
|
|
"textarea",
|
|
True,
|
|
"Edited body.",
|
|
"New body, 3-1000 characters.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"uid": "COMMENT_UID",
|
|
"content": "Edited body.",
|
|
"url": "/posts/POST_SLUG#comment-COMMENT_UID",
|
|
"updated_at": "2026-06-15T12:00:00+00:00",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="comments-delete",
|
|
method="POST",
|
|
path="/comments/delete/{comment_uid}",
|
|
title="Delete a comment",
|
|
summary="Delete a comment you own; administrators may delete any user's comment. Soft-deleted (hidden everywhere but restorable from admin trash).",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"comment_uid",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"COMMENT_UID",
|
|
"UID of the comment.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-list",
|
|
method="GET",
|
|
path="/projects",
|
|
title="Browse projects",
|
|
summary="List projects. Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"tab",
|
|
"query",
|
|
"enum",
|
|
False,
|
|
"recent",
|
|
"Sort selector.",
|
|
["recent", "popular", "released"],
|
|
),
|
|
field(
|
|
"search",
|
|
"query",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Search project title, description, and author username.",
|
|
),
|
|
field(
|
|
"project_type",
|
|
"query",
|
|
"enum",
|
|
False,
|
|
"",
|
|
"Filter by type.",
|
|
PROJECT_TYPES,
|
|
),
|
|
field("before", "query", "string", False, "", "Pagination cursor."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-detail",
|
|
method="GET",
|
|
path="/projects/{project_slug}",
|
|
title="View a project",
|
|
summary="Render the project overview with its devlog, screenshots and comments. Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"project_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"PROJECT_SLUG",
|
|
"Slug or UID of the project.",
|
|
),
|
|
field(
|
|
"before",
|
|
"query",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Devlog pagination cursor (devlog_next_cursor from the previous page).",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-screenshots",
|
|
method="POST",
|
|
path="/projects/{project_slug}/screenshots",
|
|
title="Add screenshots to a project",
|
|
summary="Link uploaded image attachments to an owned project's Screenshots gallery. Redirects to the gallery.",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field(
|
|
"project_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"my-project-1a2b3c4d",
|
|
"Project slug or uid.",
|
|
),
|
|
field(
|
|
"attachment_uids",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"ATTACHMENT_UID",
|
|
"Comma-separated attachment uids from POST /uploads/upload or /uploads/upload-url.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-create",
|
|
method="POST",
|
|
path="/projects/create",
|
|
title="Create a project",
|
|
summary="Publish a project. Redirects to the new project.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"title",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"My Project",
|
|
"Title, 1-200 characters.",
|
|
),
|
|
field(
|
|
"description",
|
|
"form",
|
|
"textarea",
|
|
True,
|
|
"What it does.",
|
|
"Description, 1-5000 characters.",
|
|
),
|
|
field(
|
|
"project_type",
|
|
"form",
|
|
"enum",
|
|
False,
|
|
"software",
|
|
"Project type.",
|
|
PROJECT_TYPES,
|
|
),
|
|
field(
|
|
"status",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"In Development",
|
|
"Free-form status label.",
|
|
),
|
|
field(
|
|
"platforms",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"Linux, Web",
|
|
"Comma-separated platforms.",
|
|
),
|
|
field(
|
|
"release_date",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"31/12/2026",
|
|
"Optional release date in DD/MM/YYYY format.",
|
|
),
|
|
field(
|
|
"demo_date",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"31/12/2026",
|
|
"Optional demo date in DD/MM/YYYY format.",
|
|
),
|
|
field(
|
|
"website_url",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"https://myproject.dev",
|
|
"Optional official website URL (http/https).",
|
|
),
|
|
field(
|
|
"repo_url",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"https://github.com/me/project",
|
|
"Optional source repository URL (http/https).",
|
|
),
|
|
field(
|
|
"cover_attachment_uid",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"ATTACHMENT_UID",
|
|
"Optional attachment uid of an uploaded cover image.",
|
|
),
|
|
field(
|
|
"logo_attachment_uid",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"ATTACHMENT_UID",
|
|
"Optional attachment uid of an uploaded project logo.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-edit",
|
|
method="POST",
|
|
path="/projects/edit/{project_slug}",
|
|
title="Edit a project",
|
|
summary="Update an owned project. Redirects to the project.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"project_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"my-project-1a2b3c4d",
|
|
"Project slug or uid.",
|
|
),
|
|
field(
|
|
"title",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"My Project",
|
|
"Title, 1-200 characters.",
|
|
),
|
|
field(
|
|
"description",
|
|
"form",
|
|
"textarea",
|
|
True,
|
|
"What it does.",
|
|
"Description, 1-5000 characters.",
|
|
),
|
|
field(
|
|
"project_type",
|
|
"form",
|
|
"enum",
|
|
False,
|
|
"software",
|
|
"Project type.",
|
|
PROJECT_TYPES,
|
|
),
|
|
field(
|
|
"status",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"In Development",
|
|
"Free-form status label.",
|
|
),
|
|
field(
|
|
"platforms",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"Linux, Web",
|
|
"Comma-separated platforms.",
|
|
),
|
|
field(
|
|
"release_date",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"31/12/2026",
|
|
"Optional release date in DD/MM/YYYY format.",
|
|
),
|
|
field(
|
|
"demo_date",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"31/12/2026",
|
|
"Optional demo date in DD/MM/YYYY format.",
|
|
),
|
|
field(
|
|
"website_url",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"https://myproject.dev",
|
|
"Optional official website URL (http/https).",
|
|
),
|
|
field(
|
|
"repo_url",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"https://github.com/me/project",
|
|
"Optional source repository URL (http/https).",
|
|
),
|
|
field(
|
|
"cover_attachment_uid",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"ATTACHMENT_UID",
|
|
"Optional attachment uid of an uploaded cover image.",
|
|
),
|
|
field(
|
|
"logo_attachment_uid",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"ATTACHMENT_UID",
|
|
"Optional attachment uid of an uploaded project logo.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-delete",
|
|
method="POST",
|
|
path="/projects/delete/{project_slug}",
|
|
title="Delete a project",
|
|
summary="Delete a project you own; administrators may delete any user's project. Soft-deleted with all of its files (hidden everywhere but restorable from admin trash).",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"project_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"PROJECT_SLUG",
|
|
"Slug or UID of the project.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-private",
|
|
method="POST",
|
|
path="/projects/{project_slug}/private",
|
|
title="Set project visibility",
|
|
summary="Mark a project you own private or public. Send value=1 for private, value=0 for public. A project you hide as a member stays visible to administrators; a project you hide as an administrator is visible only to you, not to other administrators (this also hides its files and any attached containers).",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field(
|
|
"project_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"PROJECT_SLUG",
|
|
"Slug or UID of the project.",
|
|
),
|
|
field(
|
|
"value",
|
|
"form",
|
|
"boolean",
|
|
True,
|
|
"1",
|
|
"1 to make the project private, 0 to make it public.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-readonly",
|
|
method="POST",
|
|
path="/projects/{project_slug}/readonly",
|
|
title="Set project read-only",
|
|
summary="Mark a project you own read-only so all of its files become immutable (no writes, edits, moves, deletes, or uploads succeed), or writable again. Send value=1 for read-only, value=0 for writable.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"project_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"PROJECT_SLUG",
|
|
"Slug or UID of the project.",
|
|
),
|
|
field(
|
|
"value",
|
|
"form",
|
|
"boolean",
|
|
True,
|
|
"1",
|
|
"1 to make the project read-only, 0 to make it writable.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-zip",
|
|
method="POST",
|
|
path="/projects/{project_slug}/zip",
|
|
title="Queue a project zip",
|
|
summary="Start a background job that archives the whole project. Returns the job uid and status URL to poll.",
|
|
auth="public",
|
|
params=[
|
|
field(
|
|
"project_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"PROJECT_SLUG",
|
|
"Slug or UID of the project.",
|
|
)
|
|
],
|
|
sample_response={
|
|
"uid": "ZIP_JOB_UID",
|
|
"status_url": "/zips/ZIP_JOB_UID",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="zips-status",
|
|
method="GET",
|
|
path="/zips/{uid}",
|
|
title="Zip job status",
|
|
summary="Poll a zip job. While pending or running download_url is null; once done it points at the archive.",
|
|
auth="public",
|
|
params=[
|
|
field(
|
|
"uid",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"ZIP_JOB_UID",
|
|
"Zip job uid returned when the job was queued.",
|
|
)
|
|
],
|
|
sample_response={
|
|
"uid": "ZIP_JOB_UID",
|
|
"kind": "zip",
|
|
"status": "done",
|
|
"preferred_name": "my-project",
|
|
"download_url": "/zips/ZIP_JOB_UID/download",
|
|
"error": None,
|
|
"bytes_in": 20480,
|
|
"bytes_out": 8192,
|
|
"item_count": 12,
|
|
"file_count": 10,
|
|
"dir_count": 2,
|
|
"created_at": "2026-06-09T10:00:00+00:00",
|
|
"completed_at": "2026-06-09T10:00:03+00:00",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="zips-download",
|
|
method="GET",
|
|
path="/zips/{uid}/download",
|
|
title="Download a zip archive",
|
|
summary="Stream the finished archive as application/zip. Each access extends the retention window.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"uid",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"ZIP_JOB_UID",
|
|
"Zip job uid of a finished job.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="projects-fork",
|
|
method="POST",
|
|
path="/projects/{project_slug}/fork",
|
|
title="Queue a project fork",
|
|
summary="Start a background job that copies the whole project into a new project owned by you. Returns the job uid and status URL to poll.",
|
|
auth="user",
|
|
params=[
|
|
field(
|
|
"project_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"PROJECT_SLUG",
|
|
"Slug or UID of the project to fork.",
|
|
),
|
|
field(
|
|
"title",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"My Fork",
|
|
"Title for the new forked project.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"uid": "FORK_JOB_UID",
|
|
"status_url": "/forks/FORK_JOB_UID",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="forks-status",
|
|
method="GET",
|
|
path="/forks/{uid}",
|
|
title="Fork job status",
|
|
summary="Poll a fork job. While pending or running project_url is null; once done it points at the new project.",
|
|
auth="public",
|
|
params=[
|
|
field(
|
|
"uid",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"FORK_JOB_UID",
|
|
"Fork job uid returned when the job was queued.",
|
|
)
|
|
],
|
|
sample_response={
|
|
"uid": "FORK_JOB_UID",
|
|
"kind": "fork",
|
|
"status": "done",
|
|
"preferred_name": "My Fork",
|
|
"project_uid": "NEW_PROJECT_UID",
|
|
"project_url": "/projects/new-project-slug",
|
|
"source_project_uid": "SOURCE_PROJECT_UID",
|
|
"error": None,
|
|
"item_count": 12,
|
|
"created_at": "2026-06-09T10:00:00+00:00",
|
|
"completed_at": "2026-06-09T10:00:05+00:00",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="gists-list",
|
|
method="GET",
|
|
path="/gists",
|
|
title="Browse gists",
|
|
summary="List code gists. Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"language",
|
|
"query",
|
|
"enum",
|
|
False,
|
|
"",
|
|
"Filter by language.",
|
|
GIST_LANGUAGES,
|
|
),
|
|
field(
|
|
"user_uid",
|
|
"query",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Filter by author UID.",
|
|
),
|
|
field(
|
|
"search",
|
|
"query",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Search gist title, description, and author username.",
|
|
),
|
|
field("before", "query", "string", False, "", "Pagination cursor."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="gists-detail",
|
|
method="GET",
|
|
path="/gists/{gist_slug}",
|
|
title="View a gist",
|
|
summary="Render a gist with comments. Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"gist_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"GIST_SLUG",
|
|
"Slug or UID of the gist.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="gists-create",
|
|
method="POST",
|
|
path="/gists/create",
|
|
title="Create a gist",
|
|
summary="Publish a code snippet. Redirects to the new gist.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"title",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"Quick sort",
|
|
"Title, 1-200 characters.",
|
|
),
|
|
field(
|
|
"source_code",
|
|
"form",
|
|
"textarea",
|
|
True,
|
|
"print('hello')",
|
|
"Source, 1-400000 characters.",
|
|
),
|
|
field(
|
|
"language",
|
|
"form",
|
|
"enum",
|
|
False,
|
|
"python",
|
|
"Syntax language.",
|
|
GIST_LANGUAGES,
|
|
),
|
|
field(
|
|
"description",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Optional description.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="gists-edit",
|
|
method="POST",
|
|
path="/gists/edit/{gist_slug}",
|
|
title="Edit a gist",
|
|
summary="Update a gist you own.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"gist_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"GIST_SLUG",
|
|
"Slug or UID of the gist.",
|
|
),
|
|
field(
|
|
"title",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"Quick sort",
|
|
"Title, 1-200 characters.",
|
|
),
|
|
field(
|
|
"source_code",
|
|
"form",
|
|
"textarea",
|
|
True,
|
|
"print('hi')",
|
|
"Source, 1-400000 characters.",
|
|
),
|
|
field(
|
|
"language",
|
|
"form",
|
|
"enum",
|
|
False,
|
|
"python",
|
|
"Syntax language.",
|
|
GIST_LANGUAGES,
|
|
),
|
|
field(
|
|
"description",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Optional description.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="gists-delete",
|
|
method="POST",
|
|
path="/gists/delete/{gist_slug}",
|
|
title="Delete a gist",
|
|
summary="Delete a gist you own; administrators may delete any user's gist. Soft-deleted (hidden everywhere but restorable from admin trash).",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"gist_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"GIST_SLUG",
|
|
"Slug or UID of the gist.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="news-list",
|
|
method="GET",
|
|
path="/news",
|
|
title="Browse news",
|
|
summary="Curated developer news. Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"before",
|
|
"query",
|
|
"string",
|
|
False,
|
|
"",
|
|
"Pagination cursor (synced_at).",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="news-detail",
|
|
method="GET",
|
|
path="/news/{news_slug}",
|
|
title="View a news article",
|
|
summary="Render a news article with comments. Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"news_slug",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"NEWS_SLUG",
|
|
"Slug or UID of the article.",
|
|
)
|
|
],
|
|
),
|
|
],
|
|
}
|