blahrs/docs/webapi.yaml
2024-09-13 03:32:11 -04:00

645 lines
18 KiB
YAML

openapi: 3.1.0
info:
title: Blah Chatserver Proto
version: 0.0.1
paths:
# OAPI does not support WebSocket interface definitions.
# See: https://github.com/OAI/OpenAPI-Specification/issues/55#issuecomment-929382279
/ws:
get:
summary: WebSocket endpoint
description: |
This endpoint is for server-side-event dispatching.
Once connected, client must send a JSON text message of type
`Signed-Auth` for authentication.
If server does not close it immediately, it means success.
Since OAPI does not support WebSocket interface, we use request and
response types documented here mean outgoing and incoming JSON text
messages.
parameters:
- name: Connection
in: header
required: true
- name: Upgrade
in: header
required: true
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/WSClientToServer'
responses:
101:
headers:
Connection:
required: true
Upgrade:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WSServerToClient'
/room:
get:
summary: List rooms
parameters:
- name: filter
in: query
required: true
schema:
enum:
- public
- joined
- unseen
description: |
Must be one of following values:
- "public": list all public rooms on the server.
- "joined": list rooms the user have joined.
Requires `Authorization`.
- "unseen": list rooms the user have joined and have unseen
messages.
Requires `Authorization`.
- name: top
in: query
schema:
type: string
description:
The maximum count of rooms returned in a single response. This is
only an advice and server can clamp it to a smaller value.
- name: skipToken
in: query
schema:
type: string
description:
The page token returned from a previous list response to fetch the
next page. NB. Other parameters (eg. `joined` and `page_len`)
should be included (as the same value) for each page fetch.
- name: Authorization
in: header
description: Optional proof of membership for private rooms.
schema:
$ref: '#/components/schemas/Signed-Auth'
responses:
200:
description: Filtered and paged rooms.
content:
application/json:
schema:
$ref: '#/components/schemas/RoomList'
401:
description: Missing or invalid Authorization header.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
/room/create:
post:
summary: Create a room
description:
When `typ="create_room"`, create a multi-user room.
When `typ="create_peer_chat"`, create a peer-to-peer room between two
users. There can be at most one peer room for each given user pair.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Signed-CreateRoom'
responses:
200:
description: Room created.
content:
application/json:
schema:
type: string
description: Newly created room `rid`.
403:
description: The user does not have permission to create room.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
404:
description: The peer user does not exist or disallows peer chat.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
409:
description: There is already a peer chat room between the user pair.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
/room/{rid}:
get:
summary: Get room metadata
parameters:
- name: Authorization
in: header
description: Optional proof of membership for private rooms.
schema:
$ref: '#/components/schemas/Signed-Auth'
responses:
200:
description: The metadata of the specified room.
content:
application/json:
schema:
$ref: '#/components/schemas/RoomMetadata'
404:
description: |
Room does not exist or the user does not have permission to get metadata of it.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
/room/{rid}/admin:
post:
summary: Room management
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Signed-RoomAdmin'
responses:
204:
description: Operation completed.
404:
description: |
Room does not exist or the user does not have permission for the
operation.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
409:
description:
Operation is already done, eg. joining an already joined room.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
/room/{rid}/feed.json:
get:
summary: Get JSON feed of room
description: |
Get room {rid}'s content in JSON feed v1.1 format. The room must be
public. For human and feed reader consumption only.
responses:
200:
description: The JSON feed.
content:
text/feed+json:
schema:
$ref: 'https://www.jsonfeed.org/version/1.1/'
404:
description: Room does not exist or is private.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
/room/{rid}/msg:
get:
summary: List messages in a room
description: |
Return a list of messages in reversed server time order, up to length `top`
in a single response, from room {rid}.
The last (oldest) message's `cid` will be returned as `skipToken` in
response, which can be used as query parameter for the next GET, to
repeatedly fetch more history.
parameters:
- name: Authorization
in: header
description: Optional proof of membership for private rooms.
schema:
$ref: '#/components/schemas/Signed-Auth'
- name: top
in: query
schema:
type: integer
description: |
The number of items returned in a single response. This is
an advice and may be further clamped by the server. It must not be
zero.
- name: skipToken
in: query
schema:
type: string
description: |
Return items after (older than) an existing `cid`. Useful for
pagination.
responses:
200:
content:
application/json:
schema:
$ref: '#/components/schemas/RoomMsgs'
404:
description: |
Room does not exist or the user does not have permission to read it.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
post:
summary: Post a `Msg` into a room
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Signed-Chat'
responses:
200:
content:
application/json:
schema:
type: string
description: Newly created message id `cid`.
403:
description: The user does not have permission to post in this room.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
404:
description: The room does not exist or the user is not a room member.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
/room/{rid}/msg/{cid}/seen:
post:
summary: Mark a message seen
description: |
Mark message {cid} and everything before it in room {rid} seen by the
current user.
Server may enforce that last seen message does not go backward. Marking
an older message seen or sending the same request multiple times can be
a no-op.
parameters:
- name: Authorization
in: header
required: true
description: Proof of membership for private rooms.
schema:
$ref: '#/components/schemas/Signed-Auth'
responses:
204:
description: Operation completed.
404:
description: |
Room does not exist or the user is not in the room.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
# Ideally we should generate these from src, but we need to
# WAIT: https://github.com/juhaku/utoipa/pull/1034
components:
schemas:
WSClientToServer:
anyOf:
- $ref: '#/components/schemas/Signed-Auth'
WSServerToClient:
anyOf:
- type: object
properties:
chat:
$ref: '#/components/schemas/Signed-Chat'
- type: object
properties:
lagged:
type: object
const: {}
ApiError:
type: object
properties:
error:
type: object
properties:
code:
type: string
description: A machine-readable error code string.
example: invalid_signature
message:
type: string
description: A human-readable error message.
example: signature verification failed
RoomList:
type: object
required:
- rooms
properties:
rooms:
type: array
items:
$ref: '#/components/schemas/RoomMetadataForList'
next_token:
type: string
description: An opaque token to fetch the next page.
RoomMetadataForList:
type: object
required: ['rid', 'title', 'attrs']
properties:
rid:
type: string
title:
type: string
attrs:
description: Room attributes bitset, see `RoomAttrs`.
type: integer
format: int64
last_msg:
$ref: '#/components/schemas/WithMsgId-Signed-Chat'
last_seen_cid:
description: The `cid` of the last chat being marked as seen.
type: string
unseen_cnt:
description: |
The number of unseen messages. Only available for
GET `/room?filter=unseen`.
type: integer
format: uint64
member_permission:
type: integer
format: int64
peer_user:
type: string
description: |
For peer chat room, this gives the identity of the peer user.
RoomMetadata:
type: object
required: ['rid', 'title', 'attrs']
properties:
rid:
type: string
title:
type: string
attrs:
type: integer
format: int64
RoomMsgs:
type: object
required:
- msgs
properties:
msgs:
description: Room messages in reversed server-received time order.
type: array
items:
$ref: '#/components/schemas/WithMsgId-Signed-Chat'
skip_token:
description: The token for fetching the next page.
type: string
RichText:
type: array
items:
anyOf:
- type: string
description: Unstyled text piece.
- type: array
items: false
prefixItems:
- type: string
description: The text piece to apply styles on.
- type: object
properties:
b:
type: boolean
description: Bold.
m:
type: boolean
description: Monospace.
i:
type: boolean
description: Italic.
s:
type: boolean
description: Strikethrough.
u:
type: boolean
description: Underline.
hashtag:
type: boolean
description: Hashtag.
link:
type: string
description: Link target.
Signed-Auth:
type: object
properties:
sig:
type: string
signee:
type: object
properties:
nonce:
type: integer
format: uint32
payload:
type: object
properties:
typ:
type: string
const: 'auth'
Signed-RoomAdmin:
type: object
properties:
sig:
type: string
signee:
type: object
properties:
nonce:
type: integer
format: uint32
payload:
oneOf:
- description: Add member to the room.
type: object
properties:
typ:
type: string
const: 'add_member'
room:
type: string
permission:
type: integer
format: uint64
user:
type: string
- description: Remove member from the room.
type: object
properties:
typ:
type: string
const: 'remove_member'
room:
type: string
user:
type: string
example:
sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
signee:
nonce: 670593955
payload:
permission: 1
room: 7ed9e067-ec37-4054-9fc2-b1bd890929bd
typ: add_member
user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
timestamp: 1724966284
user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
Signed-Chat:
type: object
properties:
sig:
type: string
signee:
type: object
properties:
nonce:
type: integer
format: uint32
payload:
type: object
properties:
typ:
type: string
const: 'chat'
room:
type: string
rich_text:
$ref: '$/components/schemas/RichText'
example:
sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
signee:
nonce: 670593955
payload:
typ: chat
room: 7ed9e067-ec37-4054-9fc2-b1bd890929bd
rich_text: ["before ",["bold ",{"b":true}],["italic bold ",{"b":true,"i":true}],"end"]
timestamp: 1724966284
user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
WithMsgId-Signed-Chat:
allOf:
- $ref: '#/components/schemas/Signed-Chat'
- type: object
properties:
cid:
type: string
description: An opaque server-specific identifier.
Signed-CreateRoom:
type: object
properties:
sig:
type: string
signee:
type: object
properties:
nonce:
type: integer
format: uint32
payload:
oneOf:
- type: object
properties:
typ:
type: string
const: 'create_room'
title:
type: string
members:
type: array
items:
type: object
properties:
user:
type: string
permission:
type: integer
format: int64
- type: object
properties:
typ:
type: string
const: 'create_peer_chat'
peer:
type: string
example:
sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
signee:
nonce: 670593955
payload:
typ: create_room
attrs: 1 # PUBLIC_READABLE
title: 'hello room'
members:
- user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
permission: -1
timestamp: 1724966284
user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7