|
# retoor <retoor@molodetz.nl>
|
|
|
|
ROLE_LABELS = {"public": "Public", "user": "Member"}
|
|
|
|
|
|
def field(name, location, type="string", required=False, example="", description="", options=None):
|
|
spec = {
|
|
"name": name,
|
|
"location": location,
|
|
"type": type,
|
|
"required": required,
|
|
"example": example,
|
|
"description": description,
|
|
}
|
|
if options:
|
|
spec["options"] = list(options)
|
|
return spec
|
|
|
|
|
|
def endpoint(
|
|
id,
|
|
method,
|
|
path,
|
|
title,
|
|
summary,
|
|
auth="user",
|
|
encoding="none",
|
|
destructive=False,
|
|
params=None,
|
|
sample_response=None,
|
|
):
|
|
return {
|
|
"id": id,
|
|
"method": method,
|
|
"path": path,
|
|
"title": title,
|
|
"summary": summary,
|
|
"auth": auth,
|
|
"min_role": ROLE_LABELS.get(auth, auth.title()),
|
|
"encoding": encoding,
|
|
"destructive": destructive,
|
|
"params": params or [],
|
|
"sample_response": sample_response,
|
|
}
|
|
|
|
|
|
AUTH_TOKEN_SAMPLE = {
|
|
"success": True,
|
|
"auth_token": {"id": 18966518, "key": "z6uXRZrQ...", "expire_time": 1782024794, "user_id": 42},
|
|
}
|
|
|
|
RANT_SAMPLE = {
|
|
"id": 1,
|
|
"text": "My first rant about Python",
|
|
"score": 3,
|
|
"created_time": 1781419994,
|
|
"attached_image": "",
|
|
"num_comments": 2,
|
|
"tags": ["python", "devrant"],
|
|
"vote_state": 0,
|
|
"edited": False,
|
|
"link": "rants/1/my-first-rant-about-python",
|
|
"rt": 1,
|
|
"rc": 0,
|
|
"user_id": 42,
|
|
"user_username": "alice",
|
|
"user_score": 17,
|
|
"user_avatar": {"b": "5c47fe", "i": "u/alice.png"},
|
|
"user_avatar_lg": {"b": "5c47fe", "i": "u/alice.png"},
|
|
"editable": False,
|
|
}
|
|
|
|
COMMENT_SAMPLE = {
|
|
"id": 9,
|
|
"rant_id": 1,
|
|
"body": "Nice rant!",
|
|
"score": 0,
|
|
"created_time": 1781420050,
|
|
"vote_state": 0,
|
|
"user_id": 7,
|
|
"user_username": "bob",
|
|
"user_score": 4,
|
|
"user_avatar": {"b": "1188ff", "i": "u/bob.png"},
|
|
}
|
|
|
|
|
|
DEVRANT_GROUPS = {
|
|
"devrant-auth": [
|
|
endpoint(
|
|
"devrant-login",
|
|
"POST",
|
|
"/api/users/auth-token",
|
|
"Log in",
|
|
"Authenticate with username (or email) and password. Running this here logs you in for every widget on these pages.",
|
|
auth="public",
|
|
encoding="form",
|
|
params=[
|
|
field("username", "body", required=True, example="YOUR_USERNAME", description="Username or email."),
|
|
field("password", "body", type="password", required=True, example="", description="Account password."),
|
|
],
|
|
sample_response=AUTH_TOKEN_SAMPLE,
|
|
),
|
|
endpoint(
|
|
"devrant-register",
|
|
"POST",
|
|
"/api/users",
|
|
"Register",
|
|
"Create a new account. Returns an auth token (you are logged in immediately).",
|
|
auth="public",
|
|
encoding="form",
|
|
params=[
|
|
field("username", "body", required=True, example="newdev", description="3-32 letters, numbers, hyphens, underscores."),
|
|
field("email", "body", required=True, example="newdev@example.com", description="Valid email address."),
|
|
field("password", "body", type="password", required=True, example="", description="At least 6 characters."),
|
|
],
|
|
sample_response=AUTH_TOKEN_SAMPLE,
|
|
),
|
|
endpoint(
|
|
"devrant-delete-account",
|
|
"DELETE",
|
|
"/api/users/me",
|
|
"Deactivate account",
|
|
"Deactivate the logged-in account and revoke its tokens.",
|
|
auth="user",
|
|
destructive=True,
|
|
sample_response={"success": True},
|
|
),
|
|
],
|
|
"devrant-rants": [
|
|
endpoint(
|
|
"devrant-feed",
|
|
"GET",
|
|
"/api/devrant/rants",
|
|
"Rant feed",
|
|
"List rants. Sort by recent, top, or algo.",
|
|
auth="public",
|
|
params=[
|
|
field("sort", "query", type="enum", example="recent", options=["recent", "top", "algo"], description="Sort order."),
|
|
field("limit", "query", type="int", example="20", description="Page size, 1-50."),
|
|
field("skip", "query", type="int", example="0", description="Offset for pagination."),
|
|
],
|
|
sample_response={"success": True, "rants": [RANT_SAMPLE], "num_notifs": 0},
|
|
),
|
|
endpoint(
|
|
"devrant-get-rant",
|
|
"GET",
|
|
"/api/devrant/rants/{rant_id}",
|
|
"Single rant with comments",
|
|
"Fetch one rant and its comments.",
|
|
auth="public",
|
|
params=[field("rant_id", "path", type="int", required=True, example="1", description="Rant id.")],
|
|
sample_response={"success": True, "rant": RANT_SAMPLE, "comments": [COMMENT_SAMPLE], "subscribed": 0},
|
|
),
|
|
endpoint(
|
|
"devrant-create-rant",
|
|
"POST",
|
|
"/api/devrant/rants",
|
|
"Post a rant",
|
|
"Create a new rant. Tags are comma-separated and stored verbatim.",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field("rant", "body", type="textarea", required=True, example="Posted from the docs widget", description="Rant text, 1-125000 chars."),
|
|
field("tags", "body", example="python,devrant", description="Comma-separated tags."),
|
|
],
|
|
sample_response={"success": True, "rant_id": 12},
|
|
),
|
|
endpoint(
|
|
"devrant-edit-rant",
|
|
"POST",
|
|
"/api/devrant/rants/{rant_id}",
|
|
"Edit a rant",
|
|
"Replace a rant's text and tags (owner only).",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field("rant_id", "path", type="int", required=True, example="1", description="Rant id."),
|
|
field("rant", "body", type="textarea", required=True, example="Edited text", description="New rant text."),
|
|
field("tags", "body", example="python", description="Comma-separated tags."),
|
|
],
|
|
sample_response={"success": True},
|
|
),
|
|
endpoint(
|
|
"devrant-delete-rant",
|
|
"DELETE",
|
|
"/api/devrant/rants/{rant_id}",
|
|
"Delete a rant",
|
|
"Soft-delete a rant (owner or admin).",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[field("rant_id", "path", type="int", required=True, example="1", description="Rant id.")],
|
|
sample_response={"success": True},
|
|
),
|
|
endpoint(
|
|
"devrant-vote-rant",
|
|
"POST",
|
|
"/api/devrant/rants/{rant_id}/vote",
|
|
"Vote on a rant",
|
|
"Upvote (1), downvote (-1), or clear (0). Returns the updated rant.",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field("rant_id", "path", type="int", required=True, example="1", description="Rant id."),
|
|
field("vote", "body", type="enum", required=True, example="1", options=["1", "-1", "0"], description="Vote value."),
|
|
],
|
|
sample_response={"success": True, "rant": RANT_SAMPLE},
|
|
),
|
|
endpoint(
|
|
"devrant-favorite",
|
|
"POST",
|
|
"/api/devrant/rants/{rant_id}/favorite",
|
|
"Favorite a rant",
|
|
"Bookmark a rant.",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[field("rant_id", "path", type="int", required=True, example="1", description="Rant id.")],
|
|
sample_response={"success": True},
|
|
),
|
|
endpoint(
|
|
"devrant-unfavorite",
|
|
"POST",
|
|
"/api/devrant/rants/{rant_id}/unfavorite",
|
|
"Unfavorite a rant",
|
|
"Remove a rant bookmark.",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[field("rant_id", "path", type="int", required=True, example="1", description="Rant id.")],
|
|
sample_response={"success": True},
|
|
),
|
|
endpoint(
|
|
"devrant-comment-rant",
|
|
"POST",
|
|
"/api/devrant/rants/{rant_id}/comments",
|
|
"Comment on a rant",
|
|
"Post a comment on a rant.",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field("rant_id", "path", type="int", required=True, example="1", description="Rant id."),
|
|
field("comment", "body", type="textarea", required=True, example="Great rant!", description="Comment text, 1-1000 chars."),
|
|
],
|
|
sample_response={"success": True},
|
|
),
|
|
endpoint(
|
|
"devrant-search",
|
|
"GET",
|
|
"/api/devrant/search",
|
|
"Search rants",
|
|
"Search rants by text in the title and body.",
|
|
auth="public",
|
|
params=[field("term", "query", required=True, example="python", description="Search text.")],
|
|
sample_response={"success": True, "results": [RANT_SAMPLE]},
|
|
),
|
|
],
|
|
"devrant-comments": [
|
|
endpoint(
|
|
"devrant-get-comment",
|
|
"GET",
|
|
"/api/comments/{comment_id}",
|
|
"Get a comment",
|
|
"Fetch a single comment by id.",
|
|
auth="public",
|
|
params=[field("comment_id", "path", type="int", required=True, example="1", description="Comment id.")],
|
|
sample_response={"success": True, "comment": COMMENT_SAMPLE},
|
|
),
|
|
endpoint(
|
|
"devrant-edit-comment",
|
|
"POST",
|
|
"/api/comments/{comment_id}",
|
|
"Edit a comment",
|
|
"Replace a comment's text (owner only).",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field("comment_id", "path", type="int", required=True, example="1", description="Comment id."),
|
|
field("comment", "body", type="textarea", required=True, example="Edited comment", description="New comment text."),
|
|
],
|
|
sample_response={"success": True},
|
|
),
|
|
endpoint(
|
|
"devrant-delete-comment",
|
|
"DELETE",
|
|
"/api/comments/{comment_id}",
|
|
"Delete a comment",
|
|
"Soft-delete a comment (owner or admin).",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[field("comment_id", "path", type="int", required=True, example="1", description="Comment id.")],
|
|
sample_response={"success": True},
|
|
),
|
|
endpoint(
|
|
"devrant-vote-comment",
|
|
"POST",
|
|
"/api/comments/{comment_id}/vote",
|
|
"Vote on a comment",
|
|
"Upvote (1), downvote (-1), or clear (0).",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field("comment_id", "path", type="int", required=True, example="1", description="Comment id."),
|
|
field("vote", "body", type="enum", required=True, example="1", options=["1", "-1", "0"], description="Vote value."),
|
|
],
|
|
sample_response={"success": True},
|
|
),
|
|
],
|
|
"devrant-users": [
|
|
endpoint(
|
|
"devrant-get-user-id",
|
|
"GET",
|
|
"/api/get-user-id",
|
|
"Resolve username to id",
|
|
"Look up a user's integer id from their username.",
|
|
auth="public",
|
|
params=[field("username", "query", required=True, example="alice", description="Username to look up.")],
|
|
sample_response={"success": True, "user_id": 42},
|
|
),
|
|
endpoint(
|
|
"devrant-profile",
|
|
"GET",
|
|
"/api/users/{user_id}",
|
|
"Get a profile",
|
|
"Fetch a user's devRant profile with their rants and comments.",
|
|
auth="public",
|
|
params=[field("user_id", "path", type="int", required=True, example="1", description="User id.")],
|
|
sample_response={
|
|
"success": True,
|
|
"profile": {
|
|
"username": "alice",
|
|
"score": 17,
|
|
"about": "I build things with Python",
|
|
"location": "Amsterdam",
|
|
"created_time": 1781419994,
|
|
"skills": "I build things with Python",
|
|
"github": "alice",
|
|
"website": "https://alice.dev",
|
|
"avatar": {"b": "5c47fe", "i": "u/alice.png"},
|
|
"content": {
|
|
"content": {"rants": [RANT_SAMPLE], "upvoted": [], "comments": [COMMENT_SAMPLE], "favorites": [], "viewed": []},
|
|
"counts": {"rants": 1, "upvoted": 0, "comments": 1, "favorites": 0, "collabs": 0},
|
|
},
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
"devrant-edit-profile",
|
|
"POST",
|
|
"/api/users/me/edit-profile",
|
|
"Edit your profile",
|
|
"Update bio, location, git link, and website. profile_skills is accepted but ignored (skills are derived from the bio).",
|
|
auth="user",
|
|
encoding="form",
|
|
params=[
|
|
field("profile_about", "body", type="textarea", example="I build with Python and Rust", description="Bio."),
|
|
field("profile_location", "body", example="Amsterdam", description="Location."),
|
|
field("profile_github", "body", example="alice", description="Git link."),
|
|
field("profile_website", "body", example="https://alice.dev", description="Website."),
|
|
],
|
|
sample_response={"success": True},
|
|
),
|
|
],
|
|
"devrant-notifications": [
|
|
endpoint(
|
|
"devrant-notif-feed",
|
|
"GET",
|
|
"/api/users/me/notif-feed",
|
|
"Notification feed",
|
|
"Fetch the logged-in user's notification feed with unread counts.",
|
|
auth="user",
|
|
sample_response={
|
|
"success": True,
|
|
"data": {
|
|
"items": [{"type": "comment_discuss", "rant_id": 0, "comment_id": 0, "created_time": 1781420050, "read": 0, "uid": 7, "username": "bob"}],
|
|
"check_time": 1781420060,
|
|
"username_map": {"7": "bob"},
|
|
"unread": {"all": 1, "upvotes": 0, "mentions": 0, "comments": 1, "subs": 0, "total": 1},
|
|
"num_unread": 1,
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
"devrant-clear-notif-feed",
|
|
"DELETE",
|
|
"/api/users/me/notif-feed",
|
|
"Clear notifications",
|
|
"Mark every notification read.",
|
|
auth="user",
|
|
destructive=True,
|
|
sample_response={"success": True},
|
|
),
|
|
],
|
|
}
|
|
|
|
|
|
def devrant_endpoints(slug: str) -> list:
|
|
return DEVRANT_GROUPS.get(slug, [])
|