{ "openapi": "3.0.3", "info": { "title": "b2b API", "description": "Authentication, user management, and repository time tracking API", "version": "1.0.0", "contact": { "name": "API Support", "email": "support@example.com" } }, "servers": [ { "url": "http://localhost:3000", "description": "Development server" } ], "tags": [ { "name": "Health", "description": "Health check endpoints" }, { "name": "Auth", "description": "Authentication endpoints (under /api/v1/public/auth)" }, { "name": "Languages", "description": "Language and translation endpoints" }, { "name": "Products", "description": "Product listing and description endpoints (under /api/v1/restricted, requires authentication)" }, { "name": "Product Description", "description": "Product description management and translation endpoints (under /api/v1/restricted/product-description, requires authentication)" }, { "name": "Menu", "description": "Menu and routing endpoints (under /api/v1/restricted/menu, requires authentication)" }, { "name": "Search", "description": "MeiliSearch endpoints (under /api/v1/restricted/meili-search, requires authentication)" }, { "name": "Locale", "description": "Locale selection endpoints (under /api/v1/restricted/langs-and-countries, requires authentication)" }, { "name": "Repo", "description": "Repository time tracking data endpoints (under /api/v1/restricted/repo, requires authentication)" }, { "name": "Admin", "description": "Admin-only endpoints" }, { "name": "Settings", "description": "Application settings and configuration endpoints" } ], "paths": { "/health": { "get": { "tags": ["Health"], "summary": "Health check", "description": "Returns the health status of the application", "operationId": "getHealth", "responses": { "200": { "description": "Successful response", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string", "example": "ok" }, "app": { "type": "string", "example": "b2b" }, "version": { "type": "string", "example": "1.0.0" } } } } } } } } }, "/api/v1/langs": { "get": { "tags": ["Languages"], "summary": "Get active languages", "description": "Returns a list of all active languages", "operationId": "getLanguages", "responses": { "200": { "description": "Successful response", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Language" } } } } } } } }, "/api/v1/translations": { "get": { "tags": ["Languages"], "summary": "Get translations", "description": "Returns translations from cache. Supports filtering by lang_id, scope, and components.", "operationId": "getTranslations", "parameters": [ { "name": "lang_id", "in": "query", "description": "Filter by language ID", "required": false, "schema": { "type": "integer" } }, { "name": "scope", "in": "query", "description": "Filter by scope (e.g., 'be', 'frontend')", "required": false, "schema": { "type": "string" } }, { "name": "components", "in": "query", "description": "Filter by component name", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Successful response", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string", "example": "success" }, "translations": { "type": "object", "description": "Translation data keyed by language ID, scope, component, and key" } } } } } }, "400": { "description": "Invalid request parameters", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/translations/reload": { "get": { "tags": ["Languages"], "summary": "Reload translations", "description": "Reloads translations from the database into the cache", "operationId": "reloadTranslations", "responses": { "200": { "description": "Translations reloaded successfully", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string", "example": "success" }, "message": { "type": "string", "example": "Translations reloaded successfully" } } } } } }, "500": { "description": "Failed to reload translations", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/login": { "post": { "tags": ["Auth"], "summary": "User login", "description": "Authenticate a user with email and password. Sets HTTPOnly cookies (access_token, refresh_token, is_authenticated) on success.", "operationId": "login", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginRequest" } } } }, "responses": { "200": { "description": "Login successful", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } }, "headers": { "Set-Cookie": { "schema": { "type": "string" }, "description": "HTTPOnly cookies: access_token, refresh_token (opaque), is_authenticated (non-HTTPOnly flag)" } } }, "400": { "description": "Invalid request body or missing fields", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Invalid credentials", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "Account inactive or email not verified", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/register": { "post": { "tags": ["Auth"], "summary": "User registration", "description": "Register a new user account. Sends a verification email. first_name and last_name are required.", "operationId": "register", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterRequest" } } } }, "responses": { "201": { "description": "Registration successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "registration successful, please verify your email" } } } } } }, "400": { "description": "Invalid request, missing required fields, or invalid password format", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Email already exists", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/complete-registration": { "post": { "tags": ["Auth"], "summary": "Complete registration", "description": "Complete registration after email verification using the token sent by email. Sets auth cookies on success.", "operationId": "completeRegistration", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompleteRegistrationRequest" } } } }, "responses": { "201": { "description": "Registration completed successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } }, "headers": { "Set-Cookie": { "schema": { "type": "string" }, "description": "HTTPOnly cookies: access_token, refresh_token, is_authenticated" } } }, "400": { "description": "Invalid or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/forgot-password": { "post": { "tags": ["Auth"], "summary": "Request password reset", "description": "Request a password reset email. Always returns success to prevent email enumeration.", "operationId": "forgotPassword", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "format": "email", "description": "User's email address" } } } } } }, "responses": { "200": { "description": "Password reset email sent if account exists", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "if an account with that email exists, a password reset link has been sent" } } } } } }, "400": { "description": "Invalid request or missing email", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/reset-password": { "post": { "tags": ["Auth"], "summary": "Reset password", "description": "Reset password using reset token from email. Also revokes all existing refresh tokens for the user.", "operationId": "resetPassword", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResetPasswordRequest" } } } }, "responses": { "200": { "description": "Password reset successfully", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "password reset successfully" } } } } } }, "400": { "description": "Invalid or expired token, or invalid password format", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/logout": { "post": { "tags": ["Auth"], "summary": "User logout", "description": "Revokes the refresh token from the database and clears all authentication cookies.", "operationId": "logout", "responses": { "200": { "description": "Logout successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "logged out successfully" } } } } } } } } }, "/api/v1/public/auth/refresh": { "post": { "tags": ["Auth"], "summary": "Refresh access token", "description": "Get a new access token using the refresh token. The refresh token is read from the HTTPOnly cookie first, then from the request body as fallback. Rotates the refresh token on success.", "operationId": "refreshToken", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "refresh_token": { "type": "string", "description": "Opaque refresh token (fallback if cookie not available)" } } } } } }, "responses": { "200": { "description": "Token refreshed successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } }, "headers": { "Set-Cookie": { "schema": { "type": "string" }, "description": "Rotated HTTPOnly cookies: access_token, refresh_token, is_authenticated" } } }, "400": { "description": "Refresh token required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Invalid or expired refresh token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/me": { "get": { "tags": ["Auth"], "summary": "Get current user", "description": "Returns the currently authenticated user's session information. Requires authentication via cookie.", "operationId": "getMe", "security": [ { "CookieAuth": [] } ], "responses": { "200": { "description": "Current user info", "content": { "application/json": { "schema": { "type": "object", "properties": { "user": { "$ref": "#/components/schemas/UserSession" } } } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/google": { "get": { "tags": ["Auth"], "summary": "Google OAuth2 login", "description": "Redirects the user to Google's OAuth2 consent page. Sets a short-lived oauth_state cookie for CSRF protection.", "operationId": "googleLogin", "responses": { "302": { "description": "Redirect to Google OAuth2 consent page", "headers": { "Location": { "schema": { "type": "string" }, "description": "Google OAuth2 authorization URL" }, "Set-Cookie": { "schema": { "type": "string" }, "description": "HTTPOnly oauth_state cookie for CSRF protection (10 min expiry)" } } }, "500": { "description": "Failed to generate OAuth state", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/public/auth/google/callback": { "get": { "tags": ["Auth"], "summary": "Google OAuth2 callback", "description": "Handles the OAuth2 callback from Google. Validates state, exchanges code for tokens, creates or updates user, sets auth cookies, and redirects to the app.", "operationId": "googleCallback", "parameters": [ { "name": "code", "in": "query", "description": "Authorization code from Google", "required": true, "schema": { "type": "string" } }, { "name": "state", "in": "query", "description": "State token for CSRF validation", "required": true, "schema": { "type": "string" } } ], "responses": { "302": { "description": "Redirect to app after successful authentication", "headers": { "Location": { "schema": { "type": "string" }, "description": "Redirect to /{lang} (user's preferred language)" }, "Set-Cookie": { "schema": { "type": "string" }, "description": "HTTPOnly cookies: access_token, refresh_token, is_authenticated" } } }, "400": { "description": "Invalid state (CSRF) or missing authorization code", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Google OAuth callback processing error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/repo/get-repos": { "get": { "tags": ["Repo"], "summary": "Get accessible repositories", "description": "Returns a list of repository IDs that the authenticated user has access to.", "operationId": "getRepos", "security": [ { "CookieAuth": [] } ], "responses": { "200": { "description": "List of repository IDs", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "integer", "format": "uint" }, "example": [1, 2, 5] } } } }, "400": { "description": "Invalid user session", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/repo/get-years": { "get": { "tags": ["Repo"], "summary": "Get available years for a repository", "description": "Returns a list of years for which tracked time data exists in the given repository. User must have access to the repository.", "operationId": "getYears", "security": [ { "CookieAuth": [] } ], "parameters": [ { "name": "repoID", "in": "query", "description": "Repository ID", "required": true, "schema": { "type": "integer", "format": "uint" } } ], "responses": { "200": { "description": "List of years with tracked time data", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "integer", "format": "uint" }, "example": [2023, 2024, 2025] } } } }, "400": { "description": "Invalid repoID parameter or user does not have access to the repository", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/repo/get-quarters": { "get": { "tags": ["Repo"], "summary": "Get quarterly time data for a repository", "description": "Returns time tracked per quarter for the given repository and year. All 4 quarters are returned; quarters with no data have time=0. User must have access to the repository.", "operationId": "getQuarters", "security": [ { "CookieAuth": [] } ], "parameters": [ { "name": "repoID", "in": "query", "description": "Repository ID", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "year", "in": "query", "description": "Year to retrieve quarterly data for", "required": true, "schema": { "type": "integer", "format": "uint", "example": 2024 } } ], "responses": { "200": { "description": "Quarterly time data", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/QuarterData" } } } } }, "400": { "description": "Invalid repoID or year parameter, or user does not have access to the repository", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/repo/get-issues": { "get": { "tags": ["Repo"], "summary": "Get issues with time summaries", "description": "Returns a paginated list of issues with time tracking summaries for the given repository, year, and quarter. User must have access to the repository.", "operationId": "getIssues", "security": [ { "CookieAuth": [] } ], "parameters": [ { "name": "repoID", "in": "query", "description": "Repository ID", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "year", "in": "query", "description": "Year to filter issues by", "required": true, "schema": { "type": "integer", "format": "uint", "example": 2024 } }, { "name": "quarter", "in": "query", "description": "Quarter number (1-4) to filter issues by", "required": true, "schema": { "type": "integer", "format": "uint", "minimum": 1, "maximum": 4, "example": 2 } }, { "name": "page_number", "in": "query", "description": "Page number for pagination (1-based)", "required": true, "schema": { "type": "integer", "format": "uint", "example": 1 } }, { "name": "elements_per_page", "in": "query", "description": "Number of items per page", "required": true, "schema": { "type": "integer", "format": "uint", "example": 30 } } ], "responses": { "200": { "description": "Paginated list of issues with time summaries", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedIssues" } } } }, "400": { "description": "Invalid parameters or user does not have access to the repository", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/settings": { "get": { "tags": ["Settings"], "summary": "Get application settings", "description": "Returns public application settings and configuration", "operationId": "getSettings", "responses": { "200": { "description": "Settings retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettingsResponse" } } } } } } }, "/api/v1/restricted/list-products/get-listing": { "get": { "tags": ["Products"], "summary": "Get product listing", "description": "Returns a paginated list of products with their basic information. Requires authentication.", "operationId": "getProductListing", "security": [ { "CookieAuth": [] } ], "parameters": [ { "name": "page", "in": "query", "description": "Page number (1-based)", "required": false, "schema": { "type": "integer", "default": 1 } }, { "name": "limit", "in": "query", "description": "Number of items per page", "required": false, "schema": { "type": "integer", "default": 30 } } ], "responses": { "200": { "description": "Product listing retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request parameters", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/product-description/get-product-description": { "get": { "tags": ["Product Description"], "summary": "Get product description", "description": "Returns the product description for a given product ID and language. Requires authentication.", "operationId": "getProductDescription", "security": [ { "CookieAuth": [] } ], "parameters": [ { "name": "productID", "in": "query", "description": "Product ID", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "productLangID", "in": "query", "description": "Language ID for the product description", "required": true, "schema": { "type": "integer", "format": "uint" } } ], "responses": { "200": { "description": "Product description retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request parameters", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/product-description/save-product-description": { "post": { "tags": ["Product Description"], "summary": "Save product description", "description": "Saves the product description for a given product ID in the specified language. Requires authentication.", "operationId": "saveProductDescription", "security": [ { "CookieAuth": [] } ], "parameters": [ { "name": "productID", "in": "query", "description": "Product ID", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "productLangID", "in": "query", "description": "Language ID for the product description", "required": true, "schema": { "type": "integer", "format": "uint" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductDescriptionUpdate" } } } }, "responses": { "200": { "description": "Product description saved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request parameters or body", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/product-description/translate-product-description": { "get": { "tags": ["Product Description"], "summary": "Translate product description", "description": "Translates the product description from one language to another using AI (OpenAI or Google). Requires authentication.", "operationId": "translateProductDescription", "security": [ { "CookieAuth": [] } ], "parameters": [ { "name": "productID", "in": "query", "description": "Product ID", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "productFromLangID", "in": "query", "description": "Source language ID", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "productToLangID", "in": "query", "description": "Target language ID", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "model", "in": "query", "description": "AI model to use for translation (OpenAI or Google)", "required": true, "schema": { "type": "string", "enum": ["OpenAI", "Google"] } } ], "responses": { "200": { "description": "Product description translated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request parameters", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/menu/get-menu": { "get": { "tags": ["Menu"], "summary": "Get menu structure", "description": "Returns the menu structure for the current language. Requires authentication.", "operationId": "getMenu", "security": [ { "CookieAuth": [] } ], "responses": { "200": { "description": "Menu retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/menu/get-routes": { "get": { "tags": ["Menu"], "summary": "Get routes", "description": "Returns the routing structure for the current language. Requires authentication.", "operationId": "getRoutes", "security": [ { "CookieAuth": [] } ], "responses": { "200": { "description": "Routes retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/meili-search/search": { "get": { "tags": ["Search"], "summary": "Search products", "description": "Searches products using MeiliSearch. Requires authentication.", "operationId": "searchProducts", "security": [ { "CookieAuth": [] } ], "parameters": [ { "name": "query", "in": "query", "description": "Search query string", "required": true, "schema": { "type": "string" } }, { "name": "limit", "in": "query", "description": "Maximum number of results", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "id_category", "in": "query", "description": "Filter by category ID", "required": true, "schema": { "type": "integer", "format": "uint" } }, { "name": "price_lower_bound", "in": "query", "description": "Lower price bound", "required": true, "schema": { "type": "number", "format": "double" } }, { "name": "price_upper_bound", "in": "query", "description": "Upper price bound", "required": true, "schema": { "type": "number", "format": "double" } } ], "responses": { "200": { "description": "Search results retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request parameters", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/meili-search/create-index": { "get": { "tags": ["Search"], "summary": "Create search index", "description": "Creates a MeiliSearch index for products. Requires superadmin access.", "operationId": "createSearchIndex", "security": [ { "CookieAuth": [] } ], "responses": { "200": { "description": "Index created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/meili-search/test": { "get": { "tags": ["Search"], "summary": "Test MeiliSearch", "description": "Tests the MeiliSearch connection. Requires superadmin access.", "operationId": "testMeiliSearch", "security": [ { "CookieAuth": [] } ], "responses": { "200": { "description": "MeiliSearch test successful", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/langs-and-countries/get-languages": { "get": { "tags": ["Locale"], "summary": "Get available languages", "description": "Returns a list of available languages for the application. Requires authentication.", "operationId": "getAvailableLanguages", "security": [ { "CookieAuth": [] } ], "responses": { "200": { "description": "Languages retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/v1/restricted/langs-and-countries/get-countries": { "get": { "tags": ["Locale"], "summary": "Get countries and currencies", "description": "Returns a list of countries with their associated currencies. Requires authentication.", "operationId": "getCountriesAndCurrencies", "security": [ { "CookieAuth": [] } ], "responses": { "200": { "description": "Countries and currencies retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "components": { "schemas": { "LoginRequest": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email", "description": "User's email address" }, "password": { "type": "string", "format": "password", "description": "User's password" } } }, "RegisterRequest": { "type": "object", "required": [ "email", "password", "confirm_password", "first_name", "last_name" ], "properties": { "email": { "type": "string", "format": "email", "description": "User's email address" }, "password": { "type": "string", "format": "password", "description": "User's password (must meet complexity requirements: min 8 chars, uppercase, lowercase, digit)" }, "confirm_password": { "type": "string", "format": "password", "description": "Password confirmation" }, "first_name": { "type": "string", "description": "User's first name (required)" }, "last_name": { "type": "string", "description": "User's last name (required)" }, "lang": { "type": "string", "description": "User's preferred language ISO code (e.g., 'en', 'pl', 'cs')" } } }, "CompleteRegistrationRequest": { "type": "object", "required": ["token"], "properties": { "token": { "type": "string", "description": "Email verification token received via email" } } }, "ResetPasswordRequest": { "type": "object", "required": ["token", "password"], "properties": { "token": { "type": "string", "description": "Password reset token received via email" }, "password": { "type": "string", "format": "password", "description": "New password (must meet complexity requirements)" } } }, "AuthResponse": { "type": "object", "properties": { "access_token": { "type": "string", "description": "JWT access token" }, "token_type": { "type": "string", "example": "Bearer" }, "expires_in": { "type": "integer", "description": "Access token expiration in seconds" }, "user": { "$ref": "#/components/schemas/UserSession" } } }, "UserSession": { "type": "object", "properties": { "user_id": { "type": "integer", "format": "uint", "description": "User ID" }, "email": { "type": "string", "format": "email" }, "username": { "type": "string" }, "role": { "type": "string", "enum": ["user", "admin"], "description": "User role" }, "first_name": { "type": "string" }, "last_name": { "type": "string" }, "lang": { "type": "string", "description": "User's preferred language ISO code (e.g., 'en', 'pl', 'cs')" } } }, "Error": { "type": "object", "properties": { "error": { "type": "string", "description": "Translated error message" } } }, "ApiResponse": { "type": "object", "properties": { "message": { "type": "string", "description": "Response message" }, "items": { "type": "object", "description": "Response data" }, "count": { "type": "integer", "description": "Number of items returned" } } }, "ProductDescriptionUpdate": { "type": "object", "description": "Map of fields to update for product description", "additionalProperties": { "type": "string" }, "example": { "name": "Product Name", "description": "
Product description in HTML
", "description_short": "Short description
", "meta_title": "Meta Title", "meta_description": "Meta description text", "available_now": "In Stock", "available_later": "Out of Stock", "usage": "Usage instructions
" } }, "ProductDescription": { "type": "object", "description": "Product description in a specific language", "properties": { "product_id": { "type": "integer", "format": "uint", "description": "Product ID" }, "shop_id": { "type": "integer", "format": "uint", "description": "Shop ID" }, "lang_id": { "type": "integer", "format": "uint", "description": "Language ID" }, "name": { "type": "string", "description": "Product name" }, "description": { "type": "string", "description": "Full product description (HTML)" }, "description_short": { "type": "string", "description": "Short product description (HTML)" }, "link_rewrite": { "type": "string", "description": "URL-friendly slug" }, "meta_description": { "type": "string", "description": "Meta description" }, "meta_keywords": { "type": "string", "description": "Meta keywords" }, "meta_title": { "type": "string", "description": "Meta title" }, "available_now": { "type": "string", "description": "Text shown when item is in stock" }, "available_later": { "type": "string", "description": "Text shown when item is out of stock" }, "delivery_in_stock": { "type": "string", "description": "Delivery in stock text" }, "delivery_out_stock": { "type": "string", "description": "Delivery out of stock text" }, "usage": { "type": "string", "description": "Usage instructions (HTML)" } } }, "Country": { "type": "object", "description": "Country with its currency", "properties": { "id": { "type": "integer", "format": "uint", "description": "Country ID" }, "name": { "type": "string", "description": "Country name" }, "flag": { "type": "string", "description": "Flag emoji or code" }, "currency_id": { "type": "integer", "format": "uint", "description": "Currency ID" }, "currency_iso_code": { "type": "string", "description": "Currency ISO code (e.g., EUR, USD)" }, "currency_name": { "type": "string", "description": "Currency name" } } }, "MenuItem": { "type": "object", "description": "Menu item structure", "properties": { "category_id": { "type": "integer", "format": "uint", "description": "Category ID" }, "label": { "type": "string", "description": "Menu item label" }, "params": { "$ref": "#/components/schemas/MenuItemParams" }, "children": { "type": "array", "items": { "$ref": "#/components/schemas/MenuItem" }, "description": "Child menu items" } } }, "MenuItemParams": { "type": "object", "properties": { "category_id": { "type": "integer", "format": "uint" }, "link_rewrite": { "type": "string" }, "locale": { "type": "string" } } }, "Route": { "type": "object", "description": "Application route", "properties": { "id": { "type": "integer", "format": "uint", "description": "Route ID" }, "name": { "type": "string", "description": "Route name" }, "path": { "type": "string", "description": "Route path" }, "component": { "type": "string", "description": "Vue component path" }, "layout": { "type": "string", "description": "Layout type" }, "meta": { "type": "object", "description": "Route metadata" }, "is_active": { "type": "boolean", "description": "Whether the route is active" }, "sort_order": { "type": "integer", "description": "Sort order" }, "children": { "type": "array", "items": { "$ref": "#/components/schemas/Route" }, "description": "Child routes" } } }, "MeiliSearchResult": { "type": "object", "description": "MeiliSearch product result", "properties": { "id_product": { "type": "integer", "format": "uint", "description": "Product ID" }, "name": { "type": "string", "description": "Product name" }, "active": { "type": "integer", "description": "Active status" }, "price": { "type": "number", "format": "double", "description": "Product price" }, "description": { "type": "string", "description": "Product description" }, "description_short": { "type": "string", "description": "Short description" }, "reference": { "type": "string", "description": "Product reference" }, "id_category": { "type": "integer", "format": "uint", "description": "Category ID" }, "category_name": { "type": "string", "description": "Category name" } } }, "Language": { "type": "object", "properties": { "id": { "type": "integer", "format": "uint64", "description": "Language ID" }, "name": { "type": "string", "description": "Language name" }, "iso_code": { "type": "string", "description": "ISO 639-1 code (e.g., 'en', 'pl')" }, "lang_code": { "type": "string", "description": "Full language code (e.g., 'en-US', 'pl-PL')" }, "date_format": { "type": "string", "description": "Date format string" }, "date_format_short": { "type": "string", "description": "Short date format string" }, "rtl": { "type": "boolean", "description": "Right-to-left language" }, "is_default": { "type": "boolean", "description": "Is default language" }, "active": { "type": "boolean", "description": "Is active" }, "flag": { "type": "string", "description": "Flag emoji or code" } } }, "QuarterData": { "type": "object", "description": "Time tracked in a specific quarter", "properties": { "time": { "type": "number", "format": "double", "description": "Total hours tracked in this quarter" }, "quarter": { "type": "string", "description": "Quarter identifier in format YYYY_QN (e.g., '2024_Q1')", "example": "2024_Q1" } } }, "IssueTimeSummary": { "type": "object", "description": "Time tracking summary for a single issue", "properties": { "issue_id": { "type": "integer", "format": "uint", "description": "Issue ID" }, "issue_name": { "type": "string", "description": "Issue title/name" }, "user_id": { "type": "integer", "format": "uint", "description": "ID of the user who tracked time" }, "initials": { "type": "string", "description": "Abbreviated initials of the user (e.g., 'J.D.')" }, "created_date": { "type": "string", "format": "date", "description": "Date when time was tracked" }, "total_hours_spent": { "type": "number", "format": "double", "description": "Total hours spent on this issue on the given date (rounded to 2 decimal places)" } } }, "PaginatedIssues": { "type": "object", "description": "Paginated list of issue time summaries", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/IssueTimeSummary" }, "description": "List of issue time summaries for the current page" }, "items_count": { "type": "integer", "format": "uint", "description": "Total number of items across all pages", "example": 56 } } }, "SettingsResponse": { "type": "object", "properties": { "app": { "$ref": "#/components/schemas/AppSettings" }, "server": { "$ref": "#/components/schemas/ServerSettings" }, "auth": { "$ref": "#/components/schemas/AuthSettings" }, "features": { "$ref": "#/components/schemas/FeatureFlags" }, "version": { "$ref": "#/components/schemas/VersionInfo" } } }, "AppSettings": { "type": "object", "properties": { "name": { "type": "string", "description": "Application name" }, "environment": { "type": "string", "description": "Application environment (e.g., 'development', 'production')" }, "base_url": { "type": "string", "description": "Base URL of the application" }, "password_regex": { "type": "string", "description": "Regular expression for password validation" } } }, "ServerSettings": { "type": "object", "properties": { "port": { "type": "integer", "description": "Server port" }, "host": { "type": "string", "description": "Server host" } } }, "AuthSettings": { "type": "object", "properties": { "jwt_expiration": { "type": "integer", "description": "JWT access token expiration in seconds" }, "refresh_expiration": { "type": "integer", "description": "Refresh token expiration in seconds" } } }, "FeatureFlags": { "type": "object", "properties": { "email_enabled": { "type": "boolean", "description": "Whether email functionality is enabled" }, "oauth_google": { "type": "boolean", "description": "Whether Google OAuth is enabled" } } }, "VersionInfo": { "type": "object", "properties": { "version": { "type": "string", "description": "Application version (git tag or commit hash)" }, "commit": { "type": "string", "description": "Short git commit hash" }, "build_date": { "type": "string", "format": "date-time", "description": "Build date in RFC3339 format" } } } }, "securitySchemes": { "CookieAuth": { "type": "apiKey", "in": "cookie", "name": "access_token", "description": "HTTPOnly JWT access token cookie set during login, registration, or token refresh" }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "JWT token obtained from login response (alternative to cookie-based auth)" } } } }