2346 lines
66 KiB
JSON
2346 lines
66 KiB
JSON
{
|
|
"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": "/",
|
|
"description": "Development server on same host"
|
|
}
|
|
],
|
|
"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": "p",
|
|
"in": "query",
|
|
"description": "Page number (1-based)",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "integer",
|
|
"default": 1
|
|
}
|
|
},
|
|
{
|
|
"name": "elems",
|
|
"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": "<p>Product description in HTML</p>",
|
|
"description_short": "<p>Short description</p>",
|
|
"meta_title": "Meta Title",
|
|
"meta_description": "Meta description text",
|
|
"available_now": "In Stock",
|
|
"available_later": "Out of Stock",
|
|
"usage": "<p>Usage instructions</p>"
|
|
}
|
|
},
|
|
"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)"
|
|
}
|
|
}
|
|
}
|
|
}
|