// Response JSON Schemas mirroring the core types. Shared by the backend for // OpenAPI docs and response serialization, so the documented contract cannot // drift from what the API returns. import { mediaRefSchema } from './media'; export const publicUserSchema = { type: 'object', additionalProperties: false, required: ['id', 'username', 'displayName', 'avatarUrl'], properties: { id: { type: 'string', format: 'uuid' }, username: { type: 'string' }, displayName: { type: 'string' }, avatarUrl: { type: ['string', 'null'] }, }, } as const; export const userSchema = { type: 'object', additionalProperties: false, required: [ 'id', 'username', 'displayName', 'avatarUrl', 'email', 'phone', 'createdAt', 'lastSeenAt', ], properties: { id: { type: 'string', format: 'uuid' }, username: { type: 'string' }, displayName: { type: 'string' }, avatarUrl: { type: ['string', 'null'] }, email: { type: ['string', 'null'] }, phone: { type: ['string', 'null'] }, createdAt: { type: 'string', format: 'date-time' }, lastSeenAt: { type: ['string', 'null'], format: 'date-time' }, }, } as const; // The refresh token is delivered as an httpOnly cookie, so it is deliberately // absent from the response body. export const authResultSchema = { type: 'object', additionalProperties: false, required: ['user', 'accessToken', 'accessTokenExpiresIn'], properties: { user: userSchema, accessToken: { type: 'string' }, accessTokenExpiresIn: { type: 'integer' }, }, } as const; export const sessionSchema = { type: 'object', additionalProperties: false, required: ['id', 'userAgent', 'ip', 'createdAt', 'lastUsedAt', 'current'], properties: { id: { type: 'string', format: 'uuid' }, userAgent: { type: ['string', 'null'] }, ip: { type: ['string', 'null'] }, createdAt: { type: 'string', format: 'date-time' }, lastUsedAt: { type: 'string', format: 'date-time' }, current: { type: 'boolean' }, }, } as const; export const sessionListSchema = { type: 'array', items: sessionSchema, } as const; export const centrifugoTokenSchema = { type: 'object', additionalProperties: false, required: ['token', 'expiresIn'], properties: { token: { type: 'string' }, expiresIn: { type: 'integer' }, }, } as const; export const errorSchema = { type: 'object', required: ['error'], properties: { error: { type: 'string' }, message: { type: 'string' }, }, } as const; export const publicUserListSchema = { type: 'array', items: publicUserSchema } as const; export const conversationSchema = { type: 'object', additionalProperties: false, required: [ 'id', 'type', 'title', 'peer', 'createdBy', 'createdAt', 'lastMessageAt', 'unreadCount', ], properties: { id: { type: 'string', format: 'uuid' }, type: { type: 'string', enum: ['direct', 'group'] }, title: { type: ['string', 'null'] }, peer: { oneOf: [publicUserSchema, { type: 'null' }], }, createdBy: { type: 'string', format: 'uuid' }, createdAt: { type: 'string', format: 'date-time' }, lastMessageAt: { type: 'string', format: 'date-time' }, unreadCount: { type: 'integer' }, }, } as const; export const reactionSummarySchema = { type: 'object', additionalProperties: false, required: ['emoji', 'count', 'mine'], properties: { emoji: { type: 'string' }, count: { type: 'integer' }, mine: { type: 'boolean' }, }, } as const; export const presenceSchema = { type: 'object', additionalProperties: false, required: ['userId', 'online', 'lastSeenAt'], properties: { userId: { type: 'string', format: 'uuid' }, online: { type: 'boolean' }, lastSeenAt: { type: ['string', 'null'], format: 'date-time' }, }, } as const; export const presenceListSchema = { type: 'array', items: presenceSchema } as const; export const conversationListSchema = { type: 'array', items: conversationSchema } as const; export const conversationMemberSchema = { type: 'object', additionalProperties: false, required: ['userId', 'role', 'joinedAt', 'user'], properties: { userId: { type: 'string', format: 'uuid' }, role: { type: 'string' }, joinedAt: { type: 'string', format: 'date-time' }, user: publicUserSchema, }, } as const; export const conversationMemberListSchema = { type: 'array', items: conversationMemberSchema, } as const; export const contactSchema = { type: 'object', additionalProperties: false, required: ['userId', 'user', 'createdAt'], properties: { userId: { type: 'string', format: 'uuid' }, user: publicUserSchema, createdAt: { type: 'string', format: 'date-time' }, }, } as const; export const contactListSchema = { type: 'array', items: contactSchema } as const; export const messageSchema = { type: 'object', additionalProperties: false, required: [ 'id', 'conversationId', 'senderId', 'sender', 'content', 'contentType', 'encryption', 'clientMsgId', 'seq', 'createdAt', 'editedAt', 'deletedAt', 'reactions', 'media', ], properties: { id: { type: 'string', format: 'uuid' }, conversationId: { type: 'string', format: 'uuid' }, senderId: { type: 'string', format: 'uuid' }, sender: publicUserSchema, content: { type: 'string' }, contentType: { type: 'string' }, encryption: { type: ['string', 'null'] }, clientMsgId: { type: 'string' }, seq: { type: 'integer' }, createdAt: { type: 'string', format: 'date-time' }, editedAt: { type: ['string', 'null'], format: 'date-time' }, deletedAt: { type: ['string', 'null'], format: 'date-time' }, reactions: { type: 'array', items: reactionSummarySchema }, media: { oneOf: [mediaRefSchema, { type: 'null' }] }, }, } as const; export const messageListSchema = { type: 'array', items: messageSchema } as const;