timetracker update
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "timeTracker API",
|
||||
"description": "Authentication and user management API",
|
||||
"description": "Authentication, user management, and repository time tracking API",
|
||||
"version": "1.0.0",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
@@ -22,15 +22,15 @@
|
||||
},
|
||||
{
|
||||
"name": "Auth",
|
||||
"description": "Authentication endpoints"
|
||||
"description": "Authentication endpoints (under /api/v1/public/auth)"
|
||||
},
|
||||
{
|
||||
"name": "Languages",
|
||||
"description": "Language and translation endpoints"
|
||||
},
|
||||
{
|
||||
"name": "Protected",
|
||||
"description": "Protected routes requiring authentication"
|
||||
"name": "Repo",
|
||||
"description": "Repository time tracking data endpoints (under /api/v1/restricted/repo, requires authentication)"
|
||||
},
|
||||
{
|
||||
"name": "Admin",
|
||||
@@ -208,11 +208,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/login": {
|
||||
"/api/v1/public/auth/login": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "User login",
|
||||
"description": "Authenticate a user with email and password",
|
||||
"description": "Authenticate a user with email and password. Sets HTTPOnly cookies (access_token, refresh_token, is_authenticated) on success.",
|
||||
"operationId": "login",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
@@ -239,12 +239,12 @@
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "HTTP-only cookies containing access and refresh tokens"
|
||||
"description": "HTTPOnly cookies: access_token, refresh_token (opaque), is_authenticated (non-HTTPOnly flag)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request body",
|
||||
"description": "Invalid request body or missing fields",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@@ -276,11 +276,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/register": {
|
||||
"/api/v1/public/auth/register": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "User registration",
|
||||
"description": "Register a new user account",
|
||||
"description": "Register a new user account. Sends a verification email. first_name and last_name are required.",
|
||||
"operationId": "register",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
@@ -310,7 +310,17 @@
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request or email already exists",
|
||||
"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": {
|
||||
@@ -322,11 +332,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/complete-registration": {
|
||||
"/api/v1/public/auth/complete-registration": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Complete registration",
|
||||
"description": "Complete registration after email verification",
|
||||
"description": "Complete registration after email verification using the token sent by email. Sets auth cookies on success.",
|
||||
"operationId": "completeRegistration",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
@@ -347,10 +357,18 @@
|
||||
"$ref": "#/components/schemas/AuthResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"Set-Cookie": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "HTTPOnly cookies: access_token, refresh_token, is_authenticated"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid token",
|
||||
"description": "Invalid or expired token",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@@ -362,11 +380,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/forgot-password": {
|
||||
"/api/v1/public/auth/forgot-password": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Request password reset",
|
||||
"description": "Request a password reset email",
|
||||
"description": "Request a password reset email. Always returns success to prevent email enumeration.",
|
||||
"operationId": "forgotPassword",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
@@ -404,7 +422,7 @@
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request",
|
||||
"description": "Invalid request or missing email",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@@ -416,11 +434,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/reset-password": {
|
||||
"/api/v1/public/auth/reset-password": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Reset password",
|
||||
"description": "Reset password using reset token",
|
||||
"description": "Reset password using reset token from email. Also revokes all existing refresh tokens for the user.",
|
||||
"operationId": "resetPassword",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
@@ -450,7 +468,7 @@
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid or expired token",
|
||||
"description": "Invalid or expired token, or invalid password format",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@@ -462,11 +480,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/logout": {
|
||||
"/api/v1/public/auth/logout": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "User logout",
|
||||
"description": "Clear authentication cookies",
|
||||
"description": "Revokes the refresh token from the database and clears all authentication cookies.",
|
||||
"operationId": "logout",
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -488,11 +506,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/refresh": {
|
||||
"/api/v1/public/auth/refresh": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Refresh access token",
|
||||
"description": "Get a new access token using refresh 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": {
|
||||
@@ -502,7 +520,7 @@
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
"type": "string",
|
||||
"description": "Refresh token from login response"
|
||||
"description": "Opaque refresh token (fallback if cookie not available)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -518,6 +536,14 @@
|
||||
"$ref": "#/components/schemas/AuthResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"Set-Cookie": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Rotated HTTPOnly cookies: access_token, refresh_token, is_authenticated"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@@ -543,27 +569,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/protected/dashboard": {
|
||||
"/api/v1/public/auth/me": {
|
||||
"get": {
|
||||
"tags": ["Protected"],
|
||||
"summary": "Get dashboard data",
|
||||
"description": "Protected route requiring authentication",
|
||||
"tags": ["Auth"],
|
||||
"summary": "Get current user",
|
||||
"description": "Returns the currently authenticated user's session information. Requires authentication via cookie.",
|
||||
"operationId": "getMe",
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
"CookieAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Dashboard data",
|
||||
"description": "Current user info",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"$ref": "#/components/schemas/UserSession"
|
||||
}
|
||||
@@ -585,28 +609,143 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/users": {
|
||||
"/api/v1/public/auth/google": {
|
||||
"get": {
|
||||
"tags": ["Admin"],
|
||||
"summary": "Get all users",
|
||||
"description": "Admin-only endpoint for user management",
|
||||
"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": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
"CookieAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of users",
|
||||
"description": "List of repository IDs",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "uint"
|
||||
},
|
||||
"example": [1, 2, 5]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid user session",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -620,9 +759,235 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/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]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Admin access required",
|
||||
"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": {
|
||||
@@ -675,7 +1040,13 @@
|
||||
},
|
||||
"RegisterRequest": {
|
||||
"type": "object",
|
||||
"required": ["email", "password", "confirm_password"],
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"confirm_password",
|
||||
"first_name",
|
||||
"last_name"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
@@ -685,7 +1056,7 @@
|
||||
"password": {
|
||||
"type": "string",
|
||||
"format": "password",
|
||||
"description": "User's password (min 8 chars, uppercase, lowercase, digit)"
|
||||
"description": "User's password (must meet complexity requirements: min 8 chars, uppercase, lowercase, digit)"
|
||||
},
|
||||
"confirm_password": {
|
||||
"type": "string",
|
||||
@@ -694,15 +1065,15 @@
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string",
|
||||
"description": "User's first name"
|
||||
"description": "User's first name (required)"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string",
|
||||
"description": "User's last name"
|
||||
"description": "User's last name (required)"
|
||||
},
|
||||
"lang": {
|
||||
"type": "string",
|
||||
"description": "User's preferred language (e.g., 'en', 'pl', 'cs')"
|
||||
"description": "User's preferred language ISO code (e.g., 'en', 'pl', 'cs')"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -712,7 +1083,7 @@
|
||||
"properties": {
|
||||
"token": {
|
||||
"type": "string",
|
||||
"description": "Email verification token"
|
||||
"description": "Email verification token received via email"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -722,12 +1093,12 @@
|
||||
"properties": {
|
||||
"token": {
|
||||
"type": "string",
|
||||
"description": "Password reset token"
|
||||
"description": "Password reset token received via email"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"format": "password",
|
||||
"description": "New password"
|
||||
"description": "New password (must meet complexity requirements)"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -738,17 +1109,13 @@
|
||||
"type": "string",
|
||||
"description": "JWT access token"
|
||||
},
|
||||
"refresh_token": {
|
||||
"type": "string",
|
||||
"description": "JWT refresh token"
|
||||
},
|
||||
"token_type": {
|
||||
"type": "string",
|
||||
"example": "Bearer"
|
||||
},
|
||||
"expires_in": {
|
||||
"type": "integer",
|
||||
"description": "Token expiration in seconds"
|
||||
"description": "Access token expiration in seconds"
|
||||
},
|
||||
"user": {
|
||||
"$ref": "#/components/schemas/UserSession"
|
||||
@@ -780,6 +1147,10 @@
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"lang": {
|
||||
"type": "string",
|
||||
"description": "User's preferred language ISO code (e.g., 'en', 'pl', 'cs')"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -788,7 +1159,7 @@
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "Error message"
|
||||
"description": "Translated error message"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -838,6 +1209,75 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@@ -872,6 +1312,10 @@
|
||||
"base_url": {
|
||||
"type": "string",
|
||||
"description": "Base URL of the application"
|
||||
},
|
||||
"password_regex": {
|
||||
"type": "string",
|
||||
"description": "Regular expression for password validation"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -893,7 +1337,7 @@
|
||||
"properties": {
|
||||
"jwt_expiration": {
|
||||
"type": "integer",
|
||||
"description": "JWT token expiration in seconds"
|
||||
"description": "JWT access token expiration in seconds"
|
||||
},
|
||||
"refresh_expiration": {
|
||||
"type": "integer",
|
||||
@@ -919,25 +1363,32 @@
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Application version"
|
||||
"description": "Application version (git tag or commit hash)"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string",
|
||||
"description": "Git commit hash"
|
||||
"description": "Short git commit hash"
|
||||
},
|
||||
"date": {
|
||||
"build_date": {
|
||||
"type": "string",
|
||||
"description": "Build date"
|
||||
"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"
|
||||
"description": "JWT token obtained from login response (alternative to cookie-based auth)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user