0e909182c2
- Add Support section to README with contact email and issues link - Add contact email to Swagger API documentation - Regenerate Swagger docs
796 lines
20 KiB
YAML
796 lines
20 KiB
YAML
basePath: /api/v1
|
|
definitions:
|
|
http.AuthResponse:
|
|
properties:
|
|
refresh_token:
|
|
type: string
|
|
token:
|
|
type: string
|
|
user_id:
|
|
type: string
|
|
type: object
|
|
http.CreateHabitRequest:
|
|
properties:
|
|
carry_over:
|
|
type: boolean
|
|
description:
|
|
type: string
|
|
frequency:
|
|
$ref: '#/definitions/value_objects.Frequency'
|
|
is_negative:
|
|
type: boolean
|
|
name:
|
|
type: string
|
|
specific_dates:
|
|
items:
|
|
type: integer
|
|
type: array
|
|
specific_days:
|
|
items:
|
|
type: integer
|
|
type: array
|
|
target_value:
|
|
type: number
|
|
type:
|
|
$ref: '#/definitions/value_objects.HabitType'
|
|
type: object
|
|
http.ErrorResponse:
|
|
properties:
|
|
error:
|
|
type: string
|
|
type: object
|
|
http.HabitEntriesResponse:
|
|
properties:
|
|
entries:
|
|
items:
|
|
$ref: '#/definitions/http.HabitEntryResponse'
|
|
type: array
|
|
limit:
|
|
type: integer
|
|
page:
|
|
type: integer
|
|
total:
|
|
type: integer
|
|
type: object
|
|
http.HabitEntryResponse:
|
|
properties:
|
|
completed_at:
|
|
type: string
|
|
habit_id:
|
|
type: string
|
|
id:
|
|
type: string
|
|
scheduled_date:
|
|
type: string
|
|
value:
|
|
type: number
|
|
type: object
|
|
http.HealthResponse:
|
|
properties:
|
|
database:
|
|
type: string
|
|
status:
|
|
type: string
|
|
uptime:
|
|
type: string
|
|
type: object
|
|
http.LoginRequest:
|
|
properties:
|
|
email:
|
|
type: string
|
|
password:
|
|
type: string
|
|
type: object
|
|
http.LogoutRequest:
|
|
properties:
|
|
refresh_token:
|
|
type: string
|
|
type: object
|
|
http.MarkHabitRequest:
|
|
properties:
|
|
scheduled_date:
|
|
type: string
|
|
value:
|
|
type: number
|
|
type: object
|
|
http.RefreshRequest:
|
|
properties:
|
|
refresh_token:
|
|
type: string
|
|
type: object
|
|
http.RegisterRequest:
|
|
properties:
|
|
email:
|
|
type: string
|
|
password:
|
|
type: string
|
|
timezone:
|
|
type: string
|
|
type: object
|
|
http.TodaysHabitResponse:
|
|
properties:
|
|
id:
|
|
type: string
|
|
is_carried_over:
|
|
type: boolean
|
|
is_negative:
|
|
type: boolean
|
|
name:
|
|
type: string
|
|
scheduled_date:
|
|
type: string
|
|
target_value:
|
|
type: number
|
|
type:
|
|
$ref: '#/definitions/value_objects.HabitType'
|
|
type: object
|
|
http.UpdateHabitRequest:
|
|
properties:
|
|
carry_over:
|
|
type: boolean
|
|
description:
|
|
type: string
|
|
name:
|
|
type: string
|
|
specific_dates:
|
|
items:
|
|
type: integer
|
|
type: array
|
|
specific_days:
|
|
items:
|
|
type: integer
|
|
type: array
|
|
target_value:
|
|
type: number
|
|
type: object
|
|
http.UserHabitResponse:
|
|
properties:
|
|
carry_over:
|
|
type: boolean
|
|
frequency:
|
|
$ref: '#/definitions/value_objects.Frequency'
|
|
id:
|
|
type: string
|
|
is_negative:
|
|
type: boolean
|
|
name:
|
|
type: string
|
|
specific_days:
|
|
items:
|
|
type: integer
|
|
type: array
|
|
target_value:
|
|
type: number
|
|
type:
|
|
$ref: '#/definitions/value_objects.HabitType'
|
|
type: object
|
|
queries.HabitStatsDTO:
|
|
properties:
|
|
completion_rate:
|
|
type: number
|
|
completions_this_month:
|
|
type: integer
|
|
completions_this_week:
|
|
type: integer
|
|
current_streak:
|
|
type: integer
|
|
habit_id:
|
|
type: string
|
|
habit_name:
|
|
type: string
|
|
longest_streak:
|
|
type: integer
|
|
total_completions:
|
|
type: integer
|
|
type: object
|
|
value_objects.Frequency:
|
|
enum:
|
|
- DAILY
|
|
- WEEKLY
|
|
- MONTHLY
|
|
type: string
|
|
x-enum-varnames:
|
|
- FrequencyDaily
|
|
- FrequencyWeekly
|
|
- FrequencyMonthly
|
|
value_objects.HabitType:
|
|
enum:
|
|
- BOOLEAN
|
|
- COUNTER
|
|
- VALUE
|
|
type: string
|
|
x-enum-varnames:
|
|
- HabitTypeBoolean
|
|
- HabitTypeCounter
|
|
- HabitTypeValue
|
|
info:
|
|
contact:
|
|
email: contact@apocapoc.app
|
|
name: API Support
|
|
url: https://github.com/davidfolch/apocapoc-api
|
|
description: Self-hosted habit tracking service
|
|
license:
|
|
name: MIT
|
|
url: https://opensource.org/licenses/MIT
|
|
termsOfService: http://swagger.io/terms/
|
|
title: Apocapoc API
|
|
paths:
|
|
/auth/login:
|
|
post:
|
|
consumes:
|
|
- application/json
|
|
description: Authenticate user with email and password. Returns both access
|
|
token and refresh token. The access token is used for API requests, the refresh
|
|
token is used to obtain new access tokens when they expire.
|
|
parameters:
|
|
- description: Login credentials
|
|
in: body
|
|
name: request
|
|
required: true
|
|
schema:
|
|
$ref: '#/definitions/http.LoginRequest'
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: Returns access token, refresh token, and user ID
|
|
schema:
|
|
$ref: '#/definitions/http.AuthResponse'
|
|
"400":
|
|
description: Invalid request body
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"401":
|
|
description: Invalid email or password
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal server error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
summary: Login user
|
|
tags:
|
|
- auth
|
|
/auth/logout:
|
|
post:
|
|
consumes:
|
|
- application/json
|
|
description: Revoke the refresh token to invalidate the user session. After
|
|
logout, the refresh token cannot be used to obtain new access tokens. The
|
|
user will need to login again. Always call this endpoint before clearing tokens
|
|
from client storage to ensure proper session termination.
|
|
parameters:
|
|
- description: Refresh token to revoke
|
|
in: body
|
|
name: request
|
|
required: true
|
|
schema:
|
|
$ref: '#/definitions/http.LogoutRequest'
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: Successfully logged out - the refresh token is now invalid
|
|
schema:
|
|
additionalProperties:
|
|
type: string
|
|
type: object
|
|
"400":
|
|
description: Invalid request body or missing refresh token
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"404":
|
|
description: Refresh token not found (already revoked or never existed)
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal server error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
summary: Logout user
|
|
tags:
|
|
- auth
|
|
/auth/refresh:
|
|
post:
|
|
consumes:
|
|
- application/json
|
|
description: 'Exchange a valid refresh token for a new access token and refresh
|
|
token pair. IMPORTANT: The old refresh token is automatically revoked and
|
|
you receive a NEW refresh token - always update both tokens in storage. Use
|
|
this endpoint when the access token expires to maintain the user session without
|
|
requiring re-login.'
|
|
parameters:
|
|
- description: Current refresh token
|
|
in: body
|
|
name: request
|
|
required: true
|
|
schema:
|
|
$ref: '#/definitions/http.RefreshRequest'
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: Returns NEW access token and NEW refresh token - the old refresh
|
|
token is now invalid
|
|
schema:
|
|
$ref: '#/definitions/http.AuthResponse'
|
|
"400":
|
|
description: Invalid request body
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"401":
|
|
description: Invalid or expired refresh token
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal server error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
summary: Refresh access token
|
|
tags:
|
|
- auth
|
|
/auth/register:
|
|
post:
|
|
consumes:
|
|
- application/json
|
|
description: Create a new user account and receive both access token and refresh
|
|
token. Store both tokens securely - the refresh token is used to obtain new
|
|
access tokens when they expire.
|
|
parameters:
|
|
- description: 'Registration data (password requires: min 8 chars, uppercase,
|
|
lowercase, digit, special char)'
|
|
in: body
|
|
name: request
|
|
required: true
|
|
schema:
|
|
$ref: '#/definitions/http.RegisterRequest'
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"201":
|
|
description: Returns access token, refresh token, and user ID
|
|
schema:
|
|
$ref: '#/definitions/http.AuthResponse'
|
|
"400":
|
|
description: 'Invalid input: email format, password requirements, or timezone'
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"409":
|
|
description: Email already registered
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal server error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
summary: Register a new user
|
|
tags:
|
|
- auth
|
|
/habits:
|
|
get:
|
|
description: Get all active habits for the authenticated user
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
items:
|
|
$ref: '#/definitions/http.UserHabitResponse'
|
|
type: array
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Get all user habits
|
|
tags:
|
|
- habits
|
|
post:
|
|
consumes:
|
|
- application/json
|
|
description: Create a new habit for the authenticated user
|
|
parameters:
|
|
- description: Habit data
|
|
in: body
|
|
name: request
|
|
required: true
|
|
schema:
|
|
$ref: '#/definitions/http.CreateHabitRequest'
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"201":
|
|
description: Created
|
|
schema:
|
|
additionalProperties:
|
|
type: string
|
|
type: object
|
|
"400":
|
|
description: Bad Request
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Create a new habit
|
|
tags:
|
|
- habits
|
|
/habits/{id}:
|
|
delete:
|
|
description: Archive (soft delete) a habit
|
|
parameters:
|
|
- description: Habit ID
|
|
in: path
|
|
name: id
|
|
required: true
|
|
type: string
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
additionalProperties:
|
|
type: string
|
|
type: object
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"403":
|
|
description: Forbidden
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"404":
|
|
description: Not Found
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Archive habit
|
|
tags:
|
|
- habits
|
|
get:
|
|
description: Get a specific habit by ID
|
|
parameters:
|
|
- description: Habit ID
|
|
in: path
|
|
name: id
|
|
required: true
|
|
type: string
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
$ref: '#/definitions/http.UserHabitResponse'
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"403":
|
|
description: Forbidden
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"404":
|
|
description: Not Found
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Get habit by ID
|
|
tags:
|
|
- habits
|
|
put:
|
|
consumes:
|
|
- application/json
|
|
description: Update an existing habit
|
|
parameters:
|
|
- description: Habit ID
|
|
in: path
|
|
name: id
|
|
required: true
|
|
type: string
|
|
- description: Update data
|
|
in: body
|
|
name: request
|
|
required: true
|
|
schema:
|
|
$ref: '#/definitions/http.UpdateHabitRequest'
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
additionalProperties:
|
|
type: string
|
|
type: object
|
|
"400":
|
|
description: Bad Request
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"403":
|
|
description: Forbidden
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"404":
|
|
description: Not Found
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Update habit
|
|
tags:
|
|
- habits
|
|
/habits/{id}/entries:
|
|
get:
|
|
description: Get entries (completion history) for a habit with optional date
|
|
filtering and pagination
|
|
parameters:
|
|
- description: Habit ID
|
|
in: path
|
|
name: id
|
|
required: true
|
|
type: string
|
|
- description: Start date (YYYY-MM-DD)
|
|
in: query
|
|
name: from
|
|
type: string
|
|
- description: End date (YYYY-MM-DD)
|
|
in: query
|
|
name: to
|
|
type: string
|
|
- description: Page number
|
|
in: query
|
|
name: page
|
|
type: integer
|
|
- description: Page size (max 100)
|
|
in: query
|
|
name: limit
|
|
type: integer
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
$ref: '#/definitions/http.HabitEntriesResponse'
|
|
"400":
|
|
description: Bad Request
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"403":
|
|
description: Forbidden
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"404":
|
|
description: Not Found
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Get habit entries
|
|
tags:
|
|
- habits
|
|
/habits/{id}/entries/{date}:
|
|
delete:
|
|
description: Delete a habit entry (unmark completion)
|
|
parameters:
|
|
- description: Habit ID
|
|
in: path
|
|
name: id
|
|
required: true
|
|
type: string
|
|
- description: Date (YYYY-MM-DD)
|
|
in: path
|
|
name: date
|
|
required: true
|
|
type: string
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
additionalProperties:
|
|
type: string
|
|
type: object
|
|
"400":
|
|
description: Bad Request
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"403":
|
|
description: Forbidden
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"404":
|
|
description: Not Found
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Unmark habit
|
|
tags:
|
|
- habits
|
|
/habits/{id}/mark:
|
|
post:
|
|
consumes:
|
|
- application/json
|
|
description: Mark a habit as completed for a specific date
|
|
parameters:
|
|
- description: Habit ID
|
|
in: path
|
|
name: id
|
|
required: true
|
|
type: string
|
|
- description: Mark data
|
|
in: body
|
|
name: request
|
|
required: true
|
|
schema:
|
|
$ref: '#/definitions/http.MarkHabitRequest'
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
additionalProperties:
|
|
type: string
|
|
type: object
|
|
"400":
|
|
description: Bad Request
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"404":
|
|
description: Not Found
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"409":
|
|
description: Conflict
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Mark habit as complete
|
|
tags:
|
|
- habits
|
|
/habits/today:
|
|
get:
|
|
description: Get all habits scheduled for today for the authenticated user
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
items:
|
|
$ref: '#/definitions/http.TodaysHabitResponse'
|
|
type: array
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Get today's habits
|
|
tags:
|
|
- habits
|
|
/health:
|
|
get:
|
|
description: Get API health status including database connectivity and uptime
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
$ref: '#/definitions/http.HealthResponse'
|
|
"503":
|
|
description: Service Unavailable
|
|
schema:
|
|
$ref: '#/definitions/http.HealthResponse'
|
|
summary: Health check
|
|
tags:
|
|
- system
|
|
/stats/habits/{id}:
|
|
get:
|
|
description: Get statistics for a specific habit including streaks and completion
|
|
rates
|
|
parameters:
|
|
- description: Habit ID
|
|
in: path
|
|
name: id
|
|
required: true
|
|
type: string
|
|
produces:
|
|
- application/json
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
schema:
|
|
$ref: '#/definitions/queries.HabitStatsDTO'
|
|
"401":
|
|
description: Unauthorized
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"403":
|
|
description: Forbidden
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"404":
|
|
description: Not Found
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
"500":
|
|
description: Internal Server Error
|
|
schema:
|
|
$ref: '#/definitions/http.ErrorResponse'
|
|
security:
|
|
- BearerAuth: []
|
|
summary: Get habit statistics
|
|
tags:
|
|
- stats
|
|
securityDefinitions:
|
|
BearerAuth:
|
|
description: Type "Bearer" followed by a space and JWT token.
|
|
in: header
|
|
name: Authorization
|
|
type: apiKey
|
|
swagger: "2.0"
|