{"ok":true,"data":{"name":"fxxr-forum","version":"0.1.0","auth":"Authorization: Bearer <token> (obtained from /api/auth/register or /api/auth/login)","limits":{"max_image_bytes":32000000,"max_images_per_post":4,"max_video_bytes":90000000,"max_video_upload_bytes":2147483648,"video_upload_chunk_bytes":4194304,"max_videos_per_post":2,"rate_limit_per_minute":60,"auth_rate_limit_per_minute":20,"rate_limit_per_user_per_minute":20,"post_min_interval_seconds":10,"max_content_characters":60000,"max_title_characters":120,"max_reply_characters":20000,"max_display_name_characters":32,"max_tags_per_post":5,"max_per_page":50,"session_ttl_seconds":2592000,"password_pbkdf2_iterations":10000,"one_account_per_ip":true,"display_name_unique":true,"search":{"engine":"sqlite fts5 (trigram)","min_query_characters":3,"fallback":"LIKE substring scan for shorter queries"},"image_storage":"self-hosted (TF card via r2.cloud.tdsi.top)","image_host":"http://origin.cloud.tdsi.top","video_storage":"self-hosted (TF card, streamed through the API)","video_range_requests":true,"database":"self-hosted sqlite (tf card)","database_host":"http://d1.cloud.tdsi.top"},"endpoints":[{"method":"GET","path":"/api","description":"This document."},{"method":"GET","path":"/api/health","description":"Liveness + database connectivity probe."},{"method":"GET","path":"/api/tags","description":"Tag cloud with post counts."},{"method":"POST","path":"/api/auth/register","body":{"username":"string","password":"string"},"description":"Create an account, returns a bearer token."},{"method":"POST","path":"/api/auth/login","body":{"username":"string","password":"string"},"description":"Exchange credentials for a bearer token."},{"method":"POST","path":"/api/auth/logout","auth":true,"description":"Revoke the current token."},{"method":"GET","path":"/api/auth/me","auth":true,"description":"Current user."},{"method":"POST","path":"/api/auth/password","auth":true,"body":{"current_password":"string","new_password":"string"},"description":"Change password and revoke other sessions."},{"method":"GET","path":"/api/users/:username","description":"Public profile + counters."},{"method":"GET","path":"/api/users/:username/posts","description":"Posts by a user."},{"method":"GET","path":"/api/users/:username/replies","description":"Replies by a user."},{"method":"GET","path":"/api/posts?page=1&per_page=20&sort=new|hot|active&tag=&author=&q=","description":"Paginated post feed."},{"method":"POST","path":"/api/posts","auth":true,"body":{"title":"string","content":"string","tags":["string"]},"description":"Create a post."},{"method":"GET","path":"/api/posts/:id","description":"Post detail (increments view_count)."},{"method":"PATCH","path":"/api/posts/:id","auth":true,"body":{"title":"string?","content":"string?","tags":"string[]?"},"description":"Edit your own post."},{"method":"DELETE","path":"/api/posts/:id","auth":true,"description":"Soft-delete your own post (admins may delete any)."},{"method":"GET","path":"/api/posts/:id/replies?page=1&per_page=50","description":"Replies of a post, oldest first."},{"method":"POST","path":"/api/posts/:id/replies","auth":true,"body":{"content":"string","parent_id":"number?"},"description":"Reply to a post (optionally nested)."},{"method":"DELETE","path":"/api/replies/:id","auth":true,"description":"Soft-delete your own reply."},{"method":"POST","path":"/api/posts/:id/like","auth":true,"description":"Toggle like on a post."},{"method":"POST","path":"/api/replies/:id/like","auth":true,"description":"Toggle like on a reply."},{"method":"DELETE","path":"/api/posts/:id/like","auth":true,"description":"Remove a like on a post (idempotent)."},{"method":"DELETE","path":"/api/replies/:id/like","auth":true,"description":"Remove a like on a reply (idempotent)."},{"method":"GET","path":"/api/posts/:id/likes","description":"Users who liked a post."},{"method":"POST","path":"/api/images","auth":true,"body":{"data":"data:image/png;base64,..."},"description":"Upload an image (base64 in JSON). Bytes go to the self-hosted image host; only metadata is stored in the database."},{"method":"GET","path":"/api/images/:id","description":"Raw image bytes (immutable, cached for a year). Streamed from the self-hosted image host."},{"method":"GET","path":"/api/images/:id/meta","description":"Image metadata without the bytes."},{"method":"GET","path":"/api/images?mine=true","auth":true,"description":"Your uploads."},{"method":"DELETE","path":"/api/images/:id","auth":true,"description":"Delete one of your images."},{"method":"POST","path":"/api/videos","auth":true,"body":"<raw video bytes>","description":"Upload a video. Send the bytes as the request body with Content-Type: video/mp4 (no base64/multipart); optional X-Video-Name / X-Video-Duration headers. Streamed straight to the self-hosted host."},{"method":"GET","path":"/api/videos/:id","description":"Video bytes. Supports Range requests (HTTP 206) so the browser can seek."},{"method":"GET","path":"/api/videos/:id/meta","description":"Video metadata without the bytes (duration, size, dimensions)."},{"method":"GET","path":"/api/videos?mine=true","auth":true,"description":"Your uploads."},{"method":"DELETE","path":"/api/videos/:id","auth":true,"description":"Delete one of your videos (also removes the file on the host)."},{"method":"POST","path":"/api/posts (images: [\"<id>\"])","auth":true,"description":"Attach up to MAX_IMAGES_PER_POST uploaded images to a new post."},{"method":"POST","path":"/api/posts/:id/replies (images: [\"<id>\"])","auth":true,"description":"Attach images to a new reply."},{"method":"GET","path":"/api/boards","description":"List boards (版块) with post counts."},{"method":"GET","path":"/api/boards/:slug","description":"Single board."},{"method":"POST","path":"/api/admin/boards","auth":true,"description":"Admin: create a board."},{"method":"PATCH","path":"/api/admin/boards/:slug","auth":true,"description":"Admin: rename / reorder / lock a board."},{"method":"DELETE","path":"/api/admin/boards/:slug","auth":true,"description":"Admin: delete a board (posts move to the default board)."},{"method":"POST","path":"/api/reports","auth":true,"body":{"target_type":"post|reply|user","target_id":1,"reason":"spam|abuse|illegal|off-topic|other"},"description":"Report content for moderation."},{"method":"GET","path":"/api/admin/reports?status=open","auth":true,"description":"Admin: moderation queue."},{"method":"POST","path":"/api/admin/reports/:id/resolve","auth":true,"body":{"resolution":"resolved|rejected","action":"none|delete|ban"},"description":"Admin: handle a report."},{"method":"GET","path":"/api/notifications","auth":true,"description":"Your notifications (reply/like), paginated."},{"method":"GET","path":"/api/notifications/count","auth":true,"description":"Unread notification count."},{"method":"POST","path":"/api/notifications/read","auth":true,"body":{"all":true},"description":"Mark notifications read."},{"method":"DELETE","path":"/api/notifications/:id","auth":true,"description":"Delete one notification."},{"method":"GET","path":"/api/admin/words","auth":true,"description":"Admin: banned word list (敏感词)."},{"method":"POST","path":"/api/admin/words","auth":true,"body":{"word":"x","action":"block|mask"},"description":"Admin: add a banned word."},{"method":"DELETE","path":"/api/admin/words/:id","auth":true,"description":"Admin: remove a banned word."},{"method":"GET","path":"/api/admin/users?status=banned","auth":true,"description":"Admin: user list (includes registration IP)."},{"method":"POST","path":"/api/admin/users/:username/ban","auth":true,"body":{"reason":"spam","duration":"7d"},"description":"Admin: ban a user (temporary or permanent)."},{"method":"POST","path":"/api/admin/users/:username/unban","auth":true,"description":"Admin: unban a user."},{"method":"POST","path":"/api/admin/users/:username/reset-ip","auth":true,"description":"Admin: release the \"one account per IP\" slot for a user."},{"method":"DELETE","path":"/api/admin/users/:username","auth":true,"description":"Admin: permanently delete a user and all their content (posts, replies, images, sessions, notifications). Admin accounts are protected."},{"method":"GET","path":"/api/admin/stats","auth":true,"description":"Admin: database statistics."},{"method":"PATCH","path":"/api/auth/me","auth":true,"body":{"display_name":"昵称","bio":"简介"},"description":"Edit your profile. 昵称全局唯一，不能和别人撞。"},{"method":"POST","path":"/api/auth/avatar","auth":true,"body":{"image_id":"..."},"description":"Set your avatar from one of your uploads."},{"method":"DELETE","path":"/api/auth/avatar","auth":true,"description":"Remove your avatar."},{"method":"PATCH","path":"/api/replies/:id","auth":true,"description":"Edit your own reply."}]}}