922 lines
22 KiB
YAML
922 lines
22 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Reference Board Viewer API
|
|
description: |
|
|
REST API for the Reference Board Viewer application - a web-based tool for artists
|
|
to collect, organize, and manipulate visual reference images.
|
|
version: 1.0.0
|
|
contact:
|
|
name: API Support
|
|
servers:
|
|
- url: http://localhost:8000/api/v1
|
|
description: Development server
|
|
- url: https://webref.example.com/api/v1
|
|
description: Production server
|
|
|
|
tags:
|
|
- name: Auth
|
|
description: Authentication and user management
|
|
- name: Boards
|
|
description: Board operations
|
|
- name: Images
|
|
description: Image upload and management
|
|
- name: Canvas
|
|
description: Canvas operations (positioning, transformations)
|
|
- name: Groups
|
|
description: Image grouping
|
|
- name: Sharing
|
|
description: Board sharing
|
|
|
|
security:
|
|
- BearerAuth: []
|
|
|
|
paths:
|
|
# ==================== Authentication ====================
|
|
/auth/register:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Register new user
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [email, password]
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
example: user@example.com
|
|
password:
|
|
type: string
|
|
minLength: 8
|
|
example: SecurePass123
|
|
responses:
|
|
'201':
|
|
description: User registered successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UserResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'409':
|
|
$ref: '#/components/responses/Conflict'
|
|
|
|
/auth/login:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Login user
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [email, password]
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
password:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Login successful
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
example: eyJhbGciOiJIUzI1NiIs...
|
|
token_type:
|
|
type: string
|
|
example: bearer
|
|
user:
|
|
$ref: '#/components/schemas/UserResponse'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/auth/me:
|
|
get:
|
|
tags: [Auth]
|
|
summary: Get current user
|
|
responses:
|
|
'200':
|
|
description: Current user details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UserResponse'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
# ==================== Boards ====================
|
|
/boards:
|
|
get:
|
|
tags: [Boards]
|
|
summary: List user's boards
|
|
parameters:
|
|
- name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 50
|
|
maximum: 100
|
|
- name: offset
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 0
|
|
responses:
|
|
'200':
|
|
description: List of boards
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
boards:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/BoardSummary'
|
|
total:
|
|
type: integer
|
|
limit:
|
|
type: integer
|
|
offset:
|
|
type: integer
|
|
|
|
post:
|
|
tags: [Boards]
|
|
summary: Create new board
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [title]
|
|
properties:
|
|
title:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 255
|
|
example: Character Design References
|
|
description:
|
|
type: string
|
|
example: References for fantasy knight character
|
|
responses:
|
|
'201':
|
|
description: Board created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BoardDetail'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
|
|
/boards/{board_id}:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
|
|
get:
|
|
tags: [Boards]
|
|
summary: Get board details
|
|
responses:
|
|
'200':
|
|
description: Board details with all images
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BoardDetail'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
patch:
|
|
tags: [Boards]
|
|
summary: Update board
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
title:
|
|
type: string
|
|
description:
|
|
type: string
|
|
viewport_state:
|
|
$ref: '#/components/schemas/ViewportState'
|
|
responses:
|
|
'200':
|
|
description: Board updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BoardDetail'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
delete:
|
|
tags: [Boards]
|
|
summary: Delete board
|
|
responses:
|
|
'204':
|
|
description: Board deleted
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
# ==================== Images ====================
|
|
/boards/{board_id}/images:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
|
|
post:
|
|
tags: [Images]
|
|
summary: Upload image(s) to board
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
required: [files]
|
|
properties:
|
|
files:
|
|
type: array
|
|
items:
|
|
type: string
|
|
format: binary
|
|
maxItems: 50
|
|
position:
|
|
type: string
|
|
description: JSON string of default position
|
|
example: '{"x": 0, "y": 0}'
|
|
responses:
|
|
'201':
|
|
description: Images uploaded
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
images:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/BoardImage'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'413':
|
|
description: File too large
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
/boards/{board_id}/images/{image_id}:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
- $ref: '#/components/parameters/ImageId'
|
|
|
|
patch:
|
|
tags: [Canvas]
|
|
summary: Update image position/transformations
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
position:
|
|
$ref: '#/components/schemas/Position'
|
|
transformations:
|
|
$ref: '#/components/schemas/Transformations'
|
|
z_order:
|
|
type: integer
|
|
group_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
responses:
|
|
'200':
|
|
description: Image updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BoardImage'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
delete:
|
|
tags: [Canvas]
|
|
summary: Remove image from board
|
|
responses:
|
|
'204':
|
|
description: Image removed from board
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
/boards/{board_id}/images/bulk:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
|
|
patch:
|
|
tags: [Canvas]
|
|
summary: Bulk update multiple images
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [image_ids, updates]
|
|
properties:
|
|
image_ids:
|
|
type: array
|
|
items:
|
|
type: string
|
|
format: uuid
|
|
updates:
|
|
type: object
|
|
properties:
|
|
position_delta:
|
|
type: object
|
|
properties:
|
|
dx:
|
|
type: number
|
|
dy:
|
|
type: number
|
|
transformations:
|
|
$ref: '#/components/schemas/Transformations'
|
|
z_order_delta:
|
|
type: integer
|
|
responses:
|
|
'200':
|
|
description: Images updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
updated_count:
|
|
type: integer
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
|
|
# ==================== Groups ====================
|
|
/boards/{board_id}/groups:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
|
|
get:
|
|
tags: [Groups]
|
|
summary: List board groups
|
|
responses:
|
|
'200':
|
|
description: List of groups
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Group'
|
|
|
|
post:
|
|
tags: [Groups]
|
|
summary: Create group
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [name, color, image_ids]
|
|
properties:
|
|
name:
|
|
type: string
|
|
example: Armor References
|
|
color:
|
|
type: string
|
|
pattern: '^#[0-9A-Fa-f]{6}$'
|
|
example: '#FF5733'
|
|
annotation:
|
|
type: string
|
|
example: Blue plate armor designs
|
|
image_ids:
|
|
type: array
|
|
items:
|
|
type: string
|
|
format: uuid
|
|
responses:
|
|
'201':
|
|
description: Group created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Group'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
|
|
/boards/{board_id}/groups/{group_id}:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
- $ref: '#/components/parameters/GroupId'
|
|
|
|
patch:
|
|
tags: [Groups]
|
|
summary: Update group
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
annotation:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Group updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Group'
|
|
|
|
delete:
|
|
tags: [Groups]
|
|
summary: Delete group (ungroups images)
|
|
responses:
|
|
'204':
|
|
description: Group deleted
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
# ==================== Sharing ====================
|
|
/boards/{board_id}/share-links:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
|
|
get:
|
|
tags: [Sharing]
|
|
summary: List board share links
|
|
responses:
|
|
'200':
|
|
description: List of share links
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ShareLink'
|
|
|
|
post:
|
|
tags: [Sharing]
|
|
summary: Create share link
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [permission_level]
|
|
properties:
|
|
permission_level:
|
|
type: string
|
|
enum: [view-only, view-comment]
|
|
expires_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
responses:
|
|
'201':
|
|
description: Share link created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ShareLink'
|
|
|
|
/boards/{board_id}/share-links/{link_id}:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
- name: link_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
|
|
delete:
|
|
tags: [Sharing]
|
|
summary: Revoke share link
|
|
responses:
|
|
'204':
|
|
description: Share link revoked
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
/shared/{token}:
|
|
parameters:
|
|
- name: token
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
|
|
get:
|
|
tags: [Sharing]
|
|
summary: Access shared board
|
|
security: []
|
|
responses:
|
|
'200':
|
|
description: Shared board details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
board:
|
|
$ref: '#/components/schemas/BoardDetail'
|
|
permission_level:
|
|
type: string
|
|
enum: [view-only, view-comment]
|
|
'404':
|
|
description: Invalid or expired token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
# ==================== Export ====================
|
|
/boards/{board_id}/export:
|
|
parameters:
|
|
- $ref: '#/components/parameters/BoardId'
|
|
|
|
post:
|
|
tags: [Boards]
|
|
summary: Export board
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [format]
|
|
properties:
|
|
format:
|
|
type: string
|
|
enum: [zip, composite]
|
|
resolution:
|
|
type: integer
|
|
enum: [1, 2, 4]
|
|
default: 1
|
|
description: Resolution multiplier (for composite)
|
|
responses:
|
|
'200':
|
|
description: Export file
|
|
content:
|
|
application/zip:
|
|
schema:
|
|
type: string
|
|
format: binary
|
|
image/png:
|
|
schema:
|
|
type: string
|
|
format: binary
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
|
|
# ==================== Image Library ====================
|
|
/library/images:
|
|
get:
|
|
tags: [Images]
|
|
summary: List user's image library
|
|
parameters:
|
|
- name: search
|
|
in: query
|
|
schema:
|
|
type: string
|
|
- name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 50
|
|
- name: offset
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 0
|
|
responses:
|
|
'200':
|
|
description: Image library
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
images:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ImageMetadata'
|
|
total:
|
|
type: integer
|
|
|
|
# ==================== Components ====================
|
|
components:
|
|
securitySchemes:
|
|
BearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
|
|
parameters:
|
|
BoardId:
|
|
name: board_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
|
|
ImageId:
|
|
name: image_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
|
|
GroupId:
|
|
name: group_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
|
|
schemas:
|
|
UserResponse:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
email:
|
|
type: string
|
|
format: email
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
|
|
BoardSummary:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
title:
|
|
type: string
|
|
description:
|
|
type: string
|
|
nullable: true
|
|
image_count:
|
|
type: integer
|
|
thumbnail_url:
|
|
type: string
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
|
|
BoardDetail:
|
|
allOf:
|
|
- $ref: '#/components/schemas/BoardSummary'
|
|
- type: object
|
|
properties:
|
|
viewport_state:
|
|
$ref: '#/components/schemas/ViewportState'
|
|
images:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/BoardImage'
|
|
groups:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Group'
|
|
|
|
ViewportState:
|
|
type: object
|
|
properties:
|
|
x:
|
|
type: number
|
|
example: 0
|
|
y:
|
|
type: number
|
|
example: 0
|
|
zoom:
|
|
type: number
|
|
minimum: 0.1
|
|
maximum: 5.0
|
|
example: 1.0
|
|
rotation:
|
|
type: number
|
|
minimum: 0
|
|
maximum: 360
|
|
example: 0
|
|
|
|
ImageMetadata:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
filename:
|
|
type: string
|
|
file_size:
|
|
type: integer
|
|
mime_type:
|
|
type: string
|
|
width:
|
|
type: integer
|
|
height:
|
|
type: integer
|
|
thumbnail_urls:
|
|
type: object
|
|
properties:
|
|
low:
|
|
type: string
|
|
medium:
|
|
type: string
|
|
high:
|
|
type: string
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
reference_count:
|
|
type: integer
|
|
|
|
BoardImage:
|
|
allOf:
|
|
- $ref: '#/components/schemas/ImageMetadata'
|
|
- type: object
|
|
properties:
|
|
position:
|
|
$ref: '#/components/schemas/Position'
|
|
transformations:
|
|
$ref: '#/components/schemas/Transformations'
|
|
z_order:
|
|
type: integer
|
|
group_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
|
|
Position:
|
|
type: object
|
|
properties:
|
|
x:
|
|
type: number
|
|
y:
|
|
type: number
|
|
|
|
Transformations:
|
|
type: object
|
|
properties:
|
|
scale:
|
|
type: number
|
|
minimum: 0.01
|
|
maximum: 10.0
|
|
default: 1.0
|
|
rotation:
|
|
type: number
|
|
minimum: 0
|
|
maximum: 360
|
|
default: 0
|
|
opacity:
|
|
type: number
|
|
minimum: 0.0
|
|
maximum: 1.0
|
|
default: 1.0
|
|
flipped_h:
|
|
type: boolean
|
|
default: false
|
|
flipped_v:
|
|
type: boolean
|
|
default: false
|
|
crop:
|
|
type: object
|
|
nullable: true
|
|
properties:
|
|
x:
|
|
type: number
|
|
y:
|
|
type: number
|
|
width:
|
|
type: number
|
|
height:
|
|
type: number
|
|
greyscale:
|
|
type: boolean
|
|
default: false
|
|
|
|
Group:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
pattern: '^#[0-9A-Fa-f]{6}$'
|
|
annotation:
|
|
type: string
|
|
nullable: true
|
|
member_count:
|
|
type: integer
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
|
|
ShareLink:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
token:
|
|
type: string
|
|
permission_level:
|
|
type: string
|
|
enum: [view-only, view-comment]
|
|
url:
|
|
type: string
|
|
example: https://webref.example.com/shared/abc123...
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
expires_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
access_count:
|
|
type: integer
|
|
is_revoked:
|
|
type: boolean
|
|
|
|
Error:
|
|
type: object
|
|
properties:
|
|
error:
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: string
|
|
code:
|
|
type: string
|
|
details:
|
|
type: object
|
|
nullable: true
|
|
|
|
responses:
|
|
BadRequest:
|
|
description: Bad request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
Unauthorized:
|
|
description: Unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
NotFound:
|
|
description: Resource not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
Conflict:
|
|
description: Resource conflict
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|