Files
TX/lib/api-spec/openapi.yaml
T

1188 lines
26 KiB
YAML
Raw Normal View History

2026-04-18 02:00:09 +00:00
openapi: 3.1.0
info:
# Do not change the title, if the title changes, the import paths will be broken
title: Api
version: 0.1.0
description: TeaBoy OS API specification
2026-04-18 02:00:09 +00:00
servers:
- url: /api
description: Base API path
tags:
- name: health
description: Health operations
- name: auth
description: Authentication
- name: apps
description: App management
- name: services
description: Internal services (khidmati)
- name: conversations
description: Chat conversations
- name: messages
description: Chat messages
- name: notifications
description: Notifications
- name: users
description: User management
- name: stats
description: Dashboard stats
- name: storage
description: Object storage upload and serving endpoints
2026-04-18 02:00:09 +00:00
paths:
/healthz:
get:
operationId: healthCheck
tags: [health]
summary: Health check
responses:
"200":
description: Healthy
content:
application/json:
schema:
$ref: "#/components/schemas/HealthStatus"
# Auth
/auth/register:
post:
operationId: register
tags: [auth]
summary: Register a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RegisterBody"
responses:
"201":
description: Registered
content:
application/json:
schema:
$ref: "#/components/schemas/AuthUser"
"400":
description: Validation error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/auth/login:
post:
operationId: login
tags: [auth]
summary: Login
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/LoginBody"
responses:
"200":
description: Logged in
content:
application/json:
schema:
$ref: "#/components/schemas/AuthUser"
"401":
description: Unauthorized
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/auth/logout:
post:
operationId: logout
tags: [auth]
summary: Logout
responses:
"200":
description: Logged out
content:
application/json:
schema:
$ref: "#/components/schemas/SuccessResponse"
/auth/me:
get:
operationId: getMe
tags: [auth]
summary: Get current user
responses:
"200":
description: Current user
content:
application/json:
schema:
$ref: "#/components/schemas/AuthUser"
"401":
description: Unauthorized
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/auth/me/language:
patch:
operationId: updateLanguage
tags: [auth]
summary: Update preferred language
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateLanguageBody"
responses:
"200":
description: Updated
content:
application/json:
schema:
$ref: "#/components/schemas/AuthUser"
# Apps
/apps:
get:
operationId: listApps
tags: [apps]
summary: List all active apps
responses:
"200":
description: Apps list
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/App"
post:
operationId: createApp
tags: [apps]
summary: Create a new app (admin)
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateAppBody"
responses:
"201":
description: Created
content:
application/json:
schema:
$ref: "#/components/schemas/App"
/apps/{id}:
get:
operationId: getApp
tags: [apps]
summary: Get app by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: App
content:
application/json:
schema:
$ref: "#/components/schemas/App"
patch:
operationId: updateApp
tags: [apps]
summary: Update app (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateAppBody"
responses:
"200":
description: Updated
content:
application/json:
schema:
$ref: "#/components/schemas/App"
delete:
operationId: deleteApp
tags: [apps]
summary: Delete app (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"204":
description: Deleted
# Storage
/storage/uploads/request-url:
post:
operationId: requestUploadUrl
tags: [storage]
summary: Request a presigned URL for file upload
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RequestUploadUrlBody"
responses:
"200":
description: Presigned upload URL generated
content:
application/json:
schema:
$ref: "#/components/schemas/RequestUploadUrlResponse"
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
# Services
/services:
get:
operationId: listServices
tags: [services]
summary: List all services
responses:
"200":
description: Services list
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Service"
post:
operationId: createService
tags: [services]
summary: Create a service (admin)
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateServiceBody"
responses:
"201":
description: Created
content:
application/json:
schema:
$ref: "#/components/schemas/Service"
/services/{id}:
get:
operationId: getService
tags: [services]
summary: Get service by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Service
content:
application/json:
schema:
$ref: "#/components/schemas/Service"
patch:
operationId: updateService
tags: [services]
summary: Update service (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateServiceBody"
responses:
"200":
description: Updated
content:
application/json:
schema:
$ref: "#/components/schemas/Service"
delete:
operationId: deleteService
tags: [services]
summary: Delete service (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"204":
description: Deleted
/service-categories:
get:
operationId: listServiceCategories
tags: [services]
summary: List service categories
responses:
"200":
description: Categories
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ServiceCategory"
# Conversations
/conversations:
get:
operationId: listConversations
tags: [conversations]
summary: List conversations for current user
responses:
"200":
description: Conversations
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ConversationWithDetails"
post:
operationId: createConversation
tags: [conversations]
summary: Create a conversation
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateConversationBody"
responses:
"201":
description: Created
content:
application/json:
schema:
$ref: "#/components/schemas/ConversationWithDetails"
/conversations/{id}:
get:
operationId: getConversation
tags: [conversations]
summary: Get conversation by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Conversation
content:
application/json:
schema:
$ref: "#/components/schemas/ConversationWithDetails"
/conversations/{id}/messages:
get:
operationId: listMessages
tags: [messages]
summary: List messages in a conversation
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Messages
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/MessageWithSender"
post:
operationId: sendMessage
tags: [messages]
summary: Send a message
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SendMessageBody"
responses:
"201":
description: Message sent
content:
application/json:
schema:
$ref: "#/components/schemas/MessageWithSender"
/conversations/{id}/read:
post:
operationId: markConversationRead
tags: [messages]
summary: Mark all messages in conversation as read
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Marked as read
content:
application/json:
schema:
$ref: "#/components/schemas/SuccessResponse"
# Notifications
/notifications:
get:
operationId: listNotifications
tags: [notifications]
summary: List notifications for current user
responses:
"200":
description: Notifications
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Notification"
/notifications/{id}/read:
patch:
operationId: markNotificationRead
tags: [notifications]
summary: Mark notification as read
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Marked as read
content:
application/json:
schema:
$ref: "#/components/schemas/Notification"
/notifications/read-all:
post:
operationId: markAllNotificationsRead
tags: [notifications]
summary: Mark all notifications as read
responses:
"200":
description: Done
content:
application/json:
schema:
$ref: "#/components/schemas/SuccessResponse"
# Users (admin)
/users:
get:
operationId: listUsers
tags: [users]
summary: List all users (admin)
responses:
"200":
description: Users
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/UserProfile"
post:
operationId: createUser
tags: [users]
summary: Create a user (admin)
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RegisterBody"
responses:
"201":
description: Created user
content:
application/json:
schema:
$ref: "#/components/schemas/UserProfile"
"409":
description: Username already taken
/users/{id}:
get:
operationId: getUser
tags: [users]
summary: Get user by ID (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: User
content:
application/json:
schema:
$ref: "#/components/schemas/UserProfile"
patch:
operationId: updateUser
tags: [users]
summary: Update user (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateUserBody"
responses:
"200":
description: Updated
content:
application/json:
schema:
$ref: "#/components/schemas/UserProfile"
delete:
operationId: deleteUser
tags: [users]
summary: Delete user (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"204":
description: Deleted
# Stats
/stats/home:
get:
operationId: getHomeStats
tags: [stats]
summary: Get home screen stats
responses:
"200":
description: Stats
content:
application/json:
schema:
$ref: "#/components/schemas/HomeStats"
2026-04-18 02:00:09 +00:00
components:
schemas:
HealthStatus:
type: object
properties:
status:
type: string
required:
- status
ErrorResponse:
type: object
properties:
error:
type: string
required:
- error
SuccessResponse:
type: object
properties:
success:
type: boolean
required:
- success
RegisterBody:
type: object
properties:
username:
type: string
email:
type: string
password:
type: string
displayNameAr:
type: ["string", "null"]
displayNameEn:
type: ["string", "null"]
preferredLanguage:
type: string
default: "ar"
required:
- username
- email
- password
LoginBody:
type: object
properties:
username:
type: string
password:
type: string
required:
- username
- password
UpdateLanguageBody:
type: object
properties:
language:
type: string
required:
- language
AuthUser:
type: object
properties:
id:
type: integer
username:
type: string
email:
type: string
displayNameAr:
type: ["string", "null"]
displayNameEn:
type: ["string", "null"]
preferredLanguage:
type: string
avatarUrl:
type: ["string", "null"]
isActive:
type: boolean
roles:
type: array
items:
type: string
createdAt:
type: string
format: date-time
required:
- id
- username
- email
- preferredLanguage
- isActive
- roles
- createdAt
UserProfile:
type: object
properties:
id:
type: integer
username:
type: string
email:
type: string
displayNameAr:
type: ["string", "null"]
displayNameEn:
type: ["string", "null"]
preferredLanguage:
type: string
avatarUrl:
type: ["string", "null"]
isActive:
type: boolean
roles:
type: array
items:
type: string
createdAt:
type: string
format: date-time
required:
- id
- username
- email
- preferredLanguage
- isActive
- roles
- createdAt
UpdateUserBody:
type: object
properties:
displayNameAr:
type: ["string", "null"]
displayNameEn:
type: ["string", "null"]
isActive:
type: boolean
preferredLanguage:
type: string
App:
type: object
properties:
id:
type: integer
slug:
type: string
nameAr:
type: string
nameEn:
type: string
descriptionAr:
type: ["string", "null"]
descriptionEn:
type: ["string", "null"]
iconName:
type: string
route:
type: string
color:
type: string
isActive:
type: boolean
isSystem:
type: boolean
sortOrder:
type: integer
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
required:
- id
- slug
- nameAr
- nameEn
- iconName
- route
- color
- isActive
- isSystem
- sortOrder
- createdAt
- updatedAt
CreateAppBody:
type: object
properties:
slug:
type: string
nameAr:
type: string
nameEn:
type: string
descriptionAr:
type: ["string", "null"]
descriptionEn:
type: ["string", "null"]
iconName:
type: string
route:
type: string
color:
type: string
isActive:
type: boolean
isSystem:
type: boolean
sortOrder:
type: integer
required:
- slug
- nameAr
- nameEn
- iconName
- route
- color
UpdateAppBody:
type: object
properties:
nameAr:
type: string
nameEn:
type: string
descriptionAr:
type: ["string", "null"]
descriptionEn:
type: ["string", "null"]
iconName:
type: string
route:
type: string
color:
type: string
isActive:
type: boolean
sortOrder:
type: integer
Service:
type: object
properties:
id:
type: integer
categoryId:
type: ["integer", "null"]
nameAr:
type: string
nameEn:
type: string
descriptionAr:
type: ["string", "null"]
descriptionEn:
type: ["string", "null"]
imageUrl:
type: ["string", "null"]
price:
type: ["string", "null"]
isAvailable:
type: boolean
sortOrder:
type: integer
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
required:
- id
- nameAr
- nameEn
- isAvailable
- sortOrder
- createdAt
- updatedAt
CreateServiceBody:
type: object
properties:
categoryId:
type: ["integer", "null"]
nameAr:
type: string
nameEn:
type: string
descriptionAr:
type: ["string", "null"]
descriptionEn:
type: ["string", "null"]
imageUrl:
type: ["string", "null"]
price:
type: ["string", "null"]
isAvailable:
type: boolean
sortOrder:
type: integer
required:
- nameAr
- nameEn
UpdateServiceBody:
type: object
properties:
categoryId:
type: ["integer", "null"]
nameAr:
type: string
nameEn:
type: string
descriptionAr:
type: ["string", "null"]
descriptionEn:
type: ["string", "null"]
imageUrl:
type: ["string", "null"]
price:
type: ["string", "null"]
isAvailable:
type: boolean
sortOrder:
type: integer
ServiceCategory:
type: object
properties:
id:
type: integer
nameAr:
type: string
nameEn:
type: string
sortOrder:
type: integer
createdAt:
type: string
format: date-time
required:
- id
- nameAr
- nameEn
- sortOrder
- createdAt
ConversationWithDetails:
type: object
properties:
id:
type: integer
nameAr:
type: ["string", "null"]
nameEn:
type: ["string", "null"]
isGroup:
type: boolean
createdBy:
type: integer
participants:
type: array
items:
$ref: "#/components/schemas/ParticipantInfo"
lastMessage:
$ref: "#/components/schemas/MessageWithSender"
nullable: true
unreadCount:
type: integer
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
required:
- id
- isGroup
- createdBy
- participants
- unreadCount
- createdAt
- updatedAt
ParticipantInfo:
type: object
properties:
id:
type: integer
username:
type: string
displayNameAr:
type: ["string", "null"]
displayNameEn:
type: ["string", "null"]
avatarUrl:
type: ["string", "null"]
isAdmin:
type: boolean
required:
- id
- username
- isAdmin
CreateConversationBody:
type: object
properties:
participantIds:
type: array
items:
type: integer
nameAr:
type: ["string", "null"]
nameEn:
type: ["string", "null"]
isGroup:
type: boolean
required:
- participantIds
MessageWithSender:
type: object
properties:
id:
type: integer
conversationId:
type: integer
senderId:
type: integer
content:
type: string
sender:
$ref: "#/components/schemas/ParticipantInfo"
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
required:
- id
- conversationId
- senderId
- content
- sender
- createdAt
- updatedAt
SendMessageBody:
type: object
properties:
content:
type: string
required:
- content
Notification:
type: object
properties:
id:
type: integer
userId:
type: integer
titleAr:
type: string
titleEn:
type: string
bodyAr:
type: ["string", "null"]
bodyEn:
type: ["string", "null"]
type:
type: string
isRead:
type: boolean
relatedId:
type: ["integer", "null"]
relatedType:
type: ["string", "null"]
createdAt:
type: string
format: date-time
required:
- id
- userId
- titleAr
- titleEn
- type
- isRead
- createdAt
RequestUploadUrlBody:
type: object
required: [name, size, contentType]
properties:
name:
type: string
minLength: 1
size:
type: integer
minimum: 1
contentType:
type: string
minLength: 1
RequestUploadUrlResponse:
type: object
required: [uploadURL, objectPath]
properties:
uploadURL:
type: string
format: uri
objectPath:
type: string
metadata:
$ref: "#/components/schemas/RequestUploadUrlBody"
HomeStats:
type: object
properties:
totalApps:
type: integer
totalServices:
type: integer
unreadNotifications:
type: integer
unreadMessages:
type: integer
totalUsers:
type: integer
required:
- totalApps
- totalServices
- unreadNotifications
- unreadMessages
- totalUsers