Files
Toju/electron/api/openapi.ts
2026-04-29 15:24:56 +02:00

242 lines
7.1 KiB
TypeScript

export interface OpenApiBuildOptions {
baseUrl: string;
appVersion: string;
}
export function buildOpenApiDocument(options: OpenApiBuildOptions): unknown {
const { baseUrl, appVersion } = options;
return {
openapi: '3.1.0',
info: {
title: 'MetoYou Local Desktop API',
version: appVersion,
description:
'Authenticated local HTTP API exposed by the MetoYou desktop app. '
+ 'Authentication is performed against a configured signaling server. '
+ 'Bearer tokens issued here are scoped to this device only.'
},
servers: [{ url: baseUrl }],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'opaque'
}
},
schemas: {
Error: {
type: 'object',
required: ['error'],
properties: {
error: { type: 'string' },
errorCode: { type: 'string' }
}
},
LoginRequest: {
type: 'object',
required: ['username', 'password', 'serverUrl'],
properties: {
username: { type: 'string' },
password: { type: 'string' },
serverUrl: {
type: 'string',
format: 'uri',
description: 'Base URL of the signaling server to authenticate against. Must be in the allowed list configured in the desktop app.'
}
}
},
LoginResponse: {
type: 'object',
required: ['token', 'expiresAt', 'user'],
properties: {
token: { type: 'string' },
expiresAt: { type: 'integer', format: 'int64' },
user: { $ref: '#/components/schemas/AuthUser' }
}
},
AuthUser: {
type: 'object',
required: ['id', 'username', 'displayName'],
properties: {
id: { type: 'string' },
username: { type: 'string' },
displayName: { type: 'string' }
}
},
Profile: {
type: 'object',
properties: {
id: { type: 'string' },
username: { type: 'string' },
displayName: { type: 'string' },
description: { type: 'string' },
avatarUrl: { type: 'string' },
status: { type: 'string' }
}
},
Room: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' }
},
additionalProperties: true
},
Message: {
type: 'object',
properties: {
id: { type: 'string' },
roomId: { type: 'string' },
channelId: { type: 'string' },
senderId: { type: 'string' },
senderName: { type: 'string' },
content: { type: 'string' },
timestamp: { type: 'integer', format: 'int64' },
editedAt: { type: 'integer', format: 'int64' },
isDeleted: { type: 'boolean' }
},
additionalProperties: true
}
}
},
security: [{ bearerAuth: [] }],
paths: {
'/api/health': {
get: {
security: [],
summary: 'Liveness probe',
responses: {
'200': {
description: 'Service is alive',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
status: { type: 'string' },
version: { type: 'string' }
}
}
}
}
}
}
}
},
'/api/openapi.json': {
get: {
security: [],
summary: 'OpenAPI specification',
responses: { '200': { description: 'This document' } }
}
},
'/api/auth/login': {
post: {
security: [],
summary: 'Exchange username/password (validated by a signaling server) for a bearer token',
requestBody: {
required: true,
content: {
'application/json': {
schema: { $ref: '#/components/schemas/LoginRequest' }
}
}
},
responses: {
'200': {
description: 'Token issued',
content: {
'application/json': {
schema: { $ref: '#/components/schemas/LoginResponse' }
}
}
},
'401': {
description: 'Invalid credentials',
content: {
'application/json': { schema: { $ref: '#/components/schemas/Error' } }
}
},
'403': {
description: 'Signaling server URL not allowed',
content: {
'application/json': { schema: { $ref: '#/components/schemas/Error' } }
}
}
}
}
},
'/api/auth/logout': {
post: {
summary: 'Revoke the current bearer token',
responses: { '204': { description: 'Token revoked' } }
}
},
'/api/profile': {
get: {
summary: 'Get the current user profile',
responses: {
'200': {
description: 'Current user profile',
content: {
'application/json': {
schema: { $ref: '#/components/schemas/Profile' }
}
}
},
'404': {
description: 'No current user is set on this device',
content: {
'application/json': { schema: { $ref: '#/components/schemas/Error' } }
}
}
}
}
},
'/api/rooms': {
get: {
summary: 'List rooms (servers) known to this device',
responses: {
'200': {
description: 'Rooms array',
content: {
'application/json': {
schema: {
type: 'array',
items: { $ref: '#/components/schemas/Room' }
}
}
}
}
}
}
},
'/api/rooms/{roomId}/messages': {
get: {
summary: 'List messages for a room',
parameters: [
{ name: 'roomId', in: 'path', required: true, schema: { type: 'string' } },
{ name: 'limit', in: 'query', required: false, schema: { type: 'integer', minimum: 1, maximum: 500 } },
{ name: 'offset', in: 'query', required: false, schema: { type: 'integer', minimum: 0 } }
],
responses: {
'200': {
description: 'Messages array',
content: {
'application/json': {
schema: {
type: 'array',
items: { $ref: '#/components/schemas/Message' }
}
}
}
}
}
}
}
}
};
}