From a170085b89a2527705c53606c392555c5ebaac11 Mon Sep 17 00:00:00 2001 From: Kharec Date: Mon, 10 Nov 2025 21:44:48 +0100 Subject: [PATCH] feat: update swagger using script --- docs/docs.go | 165 +- docs/swagger.json | 3999 ++++++++++++++++++++++++--------------------- docs/swagger.yaml | 1041 ++++++------ 3 files changed, 2723 insertions(+), 2482 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 85be32d..2de1690 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -14,8 +14,8 @@ const docTemplate = `{ "email": "sandro@cazzaniga.fr" }, "license": { - "name": "MIT", - "url": "https://opensource.org/licenses/MIT" + "name": "GPLv3", + "url": "https://www.gnu.org/licenses/gpl-3.0.html" }, "version": "{{.Version}}" }, @@ -24,7 +24,7 @@ const docTemplate = `{ "paths": { "/api": { "get": { - "description": "Get information about the API endpoints and version", + "description": "Retrieve API metadata, available endpoints, authentication details, and response formats", "consumes": [ "application/json" ], @@ -45,7 +45,7 @@ const docTemplate = `{ } } }, - "/auth/account": { + "/api/auth/account": { "delete": { "security": [ { @@ -91,7 +91,7 @@ const docTemplate = `{ } } }, - "/auth/account/confirm": { + "/api/auth/account/confirm": { "post": { "description": "Confirm account deletion using the provided token", "consumes": [ @@ -143,7 +143,7 @@ const docTemplate = `{ } } }, - "/auth/confirm": { + "/api/auth/confirm": { "get": { "description": "Confirm user email with verification token", "consumes": [ @@ -187,7 +187,7 @@ const docTemplate = `{ } } }, - "/auth/email": { + "/api/auth/email": { "put": { "security": [ { @@ -256,7 +256,7 @@ const docTemplate = `{ } } }, - "/auth/forgot-password": { + "/api/auth/forgot-password": { "post": { "description": "Send a password reset email using a username or email", "consumes": [ @@ -296,7 +296,7 @@ const docTemplate = `{ } } }, - "/auth/login": { + "/api/auth/login": { "post": { "description": "Authenticate user with username and password", "consumes": [ @@ -354,7 +354,7 @@ const docTemplate = `{ } } }, - "/auth/logout": { + "/api/auth/logout": { "post": { "security": [ { @@ -388,7 +388,7 @@ const docTemplate = `{ } } }, - "/auth/me": { + "/api/auth/me": { "get": { "security": [ { @@ -428,7 +428,7 @@ const docTemplate = `{ } } }, - "/auth/password": { + "/api/auth/password": { "put": { "security": [ { @@ -485,7 +485,7 @@ const docTemplate = `{ } } }, - "/auth/refresh": { + "/api/auth/refresh": { "post": { "description": "Use a refresh token to get a new access token. This endpoint allows clients to obtain a new access token using a valid refresh token without requiring user credentials.", "consumes": [ @@ -543,7 +543,7 @@ const docTemplate = `{ } } }, - "/auth/register": { + "/api/auth/register": { "post": { "description": "Register a new user with username, email and password", "consumes": [ @@ -595,7 +595,7 @@ const docTemplate = `{ } } }, - "/auth/resend-verification": { + "/api/auth/resend-verification": { "post": { "description": "Send a new verification email to the provided address", "consumes": [ @@ -665,7 +665,7 @@ const docTemplate = `{ } } }, - "/auth/reset-password": { + "/api/auth/reset-password": { "post": { "description": "Reset a user's password using a reset token", "consumes": [ @@ -711,7 +711,7 @@ const docTemplate = `{ } } }, - "/auth/revoke": { + "/api/auth/revoke": { "post": { "security": [ { @@ -768,7 +768,7 @@ const docTemplate = `{ } } }, - "/auth/revoke-all": { + "/api/auth/revoke-all": { "post": { "security": [ { @@ -808,7 +808,7 @@ const docTemplate = `{ } } }, - "/auth/username": { + "/api/auth/username": { "put": { "security": [ { @@ -871,55 +871,7 @@ const docTemplate = `{ } } }, - "/health": { - "get": { - "description": "Check if the API is healthy with comprehensive database monitoring", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "api" - ], - "summary": "Health check", - "responses": { - "200": { - "description": "Health check successful", - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - } - }, - "/metrics": { - "get": { - "description": "Get application metrics including post stats, user stats, vote stats, and database performance metrics", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "api" - ], - "summary": "Get metrics", - "responses": { - "200": { - "description": "Application metrics with vote statistics and database monitoring", - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - } - }, - "/posts": { + "/api/posts": { "get": { "description": "Get a list of posts with pagination. Posts include vote statistics (up_votes, down_votes, score) and current user's vote status.", "consumes": [ @@ -1037,7 +989,7 @@ const docTemplate = `{ } } }, - "/posts/search": { + "/api/posts/search": { "get": { "description": "Search posts by title or content keywords. Results include vote statistics and current user's vote status.", "consumes": [ @@ -1094,7 +1046,7 @@ const docTemplate = `{ } } }, - "/posts/title": { + "/api/posts/title": { "get": { "description": "Fetch the HTML title for the provided URL", "consumes": [ @@ -1144,7 +1096,7 @@ const docTemplate = `{ } } }, - "/posts/{id}": { + "/api/posts/{id}": { "get": { "description": "Get a post by ID with vote statistics and current user's vote status", "consumes": [ @@ -1333,7 +1285,7 @@ const docTemplate = `{ } } }, - "/posts/{id}/vote": { + "/api/posts/{id}/vote": { "get": { "security": [ { @@ -1515,7 +1467,7 @@ const docTemplate = `{ } } }, - "/posts/{id}/votes": { + "/api/posts/{id}/votes": { "get": { "security": [ { @@ -1570,7 +1522,7 @@ const docTemplate = `{ } } }, - "/users": { + "/api/users": { "get": { "security": [ { @@ -1687,7 +1639,7 @@ const docTemplate = `{ } } }, - "/users/{id}": { + "/api/users/{id}": { "get": { "security": [ { @@ -1748,7 +1700,7 @@ const docTemplate = `{ } } }, - "/users/{id}/posts": { + "/api/users/{id}/posts": { "get": { "security": [ { @@ -1816,6 +1768,52 @@ const docTemplate = `{ } } } + }, + "/health": { + "get": { + "description": "Check the API health status along with database connectivity details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api" + ], + "summary": "Health check", + "responses": { + "200": { + "description": "Health check successful", + "schema": { + "$ref": "#/definitions/handlers.CommonResponse" + } + } + } + } + }, + "/metrics": { + "get": { + "description": "Retrieve application metrics including aggregate counts and database performance data", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api" + ], + "summary": "Get metrics", + "responses": { + "200": { + "description": "Application metrics retrieved successfully", + "schema": { + "$ref": "#/definitions/handlers.CommonResponse" + } + } + } + } } }, "definitions": { @@ -1906,6 +1904,21 @@ const docTemplate = `{ } } }, + "handlers.CommonResponse": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, "handlers.ConfirmAccountDeletionRequest": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index febf5d8..96ca370 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,1892 +1,2119 @@ { - "schemes": ["http"], - "swagger": "2.0", - "info": { - "description": "Goyco is a Y Combinator-style news aggregation platform API.", - "title": "Goyco API", - "contact": { - "name": "Goyco Team", - "email": "sandro@cazzaniga.fr" + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "description": "Goyco is a Y Combinator-style news aggregation platform API.", + "title": "Goyco API", + "contact": { + "name": "Goyco Team", + "email": "sandro@cazzaniga.fr" + }, + "license": { + "name": "GPLv3", + "url": "https://www.gnu.org/licenses/gpl-3.0.html" + }, + "version": "0.1.0" }, - "license": { - "name": "MIT", - "url": "https://opensource.org/licenses/MIT" + "host": "localhost:8080", + "basePath": "/api", + "paths": { + "/api": { + "get": { + "description": "Retrieve API metadata, available endpoints, authentication details, and response formats", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api" + ], + "summary": "Get API information", + "responses": { + "200": { + "description": "API information retrieved successfully", + "schema": { + "$ref": "#/definitions/handlers.APIInfo" + } + } + } + } + }, + "/api/auth/account": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Initiate the deletion process for the authenticated user's account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Request account deletion", + "responses": { + "200": { + "description": "Deletion email sent", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "503": { + "description": "Email delivery unavailable", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/account/confirm": { + "post": { + "description": "Confirm account deletion using the provided token", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Confirm account deletion", + "parameters": [ + { + "description": "Account deletion data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.ConfirmAccountDeletionRequest" + } + } + ], + "responses": { + "200": { + "description": "Account deleted successfully", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Invalid or expired token", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "503": { + "description": "Email delivery unavailable", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/confirm": { + "get": { + "description": "Confirm user email with verification token", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Confirm email address", + "parameters": [ + { + "type": "string", + "description": "Email verification token", + "name": "token", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "Email confirmed successfully", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Invalid or missing token", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/email": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the authenticated user's email address", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Update email address", + "parameters": [ + { + "description": "New email address", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.UpdateEmailRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/forgot-password": { + "post": { + "description": "Send a password reset email using a username or email", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Request a password reset", + "parameters": [ + { + "description": "Username or email", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.ForgotPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "Password reset email sent if account exists", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Invalid request data", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/login": { + "post": { + "description": "Authenticate user with username and password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login user", + "parameters": [ + { + "description": "Login credentials", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.LoginRequest" + } + } + ], + "responses": { + "200": { + "description": "Authentication successful", + "schema": { + "$ref": "#/definitions/handlers.AuthTokensResponse" + } + }, + "400": { + "description": "Invalid request data or validation failed", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Invalid credentials", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "403": { + "description": "Account is locked", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/logout": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Logout the authenticated user and invalidate their session", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout user", + "responses": { + "200": { + "description": "Logged out successfully", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/me": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve the authenticated user's profile information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current user profile", + "responses": { + "200": { + "description": "User profile retrieved successfully", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "404": { + "description": "User not found", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/password": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the authenticated user's password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Update password", + "parameters": [ + { + "description": "Password update data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.UpdatePasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/refresh": { + "post": { + "description": "Use a refresh token to get a new access token. This endpoint allows clients to obtain a new access token using a valid refresh token without requiring user credentials.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Refresh access token", + "parameters": [ + { + "description": "Refresh token data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.RefreshTokenRequest" + } + } + ], + "responses": { + "200": { + "description": "Token refreshed successfully", + "schema": { + "$ref": "#/definitions/handlers.AuthTokensResponse" + } + }, + "400": { + "description": "Invalid request body or missing refresh token", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Invalid or expired refresh token", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "403": { + "description": "Account is locked", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/register": { + "post": { + "description": "Register a new user with username, email and password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Register a new user", + "parameters": [ + { + "description": "Registration data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.RegisterRequest" + } + } + ], + "responses": { + "201": { + "description": "Registration successful", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Invalid request data or validation failed", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "409": { + "description": "Username or email already exists", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/resend-verification": { + "post": { + "description": "Send a new verification email to the provided address", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Resend verification email", + "parameters": [ + { + "description": "Email address", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.ResendVerificationRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "429": { + "description": "Too Many Requests", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/reset-password": { + "post": { + "description": "Reset a user's password using a reset token", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Reset password", + "parameters": [ + { + "description": "Password reset data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.ResetPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "Password reset successfully", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Invalid or expired token, or validation failed", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/revoke": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Revoke a specific refresh token. This endpoint allows authenticated users to invalidate a specific refresh token, preventing its future use.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Revoke refresh token", + "parameters": [ + { + "description": "Token revocation data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.RevokeTokenRequest" + } + } + ], + "responses": { + "200": { + "description": "Token revoked successfully", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Invalid request body or missing refresh token", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Invalid or expired access token", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/revoke-all": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Revoke all refresh tokens for the authenticated user. This endpoint allows users to invalidate all their refresh tokens at once, effectively logging them out from all devices.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Revoke all user tokens", + "responses": { + "200": { + "description": "All tokens revoked successfully", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Invalid or expired access token", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/auth/username": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the authenticated user's username", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Update username", + "parameters": [ + { + "description": "New username", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.UpdateUsernameRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.AuthResponse" + } + } + } + } + }, + "/api/posts": { + "get": { + "description": "Get a list of posts with pagination. Posts include vote statistics (up_votes, down_votes, score) and current user's vote status.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "posts" + ], + "summary": "Get posts", + "parameters": [ + { + "type": "integer", + "default": 20, + "description": "Number of posts to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Number of posts to skip", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Posts retrieved successfully with vote statistics", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "400": { + "description": "Invalid pagination parameters", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new post with URL and optional title", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "posts" + ], + "summary": "Create a new post", + "parameters": [ + { + "description": "Post data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.CreatePostRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "400": { + "description": "Invalid request data or validation failed", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "409": { + "description": "URL already submitted", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "502": { + "description": "Failed to fetch title from URL", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + } + } + } + }, + "/api/posts/search": { + "get": { + "description": "Search posts by title or content keywords. Results include vote statistics and current user's vote status.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "posts" + ], + "summary": "Search posts", + "parameters": [ + { + "type": "string", + "description": "Search term", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Number of posts to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Number of posts to skip", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Search results with vote statistics", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "400": { + "description": "Invalid search parameters", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + } + } + } + }, + "/api/posts/title": { + "get": { + "description": "Fetch the HTML title for the provided URL", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "posts" + ], + "summary": "Fetch title from URL", + "parameters": [ + { + "type": "string", + "description": "URL to inspect", + "name": "url", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "Title fetched successfully", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "400": { + "description": "Invalid URL or URL parameter missing", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "501": { + "description": "Title fetching is not available", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "502": { + "description": "Failed to fetch title from URL", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + } + } + } + }, + "/api/posts/{id}": { + "get": { + "description": "Get a post by ID with vote statistics and current user's vote status", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "posts" + ], + "summary": "Get a single post", + "parameters": [ + { + "type": "integer", + "description": "Post ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Post retrieved successfully with vote statistics", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "400": { + "description": "Invalid post ID", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "404": { + "description": "Post not found", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the title and content of a post owned by the authenticated user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "posts" + ], + "summary": "Update a post", + "parameters": [ + { + "type": "integer", + "description": "Post ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Post update data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.UpdatePostRequest" + } + } + ], + "responses": { + "200": { + "description": "Post updated successfully", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "400": { + "description": "Invalid request data or validation failed", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "403": { + "description": "Not authorized to update this post", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "404": { + "description": "Post not found", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a post owned by the authenticated user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "posts" + ], + "summary": "Delete a post", + "parameters": [ + { + "type": "integer", + "description": "Post ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Post deleted successfully", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "400": { + "description": "Invalid post ID", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "403": { + "description": "Not authorized to delete this post", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "404": { + "description": "Post not found", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.PostResponse" + } + } + } + } + }, + "/api/posts/{id}/vote": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve the current user's vote for a specific post. Requires authentication and returns the vote type if it exists.\n\n**Response:**\n- If vote exists: Returns vote details with contextual metadata (including `is_anonymous`)\n- If no vote: Returns success with null vote data and metadata", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "votes" + ], + "summary": "Get current user's vote", + "parameters": [ + { + "type": "integer", + "description": "Post ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "No vote found for this user/post combination", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "400": { + "description": "Invalid post ID", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Vote on a post (upvote, downvote, or remove vote). Authentication is required; the vote is performed on behalf of the current user.\n\n**Vote Types:**\n- `up`: Upvote the post\n- `down`: Downvote the post\n- `none`: Remove existing vote\n\n**Response includes:**\n- Updated post vote counts (up_votes, down_votes, score)\n- Success message", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "votes" + ], + "summary": "Cast a vote on a post", + "parameters": [ + { + "type": "integer", + "description": "Post ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Vote data (type: 'up', 'down', or 'none' to remove)", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.VoteRequest" + } + } + ], + "responses": { + "200": { + "description": "Vote cast successfully with updated post statistics", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "400": { + "description": "Invalid request data or vote type", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "404": { + "description": "Post not found", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a vote from a post for the authenticated user. This is equivalent to casting a vote with type 'none'.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "votes" + ], + "summary": "Remove a vote", + "parameters": [ + { + "type": "integer", + "description": "Post ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Vote removed successfully with updated post statistics", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "400": { + "description": "Invalid post ID", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "404": { + "description": "Post not found", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + } + } + } + }, + "/api/posts/{id}/votes": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve all votes for a specific post. Returns all votes in a single format.\n\n**Authentication Required:** Yes (Bearer token)\n\n**Response includes:**\n- Array of all votes\n- Total vote count\n- Each vote includes type and unauthenticated status", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "votes" + ], + "summary": "Get post votes", + "parameters": [ + { + "type": "integer", + "description": "Post ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Votes retrieved successfully with count", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "400": { + "description": "Invalid post ID", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.VoteResponse" + } + } + } + } + }, + "/api/users": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a paginated list of users", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List users", + "parameters": [ + { + "type": "integer", + "default": 20, + "description": "Number of users to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Number of users to skip", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Users retrieved successfully", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new user account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Create user", + "parameters": [ + { + "description": "User data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.RegisterRequest" + } + } + ], + "responses": { + "201": { + "description": "User created successfully", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "400": { + "description": "Invalid request data or validation failed", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "409": { + "description": "Username or email already exists", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + } + } + } + }, + "/api/users/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a specific user by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "User retrieved successfully", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "400": { + "description": "Invalid user ID", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "404": { + "description": "User not found", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + } + } + } + }, + "/api/users/{id}/posts": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve posts created by a specific user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user posts", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "default": 20, + "description": "Number of posts to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Number of posts to skip", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "User posts retrieved successfully", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "400": { + "description": "Invalid user ID or pagination parameters", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/handlers.UserResponse" + } + } + } + } + }, + "/health": { + "get": { + "description": "Check the API health status along with database connectivity details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api" + ], + "summary": "Health check", + "responses": { + "200": { + "description": "Health check successful", + "schema": { + "$ref": "#/definitions/handlers.CommonResponse" + } + } + } + } + }, + "/metrics": { + "get": { + "description": "Retrieve application metrics including aggregate counts and database performance data", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api" + ], + "summary": "Get metrics", + "responses": { + "200": { + "description": "Application metrics retrieved successfully", + "schema": { + "$ref": "#/definitions/handlers.CommonResponse" + } + } + } + } + } }, - "version": "0.1.0" - }, - "host": "localhost:8080", - "basePath": "/api", - "paths": { - "/api": { - "get": { - "description": "Get information about the API endpoints and version", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["api"], - "summary": "Get API information", - "responses": { - "200": { - "description": "API information retrieved successfully", - "schema": { - "$ref": "#/definitions/handlers.APIInfo" + "definitions": { + "handlers.APIInfo": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "handlers.AuthResponse": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "handlers.AuthTokensDetail": { + "type": "object", + "properties": { + "access_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + }, + "refresh_token": { + "type": "string", + "example": "f94d4ddc7d9b4fcb9d3a2c44c400b780" + }, + "user": { + "$ref": "#/definitions/handlers.AuthUserSummary" + } + } + }, + "handlers.AuthTokensResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/handlers.AuthTokensDetail" + }, + "message": { + "type": "string", + "example": "Authentication successful" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "handlers.AuthUserSummary": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "jane@example.com" + }, + "email_verified": { + "type": "boolean", + "example": true + }, + "id": { + "type": "integer", + "example": 42 + }, + "locked": { + "type": "boolean", + "example": false + }, + "username": { + "type": "string", + "example": "janedoe" + } + } + }, + "handlers.CommonResponse": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "handlers.ConfirmAccountDeletionRequest": { + "type": "object", + "properties": { + "delete_posts": { + "type": "boolean" + }, + "token": { + "type": "string" + } + } + }, + "handlers.CreatePostRequest": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "handlers.ForgotPasswordRequest": { + "type": "object", + "properties": { + "username_or_email": { + "type": "string" + } + } + }, + "handlers.LoginRequest": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "handlers.PostResponse": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "handlers.RefreshTokenRequest": { + "type": "object", + "required": [ + "refresh_token" + ], + "properties": { + "refresh_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + } + }, + "handlers.RegisterRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "handlers.ResendVerificationRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + } + } + }, + "handlers.ResetPasswordRequest": { + "type": "object", + "properties": { + "new_password": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "handlers.RevokeTokenRequest": { + "type": "object", + "required": [ + "refresh_token" + ], + "properties": { + "refresh_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + } + }, + "handlers.UpdateEmailRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + } + } + }, + "handlers.UpdatePasswordRequest": { + "type": "object", + "properties": { + "current_password": { + "type": "string" + }, + "new_password": { + "type": "string" + } + } + }, + "handlers.UpdatePostRequest": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "handlers.UpdateUsernameRequest": { + "type": "object", + "properties": { + "username": { + "type": "string" + } + } + }, + "handlers.UserResponse": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "handlers.VoteRequest": { + "description": "Vote request with type field. All votes are handled the same way.", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "up", + "down", + "none" + ], + "example": "up" + } + } + }, + "handlers.VoteResponse": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } } - } } - } - }, - "/auth/account": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Initiate the deletion process for the authenticated user's account", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Request account deletion", - "responses": { - "200": { - "description": "Deletion email sent", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "503": { - "description": "Email delivery unavailable", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/account/confirm": { - "post": { - "description": "Confirm account deletion using the provided token", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Confirm account deletion", - "parameters": [ - { - "description": "Account deletion data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.ConfirmAccountDeletionRequest" - } - } - ], - "responses": { - "200": { - "description": "Account deleted successfully", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Invalid or expired token", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "503": { - "description": "Email delivery unavailable", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/confirm": { - "get": { - "description": "Confirm user email with verification token", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Confirm email address", - "parameters": [ - { - "type": "string", - "description": "Email verification token", - "name": "token", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "Email confirmed successfully", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Invalid or missing token", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/email": { - "put": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Update the authenticated user's email address", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Update email address", - "parameters": [ - { - "description": "New email address", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.UpdateEmailRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "409": { - "description": "Conflict", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "503": { - "description": "Service Unavailable", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/forgot-password": { - "post": { - "description": "Send a password reset email using a username or email", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Request a password reset", - "parameters": [ - { - "description": "Username or email", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.ForgotPasswordRequest" - } - } - ], - "responses": { - "200": { - "description": "Password reset email sent if account exists", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Invalid request data", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/login": { - "post": { - "description": "Authenticate user with username and password", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Login user", - "parameters": [ - { - "description": "Login credentials", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.LoginRequest" - } - } - ], - "responses": { - "200": { - "description": "Authentication successful", - "schema": { - "$ref": "#/definitions/handlers.AuthTokensResponse" - } - }, - "400": { - "description": "Invalid request data or validation failed", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Invalid credentials", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "403": { - "description": "Account is locked", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/logout": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Logout the authenticated user and invalidate their session", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Logout user", - "responses": { - "200": { - "description": "Logged out successfully", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/me": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve the authenticated user's profile information", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Get current user profile", - "responses": { - "200": { - "description": "User profile retrieved successfully", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "404": { - "description": "User not found", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/password": { - "put": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Update the authenticated user's password", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Update password", - "parameters": [ - { - "description": "Password update data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.UpdatePasswordRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/refresh": { - "post": { - "description": "Use a refresh token to get a new access token. This endpoint allows clients to obtain a new access token using a valid refresh token without requiring user credentials.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Refresh access token", - "parameters": [ - { - "description": "Refresh token data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.RefreshTokenRequest" - } - } - ], - "responses": { - "200": { - "description": "Token refreshed successfully", - "schema": { - "$ref": "#/definitions/handlers.AuthTokensResponse" - } - }, - "400": { - "description": "Invalid request body or missing refresh token", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Invalid or expired refresh token", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "403": { - "description": "Account is locked", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/register": { - "post": { - "description": "Register a new user with username, email and password", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Register a new user", - "parameters": [ - { - "description": "Registration data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.RegisterRequest" - } - } - ], - "responses": { - "201": { - "description": "Registration successful", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Invalid request data or validation failed", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "409": { - "description": "Username or email already exists", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/resend-verification": { - "post": { - "description": "Send a new verification email to the provided address", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Resend verification email", - "parameters": [ - { - "description": "Email address", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.ResendVerificationRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "409": { - "description": "Conflict", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "429": { - "description": "Too Many Requests", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "503": { - "description": "Service Unavailable", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/reset-password": { - "post": { - "description": "Reset a user's password using a reset token", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Reset password", - "parameters": [ - { - "description": "Password reset data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.ResetPasswordRequest" - } - } - ], - "responses": { - "200": { - "description": "Password reset successfully", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Invalid or expired token, or validation failed", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/revoke": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Revoke a specific refresh token. This endpoint allows authenticated users to invalidate a specific refresh token, preventing its future use.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Revoke refresh token", - "parameters": [ - { - "description": "Token revocation data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.RevokeTokenRequest" - } - } - ], - "responses": { - "200": { - "description": "Token revoked successfully", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Invalid request body or missing refresh token", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Invalid or expired access token", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/revoke-all": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Revoke all refresh tokens for the authenticated user. This endpoint allows users to invalidate all their refresh tokens at once, effectively logging them out from all devices.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Revoke all user tokens", - "responses": { - "200": { - "description": "All tokens revoked successfully", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Invalid or expired access token", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/auth/username": { - "put": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Update the authenticated user's username", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["auth"], - "summary": "Update username", - "parameters": [ - { - "description": "New username", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.UpdateUsernameRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "409": { - "description": "Conflict", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/handlers.AuthResponse" - } - } - } - } - }, - "/health": { - "get": { - "description": "Check if the API is healthy with comprehensive database monitoring", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["api"], - "summary": "Health check", - "responses": { - "200": { - "description": "Health check successful", - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - } - }, - "/metrics": { - "get": { - "description": "Get application metrics including post stats, user stats, vote stats, and database performance metrics", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["api"], - "summary": "Get metrics", - "responses": { - "200": { - "description": "Application metrics with vote statistics and database monitoring", - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - } - }, - "/posts": { - "get": { - "description": "Get a list of posts with pagination. Posts include vote statistics (up_votes, down_votes, score) and current user's vote status.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["posts"], - "summary": "Get posts", - "parameters": [ - { - "type": "integer", - "default": 20, - "description": "Number of posts to return", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "Number of posts to skip", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Posts retrieved successfully with vote statistics", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "400": { - "description": "Invalid pagination parameters", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Create a new post with URL and optional title", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["posts"], - "summary": "Create a new post", - "parameters": [ - { - "description": "Post data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreatePostRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "400": { - "description": "Invalid request data or validation failed", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "409": { - "description": "URL already submitted", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "502": { - "description": "Failed to fetch title from URL", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - } - } - } - }, - "/posts/search": { - "get": { - "description": "Search posts by title or content keywords. Results include vote statistics and current user's vote status.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["posts"], - "summary": "Search posts", - "parameters": [ - { - "type": "string", - "description": "Search term", - "name": "q", - "in": "query" - }, - { - "type": "integer", - "default": 20, - "description": "Number of posts to return", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "Number of posts to skip", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Search results with vote statistics", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "400": { - "description": "Invalid search parameters", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - } - } - } - }, - "/posts/title": { - "get": { - "description": "Fetch the HTML title for the provided URL", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["posts"], - "summary": "Fetch title from URL", - "parameters": [ - { - "type": "string", - "description": "URL to inspect", - "name": "url", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "Title fetched successfully", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "400": { - "description": "Invalid URL or URL parameter missing", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "501": { - "description": "Title fetching is not available", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "502": { - "description": "Failed to fetch title from URL", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - } - } - } - }, - "/posts/{id}": { - "get": { - "description": "Get a post by ID with vote statistics and current user's vote status", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["posts"], - "summary": "Get a single post", - "parameters": [ - { - "type": "integer", - "description": "Post ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Post retrieved successfully with vote statistics", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "400": { - "description": "Invalid post ID", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "404": { - "description": "Post not found", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - } - } - }, - "put": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Update the title and content of a post owned by the authenticated user", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["posts"], - "summary": "Update a post", - "parameters": [ - { - "type": "integer", - "description": "Post ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Post update data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.UpdatePostRequest" - } - } - ], - "responses": { - "200": { - "description": "Post updated successfully", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "400": { - "description": "Invalid request data or validation failed", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "403": { - "description": "Not authorized to update this post", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "404": { - "description": "Post not found", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - } - } - }, - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Delete a post owned by the authenticated user", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["posts"], - "summary": "Delete a post", - "parameters": [ - { - "type": "integer", - "description": "Post ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Post deleted successfully", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "400": { - "description": "Invalid post ID", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "403": { - "description": "Not authorized to delete this post", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "404": { - "description": "Post not found", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.PostResponse" - } - } - } - } - }, - "/posts/{id}/vote": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve the current user's vote for a specific post. Requires authentication and returns the vote type if it exists.\n\n**Response:**\n- If vote exists: Returns vote details with contextual metadata (including `is_anonymous`)\n- If no vote: Returns success with null vote data and metadata", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["votes"], - "summary": "Get current user's vote", - "parameters": [ - { - "type": "integer", - "description": "Post ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "No vote found for this user/post combination", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "400": { - "description": "Invalid post ID", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Vote on a post (upvote, downvote, or remove vote). Authentication is required; the vote is performed on behalf of the current user.\n\n**Vote Types:**\n- `up`: Upvote the post\n- `down`: Downvote the post\n- `none`: Remove existing vote\n\n**Response includes:**\n- Updated post vote counts (up_votes, down_votes, score)\n- Success message", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["votes"], - "summary": "Cast a vote on a post", - "parameters": [ - { - "type": "integer", - "description": "Post ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Vote data (type: 'up', 'down', or 'none' to remove)", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.VoteRequest" - } - } - ], - "responses": { - "200": { - "description": "Vote cast successfully with updated post statistics", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "400": { - "description": "Invalid request data or vote type", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "404": { - "description": "Post not found", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - } - } - }, - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Remove a vote from a post for the authenticated user. This is equivalent to casting a vote with type 'none'.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["votes"], - "summary": "Remove a vote", - "parameters": [ - { - "type": "integer", - "description": "Post ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Vote removed successfully with updated post statistics", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "400": { - "description": "Invalid post ID", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "404": { - "description": "Post not found", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - } - } - } - }, - "/posts/{id}/votes": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve all votes for a specific post. Returns all votes in a single format.\n\n**Authentication Required:** Yes (Bearer token)\n\n**Response includes:**\n- Array of all votes\n- Total vote count\n- Each vote includes type and unauthenticated status", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["votes"], - "summary": "Get post votes", - "parameters": [ - { - "type": "integer", - "description": "Post ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Votes retrieved successfully with count", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "400": { - "description": "Invalid post ID", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.VoteResponse" - } - } - } - } - }, - "/users": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve a paginated list of users", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["users"], - "summary": "List users", - "parameters": [ - { - "type": "integer", - "default": 20, - "description": "Number of users to return", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "Number of users to skip", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Users retrieved successfully", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Create a new user account", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["users"], - "summary": "Create user", - "parameters": [ - { - "description": "User data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.RegisterRequest" - } - } - ], - "responses": { - "201": { - "description": "User created successfully", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "400": { - "description": "Invalid request data or validation failed", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "409": { - "description": "Username or email already exists", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - } - } - } - }, - "/users/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve a specific user by ID", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["users"], - "summary": "Get user", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "User retrieved successfully", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "400": { - "description": "Invalid user ID", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "404": { - "description": "User not found", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - } - } - } - }, - "/users/{id}/posts": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve posts created by a specific user", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["users"], - "summary": "Get user posts", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "default": 20, - "description": "Number of posts to return", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "Number of posts to skip", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "User posts retrieved successfully", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "400": { - "description": "Invalid user ID or pagination parameters", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "401": { - "description": "Authentication required", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/handlers.UserResponse" - } - } - } - } } - }, - "definitions": { - "handlers.APIInfo": { - "type": "object", - "properties": { - "data": {}, - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "success": { - "type": "boolean" - } - } - }, - "handlers.AuthResponse": { - "type": "object", - "properties": { - "data": {}, - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "success": { - "type": "boolean" - } - } - }, - "handlers.AuthTokensDetail": { - "type": "object", - "properties": { - "access_token": { - "type": "string", - "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - }, - "refresh_token": { - "type": "string", - "example": "f94d4ddc7d9b4fcb9d3a2c44c400b780" - }, - "user": { - "$ref": "#/definitions/handlers.AuthUserSummary" - } - } - }, - "handlers.AuthTokensResponse": { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/handlers.AuthTokensDetail" - }, - "message": { - "type": "string", - "example": "Authentication successful" - }, - "success": { - "type": "boolean", - "example": true - } - } - }, - "handlers.AuthUserSummary": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "jane@example.com" - }, - "email_verified": { - "type": "boolean", - "example": true - }, - "id": { - "type": "integer", - "example": 42 - }, - "locked": { - "type": "boolean", - "example": false - }, - "username": { - "type": "string", - "example": "janedoe" - } - } - }, - "handlers.ConfirmAccountDeletionRequest": { - "type": "object", - "properties": { - "delete_posts": { - "type": "boolean" - }, - "token": { - "type": "string" - } - } - }, - "handlers.CreatePostRequest": { - "type": "object", - "properties": { - "content": { - "type": "string" - }, - "title": { - "type": "string" - }, - "url": { - "type": "string" - } - } - }, - "handlers.ForgotPasswordRequest": { - "type": "object", - "properties": { - "username_or_email": { - "type": "string" - } - } - }, - "handlers.LoginRequest": { - "type": "object", - "properties": { - "password": { - "type": "string" - }, - "username": { - "type": "string" - } - } - }, - "handlers.PostResponse": { - "type": "object", - "properties": { - "data": {}, - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "success": { - "type": "boolean" - } - } - }, - "handlers.RefreshTokenRequest": { - "type": "object", - "required": ["refresh_token"], - "properties": { - "refresh_token": { - "type": "string", - "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - } - }, - "handlers.RegisterRequest": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "password": { - "type": "string" - }, - "username": { - "type": "string" - } - } - }, - "handlers.ResendVerificationRequest": { - "type": "object", - "properties": { - "email": { - "type": "string" - } - } - }, - "handlers.ResetPasswordRequest": { - "type": "object", - "properties": { - "new_password": { - "type": "string" - }, - "token": { - "type": "string" - } - } - }, - "handlers.RevokeTokenRequest": { - "type": "object", - "required": ["refresh_token"], - "properties": { - "refresh_token": { - "type": "string", - "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - } - }, - "handlers.UpdateEmailRequest": { - "type": "object", - "properties": { - "email": { - "type": "string" - } - } - }, - "handlers.UpdatePasswordRequest": { - "type": "object", - "properties": { - "current_password": { - "type": "string" - }, - "new_password": { - "type": "string" - } - } - }, - "handlers.UpdatePostRequest": { - "type": "object", - "properties": { - "content": { - "type": "string" - }, - "title": { - "type": "string" - } - } - }, - "handlers.UpdateUsernameRequest": { - "type": "object", - "properties": { - "username": { - "type": "string" - } - } - }, - "handlers.UserResponse": { - "type": "object", - "properties": { - "data": {}, - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "success": { - "type": "boolean" - } - } - }, - "handlers.VoteRequest": { - "description": "Vote request with type field. All votes are handled the same way.", - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["up", "down", "none"], - "example": "up" - } - } - }, - "handlers.VoteResponse": { - "type": "object", - "properties": { - "data": {}, - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "success": { - "type": "boolean" - } - } - } - } -} +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 822cb07..de4d607 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -29,12 +29,12 @@ definitions: example: f94d4ddc7d9b4fcb9d3a2c44c400b780 type: string user: - $ref: "#/definitions/handlers.AuthUserSummary" + $ref: '#/definitions/handlers.AuthUserSummary' type: object handlers.AuthTokensResponse: properties: data: - $ref: "#/definitions/handlers.AuthTokensDetail" + $ref: '#/definitions/handlers.AuthTokensDetail' message: example: Authentication successful type: string @@ -60,6 +60,16 @@ definitions: example: janedoe type: string type: object + handlers.CommonResponse: + properties: + data: {} + error: + type: string + message: + type: string + success: + type: boolean + type: object handlers.ConfirmAccountDeletionRequest: properties: delete_posts: @@ -104,7 +114,7 @@ definitions: example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... type: string required: - - refresh_token + - refresh_token type: object handlers.RegisterRequest: properties: @@ -133,7 +143,7 @@ definitions: example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... type: string required: - - refresh_token + - refresh_token type: object handlers.UpdateEmailRequest: properties: @@ -174,9 +184,9 @@ definitions: properties: type: enum: - - up - - down - - none + - up + - down + - none example: up type: string type: object @@ -197,847 +207,808 @@ info: name: Goyco Team description: Goyco is a Y Combinator-style news aggregation platform API. license: - name: MIT - url: https://opensource.org/licenses/MIT + name: GPLv3 + url: https://www.gnu.org/licenses/gpl-3.0.html title: Goyco API version: 0.1.0 paths: /api: get: consumes: - - application/json - description: Get information about the API endpoints and version + - application/json + description: Retrieve API metadata, available endpoints, authentication details, + and response formats produces: - - application/json + - application/json responses: "200": description: API information retrieved successfully schema: - $ref: "#/definitions/handlers.APIInfo" + $ref: '#/definitions/handlers.APIInfo' summary: Get API information tags: - - api - /auth/account: + - api + /api/auth/account: delete: consumes: - - application/json + - application/json description: Initiate the deletion process for the authenticated user's account produces: - - application/json + - application/json responses: "200": description: Deletion email sent schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "503": description: Email delivery unavailable schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Request account deletion tags: - - auth - /auth/account/confirm: + - auth + /api/auth/account/confirm: post: consumes: - - application/json + - application/json description: Confirm account deletion using the provided token parameters: - - description: Account deletion data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.ConfirmAccountDeletionRequest" + - description: Account deletion data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.ConfirmAccountDeletionRequest' produces: - - application/json + - application/json responses: "200": description: Account deleted successfully schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Invalid or expired token schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "503": description: Email delivery unavailable schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' summary: Confirm account deletion tags: - - auth - /auth/confirm: + - auth + /api/auth/confirm: get: consumes: - - application/json + - application/json description: Confirm user email with verification token parameters: - - description: Email verification token - in: query - name: token - required: true - type: string + - description: Email verification token + in: query + name: token + required: true + type: string produces: - - application/json + - application/json responses: "200": description: Email confirmed successfully schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Invalid or missing token schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' summary: Confirm email address tags: - - auth - /auth/email: + - auth + /api/auth/email: put: consumes: - - application/json + - application/json description: Update the authenticated user's email address parameters: - - description: New email address - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.UpdateEmailRequest" + - description: New email address + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.UpdateEmailRequest' produces: - - application/json + - application/json responses: "200": description: OK schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Bad Request schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Unauthorized schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "409": description: Conflict schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal Server Error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "503": description: Service Unavailable schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Update email address tags: - - auth - /auth/forgot-password: + - auth + /api/auth/forgot-password: post: consumes: - - application/json + - application/json description: Send a password reset email using a username or email parameters: - - description: Username or email - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.ForgotPasswordRequest" + - description: Username or email + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.ForgotPasswordRequest' produces: - - application/json + - application/json responses: "200": description: Password reset email sent if account exists schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Invalid request data schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' summary: Request a password reset tags: - - auth - /auth/login: + - auth + /api/auth/login: post: consumes: - - application/json + - application/json description: Authenticate user with username and password parameters: - - description: Login credentials - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.LoginRequest" + - description: Login credentials + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.LoginRequest' produces: - - application/json + - application/json responses: "200": description: Authentication successful schema: - $ref: "#/definitions/handlers.AuthTokensResponse" + $ref: '#/definitions/handlers.AuthTokensResponse' "400": description: Invalid request data or validation failed schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Invalid credentials schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "403": description: Account is locked schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' summary: Login user tags: - - auth - /auth/logout: + - auth + /api/auth/logout: post: consumes: - - application/json + - application/json description: Logout the authenticated user and invalidate their session produces: - - application/json + - application/json responses: "200": description: Logged out successfully schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Logout user tags: - - auth - /auth/me: + - auth + /api/auth/me: get: consumes: - - application/json + - application/json description: Retrieve the authenticated user's profile information produces: - - application/json + - application/json responses: "200": description: User profile retrieved successfully schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "404": description: User not found schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Get current user profile tags: - - auth - /auth/password: + - auth + /api/auth/password: put: consumes: - - application/json + - application/json description: Update the authenticated user's password parameters: - - description: Password update data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.UpdatePasswordRequest" + - description: Password update data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.UpdatePasswordRequest' produces: - - application/json + - application/json responses: "200": description: OK schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Bad Request schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Unauthorized schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal Server Error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Update password tags: - - auth - /auth/refresh: + - auth + /api/auth/refresh: post: consumes: - - application/json - description: - Use a refresh token to get a new access token. This endpoint allows + - application/json + description: Use a refresh token to get a new access token. This endpoint allows clients to obtain a new access token using a valid refresh token without requiring user credentials. parameters: - - description: Refresh token data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.RefreshTokenRequest" + - description: Refresh token data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.RefreshTokenRequest' produces: - - application/json + - application/json responses: "200": description: Token refreshed successfully schema: - $ref: "#/definitions/handlers.AuthTokensResponse" + $ref: '#/definitions/handlers.AuthTokensResponse' "400": description: Invalid request body or missing refresh token schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Invalid or expired refresh token schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "403": description: Account is locked schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' summary: Refresh access token tags: - - auth - /auth/register: + - auth + /api/auth/register: post: consumes: - - application/json + - application/json description: Register a new user with username, email and password parameters: - - description: Registration data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.RegisterRequest" + - description: Registration data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.RegisterRequest' produces: - - application/json + - application/json responses: "201": description: Registration successful schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Invalid request data or validation failed schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "409": description: Username or email already exists schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' summary: Register a new user tags: - - auth - /auth/resend-verification: + - auth + /api/auth/resend-verification: post: consumes: - - application/json + - application/json description: Send a new verification email to the provided address parameters: - - description: Email address - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.ResendVerificationRequest" + - description: Email address + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.ResendVerificationRequest' produces: - - application/json + - application/json responses: "200": description: OK schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Bad Request schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "404": description: Not Found schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "409": description: Conflict schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "429": description: Too Many Requests schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal Server Error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "503": description: Service Unavailable schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' summary: Resend verification email tags: - - auth - /auth/reset-password: + - auth + /api/auth/reset-password: post: consumes: - - application/json + - application/json description: Reset a user's password using a reset token parameters: - - description: Password reset data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.ResetPasswordRequest" + - description: Password reset data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.ResetPasswordRequest' produces: - - application/json + - application/json responses: "200": description: Password reset successfully schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Invalid or expired token, or validation failed schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' summary: Reset password tags: - - auth - /auth/revoke: + - auth + /api/auth/revoke: post: consumes: - - application/json - description: - Revoke a specific refresh token. This endpoint allows authenticated + - application/json + description: Revoke a specific refresh token. This endpoint allows authenticated users to invalidate a specific refresh token, preventing its future use. parameters: - - description: Token revocation data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.RevokeTokenRequest" + - description: Token revocation data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.RevokeTokenRequest' produces: - - application/json + - application/json responses: "200": description: Token revoked successfully schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Invalid request body or missing refresh token schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Invalid or expired access token schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Revoke refresh token tags: - - auth - /auth/revoke-all: + - auth + /api/auth/revoke-all: post: consumes: - - application/json - description: - Revoke all refresh tokens for the authenticated user. This endpoint + - application/json + description: Revoke all refresh tokens for the authenticated user. This endpoint allows users to invalidate all their refresh tokens at once, effectively logging them out from all devices. produces: - - application/json + - application/json responses: "200": description: All tokens revoked successfully schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Invalid or expired access token schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Revoke all user tokens tags: - - auth - /auth/username: + - auth + /api/auth/username: put: consumes: - - application/json + - application/json description: Update the authenticated user's username parameters: - - description: New username - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.UpdateUsernameRequest" + - description: New username + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.UpdateUsernameRequest' produces: - - application/json + - application/json responses: "200": description: OK schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "400": description: Bad Request schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "401": description: Unauthorized schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "409": description: Conflict schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' "500": description: Internal Server Error schema: - $ref: "#/definitions/handlers.AuthResponse" + $ref: '#/definitions/handlers.AuthResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Update username tags: - - auth - /health: + - auth + /api/posts: get: consumes: - - application/json - description: Check if the API is healthy with comprehensive database monitoring - produces: - - application/json - responses: - "200": - description: Health check successful - schema: - additionalProperties: true - type: object - summary: Health check - tags: - - api - /metrics: - get: - consumes: - - application/json - description: - Get application metrics including post stats, user stats, vote - stats, and database performance metrics - produces: - - application/json - responses: - "200": - description: Application metrics with vote statistics and database monitoring - schema: - additionalProperties: true - type: object - summary: Get metrics - tags: - - api - /posts: - get: - consumes: - - application/json - description: - Get a list of posts with pagination. Posts include vote statistics + - application/json + description: Get a list of posts with pagination. Posts include vote statistics (up_votes, down_votes, score) and current user's vote status. parameters: - - default: 20 - description: Number of posts to return - in: query - name: limit - type: integer - - default: 0 - description: Number of posts to skip - in: query - name: offset - type: integer + - default: 20 + description: Number of posts to return + in: query + name: limit + type: integer + - default: 0 + description: Number of posts to skip + in: query + name: offset + type: integer produces: - - application/json + - application/json responses: "200": description: Posts retrieved successfully with vote statistics schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "400": description: Invalid pagination parameters schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' summary: Get posts tags: - - posts + - posts post: consumes: - - application/json + - application/json description: Create a new post with URL and optional title parameters: - - description: Post data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.CreatePostRequest" + - description: Post data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.CreatePostRequest' produces: - - application/json + - application/json responses: "201": description: Created schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "400": description: Invalid request data or validation failed schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "409": description: URL already submitted schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "502": description: Failed to fetch title from URL schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Create a new post tags: - - posts - /posts/{id}: + - posts + /api/posts/{id}: delete: consumes: - - application/json + - application/json description: Delete a post owned by the authenticated user parameters: - - description: Post ID - in: path - name: id - required: true - type: integer + - description: Post ID + in: path + name: id + required: true + type: integer produces: - - application/json + - application/json responses: "200": description: Post deleted successfully schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "400": description: Invalid post ID schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "403": description: Not authorized to delete this post schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "404": description: Post not found schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Delete a post tags: - - posts + - posts get: consumes: - - application/json + - application/json description: Get a post by ID with vote statistics and current user's vote status parameters: - - description: Post ID - in: path - name: id - required: true - type: integer + - description: Post ID + in: path + name: id + required: true + type: integer produces: - - application/json + - application/json responses: "200": description: Post retrieved successfully with vote statistics schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "400": description: Invalid post ID schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "404": description: Post not found schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' summary: Get a single post tags: - - posts + - posts put: consumes: - - application/json - description: - Update the title and content of a post owned by the authenticated + - application/json + description: Update the title and content of a post owned by the authenticated user parameters: - - description: Post ID - in: path - name: id - required: true - type: integer - - description: Post update data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.UpdatePostRequest" + - description: Post ID + in: path + name: id + required: true + type: integer + - description: Post update data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.UpdatePostRequest' produces: - - application/json + - application/json responses: "200": description: Post updated successfully schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "400": description: Invalid request data or validation failed schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "403": description: Not authorized to update this post schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "404": description: Post not found schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Update a post tags: - - posts - /posts/{id}/vote: + - posts + /api/posts/{id}/vote: delete: consumes: - - application/json - description: - Remove a vote from a post for the authenticated user. This is equivalent + - application/json + description: Remove a vote from a post for the authenticated user. This is equivalent to casting a vote with type 'none'. parameters: - - description: Post ID - in: path - name: id - required: true - type: integer + - description: Post ID + in: path + name: id + required: true + type: integer produces: - - application/json + - application/json responses: "200": description: Vote removed successfully with updated post statistics schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "400": description: Invalid post ID schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "404": description: Post not found schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Remove a vote tags: - - votes + - votes get: consumes: - - application/json + - application/json description: |- Retrieve the current user's vote for a specific post. Requires authentication and returns the vote type if it exists. @@ -1045,38 +1016,38 @@ paths: - If vote exists: Returns vote details with contextual metadata (including `is_anonymous`) - If no vote: Returns success with null vote data and metadata parameters: - - description: Post ID - in: path - name: id - required: true - type: integer + - description: Post ID + in: path + name: id + required: true + type: integer produces: - - application/json + - application/json responses: "200": description: No vote found for this user/post combination schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "400": description: Invalid post ID schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Get current user's vote tags: - - votes + - votes post: consumes: - - application/json + - application/json description: |- Vote on a post (upvote, downvote, or remove vote). Authentication is required; the vote is performed on behalf of the current user. @@ -1089,49 +1060,49 @@ paths: - Updated post vote counts (up_votes, down_votes, score) - Success message parameters: - - description: Post ID - in: path - name: id - required: true - type: integer - - description: "Vote data (type: 'up', 'down', or 'none' to remove)" - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.VoteRequest" + - description: Post ID + in: path + name: id + required: true + type: integer + - description: 'Vote data (type: ''up'', ''down'', or ''none'' to remove)' + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.VoteRequest' produces: - - application/json + - application/json responses: "200": description: Vote cast successfully with updated post statistics schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "400": description: Invalid request data or vote type schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "404": description: Post not found schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Cast a vote on a post tags: - - votes - /posts/{id}/votes: + - votes + /api/posts/{id}/votes: get: consumes: - - application/json + - application/json description: |- Retrieve all votes for a specific post. Returns all votes in a single format. @@ -1142,267 +1113,297 @@ paths: - Total vote count - Each vote includes type and unauthenticated status parameters: - - description: Post ID - in: path - name: id - required: true - type: integer + - description: Post ID + in: path + name: id + required: true + type: integer produces: - - application/json + - application/json responses: "200": description: Votes retrieved successfully with count schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "400": description: Invalid post ID schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.VoteResponse" + $ref: '#/definitions/handlers.VoteResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Get post votes tags: - - votes - /posts/search: + - votes + /api/posts/search: get: consumes: - - application/json - description: - Search posts by title or content keywords. Results include vote + - application/json + description: Search posts by title or content keywords. Results include vote statistics and current user's vote status. parameters: - - description: Search term - in: query - name: q - type: string - - default: 20 - description: Number of posts to return - in: query - name: limit - type: integer - - default: 0 - description: Number of posts to skip - in: query - name: offset - type: integer + - description: Search term + in: query + name: q + type: string + - default: 20 + description: Number of posts to return + in: query + name: limit + type: integer + - default: 0 + description: Number of posts to skip + in: query + name: offset + type: integer produces: - - application/json + - application/json responses: "200": description: Search results with vote statistics schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "400": description: Invalid search parameters schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' summary: Search posts tags: - - posts - /posts/title: + - posts + /api/posts/title: get: consumes: - - application/json + - application/json description: Fetch the HTML title for the provided URL parameters: - - description: URL to inspect - in: query - name: url - required: true - type: string + - description: URL to inspect + in: query + name: url + required: true + type: string produces: - - application/json + - application/json responses: "200": description: Title fetched successfully schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "400": description: Invalid URL or URL parameter missing schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "501": description: Title fetching is not available schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' "502": description: Failed to fetch title from URL schema: - $ref: "#/definitions/handlers.PostResponse" + $ref: '#/definitions/handlers.PostResponse' summary: Fetch title from URL tags: - - posts - /users: + - posts + /api/users: get: consumes: - - application/json + - application/json description: Retrieve a paginated list of users parameters: - - default: 20 - description: Number of users to return - in: query - name: limit - type: integer - - default: 0 - description: Number of users to skip - in: query - name: offset - type: integer + - default: 20 + description: Number of users to return + in: query + name: limit + type: integer + - default: 0 + description: Number of users to skip + in: query + name: offset + type: integer produces: - - application/json + - application/json responses: "200": description: Users retrieved successfully schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: List users tags: - - users + - users post: consumes: - - application/json + - application/json description: Create a new user account parameters: - - description: User data - in: body - name: request - required: true - schema: - $ref: "#/definitions/handlers.RegisterRequest" + - description: User data + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.RegisterRequest' produces: - - application/json + - application/json responses: "201": description: User created successfully schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "400": description: Invalid request data or validation failed schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "409": description: Username or email already exists schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Create user tags: - - users - /users/{id}: + - users + /api/users/{id}: get: consumes: - - application/json + - application/json description: Retrieve a specific user by ID parameters: - - description: User ID - in: path - name: id - required: true - type: integer + - description: User ID + in: path + name: id + required: true + type: integer produces: - - application/json + - application/json responses: "200": description: User retrieved successfully schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "400": description: Invalid user ID schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "404": description: User not found schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Get user tags: - - users - /users/{id}/posts: + - users + /api/users/{id}/posts: get: consumes: - - application/json + - application/json description: Retrieve posts created by a specific user parameters: - - description: User ID - in: path - name: id - required: true - type: integer - - default: 20 - description: Number of posts to return - in: query - name: limit - type: integer - - default: 0 - description: Number of posts to skip - in: query - name: offset - type: integer + - description: User ID + in: path + name: id + required: true + type: integer + - default: 20 + description: Number of posts to return + in: query + name: limit + type: integer + - default: 0 + description: Number of posts to skip + in: query + name: offset + type: integer produces: - - application/json + - application/json responses: "200": description: User posts retrieved successfully schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "400": description: Invalid user ID or pagination parameters schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "401": description: Authentication required schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' "500": description: Internal server error schema: - $ref: "#/definitions/handlers.UserResponse" + $ref: '#/definitions/handlers.UserResponse' security: - - BearerAuth: [] + - BearerAuth: [] summary: Get user posts tags: - - users + - users + /health: + get: + consumes: + - application/json + description: Check the API health status along with database connectivity details + produces: + - application/json + responses: + "200": + description: Health check successful + schema: + $ref: '#/definitions/handlers.CommonResponse' + summary: Health check + tags: + - api + /metrics: + get: + consumes: + - application/json + description: Retrieve application metrics including aggregate counts and database + performance data + produces: + - application/json + responses: + "200": + description: Application metrics retrieved successfully + schema: + $ref: '#/definitions/handlers.CommonResponse' + summary: Get metrics + tags: + - api schemes: - - http +- http swagger: "2.0"