diff --git a/docs/docs.go b/docs/docs.go index fd738c8..e8dff1d 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -14,7 +14,3446 @@ const docTemplate = `{ }, "host": "{{.Host}}", "basePath": "{{.BasePath}}", - "paths": {}, + "paths": { + "/auth/login/password": { + "post": { + "description": "Authenticate with email and password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login with email and password", + "parameters": [ + { + "description": "Login credentials", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.loginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/logout": { + "post": { + "description": "End the current session", + "tags": [ + "auth" + ], + "summary": "Logout", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/auth/me": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get the authenticated user's profile", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current user profile", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/auth.UserProfile" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Update the authenticated user's profile", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Update current user profile", + "parameters": [ + { + "description": "Profile update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.updateProfileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/auth.UserProfile" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/register": { + "post": { + "description": "Create a new user account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Register a new user", + "parameters": [ + { + "description": "Registration data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.registerRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/webauthn/login/begin": { + "post": { + "description": "Start passkey authentication", + "produces": [ + "application/json" + ], + "tags": [ + "webauthn" + ], + "summary": "Begin passkey login", + "responses": { + "200": { + "description": "WebAuthn login options", + "schema": { + "type": "object" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/webauthn/login/finish": { + "post": { + "description": "Complete passkey authentication", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "webauthn" + ], + "summary": "Finish passkey login", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/webauthn/register/begin": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Start passkey registration for an authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "webauthn" + ], + "summary": "Begin passkey registration", + "responses": { + "200": { + "description": "WebAuthn registration options", + "schema": { + "type": "object" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/webauthn/register/finish": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Complete passkey registration", + "produces": [ + "application/json" + ], + "tags": [ + "webauthn" + ], + "summary": "Finish passkey registration", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List all bots owned by the current user", + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "List bots", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/bot.botResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new bot", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Create a bot", + "parameters": [ + { + "description": "Bot data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bot.createBotRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/bot.botWithToken" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/servers/{serverID}/commands": { + "get": { + "description": "List all slash commands registered in a server (public)", + "produces": [ + "application/json" + ], + "tags": [ + "bot commands" + ], + "summary": "List server commands", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/bot.slashCommandResponse" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get a bot by ID (owner only)", + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Get a bot", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/bot.botResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a bot (owner only)", + "tags": [ + "bots" + ], + "summary": "Delete a bot", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Update a bot's name, description, or avatar (owner only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Update a bot", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "description": "Bot update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bot.updateBotRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/bot.botResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/commands": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List all slash commands for a bot", + "produces": [ + "application/json" + ], + "tags": [ + "bot commands" + ], + "summary": "List bot commands", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/bot.slashCommandResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new slash command for a bot", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "bot commands" + ], + "summary": "Create a slash command", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "description": "Command data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bot.createCommandRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/bot.slashCommandResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/commands/{cmdID}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a slash command", + "tags": [ + "bot commands" + ], + "summary": "Delete a slash command", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Command ID", + "name": "cmdID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/regenerate-token": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Regenerate a bot's API token (owner only)", + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Regenerate bot token", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/bot.botWithToken" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/servers": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Add a bot to a server", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Add bot to server", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "description": "Server data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bot.addToServerRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/servers/{serverID}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Remove a bot from a server", + "tags": [ + "bots" + ], + "summary": "Remove bot from server", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/channels/{channelID}/messages": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List messages in a channel with pagination", + "produces": [ + "application/json" + ], + "tags": [ + "messages" + ], + "summary": "List messages", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Max messages to return (1-100, default 50)", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Message ID to fetch messages before", + "name": "before", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/message.messageResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Send a message to a channel", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "messages" + ], + "summary": "Create a message", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "description": "Message data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/message.createMessageRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/message.messageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/channels/{channelID}/messages/{messageID}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a message (author only)", + "tags": [ + "messages" + ], + "summary": "Delete a message", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Edit a message (author only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "messages" + ], + "summary": "Update a message", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + }, + { + "description": "Message update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/message.updateMessageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/message.messageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/channels/{channelID}/webhooks": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List webhooks for a channel", + "produces": [ + "application/json" + ], + "tags": [ + "webhooks" + ], + "summary": "List webhooks", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/webhook.webhookResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new webhook for a channel", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "webhooks" + ], + "summary": "Create a webhook", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "description": "Webhook data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/webhook.createWebhookRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/webhook.webhookWithToken" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/invites/{code}": { + "get": { + "description": "Get information about an invite by code", + "produces": [ + "application/json" + ], + "tags": [ + "invites" + ], + "summary": "Get invite info", + "parameters": [ + { + "type": "string", + "description": "Invite code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/invite.inviteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/invites/{code}/join": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Join a server using an invite code", + "produces": [ + "application/json" + ], + "tags": [ + "invites" + ], + "summary": "Join server via invite", + "parameters": [ + { + "type": "string", + "description": "Invite code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/messages/{messageID}/reactions": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List reactions on a message grouped by emoji", + "produces": [ + "application/json" + ], + "tags": [ + "reactions" + ], + "summary": "List reactions", + "parameters": [ + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/reaction.emojiGroup" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Add a reaction to a message", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "reactions" + ], + "summary": "Add a reaction", + "parameters": [ + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + }, + { + "description": "Reaction data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/reaction.addReactionRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/reaction.reactionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/messages/{messageID}/reactions/{emoji}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Remove a reaction from a message", + "tags": [ + "reactions" + ], + "summary": "Remove a reaction", + "parameters": [ + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Emoji to remove", + "name": "emoji", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/push/subscribe": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Subscribe to push notifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "push" + ], + "summary": "Subscribe to push notifications", + "parameters": [ + { + "description": "Push subscription with endpoint, p256dh, and auth", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/push/unsubscribe": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Unsubscribe from push notifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "push" + ], + "summary": "Unsubscribe from push notifications", + "parameters": [ + { + "description": "Endpoint to unsubscribe", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/push/vapid-public-key": { + "get": { + "description": "Get the VAPID public key for push notification subscriptions", + "produces": [ + "application/json" + ], + "tags": [ + "push" + ], + "summary": "Get VAPID public key", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List all servers the current user is a member of", + "produces": [ + "application/json" + ], + "tags": [ + "servers" + ], + "summary": "List servers", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/server.serverResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new server", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "servers" + ], + "summary": "Create a server", + "parameters": [ + { + "description": "Server data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.createServerRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/server.serverResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers/{serverID}": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get a server by ID", + "produces": [ + "application/json" + ], + "tags": [ + "servers" + ], + "summary": "Get a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/server.serverResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a server (owner only)", + "tags": [ + "servers" + ], + "summary": "Delete a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Update a server's name or icon (owner only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "servers" + ], + "summary": "Update a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + }, + { + "description": "Server update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.updateServerRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/server.serverResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers/{serverID}/channels": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List channels in a server", + "produces": [ + "application/json" + ], + "tags": [ + "channels" + ], + "summary": "List channels", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "server_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/channel.channelResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new channel in a server", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "channels" + ], + "summary": "Create a channel", + "parameters": [ + { + "description": "Channel data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/channel.createChannelRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/channel.channelResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers/{serverID}/channels/{channelID}": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get a channel by ID", + "produces": [ + "application/json" + ], + "tags": [ + "channels" + ], + "summary": "Get a channel", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/channel.channelResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a channel (server owner only)", + "tags": [ + "channels" + ], + "summary": "Delete a channel", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Update a channel's properties", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "channels" + ], + "summary": "Update a channel", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "description": "Channel update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/channel.updateChannelRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/channel.channelResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers/{serverID}/invites": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create an invite code for a server", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invites" + ], + "summary": "Create an invite", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + }, + { + "description": "Invite options", + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/invite.createInviteRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/invite.inviteResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/voice/rooms/{roomID}/participants": { + "get": { + "description": "Get participants in a voice room", + "produces": [ + "application/json" + ], + "tags": [ + "voice" + ], + "summary": "Get voice room participants", + "parameters": [ + { + "type": "string", + "description": "Room ID", + "name": "roomID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/voice/token": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get a LiveKit access token for a voice room", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "voice" + ], + "summary": "Get voice token", + "parameters": [ + { + "description": "Room data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/voice.tokenRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/voice.tokenResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/webhooks/{webhookID}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a webhook", + "tags": [ + "webhooks" + ], + "summary": "Delete a webhook", + "parameters": [ + { + "type": "string", + "description": "Webhook ID", + "name": "webhookID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/webhooks/{webhookID}/{token}": { + "post": { + "description": "Execute a webhook to send a message (no auth required)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "webhooks" + ], + "summary": "Execute a webhook", + "parameters": [ + { + "type": "string", + "description": "Webhook ID", + "name": "webhookID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Webhook token", + "name": "token", + "in": "path", + "required": true + }, + { + "description": "Message data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/webhook.executeWebhookRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } + }, + "definitions": { + "auth.UserProfile": { + "type": "object", + "properties": { + "accent_color": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "status_text": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "auth.loginRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "auth.registerRequest": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "auth.updateProfileRequest": { + "type": "object", + "properties": { + "accent_color": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "status_text": { + "type": "string" + } + } + }, + "bot.addToServerRequest": { + "type": "object", + "properties": { + "server_id": { + "type": "string" + } + } + }, + "bot.botResponse": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner_id": { + "type": "string" + } + } + }, + "bot.botWithToken": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner_id": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "bot.createBotRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "bot.createCommandRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "options": {}, + "server_id": { + "type": "string" + } + } + }, + "bot.slashCommandResponse": { + "type": "object", + "properties": { + "bot_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "options": {}, + "server_id": { + "type": "string" + } + } + }, + "bot.updateBotRequest": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "channel.channelResponse": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "server_id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "channel.createChannelRequest": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "server_id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "channel.updateChannelRequest": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, + "invite.createInviteRequest": { + "type": "object", + "properties": { + "expires_hours": { + "type": "integer" + }, + "max_uses": { + "type": "integer" + } + } + }, + "invite.inviteResponse": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "creator_id": { + "type": "string" + }, + "expires_at": { + "type": "string" + }, + "id": { + "type": "string" + }, + "max_uses": { + "type": "integer" + }, + "server_id": { + "type": "string" + }, + "server_name": { + "type": "string" + }, + "use_count": { + "type": "integer" + } + } + }, + "message.createMessageRequest": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "reply_to": { + "type": "string" + } + } + }, + "message.messageResponse": { + "type": "object", + "properties": { + "author_display_name": { + "type": "string" + }, + "author_id": { + "type": "string" + }, + "author_username": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "content": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "edited_at": { + "type": "string" + }, + "id": { + "type": "string" + } + } + }, + "message.updateMessageRequest": { + "type": "object", + "properties": { + "content": { + "type": "string" + } + } + }, + "reaction.addReactionRequest": { + "type": "object", + "properties": { + "emoji": { + "type": "string" + } + } + }, + "reaction.emojiGroup": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/reaction.reactionResponse" + } + }, + "emoji": { + "type": "string" + }, + "users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "reaction.reactionResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "emoji": { + "type": "string" + }, + "id": { + "type": "string" + }, + "message_id": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, + "server.createServerRequest": { + "type": "object", + "properties": { + "icon": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "server.serverResponse": { + "type": "object", + "properties": { + "icon": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner_id": { + "type": "string" + } + } + }, + "server.updateServerRequest": { + "type": "object", + "properties": { + "icon": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "voice.tokenRequest": { + "type": "object", + "properties": { + "room_name": { + "type": "string" + } + } + }, + "voice.tokenResponse": { + "type": "object", + "properties": { + "livekit_url": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "webhook.createWebhookRequest": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "webhook.executeWebhookRequest": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "content": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "webhook.webhookResponse": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "webhook.webhookWithToken": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "token": { + "type": "string" + } + } + } + }, "securityDefinitions": { "SessionAuth": { "type": "apiKey", diff --git a/docs/swagger.json b/docs/swagger.json index b98d5e0..2354157 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -12,7 +12,3446 @@ }, "host": "localhost:8080", "basePath": "/api/v1", - "paths": {}, + "paths": { + "/auth/login/password": { + "post": { + "description": "Authenticate with email and password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login with email and password", + "parameters": [ + { + "description": "Login credentials", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.loginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/logout": { + "post": { + "description": "End the current session", + "tags": [ + "auth" + ], + "summary": "Logout", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/auth/me": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get the authenticated user's profile", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current user profile", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/auth.UserProfile" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Update the authenticated user's profile", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Update current user profile", + "parameters": [ + { + "description": "Profile update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.updateProfileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/auth.UserProfile" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/register": { + "post": { + "description": "Create a new user account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Register a new user", + "parameters": [ + { + "description": "Registration data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.registerRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/webauthn/login/begin": { + "post": { + "description": "Start passkey authentication", + "produces": [ + "application/json" + ], + "tags": [ + "webauthn" + ], + "summary": "Begin passkey login", + "responses": { + "200": { + "description": "WebAuthn login options", + "schema": { + "type": "object" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/webauthn/login/finish": { + "post": { + "description": "Complete passkey authentication", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "webauthn" + ], + "summary": "Finish passkey login", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/webauthn/register/begin": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Start passkey registration for an authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "webauthn" + ], + "summary": "Begin passkey registration", + "responses": { + "200": { + "description": "WebAuthn registration options", + "schema": { + "type": "object" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/auth/webauthn/register/finish": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Complete passkey registration", + "produces": [ + "application/json" + ], + "tags": [ + "webauthn" + ], + "summary": "Finish passkey registration", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List all bots owned by the current user", + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "List bots", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/bot.botResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new bot", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Create a bot", + "parameters": [ + { + "description": "Bot data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bot.createBotRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/bot.botWithToken" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/servers/{serverID}/commands": { + "get": { + "description": "List all slash commands registered in a server (public)", + "produces": [ + "application/json" + ], + "tags": [ + "bot commands" + ], + "summary": "List server commands", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/bot.slashCommandResponse" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get a bot by ID (owner only)", + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Get a bot", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/bot.botResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a bot (owner only)", + "tags": [ + "bots" + ], + "summary": "Delete a bot", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Update a bot's name, description, or avatar (owner only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Update a bot", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "description": "Bot update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bot.updateBotRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/bot.botResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/commands": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List all slash commands for a bot", + "produces": [ + "application/json" + ], + "tags": [ + "bot commands" + ], + "summary": "List bot commands", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/bot.slashCommandResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new slash command for a bot", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "bot commands" + ], + "summary": "Create a slash command", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "description": "Command data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bot.createCommandRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/bot.slashCommandResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/commands/{cmdID}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a slash command", + "tags": [ + "bot commands" + ], + "summary": "Delete a slash command", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Command ID", + "name": "cmdID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/regenerate-token": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Regenerate a bot's API token (owner only)", + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Regenerate bot token", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/bot.botWithToken" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/servers": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Add a bot to a server", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "bots" + ], + "summary": "Add bot to server", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "description": "Server data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bot.addToServerRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/bots/{botID}/servers/{serverID}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Remove a bot from a server", + "tags": [ + "bots" + ], + "summary": "Remove bot from server", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "botID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/channels/{channelID}/messages": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List messages in a channel with pagination", + "produces": [ + "application/json" + ], + "tags": [ + "messages" + ], + "summary": "List messages", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Max messages to return (1-100, default 50)", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Message ID to fetch messages before", + "name": "before", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/message.messageResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Send a message to a channel", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "messages" + ], + "summary": "Create a message", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "description": "Message data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/message.createMessageRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/message.messageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/channels/{channelID}/messages/{messageID}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a message (author only)", + "tags": [ + "messages" + ], + "summary": "Delete a message", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Edit a message (author only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "messages" + ], + "summary": "Update a message", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + }, + { + "description": "Message update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/message.updateMessageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/message.messageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/channels/{channelID}/webhooks": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List webhooks for a channel", + "produces": [ + "application/json" + ], + "tags": [ + "webhooks" + ], + "summary": "List webhooks", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/webhook.webhookResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new webhook for a channel", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "webhooks" + ], + "summary": "Create a webhook", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "description": "Webhook data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/webhook.createWebhookRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/webhook.webhookWithToken" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/invites/{code}": { + "get": { + "description": "Get information about an invite by code", + "produces": [ + "application/json" + ], + "tags": [ + "invites" + ], + "summary": "Get invite info", + "parameters": [ + { + "type": "string", + "description": "Invite code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/invite.inviteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/invites/{code}/join": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Join a server using an invite code", + "produces": [ + "application/json" + ], + "tags": [ + "invites" + ], + "summary": "Join server via invite", + "parameters": [ + { + "type": "string", + "description": "Invite code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/messages/{messageID}/reactions": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List reactions on a message grouped by emoji", + "produces": [ + "application/json" + ], + "tags": [ + "reactions" + ], + "summary": "List reactions", + "parameters": [ + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/reaction.emojiGroup" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Add a reaction to a message", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "reactions" + ], + "summary": "Add a reaction", + "parameters": [ + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + }, + { + "description": "Reaction data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/reaction.addReactionRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/reaction.reactionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/messages/{messageID}/reactions/{emoji}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Remove a reaction from a message", + "tags": [ + "reactions" + ], + "summary": "Remove a reaction", + "parameters": [ + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Emoji to remove", + "name": "emoji", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/push/subscribe": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Subscribe to push notifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "push" + ], + "summary": "Subscribe to push notifications", + "parameters": [ + { + "description": "Push subscription with endpoint, p256dh, and auth", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/push/unsubscribe": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Unsubscribe from push notifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "push" + ], + "summary": "Unsubscribe from push notifications", + "parameters": [ + { + "description": "Endpoint to unsubscribe", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/push/vapid-public-key": { + "get": { + "description": "Get the VAPID public key for push notification subscriptions", + "produces": [ + "application/json" + ], + "tags": [ + "push" + ], + "summary": "Get VAPID public key", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List all servers the current user is a member of", + "produces": [ + "application/json" + ], + "tags": [ + "servers" + ], + "summary": "List servers", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/server.serverResponse" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new server", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "servers" + ], + "summary": "Create a server", + "parameters": [ + { + "description": "Server data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.createServerRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/server.serverResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers/{serverID}": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get a server by ID", + "produces": [ + "application/json" + ], + "tags": [ + "servers" + ], + "summary": "Get a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/server.serverResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a server (owner only)", + "tags": [ + "servers" + ], + "summary": "Delete a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Update a server's name or icon (owner only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "servers" + ], + "summary": "Update a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + }, + { + "description": "Server update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.updateServerRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/server.serverResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers/{serverID}/channels": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "List channels in a server", + "produces": [ + "application/json" + ], + "tags": [ + "channels" + ], + "summary": "List channels", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "server_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/channel.channelResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create a new channel in a server", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "channels" + ], + "summary": "Create a channel", + "parameters": [ + { + "description": "Channel data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/channel.createChannelRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/channel.channelResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers/{serverID}/channels/{channelID}": { + "get": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get a channel by ID", + "produces": [ + "application/json" + ], + "tags": [ + "channels" + ], + "summary": "Get a channel", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/channel.channelResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a channel (server owner only)", + "tags": [ + "channels" + ], + "summary": "Delete a channel", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "patch": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Update a channel's properties", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "channels" + ], + "summary": "Update a channel", + "parameters": [ + { + "type": "string", + "description": "Channel ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "description": "Channel update data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/channel.updateChannelRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/channel.channelResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/servers/{serverID}/invites": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Create an invite code for a server", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invites" + ], + "summary": "Create an invite", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "serverID", + "in": "path", + "required": true + }, + { + "description": "Invite options", + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/invite.createInviteRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/invite.inviteResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/voice/rooms/{roomID}/participants": { + "get": { + "description": "Get participants in a voice room", + "produces": [ + "application/json" + ], + "tags": [ + "voice" + ], + "summary": "Get voice room participants", + "parameters": [ + { + "type": "string", + "description": "Room ID", + "name": "roomID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/voice/token": { + "post": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Get a LiveKit access token for a voice room", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "voice" + ], + "summary": "Get voice token", + "parameters": [ + { + "description": "Room data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/voice.tokenRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/voice.tokenResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/webhooks/{webhookID}": { + "delete": { + "security": [ + { + "SessionAuth": [] + } + ], + "description": "Delete a webhook", + "tags": [ + "webhooks" + ], + "summary": "Delete a webhook", + "parameters": [ + { + "type": "string", + "description": "Webhook ID", + "name": "webhookID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/webhooks/{webhookID}/{token}": { + "post": { + "description": "Execute a webhook to send a message (no auth required)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "webhooks" + ], + "summary": "Execute a webhook", + "parameters": [ + { + "type": "string", + "description": "Webhook ID", + "name": "webhookID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Webhook token", + "name": "token", + "in": "path", + "required": true + }, + { + "description": "Message data", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/webhook.executeWebhookRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } + }, + "definitions": { + "auth.UserProfile": { + "type": "object", + "properties": { + "accent_color": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "status_text": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "auth.loginRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "auth.registerRequest": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "auth.updateProfileRequest": { + "type": "object", + "properties": { + "accent_color": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "status_text": { + "type": "string" + } + } + }, + "bot.addToServerRequest": { + "type": "object", + "properties": { + "server_id": { + "type": "string" + } + } + }, + "bot.botResponse": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner_id": { + "type": "string" + } + } + }, + "bot.botWithToken": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner_id": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "bot.createBotRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "bot.createCommandRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "options": {}, + "server_id": { + "type": "string" + } + } + }, + "bot.slashCommandResponse": { + "type": "object", + "properties": { + "bot_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "options": {}, + "server_id": { + "type": "string" + } + } + }, + "bot.updateBotRequest": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "channel.channelResponse": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "server_id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "channel.createChannelRequest": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "server_id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "channel.updateChannelRequest": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, + "invite.createInviteRequest": { + "type": "object", + "properties": { + "expires_hours": { + "type": "integer" + }, + "max_uses": { + "type": "integer" + } + } + }, + "invite.inviteResponse": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "creator_id": { + "type": "string" + }, + "expires_at": { + "type": "string" + }, + "id": { + "type": "string" + }, + "max_uses": { + "type": "integer" + }, + "server_id": { + "type": "string" + }, + "server_name": { + "type": "string" + }, + "use_count": { + "type": "integer" + } + } + }, + "message.createMessageRequest": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "reply_to": { + "type": "string" + } + } + }, + "message.messageResponse": { + "type": "object", + "properties": { + "author_display_name": { + "type": "string" + }, + "author_id": { + "type": "string" + }, + "author_username": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "content": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "edited_at": { + "type": "string" + }, + "id": { + "type": "string" + } + } + }, + "message.updateMessageRequest": { + "type": "object", + "properties": { + "content": { + "type": "string" + } + } + }, + "reaction.addReactionRequest": { + "type": "object", + "properties": { + "emoji": { + "type": "string" + } + } + }, + "reaction.emojiGroup": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/reaction.reactionResponse" + } + }, + "emoji": { + "type": "string" + }, + "users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "reaction.reactionResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "emoji": { + "type": "string" + }, + "id": { + "type": "string" + }, + "message_id": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, + "server.createServerRequest": { + "type": "object", + "properties": { + "icon": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "server.serverResponse": { + "type": "object", + "properties": { + "icon": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner_id": { + "type": "string" + } + } + }, + "server.updateServerRequest": { + "type": "object", + "properties": { + "icon": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "voice.tokenRequest": { + "type": "object", + "properties": { + "room_name": { + "type": "string" + } + } + }, + "voice.tokenResponse": { + "type": "object", + "properties": { + "livekit_url": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "webhook.createWebhookRequest": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "webhook.executeWebhookRequest": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "content": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "webhook.webhookResponse": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "webhook.webhookWithToken": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "token": { + "type": "string" + } + } + } + }, "securityDefinitions": { "SessionAuth": { "type": "apiKey", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 2cadcaa..9f0a4ee 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,11 +1,2227 @@ basePath: /api/v1 +definitions: + auth.UserProfile: + properties: + accent_color: + type: string + avatar: + type: string + bio: + type: string + created_at: + type: string + display_name: + type: string + email: + type: string + id: + type: string + status: + type: string + status_text: + type: string + username: + type: string + type: object + auth.loginRequest: + properties: + email: + type: string + password: + type: string + type: object + auth.registerRequest: + properties: + display_name: + type: string + email: + type: string + password: + type: string + username: + type: string + type: object + auth.updateProfileRequest: + properties: + accent_color: + type: string + bio: + type: string + display_name: + type: string + status_text: + type: string + type: object + bot.addToServerRequest: + properties: + server_id: + type: string + type: object + bot.botResponse: + properties: + avatar: + type: string + created_at: + type: string + description: + type: string + id: + type: string + name: + type: string + owner_id: + type: string + type: object + bot.botWithToken: + properties: + avatar: + type: string + created_at: + type: string + description: + type: string + id: + type: string + name: + type: string + owner_id: + type: string + token: + type: string + type: object + bot.createBotRequest: + properties: + description: + type: string + name: + type: string + type: object + bot.createCommandRequest: + properties: + description: + type: string + name: + type: string + options: {} + server_id: + type: string + type: object + bot.slashCommandResponse: + properties: + bot_id: + type: string + created_at: + type: string + description: + type: string + id: + type: string + name: + type: string + options: {} + server_id: + type: string + type: object + bot.updateBotRequest: + properties: + avatar: + type: string + description: + type: string + name: + type: string + type: object + channel.channelResponse: + properties: + category: + type: string + created_at: + type: string + id: + type: string + name: + type: string + position: + type: integer + server_id: + type: string + type: + type: string + type: object + channel.createChannelRequest: + properties: + category: + type: string + name: + type: string + position: + type: integer + server_id: + type: string + type: + type: string + type: object + channel.updateChannelRequest: + properties: + category: + type: string + name: + type: string + position: + type: integer + type: + type: string + type: object + invite.createInviteRequest: + properties: + expires_hours: + type: integer + max_uses: + type: integer + type: object + invite.inviteResponse: + properties: + code: + type: string + created_at: + type: string + creator_id: + type: string + expires_at: + type: string + id: + type: string + max_uses: + type: integer + server_id: + type: string + server_name: + type: string + use_count: + type: integer + type: object + message.createMessageRequest: + properties: + content: + type: string + reply_to: + type: string + type: object + message.messageResponse: + properties: + author_display_name: + type: string + author_id: + type: string + author_username: + type: string + channel_id: + type: string + content: + type: string + created_at: + type: string + edited_at: + type: string + id: + type: string + type: object + message.updateMessageRequest: + properties: + content: + type: string + type: object + reaction.addReactionRequest: + properties: + emoji: + type: string + type: object + reaction.emojiGroup: + properties: + count: + type: integer + details: + items: + $ref: '#/definitions/reaction.reactionResponse' + type: array + emoji: + type: string + users: + items: + type: string + type: array + type: object + reaction.reactionResponse: + properties: + created_at: + type: string + emoji: + type: string + id: + type: string + message_id: + type: string + user_id: + type: string + type: object + server.createServerRequest: + properties: + icon: + type: string + name: + type: string + type: object + server.serverResponse: + properties: + icon: + type: string + id: + type: string + name: + type: string + owner_id: + type: string + type: object + server.updateServerRequest: + properties: + icon: + type: string + name: + type: string + type: object + voice.tokenRequest: + properties: + room_name: + type: string + type: object + voice.tokenResponse: + properties: + livekit_url: + type: string + token: + type: string + type: object + webhook.createWebhookRequest: + properties: + avatar: + type: string + name: + type: string + type: object + webhook.executeWebhookRequest: + properties: + avatar: + type: string + content: + type: string + username: + type: string + type: object + webhook.webhookResponse: + properties: + avatar: + type: string + channel_id: + type: string + created_at: + type: string + created_by: + type: string + id: + type: string + name: + type: string + type: object + webhook.webhookWithToken: + properties: + avatar: + type: string + channel_id: + type: string + created_at: + type: string + created_by: + type: string + id: + type: string + name: + type: string + token: + type: string + type: object host: localhost:8080 info: contact: {} description: A chaotic, self-hosted Discord-like platform API title: Dumpster API version: "1.0" -paths: {} +paths: + /auth/login/password: + post: + consumes: + - application/json + description: Authenticate with email and password + parameters: + - description: Login credentials + in: body + name: body + required: true + schema: + $ref: '#/definitions/auth.loginRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + summary: Login with email and password + tags: + - auth + /auth/logout: + post: + description: End the current session + responses: + "204": + description: No Content + summary: Logout + tags: + - auth + /auth/me: + get: + description: Get the authenticated user's profile + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/auth.UserProfile' + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Get current user profile + tags: + - auth + patch: + consumes: + - application/json + description: Update the authenticated user's profile + parameters: + - description: Profile update data + in: body + name: body + required: true + schema: + $ref: '#/definitions/auth.updateProfileRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/auth.UserProfile' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Update current user profile + tags: + - auth + /auth/register: + post: + consumes: + - application/json + description: Create a new user account + parameters: + - description: Registration data + in: body + name: body + required: true + schema: + $ref: '#/definitions/auth.registerRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object + summary: Register a new user + tags: + - auth + /auth/webauthn/login/begin: + post: + description: Start passkey authentication + produces: + - application/json + responses: + "200": + description: WebAuthn login options + schema: + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + summary: Begin passkey login + tags: + - webauthn + /auth/webauthn/login/finish: + post: + consumes: + - application/json + description: Complete passkey authentication + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + summary: Finish passkey login + tags: + - webauthn + /auth/webauthn/register/begin: + post: + description: Start passkey registration for an authenticated user + produces: + - application/json + responses: + "200": + description: WebAuthn registration options + schema: + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Begin passkey registration + tags: + - webauthn + /auth/webauthn/register/finish: + post: + description: Complete passkey registration + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Finish passkey registration + tags: + - webauthn + /bots: + get: + description: List all bots owned by the current user + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/bot.botResponse' + type: array + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: List bots + tags: + - bots + post: + consumes: + - application/json + description: Create a new bot + parameters: + - description: Bot data + in: body + name: body + required: true + schema: + $ref: '#/definitions/bot.createBotRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/bot.botWithToken' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Create a bot + tags: + - bots + /bots/{botID}: + delete: + description: Delete a bot (owner only) + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Delete a bot + tags: + - bots + get: + description: Get a bot by ID (owner only) + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/bot.botResponse' + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Get a bot + tags: + - bots + patch: + consumes: + - application/json + description: Update a bot's name, description, or avatar (owner only) + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + - description: Bot update data + in: body + name: body + required: true + schema: + $ref: '#/definitions/bot.updateBotRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/bot.botResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Update a bot + tags: + - bots + /bots/{botID}/commands: + get: + description: List all slash commands for a bot + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/bot.slashCommandResponse' + type: array + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: List bot commands + tags: + - bot commands + post: + consumes: + - application/json + description: Create a new slash command for a bot + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + - description: Command data + in: body + name: body + required: true + schema: + $ref: '#/definitions/bot.createCommandRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/bot.slashCommandResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Create a slash command + tags: + - bot commands + /bots/{botID}/commands/{cmdID}: + delete: + description: Delete a slash command + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + - description: Command ID + in: path + name: cmdID + required: true + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Delete a slash command + tags: + - bot commands + /bots/{botID}/regenerate-token: + post: + description: Regenerate a bot's API token (owner only) + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/bot.botWithToken' + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Regenerate bot token + tags: + - bots + /bots/{botID}/servers: + post: + consumes: + - application/json + description: Add a bot to a server + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + - description: Server data + in: body + name: body + required: true + schema: + $ref: '#/definitions/bot.addToServerRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Add bot to server + tags: + - bots + /bots/{botID}/servers/{serverID}: + delete: + description: Remove a bot from a server + parameters: + - description: Bot ID + in: path + name: botID + required: true + type: string + - description: Server ID + in: path + name: serverID + required: true + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Remove bot from server + tags: + - bots + /bots/servers/{serverID}/commands: + get: + description: List all slash commands registered in a server (public) + parameters: + - description: Server ID + in: path + name: serverID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/bot.slashCommandResponse' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + summary: List server commands + tags: + - bot commands + /channels/{channelID}/messages: + get: + description: List messages in a channel with pagination + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + - description: Max messages to return (1-100, default 50) + in: query + name: limit + type: integer + - description: Message ID to fetch messages before + in: query + name: before + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/message.messageResponse' + type: array + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: List messages + tags: + - messages + post: + consumes: + - application/json + description: Send a message to a channel + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + - description: Message data + in: body + name: body + required: true + schema: + $ref: '#/definitions/message.createMessageRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/message.messageResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Create a message + tags: + - messages + /channels/{channelID}/messages/{messageID}: + delete: + description: Delete a message (author only) + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + - description: Message ID + in: path + name: messageID + required: true + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Delete a message + tags: + - messages + patch: + consumes: + - application/json + description: Edit a message (author only) + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + - description: Message ID + in: path + name: messageID + required: true + type: string + - description: Message update data + in: body + name: body + required: true + schema: + $ref: '#/definitions/message.updateMessageRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/message.messageResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Update a message + tags: + - messages + /channels/{channelID}/webhooks: + get: + description: List webhooks for a channel + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/webhook.webhookResponse' + type: array + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: List webhooks + tags: + - webhooks + post: + consumes: + - application/json + description: Create a new webhook for a channel + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + - description: Webhook data + in: body + name: body + required: true + schema: + $ref: '#/definitions/webhook.createWebhookRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/webhook.webhookWithToken' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Create a webhook + tags: + - webhooks + /invites/{code}: + get: + description: Get information about an invite by code + parameters: + - description: Invite code + in: path + name: code + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/invite.inviteResponse' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + summary: Get invite info + tags: + - invites + /invites/{code}/join: + post: + description: Join a server using an invite code + parameters: + - description: Invite code + in: path + name: code + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Join server via invite + tags: + - invites + /messages/{messageID}/reactions: + get: + description: List reactions on a message grouped by emoji + parameters: + - description: Message ID + in: path + name: messageID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/reaction.emojiGroup' + type: array + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: List reactions + tags: + - reactions + post: + consumes: + - application/json + description: Add a reaction to a message + parameters: + - description: Message ID + in: path + name: messageID + required: true + type: string + - description: Reaction data + in: body + name: body + required: true + schema: + $ref: '#/definitions/reaction.addReactionRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/reaction.reactionResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Add a reaction + tags: + - reactions + /messages/{messageID}/reactions/{emoji}: + delete: + description: Remove a reaction from a message + parameters: + - description: Message ID + in: path + name: messageID + required: true + type: string + - description: Emoji to remove + in: path + name: emoji + required: true + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Remove a reaction + tags: + - reactions + /push/subscribe: + post: + consumes: + - application/json + description: Subscribe to push notifications + parameters: + - description: Push subscription with endpoint, p256dh, and auth + in: body + name: body + required: true + schema: + type: object + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Subscribe to push notifications + tags: + - push + /push/unsubscribe: + post: + consumes: + - application/json + description: Unsubscribe from push notifications + parameters: + - description: Endpoint to unsubscribe + in: body + name: body + required: true + schema: + type: object + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Unsubscribe from push notifications + tags: + - push + /push/vapid-public-key: + get: + description: Get the VAPID public key for push notification subscriptions + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + summary: Get VAPID public key + tags: + - push + /servers: + get: + description: List all servers the current user is a member of + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/server.serverResponse' + type: array + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: List servers + tags: + - servers + post: + consumes: + - application/json + description: Create a new server + parameters: + - description: Server data + in: body + name: body + required: true + schema: + $ref: '#/definitions/server.createServerRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/server.serverResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Create a server + tags: + - servers + /servers/{serverID}: + delete: + description: Delete a server (owner only) + parameters: + - description: Server ID + in: path + name: serverID + required: true + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Delete a server + tags: + - servers + get: + description: Get a server by ID + parameters: + - description: Server ID + in: path + name: serverID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/server.serverResponse' + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Get a server + tags: + - servers + patch: + consumes: + - application/json + description: Update a server's name or icon (owner only) + parameters: + - description: Server ID + in: path + name: serverID + required: true + type: string + - description: Server update data + in: body + name: body + required: true + schema: + $ref: '#/definitions/server.updateServerRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/server.serverResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Update a server + tags: + - servers + /servers/{serverID}/channels: + get: + description: List channels in a server + parameters: + - description: Server ID + in: query + name: server_id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/channel.channelResponse' + type: array + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: List channels + tags: + - channels + post: + consumes: + - application/json + description: Create a new channel in a server + parameters: + - description: Channel data + in: body + name: body + required: true + schema: + $ref: '#/definitions/channel.createChannelRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/channel.channelResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Create a channel + tags: + - channels + /servers/{serverID}/channels/{channelID}: + delete: + description: Delete a channel (server owner only) + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Delete a channel + tags: + - channels + get: + description: Get a channel by ID + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/channel.channelResponse' + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Get a channel + tags: + - channels + patch: + consumes: + - application/json + description: Update a channel's properties + parameters: + - description: Channel ID + in: path + name: channelID + required: true + type: string + - description: Channel update data + in: body + name: body + required: true + schema: + $ref: '#/definitions/channel.updateChannelRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/channel.channelResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Update a channel + tags: + - channels + /servers/{serverID}/invites: + post: + consumes: + - application/json + description: Create an invite code for a server + parameters: + - description: Server ID + in: path + name: serverID + required: true + type: string + - description: Invite options + in: body + name: body + schema: + $ref: '#/definitions/invite.createInviteRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/invite.inviteResponse' + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Create an invite + tags: + - invites + /voice/rooms/{roomID}/participants: + get: + description: Get participants in a voice room + parameters: + - description: Room ID + in: path + name: roomID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: object + type: array + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + summary: Get voice room participants + tags: + - voice + /voice/token: + post: + consumes: + - application/json + description: Get a LiveKit access token for a voice room + parameters: + - description: Room data + in: body + name: body + required: true + schema: + $ref: '#/definitions/voice.tokenRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/voice.tokenResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Get voice token + tags: + - voice + /webhooks/{webhookID}: + delete: + description: Delete a webhook + parameters: + - description: Webhook ID + in: path + name: webhookID + required: true + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "403": + description: Forbidden + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + security: + - SessionAuth: [] + summary: Delete a webhook + tags: + - webhooks + /webhooks/{webhookID}/{token}: + post: + consumes: + - application/json + description: Execute a webhook to send a message (no auth required) + parameters: + - description: Webhook ID + in: path + name: webhookID + required: true + type: string + - description: Webhook token + in: path + name: token + required: true + type: string + - description: Message data + in: body + name: body + required: true + schema: + $ref: '#/definitions/webhook.executeWebhookRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "401": + description: Unauthorized + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + summary: Execute a webhook + tags: + - webhooks schemes: - http - https diff --git a/internal/auth/handlers.go b/internal/auth/handlers.go index 406a7ab..1e0ab01 100644 --- a/internal/auth/handlers.go +++ b/internal/auth/handlers.go @@ -78,6 +78,16 @@ func gravatarURL(email string) string { return fmt.Sprintf("https://www.gravatar.com/avatar/%s?d=identicon&s=256", hex.EncodeToString(hash[:])) } +// @Summary Register a new user +// @Description Create a new user account +// @Tags auth +// @Accept json +// @Produce json +// @Param body body registerRequest true "Registration data" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 409 {object} map[string]string +// @Router /auth/register [post] func (h *Handler) Register(w http.ResponseWriter, r *http.Request) { var req registerRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { @@ -123,6 +133,16 @@ func (h *Handler) Register(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{"id": userID}) } +// @Summary Login with email and password +// @Description Authenticate with email and password +// @Tags auth +// @Accept json +// @Produce json +// @Param body body loginRequest true "Login credentials" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Router /auth/login/password [post] func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { var req loginRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { @@ -156,6 +176,11 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{"id": userID}) } +// @Summary Logout +// @Description End the current session +// @Tags auth +// @Success 204 +// @Router /auth/logout [post] func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie(h.cfg.Session.CookieName) if err == nil { @@ -173,6 +198,14 @@ func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// @Summary Get current user profile +// @Description Get the authenticated user's profile +// @Tags auth +// @Produce json +// @Security SessionAuth +// @Success 200 {object} UserProfile +// @Failure 401 {object} map[string]string +// @Router /auth/me [get] func (h *Handler) Me(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -190,6 +223,17 @@ func (h *Handler) Me(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(user) } +// @Summary Update current user profile +// @Description Update the authenticated user's profile +// @Tags auth +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param body body updateProfileRequest true "Profile update data" +// @Success 200 {object} UserProfile +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Router /auth/me [patch] func (h *Handler) UpdateProfile(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { diff --git a/internal/auth/webauthn.go b/internal/auth/webauthn.go index 7f3ae83..818a639 100644 --- a/internal/auth/webauthn.go +++ b/internal/auth/webauthn.go @@ -88,6 +88,16 @@ func (h *WebAuthnHandler) RegisterRoutes(r *http.ServeMux) { r.HandleFunc("POST /auth/webauthn/login/finish", h.LoginFinish) } +// @Summary Begin passkey registration +// @Description Start passkey registration for an authenticated user +// @Tags webauthn +// @Produce json +// @Security SessionAuth +// @Success 200 {object} object "WebAuthn registration options" +// @Failure 401 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Router /auth/webauthn/register/begin [post] // RegisterBegin starts passkey registration for an authenticated user. func (h *WebAuthnHandler) RegisterBegin(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) @@ -116,6 +126,16 @@ func (h *WebAuthnHandler) RegisterBegin(w http.ResponseWriter, r *http.Request) json.NewEncoder(w).Encode(options) } +// @Summary Finish passkey registration +// @Description Complete passkey registration +// @Tags webauthn +// @Produce json +// @Security SessionAuth +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Router /auth/webauthn/register/finish [post] // RegisterFinish completes passkey registration. func (h *WebAuthnHandler) RegisterFinish(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) @@ -163,6 +183,13 @@ func (h *WebAuthnHandler) RegisterFinish(w http.ResponseWriter, r *http.Request) json.NewEncoder(w).Encode(map[string]string{"status": "registered"}) } +// @Summary Begin passkey login +// @Description Start passkey authentication +// @Tags webauthn +// @Produce json +// @Success 200 {object} object "WebAuthn login options" +// @Failure 500 {object} map[string]string +// @Router /auth/webauthn/login/begin [post] // LoginBegin starts passkey authentication (no auth required). func (h *WebAuthnHandler) LoginBegin(w http.ResponseWriter, r *http.Request) { // Get all registered credentials for the login ceremony @@ -213,6 +240,15 @@ func (h *WebAuthnHandler) LoginBegin(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(options) } +// @Summary Finish passkey login +// @Description Complete passkey authentication +// @Tags webauthn +// @Accept json +// @Produce json +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Router /auth/webauthn/login/finish [post] // LoginFinish completes passkey authentication. func (h *WebAuthnHandler) LoginFinish(w http.ResponseWriter, r *http.Request) { // Get challenge key from cookie diff --git a/internal/bot/commands.go b/internal/bot/commands.go index 078857c..87cf4e2 100644 --- a/internal/bot/commands.go +++ b/internal/bot/commands.go @@ -58,6 +58,20 @@ type createCommandRequest struct { // ---- handlers ---- +// @Summary Create a slash command +// @Description Create a new slash command for a bot +// @Tags bot commands +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Param body body createCommandRequest true "Command data" +// @Success 201 {object} slashCommandResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 409 {object} map[string]string +// @Router /bots/{botID}/commands [post] func (h *CommandHandler) Create(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -131,6 +145,17 @@ func (h *CommandHandler) Create(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusCreated, cmd) } +// @Summary List bot commands +// @Description List all slash commands for a bot +// @Tags bot commands +// @Produce json +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Success 200 {array} slashCommandResponse +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /bots/{botID}/commands [get] func (h *CommandHandler) ListByBot(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -180,6 +205,17 @@ func (h *CommandHandler) ListByBot(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, cmds) } +// @Summary Delete a slash command +// @Description Delete a slash command +// @Tags bot commands +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Param cmdID path string true "Command ID" +// @Success 204 +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /bots/{botID}/commands/{cmdID} [delete] func (h *CommandHandler) Delete(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -217,7 +253,14 @@ func (h *CommandHandler) Delete(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } -// ListByServer returns all slash commands registered in a server (public, no auth). +// @Summary List server commands +// @Description List all slash commands registered in a server (public) +// @Tags bot commands +// @Produce json +// @Param serverID path string true "Server ID" +// @Success 200 {array} slashCommandResponse +// @Failure 500 {object} map[string]string +// @Router /bots/servers/{serverID}/commands [get] func (h *CommandHandler) ListByServer(w http.ResponseWriter, r *http.Request) { serverID := chi.URLParam(r, "serverID") diff --git a/internal/bot/handlers.go b/internal/bot/handlers.go index 3f43647..dc4bf50 100644 --- a/internal/bot/handlers.go +++ b/internal/bot/handlers.go @@ -78,6 +78,17 @@ func writeErr(w http.ResponseWriter, status int, msg string) { // ---- handlers ---- +// @Summary Create a bot +// @Description Create a new bot +// @Tags bots +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param body body createBotRequest true "Bot data" +// @Success 201 {object} botWithToken +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Router /bots [post] func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -121,6 +132,14 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusCreated, bot) } +// @Summary List bots +// @Description List all bots owned by the current user +// @Tags bots +// @Produce json +// @Security SessionAuth +// @Success 200 {array} botResponse +// @Failure 401 {object} map[string]string +// @Router /bots [get] func (h *Handler) List(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -161,6 +180,17 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, bots) } +// @Summary Get a bot +// @Description Get a bot by ID (owner only) +// @Tags bots +// @Produce json +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Success 200 {object} botResponse +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /bots/{botID} [get] func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -197,6 +227,20 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, b) } +// @Summary Update a bot +// @Description Update a bot's name, description, or avatar (owner only) +// @Tags bots +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Param body body updateBotRequest true "Bot update data" +// @Success 200 {object} botResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /bots/{botID} [patch] func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -259,6 +303,16 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, b) } +// @Summary Delete a bot +// @Description Delete a bot (owner only) +// @Tags bots +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Success 204 +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /bots/{botID} [delete] func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -294,6 +348,20 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// @Summary Add bot to server +// @Description Add a bot to a server +// @Tags bots +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Param body body addToServerRequest true "Server data" +// @Success 201 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /bots/{botID}/servers [post] func (h *Handler) AddToServer(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -361,6 +429,17 @@ func (h *Handler) AddToServer(w http.ResponseWriter, r *http.Request) { }) } +// @Summary Remove bot from server +// @Description Remove a bot from a server +// @Tags bots +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Param serverID path string true "Server ID" +// @Success 204 +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /bots/{botID}/servers/{serverID} [delete] func (h *Handler) RemoveFromServer(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -405,6 +484,17 @@ func (h *Handler) RemoveFromServer(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// @Summary Regenerate bot token +// @Description Regenerate a bot's API token (owner only) +// @Tags bots +// @Produce json +// @Security SessionAuth +// @Param botID path string true "Bot ID" +// @Success 200 {object} botWithToken +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /bots/{botID}/regenerate-token [post] func (h *Handler) RegenerateToken(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { diff --git a/internal/channel/handlers.go b/internal/channel/handlers.go index 0b1a0cc..e268afd 100644 --- a/internal/channel/handlers.go +++ b/internal/channel/handlers.go @@ -63,6 +63,18 @@ func (h *Handler) serverIDForChannel(ctx context.Context, channelID string) (str return serverID, err } +// @Summary Create a channel +// @Description Create a new channel in a server +// @Tags channels +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param body body createChannelRequest true "Channel data" +// @Success 201 {object} channelResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Router /servers/{serverID}/channels [post] func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -121,6 +133,17 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(ch) } +// @Summary List channels +// @Description List channels in a server +// @Tags channels +// @Produce json +// @Security SessionAuth +// @Param server_id query string true "Server ID" +// @Success 200 {array} channelResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Router /servers/{serverID}/channels [get] func (h *Handler) List(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -174,6 +197,17 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(channels) } +// @Summary Get a channel +// @Description Get a channel by ID +// @Tags channels +// @Produce json +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Success 200 {object} channelResponse +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /servers/{serverID}/channels/{channelID} [get] func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -218,6 +252,20 @@ type updateChannelRequest struct { Position *int `json:"position"` } +// @Summary Update a channel +// @Description Update a channel's properties +// @Tags channels +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Param body body updateChannelRequest true "Channel update data" +// @Success 200 {object} channelResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /servers/{serverID}/channels/{channelID} [patch] func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -279,6 +327,16 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(ch) } +// @Summary Delete a channel +// @Description Delete a channel (server owner only) +// @Tags channels +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Success 204 +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /servers/{serverID}/channels/{channelID} [delete] func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { diff --git a/internal/invite/handlers.go b/internal/invite/handlers.go index 49b35d3..aa63c24 100644 --- a/internal/invite/handlers.go +++ b/internal/invite/handlers.go @@ -61,6 +61,18 @@ func generateCode(length int) (string, error) { return string(result), nil } +// @Summary Create an invite +// @Description Create an invite code for a server +// @Tags invites +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param serverID path string true "Server ID" +// @Param body body createInviteRequest false "Invite options" +// @Success 201 {object} inviteResponse +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Router /servers/{serverID}/invites [post] func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { serverID := chi.URLParam(r, "serverID") userID, ok := middleware.UserIDFromContext(r.Context()) @@ -163,6 +175,14 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(invite) } +// @Summary Get invite info +// @Description Get information about an invite by code +// @Tags invites +// @Produce json +// @Param code path string true "Invite code" +// @Success 200 {object} inviteResponse +// @Failure 404 {object} map[string]string +// @Router /invites/{code} [get] func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { code := chi.URLParam(r, "code") @@ -197,6 +217,18 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(invite) } +// @Summary Join server via invite +// @Description Join a server using an invite code +// @Tags invites +// @Produce json +// @Security SessionAuth +// @Param code path string true "Invite code" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 409 {object} map[string]string +// @Router /invites/{code}/join [post] func (h *Handler) Join(w http.ResponseWriter, r *http.Request) { code := chi.URLParam(r, "code") userID, ok := middleware.UserIDFromContext(r.Context()) diff --git a/internal/message/handlers.go b/internal/message/handlers.go index 39e0529..4aabf2e 100644 --- a/internal/message/handlers.go +++ b/internal/message/handlers.go @@ -61,6 +61,19 @@ type createMessageRequest struct { ReplyTo *string `json:"reply_to,omitempty"` } +// @Summary Create a message +// @Description Send a message to a channel +// @Tags messages +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Param body body createMessageRequest true "Message data" +// @Success 201 {object} messageResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Router /channels/{channelID}/messages [post] func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { channelID := chi.URLParam(r, "channelID") userID, ok := h.requireChannelAccess(w, r, channelID) @@ -129,6 +142,20 @@ type updateMessageRequest struct { Content string `json:"content"` } +// @Summary Update a message +// @Description Edit a message (author only) +// @Tags messages +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Param messageID path string true "Message ID" +// @Param body body updateMessageRequest true "Message update data" +// @Success 200 {object} messageResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /channels/{channelID}/messages/{messageID} [patch] func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { messageID := chi.URLParam(r, "messageID") userID, ok := middleware.UserIDFromContext(r.Context()) @@ -188,6 +215,16 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(msg) } +// @Summary Delete a message +// @Description Delete a message (author only) +// @Tags messages +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Param messageID path string true "Message ID" +// @Success 204 +// @Failure 401 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /channels/{channelID}/messages/{messageID} [delete] func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { messageID := chi.URLParam(r, "messageID") userID, ok := middleware.UserIDFromContext(r.Context()) @@ -221,6 +258,18 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// @Summary List messages +// @Description List messages in a channel with pagination +// @Tags messages +// @Produce json +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Param limit query int false "Max messages to return (1-100, default 50)" +// @Param before query string false "Message ID to fetch messages before" +// @Success 200 {array} messageResponse +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Router /channels/{channelID}/messages [get] func (h *Handler) List(w http.ResponseWriter, r *http.Request) { channelID := chi.URLParam(r, "channelID") userID, ok := h.requireChannelAccess(w, r, channelID) diff --git a/internal/push/handlers.go b/internal/push/handlers.go index 8243791..254b17e 100644 --- a/internal/push/handlers.go +++ b/internal/push/handlers.go @@ -37,11 +37,28 @@ func (h *Handler) RegisterRoutes(r *http.ServeMux) { r.HandleFunc("GET /push/vapid-public-key", h.GetPublicKey) } +// @Summary Get VAPID public key +// @Description Get the VAPID public key for push notification subscriptions +// @Tags push +// @Produce json +// @Success 200 {object} map[string]string +// @Router /push/vapid-public-key [get] func (h *Handler) GetPublicKey(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{"publicKey": h.vapidPub}) } +// @Summary Subscribe to push notifications +// @Description Subscribe to push notifications +// @Tags push +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param body body object true "Push subscription with endpoint, p256dh, and auth" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Router /push/subscribe [post] func (h *Handler) Subscribe(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -75,6 +92,17 @@ func (h *Handler) Subscribe(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{"status": "subscribed"}) } +// @Summary Unsubscribe from push notifications +// @Description Unsubscribe from push notifications +// @Tags push +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param body body object true "Endpoint to unsubscribe" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Router /push/unsubscribe [post] func (h *Handler) Unsubscribe(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { diff --git a/internal/reaction/handlers.go b/internal/reaction/handlers.go index 40d4fdd..476d2e0 100644 --- a/internal/reaction/handlers.go +++ b/internal/reaction/handlers.go @@ -99,6 +99,19 @@ func (h *Handler) requireMessageAccess(w http.ResponseWriter, r *http.Request, m return userID, channelID, serverID, true } +// @Summary Add a reaction +// @Description Add a reaction to a message +// @Tags reactions +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param messageID path string true "Message ID" +// @Param body body addReactionRequest true "Reaction data" +// @Success 201 {object} reactionResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Router /messages/{messageID}/reactions [post] func (h *Handler) Add(w http.ResponseWriter, r *http.Request) { messageID := chi.URLParam(r, "messageID") userID, channelID, _, ok := h.requireMessageAccess(w, r, messageID) @@ -146,6 +159,17 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(reaction) } +// @Summary Remove a reaction +// @Description Remove a reaction from a message +// @Tags reactions +// @Security SessionAuth +// @Param messageID path string true "Message ID" +// @Param emoji path string true "Emoji to remove" +// @Success 204 +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /messages/{messageID}/reactions/{emoji} [delete] func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) { messageID := chi.URLParam(r, "messageID") emoji := chi.URLParam(r, "emoji") @@ -185,6 +209,16 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// @Summary List reactions +// @Description List reactions on a message grouped by emoji +// @Tags reactions +// @Produce json +// @Security SessionAuth +// @Param messageID path string true "Message ID" +// @Success 200 {array} emojiGroup +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Router /messages/{messageID}/reactions [get] func (h *Handler) List(w http.ResponseWriter, r *http.Request) { messageID := chi.URLParam(r, "messageID") if _, _, _, ok := h.requireMessageAccess(w, r, messageID); !ok { diff --git a/internal/voice/handlers.go b/internal/voice/handlers.go index 24db296..dc625fa 100644 --- a/internal/voice/handlers.go +++ b/internal/voice/handlers.go @@ -39,6 +39,18 @@ type tokenResponse struct { LiveKitURL string `json:"livekit_url"` } +// @Summary Get voice token +// @Description Get a LiveKit access token for a voice room +// @Tags voice +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param body body tokenRequest true "Room data" +// @Success 200 {object} tokenResponse +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /voice/token [post] func (h *Handler) GetToken(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { @@ -94,6 +106,15 @@ func (h *Handler) GetToken(w http.ResponseWriter, r *http.Request) { }) } +// @Summary Get voice room participants +// @Description Get participants in a voice room +// @Tags voice +// @Produce json +// @Param roomID path string true "Room ID" +// @Success 200 {array} object +// @Failure 400 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Router /voice/rooms/{roomID}/participants [get] func (h *Handler) GetParticipants(w http.ResponseWriter, r *http.Request) { roomID := chi.URLParam(r, "roomID") if roomID == "" { diff --git a/internal/webhook/handlers.go b/internal/webhook/handlers.go index 23197cd..4f057da 100644 --- a/internal/webhook/handlers.go +++ b/internal/webhook/handlers.go @@ -82,6 +82,20 @@ func writeErr(w http.ResponseWriter, status int, msg string) { // ---- handlers ---- +// @Summary Create a webhook +// @Description Create a new webhook for a channel +// @Tags webhooks +// @Accept json +// @Produce json +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Param body body createWebhookRequest true "Webhook data" +// @Success 201 {object} webhookWithToken +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /channels/{channelID}/webhooks [post] // Create creates a new webhook for a channel. func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) @@ -148,6 +162,17 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusCreated, wh) } +// @Summary List webhooks +// @Description List webhooks for a channel +// @Tags webhooks +// @Produce json +// @Security SessionAuth +// @Param channelID path string true "Channel ID" +// @Success 200 {array} webhookResponse +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /channels/{channelID}/webhooks [get] // List returns all webhooks for a channel. func (h *Handler) List(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) @@ -211,6 +236,16 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, webhooks) } +// @Summary Delete a webhook +// @Description Delete a webhook +// @Tags webhooks +// @Security SessionAuth +// @Param webhookID path string true "Webhook ID" +// @Success 204 +// @Failure 401 {object} map[string]string +// @Failure 403 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /webhooks/{webhookID} [delete] // Delete removes a webhook. func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) @@ -247,6 +282,19 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// @Summary Execute a webhook +// @Description Execute a webhook to send a message (no auth required) +// @Tags webhooks +// @Accept json +// @Produce json +// @Param webhookID path string true "Webhook ID" +// @Param token path string true "Webhook token" +// @Param body body executeWebhookRequest true "Message data" +// @Success 201 {object} map[string]interface{} +// @Failure 400 {object} map[string]string +// @Failure 401 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Router /webhooks/{webhookID}/{token} [post] // Execute sends a message to a channel via webhook (no auth required). func (h *Handler) Execute(w http.ResponseWriter, r *http.Request) { webhookID := chi.URLParam(r, "webhookID")