Files
TX/lib/api-spec/openapi.yaml
T
riyadhafraa 8df5e76d29 Add ability to upload and manage service images
Integrates Uppy.js for file uploads, adds new API endpoints for requesting upload URLs, and updates UI components to support image uploads.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 804c1330-3360-45df-814d-221ee0d46866
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/JyUisd3
Replit-Helium-Checkpoint-Created: true
2026-04-20 10:55:11 +00:00

1188 lines
26 KiB
YAML

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
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
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"
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