go-chi-oapi-codegen-todolist/backend/openapi.yaml
2025-04-21 00:10:30 +07:00

1018 lines
32 KiB
YAML

openapi: 3.0.3
info:
title: Todolist API
version: 1.3.0 # Incremented version
description: |
API for managing Todo items, including CRUD operations, subtasks, deadlines, attachments (stored in GCS), and user-defined Tags.
Supports user authentication via email/password (JWT) and Google OAuth.
Designed for use with oapi-codegen and Chi.
**Note on Notifications:** Real-time notifications (e.g., via SSE or WebSockets) are planned but not fully described in this OpenAPI specification due to limitations in representing asynchronous APIs. These will be documented separately.
**Note on Tag Deletion:** Deleting a Tag will typically remove its association from any Todo items currently using it.
servers:
- url: /api/v1
description: API version 1
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT authentication token provided in the Authorization header.
CookieAuth:
type: apiKey
in: cookie
name: jwt_token
description: JWT authentication token provided via an HTTP-only cookie.
schemas:
# --- User Schemas ---
User:
type: object
description: Represents a registered user.
properties:
id:
type: string
format: uuid
readOnly: true
username:
type: string
email:
type: string
format: email
emailVerified:
type: boolean
readOnly: true
description: Indicates if the user's email has been verified (e.g., via OAuth or email confirmation).
createdAt:
type: string
format: date-time
readOnly: true
updatedAt:
type: string
format: date-time
readOnly: true
required:
- id
- username
- email
- emailVerified
- createdAt
- updatedAt
SignupRequest:
type: object
description: Data required for signing up a new user via email/password.
properties:
username:
type: string
minLength: 3
maxLength: 50
email:
type: string
format: email
password:
type: string
minLength: 6
writeOnly: true
required:
- username
- email
- password
LoginRequest:
type: object
description: Data required for logging in via email/password.
properties:
email:
type: string
format: email
password:
type: string
writeOnly: true
required:
- email
- password
LoginResponse:
type: object
description: Response containing the JWT access token for API clients. For browser clients, a cookie is typically set instead.
properties:
accessToken:
type: string
description: JWT access token.
tokenType:
type: string
default: "Bearer"
description: Type of the token (always Bearer).
required:
- accessToken
- tokenType
UpdateUserRequest:
type: object
description: Data for updating user details.
properties:
username:
type: string
minLength: 3
maxLength: 50
# --- Tag Schemas ---
Tag:
type: object
description: Represents a user-defined tag for organizing Todos.
properties:
id:
type: string
format: uuid
readOnly: true
userId:
type: string
format: uuid
readOnly: true
description: The ID of the user who owns this Tag.
name:
type: string
description: Name of the tag (e.g., "Work", "Personal"). Must be unique per user.
color:
type: string
format: hexcolor
nullable: true
description: Optional color associated with the tag.
icon:
type: string
nullable: true
description: Optional identifier for an icon associated with the tag (e.g., 'briefcase', 'home'). Frontend maps this to actual icon display.
createdAt:
type: string
format: date-time
readOnly: true
updatedAt:
type: string
format: date-time
readOnly: true
required:
- id
- userId
- name
- createdAt
- updatedAt
CreateTagRequest:
type: object
description: Data required to create a new Tag.
properties:
name:
type: string
minLength: 1
maxLength: 50
description: Name of the tag. Must be unique for the user.
color:
type: string
format: hexcolor
nullable: true
description: Optional color code (e.g., #FF5733).
icon:
type: string
nullable: true
maxLength: 30
description: Optional icon identifier.
required:
- name
UpdateTagRequest:
type: object
description: Data for updating an existing Tag. All fields are optional.
properties:
name:
type: string
minLength: 1
maxLength: 50
description: New name for the tag. Must be unique for the user.
color:
type: string
format: hexcolor
nullable: true
description: New color code.
icon:
type: string
nullable: true
maxLength: 30
description: New icon identifier.
# --- Attachment Info Schema ---
AttachmentInfo:
type: object
description: Metadata about an uploaded attachment.
properties:
fileId:
type: string
description: Unique storage identifier/path for the file (used for deletion).
fileName:
type: string
description: Original name of the uploaded file.
fileUrl:
type: string
format: url
description: URL to access the uploaded file (e.g., a signed GCS URL).
contentType:
type: string
description: MIME type of the uploaded file.
size:
type: integer
format: int64
description: Size of the uploaded file in bytes.
required:
- fileId
- fileName
- fileUrl
- contentType
- size
# --- Todo Schemas ---
Todo:
type: object
description: Represents a Todo item.
properties:
id: { type: string, format: uuid, readOnly: true }
userId: { type: string, format: uuid, readOnly: true }
title: { type: string }
description: { type: string, nullable: true }
status: { type: string, enum: [pending, in-progress, completed], default: pending }
deadline: { type: string, format: date-time, nullable: true }
tagIds:
type: array
items: { type: string, format: uuid }
default: []
attachmentUrl: # <-- Changed from attachments array
type: string
format: url
nullable: true
description: Publicly accessible URL of the attached image, if any.
subtasks:
type: array
items: { $ref: '#/components/schemas/Subtask' }
readOnly: true
default: []
createdAt: { type: string, format: date-time, readOnly: true }
updatedAt: { type: string, format: date-time, readOnly: true }
required:
- id
- userId
- title
- status
- tagIds
- createdAt
- updatedAt
CreateTodoRequest:
type: object
description: Data required to create a new Todo item.
properties:
title:
type: string
minLength: 1
description:
type: string
nullable: true
status:
type: string
enum: [pending, in-progress, completed]
default: pending
deadline:
type: string
format: date-time
nullable: true
tagIds:
type: array
items:
type: string
format: uuid
description: Optional list of existing Tag IDs to associate with the new Todo. IDs must belong to the user.
default: []
required:
- title
UpdateTodoRequest:
type: object
description: Data for updating an existing Todo item. Attachment is managed via dedicated endpoints.
properties:
title: { type: string, minLength: 1 }
description: { type: string, nullable: true }
status: { type: string, enum: [pending, in-progress, completed] }
deadline: { type: string, format: date-time, nullable: true }
tagIds:
type: array
items: { type: string, format: uuid }
# --- Subtask Schemas ---
Subtask:
type: object
description: Represents a subtask associated with a Todo item.
properties:
id:
type: string
format: uuid
readOnly: true
todoId:
type: string
format: uuid
readOnly: true
description: The ID of the parent Todo item.
description:
type: string
description: Description of the subtask.
completed:
type: boolean
default: false
description: Whether the subtask is completed.
createdAt:
type: string
format: date-time
readOnly: true
updatedAt:
type: string
format: date-time
readOnly: true
required:
- id
- todoId
- description
- completed
- createdAt
- updatedAt
CreateSubtaskRequest:
type: object
description: Data required to create a new Subtask.
properties:
description:
type: string
minLength: 1
required:
- description
UpdateSubtaskRequest:
type: object
description: Data for updating an existing Subtask. Both fields are optional.
properties:
description:
type: string
minLength: 1
completed:
type: boolean
# --- File Upload Response Schema ---
FileUploadResponse: # This is the same as AttachmentInfo, could reuse definition with $ref
$ref: '#/components/schemas/AttachmentInfo'
# --- Error Schema ---
Error:
type: object
description: Standard error response format.
properties:
code:
type: integer
format: int32
description: HTTP status code or application-specific code.
message:
type: string
description: Detailed error message.
required:
- code
- message
responses:
BadRequest:
description: Invalid input (e.g., validation error, missing fields, invalid tag ID).
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Unauthorized:
description: Authentication failed (e.g., invalid credentials, invalid/expired token/cookie, missing authentication).
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Forbidden:
description: Authorization failed (e.g., user does not have permission to access or modify the resource, such as another user's tag).
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
NotFound:
description: The requested resource (e.g., Todo, Tag, Subtask, Attachment) was not found.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Conflict:
description: Conflict (e.g., username or email already exists, tag name already exists for the user, resource state conflict).
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
InternalServerError:
description: Internal server error.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
security:
- BearerAuth: []
- CookieAuth: []
paths:
# --- Authentication Endpoints ---
/auth/signup:
post:
summary: Register a new user via email/password (API).
operationId: signupUserApi
tags: [Auth]
security: []
requestBody:
required: true
description: User details for registration.
content:
application/json:
schema:
$ref: "#/components/schemas/SignupRequest"
responses:
"201":
description: User created successfully. Returns the new user object.
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
$ref: "#/components/responses/BadRequest"
"409":
$ref: "#/components/responses/Conflict"
"500":
$ref: "#/components/responses/InternalServerError"
/auth/login:
post:
summary: Log in a user via email/password (API).
description: Authenticates a user and returns a JWT access token in the response body for API clients. For browser clients, this endpoint typically also sets an HTTP-only cookie containing the JWT.
operationId: loginUserApi
tags: [Auth]
security: []
requestBody:
required: true
description: User credentials for login.
content:
application/json:
schema:
$ref: "#/components/schemas/LoginRequest"
responses:
"200":
description: Login successful. Returns JWT token for API clients. Sets auth cookie for browsers.
content:
application/json:
schema:
$ref: "#/components/schemas/LoginResponse"
headers:
Set-Cookie:
schema:
type: string
description: Contains the JWT authentication cookie (e.g., `jwt_token=...; HttpOnly; Secure; Path=/; SameSite=Lax`)
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"500":
$ref: "#/components/responses/InternalServerError"
/auth/logout:
post:
summary: Log out the current user.
description: Invalidates the current session (e.g., clears the authentication cookie).
operationId: logoutUser
tags: [Auth]
security:
- BearerAuth: []
- CookieAuth: []
responses:
"204":
description: Logout successful. No content returned.
headers:
Set-Cookie:
schema:
type: string
description: Clears the JWT authentication cookie (e.g., `jwt_token=; HttpOnly; Secure; Path=/; Max-Age=0; SameSite=Lax`)
"401":
$ref: "#/components/responses/Unauthorized"
"500":
$ref: "#/components/responses/InternalServerError"
/auth/google/login:
get:
summary: Initiate Google OAuth login flow.
description: Redirects the user's browser to Google's authentication page. Not a typical REST endpoint, part of the web flow.
operationId: initiateGoogleLogin
tags: [Auth]
security: []
responses:
"302":
description: Redirect to Google's OAuth consent screen. The 'Location' header contains the redirect URL.
headers:
Location:
schema:
type: string
format: url
description: URL to Google's OAuth endpoint.
"500":
description: Server error during redirect URL generation.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/auth/google/callback:
get:
summary: Callback endpoint for Google OAuth flow.
description: Google redirects the user here after authentication. The server exchanges the received code for tokens, finds/creates the user, generates a JWT, sets the auth cookie, and redirects the user (e.g., to the web app dashboard).
operationId: handleGoogleCallback
tags: [Auth]
security: []
responses:
"302":
description: Authentication successful. Redirects the user to the frontend application (e.g., '/dashboard'). Sets auth cookie.
headers:
Location:
schema:
type: string
description: Redirect URL within the application after successful login.
Set-Cookie:
schema:
type: string
description: Contains the JWT authentication cookie.
"401":
description: Authentication failed with Google or failed to process callback. Redirects to a login/error page.
headers:
Location:
schema:
type: string
description: Redirect URL to an error or login page.
"500":
description: Internal server error during callback processing. Redirects to an error page.
headers:
Location:
schema:
type: string
description: Redirect URL to an error page.
# --- User Endpoints ---
/users/me:
get:
summary: Get current authenticated user's details.
operationId: getCurrentUser
tags: [Users]
security:
- BearerAuth: []
- CookieAuth: []
responses:
"200":
description: Current user details.
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"401":
$ref: "#/components/responses/Unauthorized"
"500":
$ref: "#/components/responses/InternalServerError"
patch:
summary: Update current authenticated user's details.
operationId: updateCurrentUser
tags: [Users]
security:
- BearerAuth: []
- CookieAuth: []
requestBody:
required: true
description: User details to update. Only fields provided will be updated.
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
responses:
"200":
description: User updated successfully. Returns the updated user object.
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"409":
$ref: "#/components/responses/Conflict"
"500":
$ref: "#/components/responses/InternalServerError"
# --- Tag Endpoints ---
/tags:
get:
summary: List all tags created by the current user.
operationId: listUserTags
tags: [Tags]
security:
- BearerAuth: []
- CookieAuth: []
responses:
"200":
description: A list of the user's tags.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Tag'
"401":
$ref: "#/components/responses/Unauthorized"
"500":
$ref: "#/components/responses/InternalServerError"
post:
summary: Create a new tag.
operationId: createTag
tags: [Tags]
security:
- BearerAuth: []
- CookieAuth: []
requestBody:
required: true
description: Details of the tag to create.
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTagRequest'
responses:
"201":
description: Tag created successfully. Returns the new tag.
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"409":
$ref: "#/components/responses/Conflict"
"500":
$ref: "#/components/responses/InternalServerError"
/tags/{tagId}:
parameters:
- name: tagId
in: path
required: true
schema:
type: string
format: uuid
description: ID of the Tag.
get:
summary: Get a specific tag by ID.
operationId: getTagById
tags: [Tags]
security:
- BearerAuth: []
- CookieAuth: []
responses:
"200":
description: The requested Tag details.
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"
patch:
summary: Update a specific tag by ID.
operationId: updateTagById
tags: [Tags]
security:
- BearerAuth: []
- CookieAuth: []
requestBody:
required: true
description: Fields of the tag to update.
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateTagRequest'
responses:
"200":
description: Tag updated successfully. Returns the updated tag.
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"409":
$ref: "#/components/responses/Conflict"
"500":
$ref: "#/components/responses/InternalServerError"
delete:
summary: Delete a specific tag by ID.
description: Deletes a tag owned by the user. This will typically remove the tag's ID from any Todos currently associated with it.
operationId: deleteTagById
tags: [Tags]
security:
- BearerAuth: []
- CookieAuth: []
responses:
"204":
description: Tag deleted successfully. No content.
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"
# --- Todo Endpoints ---
/todos:
get:
summary: List Todo items for the current user.
operationId: listTodos
tags: [Todos]
security:
- BearerAuth: []
- CookieAuth: []
parameters:
- { name: status, in: query, required: false, schema: { type: string, enum: [pending, in-progress, completed] } }
- { name: tagId, in: query, required: false, schema: { type: string, format: uuid } }
- { name: limit, in: query, required: false, schema: { type: integer, minimum: 1, default: 20 } }
- { name: offset, in: query, required: false, schema: { type: integer, minimum: 0, default: 0 } }
responses:
"200":
description: A list of Todo items.
content: { application/json: { schema: { type: array, items: { $ref: "#/components/schemas/Todo" } } } }
"401":
$ref: "#/components/responses/Unauthorized"
"500":
$ref: "#/components/responses/InternalServerError"
post:
summary: Create a new Todo item.
operationId: createTodo
tags: [Todos]
requestBody:
required: true
content: { application/json: { schema: { $ref: "#/components/schemas/CreateTodoRequest" } } }
responses:
"201":
description: Todo item created successfully.
content: { application/json: { schema: { $ref: "#/components/schemas/Todo" } } }
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"500":
$ref: "#/components/responses/InternalServerError"
/todos/{todoId}:
parameters:
- { name: todoId, in: path, required: true, schema: { type: string, format: uuid } }
get:
summary: Get a specific Todo item by ID.
operationId: getTodoById
tags: [Todos]
responses:
"200":
description: The requested Todo item.
content: { application/json: { schema: { $ref: "#/components/schemas/Todo" } } }
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"
patch:
summary: Update a specific Todo item by ID.
operationId: updateTodoById
tags: [Todos]
requestBody:
required: true
content: { application/json: { schema: { $ref: "#/components/schemas/UpdateTodoRequest" } } }
responses:
"200":
description: Todo item updated successfully.
content: { application/json: { schema: { $ref: "#/components/schemas/Todo" } } }
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"
delete:
summary: Delete a specific Todo item by ID.
operationId: deleteTodoById
tags: [Todos]
security:
- BearerAuth: []
- CookieAuth: []
responses:
"204":
description: Todo item deleted successfully. No content.
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"
# --- Attachment Endpoints ---
/todos/{todoId}/attachments:
parameters:
- { name: todoId, in: path, required: true, schema: { type: string, format: uuid } }
post:
summary: Upload or replace the image attachment for a Todo item.
operationId: uploadOrReplaceTodoAttachment # Renamed for clarity
tags: [Attachments, Todos]
requestBody:
required: true
description: The image file to upload.
content:
multipart/form-data:
schema:
type: object
properties: { file: { type: string, format: binary } }
required: [file]
responses:
"201": # Use 201 Created (or 200 OK if replacing)
description: Image uploaded/replaced successfully. Returns file details.
content: { application/json: { schema: { $ref: '#/components/schemas/FileUploadResponse' } } } # Reusing this schema
"400": { $ref: "#/components/responses/BadRequest" } # Invalid file type, size limit etc.
"401": { $ref: "#/components/responses/Unauthorized" }
"403": { $ref: "#/components/responses/Forbidden" }
"404": { $ref: "#/components/responses/NotFound" } # Todo not found
"500": { $ref: "#/components/responses/InternalServerError" }
delete:
summary: Delete the image attachment from a Todo item.
operationId: deleteTodoAttachment # Reused name is fine
tags: [Attachments, Todos]
responses:
"204": { description: Attachment deleted successfully. }
"401": { $ref: "#/components/responses/Unauthorized" }
"403": { $ref: "#/components/responses/Forbidden" }
"404": { $ref: "#/components/responses/NotFound" } # Todo or attachment not found
"500": { $ref: "#/components/responses/InternalServerError" }
# --- Subtask Endpoints ---
/todos/{todoId}/subtasks:
parameters:
- { name: todoId, in: path, required: true, schema: { type: string, format: uuid } }
get:
summary: List all subtasks for a specific Todo item.
operationId: listSubtasksForTodo
tags: [Subtasks, Todos]
security:
- BearerAuth: []
- CookieAuth: []
responses:
"200":
description: A list of subtasks for the specified Todo.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Subtask'
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"
post:
summary: Create a new subtask for a specific Todo item.
operationId: createSubtaskForTodo
tags: [Subtasks, Todos]
security:
- BearerAuth: []
- CookieAuth: []
requestBody:
required: true
description: Details of the subtask to create.
content:
application/json:
schema:
$ref: '#/components/schemas/CreateSubtaskRequest'
responses:
"201":
description: Subtask created successfully. Returns the new subtask.
content:
application/json:
schema:
$ref: '#/components/schemas/Subtask'
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"
/todos/{todoId}/subtasks/{subtaskId}:
parameters:
- name: todoId
in: path
required: true
schema:
type: string
format: uuid
description: ID of the parent Todo item.
- name: subtaskId
in: path
required: true
schema:
type: string
format: uuid
description: ID of the Subtask item.
patch:
summary: Update a specific subtask.
operationId: updateSubtaskById
tags: [Subtasks, Todos]
security:
- BearerAuth: []
- CookieAuth: []
requestBody:
required: true
description: Fields of the subtask to update.
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateSubtaskRequest'
responses:
"200":
description: Subtask updated successfully. Returns the updated subtask.
content:
application/json:
schema:
$ref: '#/components/schemas/Subtask'
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"
delete:
summary: Delete a specific subtask.
operationId: deleteSubtaskById
tags: [Subtasks, Todos]
security:
- BearerAuth: []
- CookieAuth: []
responses:
"204":
description: Subtask deleted successfully. No content.
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"