chore: import upstream snapshot with attribution
This commit is contained in:
+686
@@ -0,0 +1,686 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge AI API
|
||||
version: 1.0.0
|
||||
description: Model Gateway helper APIs for OpenRouter key provisioning, model discovery, and deprecated compatibility proxy routes
|
||||
|
||||
paths:
|
||||
/api/ai/models:
|
||||
get:
|
||||
summary: Get all available AI models
|
||||
description: Returns the normalized OpenRouter model catalog fetched from OpenRouter with output_modalities=all.
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Flat array of available models from the OpenRouter catalog
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ListModelsResponse'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'500':
|
||||
description: Failed to get models list
|
||||
|
||||
/api/ai/overview:
|
||||
get:
|
||||
summary: Get Model Gateway overview
|
||||
description: Returns key-level OpenRouter usage and activity time series when the key has activity access.
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Model Gateway overview
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AIOverview'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'500':
|
||||
description: Failed to fetch overview
|
||||
|
||||
/api/ai/openrouter/api-key:
|
||||
get:
|
||||
summary: Get active OpenRouter API key
|
||||
description: Returns the active OpenRouter API key details for admin copy workflows.
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Active OpenRouter API key details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/OpenRouterKeyResponse'
|
||||
'400':
|
||||
description: Missing OpenRouter API key
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'500':
|
||||
description: Failed to fetch API key details
|
||||
|
||||
/api/ai/chat/completion:
|
||||
post:
|
||||
summary: Generate chat completion (deprecated)
|
||||
deprecated: true
|
||||
description: Deprecated compatibility proxy. New integrations should call https://openrouter.ai/api/v1/chat/completions directly with the provisioned OpenRouter key.
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Chat completion response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatCompletionResponse'
|
||||
text/event-stream:
|
||||
schema:
|
||||
type: string
|
||||
description: Server-sent events stream for streaming responses
|
||||
'400':
|
||||
description: Invalid request - missing model or messages
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'500':
|
||||
description: Failed to get response
|
||||
|
||||
/api/ai/image/generation:
|
||||
post:
|
||||
summary: Generate images (deprecated)
|
||||
deprecated: true
|
||||
description: Deprecated compatibility proxy. New integrations should use OpenRouter image-capable models directly with the provisioned OpenRouter key.
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ImageGenerationOptions'
|
||||
responses:
|
||||
'200':
|
||||
description: Images generated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
description: Model used for generation
|
||||
images:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/OpenRouterImageMessage'
|
||||
text:
|
||||
type: string
|
||||
description: Text content from multimodal models
|
||||
count:
|
||||
type: integer
|
||||
description: Number of images generated
|
||||
metadata:
|
||||
type: object
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
revisedPrompt:
|
||||
type: string
|
||||
usage:
|
||||
$ref: '#/components/schemas/TokenUsage'
|
||||
nextActions:
|
||||
type: string
|
||||
example: Images have been generated successfully. Use the returned URLs or base64 data to access them.
|
||||
'400':
|
||||
description: Invalid request - missing model or prompt
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'500':
|
||||
description: Failed to generate image
|
||||
|
||||
/api/ai/embeddings:
|
||||
post:
|
||||
summary: Generate embeddings (deprecated)
|
||||
deprecated: true
|
||||
description: Deprecated compatibility proxy. New integrations should call https://openrouter.ai/api/v1/embeddings directly with the provisioned OpenRouter key.
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmbeddingsRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Embeddings generated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmbeddingsResponse'
|
||||
'400':
|
||||
description: Invalid request - missing model or input
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'500':
|
||||
description: Failed to generate embeddings
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
|
||||
schemas:
|
||||
ChatMessage:
|
||||
type: object
|
||||
required:
|
||||
- role
|
||||
properties:
|
||||
role:
|
||||
type: string
|
||||
enum: [user, assistant, system, tool]
|
||||
description: Role of the message sender
|
||||
content:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Message content (nullable for assistant messages with tool_calls)
|
||||
tool_calls:
|
||||
type: array
|
||||
description: Tool calls made by the assistant (assistant messages only)
|
||||
items:
|
||||
$ref: '#/components/schemas/ToolCall'
|
||||
tool_call_id:
|
||||
type: string
|
||||
description: ID of the tool call being responded to (tool messages only)
|
||||
|
||||
ChatRequest:
|
||||
type: object
|
||||
required:
|
||||
- model
|
||||
- messages
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
description: OpenRouter model identifier
|
||||
example: openai/gpt-4
|
||||
messages:
|
||||
type: array
|
||||
description: Array of messages for conversation (supports text, images, audio, and files/PDFs)
|
||||
items:
|
||||
$ref: '#/components/schemas/ChatMessage'
|
||||
stream:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Enable streaming response via Server-Sent Events
|
||||
temperature:
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 2
|
||||
description: Controls randomness in generation
|
||||
maxTokens:
|
||||
type: integer
|
||||
description: Maximum number of tokens to generate
|
||||
topP:
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
description: Nucleus sampling parameter
|
||||
webSearch:
|
||||
$ref: '#/components/schemas/WebSearchPlugin'
|
||||
fileParser:
|
||||
$ref: '#/components/schemas/FileParserPlugin'
|
||||
thinking:
|
||||
type: boolean
|
||||
description: Enable extended reasoning capabilities (appends :thinking to model ID if not already present). Not every model supports this, only works with Anthropic models with the :thinking suffix.
|
||||
tools:
|
||||
type: array
|
||||
description: Tool definitions for function calling
|
||||
items:
|
||||
$ref: '#/components/schemas/Tool'
|
||||
toolChoice:
|
||||
description: 'Controls tool usage: auto, none, required, or a specific function'
|
||||
oneOf:
|
||||
- type: string
|
||||
enum: [auto, none, required]
|
||||
- type: object
|
||||
required:
|
||||
- type
|
||||
- function
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: [function]
|
||||
function:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
parallelToolCalls:
|
||||
type: boolean
|
||||
description: Allow the model to call multiple tools in parallel
|
||||
|
||||
ChatCompletionResponse:
|
||||
type: object
|
||||
properties:
|
||||
text:
|
||||
type: string
|
||||
description: AI model response content
|
||||
tool_calls:
|
||||
type: array
|
||||
description: Tool calls requested by the model (present when the model invokes tools)
|
||||
items:
|
||||
$ref: '#/components/schemas/ToolCall'
|
||||
annotations:
|
||||
type: array
|
||||
description: URL citations from web search results (present when webSearch is enabled)
|
||||
items:
|
||||
$ref: '#/components/schemas/UrlCitationAnnotation'
|
||||
metadata:
|
||||
type: object
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
usage:
|
||||
$ref: '#/components/schemas/TokenUsage'
|
||||
|
||||
TokenUsage:
|
||||
type: object
|
||||
properties:
|
||||
promptTokens:
|
||||
type: integer
|
||||
completionTokens:
|
||||
type: integer
|
||||
totalTokens:
|
||||
type: integer
|
||||
|
||||
WebSearchPlugin:
|
||||
type: object
|
||||
required:
|
||||
- enabled
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Enable web search integration
|
||||
engine:
|
||||
type: string
|
||||
enum: [native, exa]
|
||||
description: |
|
||||
Search engine selection:
|
||||
- native: Use provider's built-in web search (OpenAI, Anthropic, Perplexity, xAI)
|
||||
- exa: Use Exa's search API
|
||||
- undefined: Auto-select (native if available, otherwise Exa)
|
||||
maxResults:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 10
|
||||
default: 5
|
||||
description: Maximum number of search results to include
|
||||
searchPrompt:
|
||||
type: string
|
||||
description: Custom prompt for attaching search results to the message
|
||||
|
||||
FileParserPlugin:
|
||||
type: object
|
||||
required:
|
||||
- enabled
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Enable file parsing for PDFs in messages
|
||||
pdf:
|
||||
type: object
|
||||
properties:
|
||||
engine:
|
||||
type: string
|
||||
enum: [pdf-text, mistral-ocr, native]
|
||||
description: |
|
||||
PDF processing engine:
|
||||
- pdf-text: Best for well-structured PDFs with clear text content (Free)
|
||||
- mistral-ocr: Best for scanned documents or PDFs with images ($2 per 1,000 pages)
|
||||
- native: Only for models with native file support (charged as input tokens)
|
||||
If not specified, defaults to native if available, otherwise mistral-ocr
|
||||
|
||||
Tool:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- function
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: [function]
|
||||
function:
|
||||
$ref: '#/components/schemas/FunctionDefinition'
|
||||
|
||||
FunctionDefinition:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: The name of the function to call
|
||||
description:
|
||||
type: string
|
||||
description: A description of what the function does
|
||||
parameters:
|
||||
type: object
|
||||
description: JSON Schema describing the function parameters
|
||||
|
||||
ToolCall:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- type
|
||||
- function
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Unique identifier for this tool call
|
||||
type:
|
||||
type: string
|
||||
enum: [function]
|
||||
function:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- arguments
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: The name of the function called
|
||||
arguments:
|
||||
type: string
|
||||
description: JSON string of the function arguments
|
||||
|
||||
FileContent:
|
||||
type: object
|
||||
description: File content for PDFs and other documents
|
||||
required:
|
||||
- type
|
||||
- file
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: [file]
|
||||
file:
|
||||
type: object
|
||||
required:
|
||||
- filename
|
||||
- file_data
|
||||
properties:
|
||||
filename:
|
||||
type: string
|
||||
description: Filename with extension (e.g., "document.pdf")
|
||||
example: document.pdf
|
||||
file_data:
|
||||
type: string
|
||||
description: |
|
||||
File data can be:
|
||||
- Public URL: "https://example.com/document.pdf"
|
||||
- Base64 data URL: "data:application/pdf;base64,..."
|
||||
example: "https://example.com/document.pdf"
|
||||
|
||||
UrlCitationAnnotation:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: [url_citation]
|
||||
urlCitation:
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: URL of the cited source
|
||||
title:
|
||||
type: string
|
||||
description: Title of the cited page
|
||||
content:
|
||||
type: string
|
||||
description: Relevant content snippet from the source
|
||||
startIndex:
|
||||
type: integer
|
||||
description: Start character index in response text where citation applies
|
||||
endIndex:
|
||||
type: integer
|
||||
description: End character index in response text where citation applies
|
||||
|
||||
ImageGenerationOptions:
|
||||
type: object
|
||||
required:
|
||||
- model
|
||||
- prompt
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
description: OpenRouter model identifier for image generation
|
||||
example: openai/dall-e-3
|
||||
prompt:
|
||||
type: string
|
||||
description: Text prompt describing the desired image
|
||||
example: "A serene landscape with mountains and a lake at sunset"
|
||||
|
||||
OpenRouterImageMessage:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: [image_url]
|
||||
image_url:
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: Can be a direct URL or data:image base64 URL
|
||||
|
||||
ListModelsResponse:
|
||||
type: array
|
||||
description: Normalized OpenRouter model catalog returned by GET /api/ai/models.
|
||||
items:
|
||||
$ref: '#/components/schemas/AIModel'
|
||||
|
||||
AIModel:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
example: openai/gpt-4o
|
||||
created:
|
||||
type: integer
|
||||
inputModality:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: [text, image]
|
||||
outputModality:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: [text]
|
||||
provider:
|
||||
type: string
|
||||
example: openrouter
|
||||
modelId:
|
||||
type: string
|
||||
example: openai/gpt-4o
|
||||
inputPrice:
|
||||
type: number
|
||||
description: Input price per million tokens in USD when token pricing is available.
|
||||
outputPrice:
|
||||
type: number
|
||||
description: Output price per million tokens in USD when token pricing is available.
|
||||
inputPriceLabel:
|
||||
type: string
|
||||
description: Human-readable input pricing label, such as "$2.50 / 1M tokens" or "Free", when available.
|
||||
outputPriceLabel:
|
||||
type: string
|
||||
description: Human-readable output pricing label, such as "$10.00 / 1M tokens" or "Free", when available.
|
||||
|
||||
OpenRouterKeyResponse:
|
||||
type: object
|
||||
properties:
|
||||
apiKey:
|
||||
type: string
|
||||
description: Active OpenRouter API key for admin copy workflows
|
||||
maskedKey:
|
||||
type: string
|
||||
description: Masked OpenRouter API key for display
|
||||
|
||||
AIOverviewMetricPoint:
|
||||
type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
value:
|
||||
type: number
|
||||
|
||||
AIOverview:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
limit:
|
||||
type: number
|
||||
nullable: true
|
||||
limitRemaining:
|
||||
type: number
|
||||
nullable: true
|
||||
limitReset:
|
||||
type: string
|
||||
nullable: true
|
||||
usage:
|
||||
type: number
|
||||
usageDaily:
|
||||
type: number
|
||||
usageWeekly:
|
||||
type: number
|
||||
usageMonthly:
|
||||
type: number
|
||||
isFreeTier:
|
||||
type: boolean
|
||||
observabilityAvailable:
|
||||
type: boolean
|
||||
observabilityError:
|
||||
type: string
|
||||
charts:
|
||||
type: object
|
||||
properties:
|
||||
spend:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/AIOverviewMetricPoint'
|
||||
requests:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/AIOverviewMetricPoint'
|
||||
tokens:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/AIOverviewMetricPoint'
|
||||
EmbeddingsRequest:
|
||||
type: object
|
||||
required:
|
||||
- model
|
||||
- input
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
description: Embedding model identifier
|
||||
example: google/gemini-embedding-001
|
||||
input:
|
||||
oneOf:
|
||||
- type: string
|
||||
description: Single text input to embed
|
||||
- type: array
|
||||
items:
|
||||
type: string
|
||||
description: Array of text inputs to embed
|
||||
example: "Hello world"
|
||||
encoding_format:
|
||||
type: string
|
||||
enum: [float, base64]
|
||||
default: float
|
||||
description: The format to return the embeddings in. Can be either float or base64.
|
||||
dimensions:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: The number of dimensions the resulting output embeddings should have. Only supported in certain models.
|
||||
|
||||
EmbeddingsResponse:
|
||||
type: object
|
||||
properties:
|
||||
object:
|
||||
type: string
|
||||
enum: [list]
|
||||
description: Object type, always "list"
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/EmbeddingObject'
|
||||
metadata:
|
||||
type: object
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
description: The model used for embedding
|
||||
example: text-embedding-ada-002
|
||||
usage:
|
||||
$ref: '#/components/schemas/TokenUsage'
|
||||
|
||||
EmbeddingObject:
|
||||
type: object
|
||||
properties:
|
||||
object:
|
||||
type: string
|
||||
enum: [embedding]
|
||||
description: Object type, always "embedding"
|
||||
embedding:
|
||||
type: array
|
||||
items:
|
||||
type: number
|
||||
description: The embedding vector (array of floats)
|
||||
index:
|
||||
type: integer
|
||||
description: Index of this embedding in the input array
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error message
|
||||
details:
|
||||
type: string
|
||||
description: Additional error details
|
||||
code:
|
||||
type: string
|
||||
description: Error code for programmatic handling
|
||||
+2068
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,158 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Email API
|
||||
version: 1.0.0
|
||||
description: Custom email sending endpoints for sending raw HTML emails
|
||||
|
||||
paths:
|
||||
/api/email/send-raw:
|
||||
post:
|
||||
summary: Send raw HTML email
|
||||
description: Send a custom HTML email to one or more recipients. Requires user authentication.
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SendRawEmailRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Email sent successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SendEmailResponse'
|
||||
'400':
|
||||
description: Invalid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'401':
|
||||
description: Unauthorized - requires authentication
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: Forbidden - authentication required
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: Email provider error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
|
||||
schemas:
|
||||
SendRawEmailRequest:
|
||||
type: object
|
||||
required:
|
||||
- to
|
||||
- subject
|
||||
- html
|
||||
properties:
|
||||
to:
|
||||
oneOf:
|
||||
- type: string
|
||||
format: email
|
||||
description: Single recipient email address
|
||||
example: user@example.com
|
||||
- type: array
|
||||
items:
|
||||
type: string
|
||||
format: email
|
||||
minItems: 1
|
||||
maxItems: 50
|
||||
description: Multiple recipient email addresses (max 50)
|
||||
example: ["user1@example.com", "user2@example.com"]
|
||||
description: Recipient email address(es) - maximum 50 recipients
|
||||
subject:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 500
|
||||
description: Email subject line
|
||||
example: "Welcome to our platform"
|
||||
html:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: HTML content of the email body
|
||||
example: "<h1>Welcome!</h1><p>Thank you for joining us.</p>"
|
||||
cc:
|
||||
oneOf:
|
||||
- type: string
|
||||
format: email
|
||||
description: Single CC recipient email address
|
||||
- type: array
|
||||
items:
|
||||
type: string
|
||||
format: email
|
||||
minItems: 1
|
||||
maxItems: 50
|
||||
description: Multiple CC recipient email addresses (max 50)
|
||||
description: Carbon copy recipient(s) - maximum 50 recipients
|
||||
bcc:
|
||||
oneOf:
|
||||
- type: string
|
||||
format: email
|
||||
description: Single BCC recipient email address
|
||||
- type: array
|
||||
items:
|
||||
type: string
|
||||
format: email
|
||||
minItems: 1
|
||||
maxItems: 50
|
||||
description: Multiple BCC recipient email addresses (max 50)
|
||||
description: Blind carbon copy recipient(s) - maximum 50 recipients
|
||||
from:
|
||||
type: string
|
||||
maxLength: 100
|
||||
description: Custom sender name (uses default if not provided)
|
||||
example: "My App"
|
||||
replyTo:
|
||||
type: string
|
||||
format: email
|
||||
description: Reply-to email address
|
||||
example: "support@example.com"
|
||||
|
||||
SendEmailResponse:
|
||||
type: object
|
||||
description: Empty object on success - extend with optional fields later if needed
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
- message
|
||||
- statusCode
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error code for programmatic handling
|
||||
example: "VALIDATION_ERROR"
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message
|
||||
example: "Subject is required"
|
||||
statusCode:
|
||||
type: integer
|
||||
description: HTTP status code
|
||||
example: 400
|
||||
nextActions:
|
||||
type: string
|
||||
description: Suggested action to resolve the error
|
||||
example: "Please provide a valid subject line"
|
||||
@@ -0,0 +1,617 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Functions API
|
||||
version: 1.0.0
|
||||
description: Serverless functions running in Deno runtime
|
||||
|
||||
paths:
|
||||
/api/functions:
|
||||
get:
|
||||
summary: List all functions
|
||||
description: Get all functions with their metadata
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: List of functions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FunctionMetadata'
|
||||
example:
|
||||
- id: "123e4567-e89b-12d3-a456-426614174000"
|
||||
slug: "hello-world"
|
||||
name: "Hello World Function"
|
||||
description: "Returns a greeting message"
|
||||
status: "active"
|
||||
created_at: "2024-01-21T10:30:00Z"
|
||||
updated_at: "2024-01-21T10:35:00Z"
|
||||
deployed_at: "2024-01-21T10:35:00Z"
|
||||
- id: "223e4567-e89b-12d3-a456-426614174001"
|
||||
slug: "process-webhook"
|
||||
name: "Webhook Processor"
|
||||
description: "Processes incoming webhooks"
|
||||
status: "draft"
|
||||
created_at: "2024-01-22T14:20:00Z"
|
||||
updated_at: "2024-01-22T14:20:00Z"
|
||||
deployed_at: null
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'403':
|
||||
description: Forbidden - Admin only
|
||||
'500':
|
||||
description: Failed to list functions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
post:
|
||||
summary: Create new function
|
||||
description: Create a new function with code that runs in Deno runtime
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- code
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Display name for the function
|
||||
example: "Hello World Function"
|
||||
slug:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: URL-friendly identifier (auto-generated from name if not provided)
|
||||
example: "hello-world"
|
||||
code:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: JavaScript/TypeScript code that exports an async function
|
||||
example: |
|
||||
export default async function(request) {
|
||||
const { name = 'World' } = await request.json();
|
||||
return new Response(
|
||||
JSON.stringify({ message: `Hello, ${name}!` }),
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
description:
|
||||
type: string
|
||||
description: Description of what the function does
|
||||
example: "Returns a personalized greeting message"
|
||||
status:
|
||||
type: string
|
||||
enum: ["draft", "active"]
|
||||
default: "active"
|
||||
description: Initial status (draft or active/deployed)
|
||||
responses:
|
||||
'201':
|
||||
description: Function created successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
function:
|
||||
$ref: '#/components/schemas/FunctionMetadata'
|
||||
example:
|
||||
success: true
|
||||
function:
|
||||
id: "123e4567-e89b-12d3-a456-426614174000"
|
||||
slug: "hello-world"
|
||||
name: "Hello World Function"
|
||||
description: "Returns a greeting message"
|
||||
status: "active"
|
||||
created_at: "2024-01-21T10:30:00Z"
|
||||
'400':
|
||||
description: Invalid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
examples:
|
||||
validation:
|
||||
value:
|
||||
error: "Invalid request"
|
||||
details:
|
||||
- code: "too_small"
|
||||
minimum: 1
|
||||
path: ["name"]
|
||||
message: "Name is required"
|
||||
dangerousCode:
|
||||
value:
|
||||
error: "Code contains potentially dangerous patterns"
|
||||
pattern: "/Deno\\.run/i"
|
||||
'409':
|
||||
description: Function slug already exists
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "Function with this slug already exists"
|
||||
details: "duplicate key value violates unique constraint"
|
||||
'500':
|
||||
description: Failed to create function
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/functions/{slug}:
|
||||
get:
|
||||
summary: Get function details
|
||||
description: Get a specific function including its code
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: Function slug identifier
|
||||
example: "hello-world"
|
||||
responses:
|
||||
'200':
|
||||
description: Function details with code
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/FunctionDetails'
|
||||
example:
|
||||
id: "123e4567-e89b-12d3-a456-426614174000"
|
||||
slug: "hello-world"
|
||||
name: "Hello World Function"
|
||||
description: "Returns a greeting message"
|
||||
code: |
|
||||
export default async function(request) {
|
||||
const { name = 'World' } = await request.json();
|
||||
return new Response(
|
||||
JSON.stringify({ message: `Hello, ${name}!` }),
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
status: "active"
|
||||
created_at: "2024-01-21T10:30:00Z"
|
||||
updated_at: "2024-01-21T10:35:00Z"
|
||||
deployed_at: "2024-01-21T10:35:00Z"
|
||||
'404':
|
||||
description: Function not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "Function not found"
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'403':
|
||||
description: Forbidden - Admin only
|
||||
'500':
|
||||
description: Failed to get function
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
put:
|
||||
summary: Update function
|
||||
description: Update an existing function's code or metadata
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: Function slug identifier
|
||||
example: "hello-world"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: New display name
|
||||
code:
|
||||
type: string
|
||||
description: Updated function code
|
||||
description:
|
||||
type: string
|
||||
description: Updated description
|
||||
status:
|
||||
type: string
|
||||
enum: ["draft", "active", "error"]
|
||||
description: Function status
|
||||
responses:
|
||||
'200':
|
||||
description: Function updated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
function:
|
||||
$ref: '#/components/schemas/FunctionMetadata'
|
||||
example:
|
||||
success: true
|
||||
function:
|
||||
id: "123e4567-e89b-12d3-a456-426614174000"
|
||||
slug: "hello-world"
|
||||
name: "Hello World Function v2"
|
||||
description: "Returns a greeting message"
|
||||
status: "active"
|
||||
updated_at: "2024-01-21T11:00:00Z"
|
||||
'400':
|
||||
description: Invalid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: Function not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "Function not found"
|
||||
'500':
|
||||
description: Failed to update function
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
delete:
|
||||
summary: Delete function
|
||||
description: Permanently delete a function
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: Function slug identifier
|
||||
example: "hello-world"
|
||||
responses:
|
||||
'200':
|
||||
description: Function deleted successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
example:
|
||||
success: true
|
||||
message: "Function hello-world deleted successfully"
|
||||
'404':
|
||||
description: Function not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "Function not found"
|
||||
'500':
|
||||
description: Failed to delete function
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/functions/{slug}:
|
||||
get:
|
||||
summary: Execute function (GET)
|
||||
description: Execute a function using GET method. Proxied to Deno runtime.
|
||||
tags:
|
||||
- Client
|
||||
parameters:
|
||||
- name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: Function slug identifier
|
||||
example: "hello-world"
|
||||
responses:
|
||||
'200':
|
||||
description: Function executed successfully
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
example:
|
||||
message: "Hello, World!"
|
||||
'404':
|
||||
description: Function not found or not active
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "Function not found or not active"
|
||||
'502':
|
||||
description: Failed to proxy to Deno runtime
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "Failed to connect to Deno runtime"
|
||||
|
||||
post:
|
||||
summary: Execute function (POST)
|
||||
description: Execute a function using POST method with JSON body. Proxied to Deno runtime.
|
||||
tags:
|
||||
- Client
|
||||
parameters:
|
||||
- name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: Function slug identifier
|
||||
example: "hello-world"
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
example:
|
||||
name: "John"
|
||||
age: 30
|
||||
responses:
|
||||
'200':
|
||||
description: Function executed successfully
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
example:
|
||||
message: "Hello, John!"
|
||||
'404':
|
||||
description: Function not found or not active
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "Function not found or not active"
|
||||
'502':
|
||||
description: Failed to proxy to Deno runtime
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "Failed to connect to Deno runtime"
|
||||
|
||||
put:
|
||||
summary: Execute function (PUT)
|
||||
description: Execute a function using PUT method. Proxied to Deno runtime.
|
||||
tags:
|
||||
- Client
|
||||
parameters:
|
||||
- name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: Function slug identifier
|
||||
example: "hello-world"
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
responses:
|
||||
'200':
|
||||
description: Function executed successfully
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
'502':
|
||||
description: Failed to proxy to Deno runtime
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
patch:
|
||||
summary: Execute function (PATCH)
|
||||
description: Execute a function using PATCH method. Proxied to Deno runtime.
|
||||
tags:
|
||||
- Client
|
||||
parameters:
|
||||
- name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: Function slug identifier
|
||||
example: "hello-world"
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
responses:
|
||||
'200':
|
||||
description: Function executed successfully
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
'502':
|
||||
description: Failed to proxy to Deno runtime
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
delete:
|
||||
summary: Execute function (DELETE)
|
||||
description: Execute a function using DELETE method. Proxied to Deno runtime.
|
||||
tags:
|
||||
- Client
|
||||
parameters:
|
||||
- name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
description: Function slug identifier
|
||||
example: "hello-world"
|
||||
responses:
|
||||
'200':
|
||||
description: Function executed successfully
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
'502':
|
||||
description: Failed to proxy to Deno runtime
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
|
||||
schemas:
|
||||
FunctionMetadata:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Unique identifier for the function
|
||||
slug:
|
||||
type: string
|
||||
description: URL-friendly identifier
|
||||
example: "hello-world"
|
||||
name:
|
||||
type: string
|
||||
description: Display name for the function
|
||||
example: "Hello World Function"
|
||||
description:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Description of what the function does
|
||||
status:
|
||||
type: string
|
||||
enum: ["draft", "active", "error"]
|
||||
description: Current status of the function
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: When the function was created
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: When the function was last updated
|
||||
deployed_at:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
description: When the function was last deployed (null if never deployed)
|
||||
required:
|
||||
- id
|
||||
- slug
|
||||
- name
|
||||
- status
|
||||
- created_at
|
||||
- updated_at
|
||||
|
||||
FunctionDetails:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/FunctionMetadata'
|
||||
- type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
description: The function's JavaScript/TypeScript code
|
||||
required:
|
||||
- code
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error message
|
||||
details:
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: array
|
||||
items:
|
||||
type: object
|
||||
- type: object
|
||||
description: Additional error details
|
||||
message:
|
||||
type: string
|
||||
description: Detailed error message
|
||||
pattern:
|
||||
type: string
|
||||
description: Pattern that caused validation failure (for dangerous code detection)
|
||||
@@ -0,0 +1,30 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Health API
|
||||
version: 1.0.0
|
||||
|
||||
paths:
|
||||
/api/health:
|
||||
get:
|
||||
summary: Health check
|
||||
description: Check if the API is running and healthy
|
||||
tags:
|
||||
- Client
|
||||
responses:
|
||||
'200':
|
||||
description: API is healthy
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: ok
|
||||
service:
|
||||
type: string
|
||||
example: Insforge Backend
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2025-01-21T03:45:22.194Z"
|
||||
@@ -0,0 +1,222 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Logs API
|
||||
version: 1.0.0
|
||||
|
||||
paths:
|
||||
/api/logs:
|
||||
get:
|
||||
summary: List activity logs
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 100
|
||||
- name: offset
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 0
|
||||
- name: action
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [INSERT, UPDATE, DELETE, LOGIN]
|
||||
description: Filter by action type
|
||||
- name: table
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: List of logs with pagination
|
||||
headers:
|
||||
X-Total-Count:
|
||||
schema:
|
||||
type: integer
|
||||
description: Total number of logs
|
||||
X-Page:
|
||||
schema:
|
||||
type: integer
|
||||
description: Current page number
|
||||
X-Page-Size:
|
||||
schema:
|
||||
type: integer
|
||||
description: Number of items per page
|
||||
X-Total-Pages:
|
||||
schema:
|
||||
type: integer
|
||||
description: Total number of pages
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
action:
|
||||
type: string
|
||||
enum: [INSERT, UPDATE, DELETE, LOGIN]
|
||||
table:
|
||||
type: string
|
||||
nullable: true
|
||||
record_id:
|
||||
type: string
|
||||
nullable: true
|
||||
user_id:
|
||||
type: string
|
||||
details:
|
||||
type: object
|
||||
nullable: true
|
||||
example:
|
||||
- id: "log-123"
|
||||
timestamp: "2024-01-21T10:30:00Z"
|
||||
action: "INSERT"
|
||||
table: "posts"
|
||||
record_id: "post-456"
|
||||
user_id: "user-789"
|
||||
details:
|
||||
title: "New Post"
|
||||
author: "John Doe"
|
||||
- id: "log-124"
|
||||
timestamp: "2024-01-21T10:31:00Z"
|
||||
action: "LOGIN"
|
||||
table: null
|
||||
record_id: null
|
||||
user_id: "user-789"
|
||||
details:
|
||||
ip: "192.168.1.1"
|
||||
user_agent: "Mozilla/5.0..."
|
||||
|
||||
delete:
|
||||
summary: Clear logs
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: before
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
responses:
|
||||
'200':
|
||||
description: Logs cleared
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
deleted_count:
|
||||
type: integer
|
||||
example:
|
||||
message: "Logs cleared successfully"
|
||||
deleted_count: 150
|
||||
'400':
|
||||
description: Bad request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_DATE"
|
||||
message: "Invalid date format for 'before' parameter"
|
||||
statusCode: 400
|
||||
nextAction: "Use ISO 8601 date format (YYYY-MM-DDTHH:mm:ssZ)"
|
||||
|
||||
/api/logs/stats:
|
||||
get:
|
||||
summary: Get logs statistics
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Logs statistics
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
actionStats:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
action:
|
||||
type: string
|
||||
count:
|
||||
type: integer
|
||||
tableStats:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
table_name:
|
||||
type: string
|
||||
count:
|
||||
type: integer
|
||||
recentActivity:
|
||||
type: integer
|
||||
description: Count of logs in last 24 hours
|
||||
totalLogs:
|
||||
type: integer
|
||||
description: Total number of logs
|
||||
example:
|
||||
actionStats:
|
||||
- action: "INSERT"
|
||||
count: 245
|
||||
- action: "UPDATE"
|
||||
count: 189
|
||||
- action: "DELETE"
|
||||
count: 34
|
||||
- action: "LOGIN"
|
||||
count: 567
|
||||
tableStats:
|
||||
- table_name: "posts"
|
||||
count: 156
|
||||
- table_name: "comments"
|
||||
count: 223
|
||||
recentActivity: 47
|
||||
totalLogs: 1035
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
|
||||
schemas:
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
- message
|
||||
- statusCode
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error code for programmatic handling
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message
|
||||
statusCode:
|
||||
type: integer
|
||||
description: HTTP status code
|
||||
nextAction:
|
||||
type: string
|
||||
description: Suggested action to resolve the error
|
||||
@@ -0,0 +1,326 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Metadata API
|
||||
version: 1.0.0
|
||||
|
||||
paths:
|
||||
/api/metadata:
|
||||
get:
|
||||
summary: Get app metadata
|
||||
description: >-
|
||||
Returns aggregated application metadata (auth, database, storage,
|
||||
functions, realtime, deployments). Use the `format` query parameter
|
||||
to choose between JSON and Markdown output.
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- in: query
|
||||
name: format
|
||||
schema:
|
||||
type: string
|
||||
enum: [json, markdown]
|
||||
default: json
|
||||
description: >-
|
||||
Response format. `json` returns structured JSON (default).
|
||||
`markdown` returns a human-readable Markdown document suitable
|
||||
for AI coding tools and developer onboarding.
|
||||
responses:
|
||||
'200':
|
||||
description: Application metadata
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
auth:
|
||||
type: object
|
||||
properties:
|
||||
oAuthProviders:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
requireEmailVerification:
|
||||
type: boolean
|
||||
disableSignup:
|
||||
type: boolean
|
||||
database:
|
||||
type: object
|
||||
properties:
|
||||
tables:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
tableName:
|
||||
type: string
|
||||
recordCount:
|
||||
type: integer
|
||||
totalSizeInGB:
|
||||
type: number
|
||||
hint:
|
||||
type: string
|
||||
storage:
|
||||
type: object
|
||||
properties:
|
||||
buckets:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
public:
|
||||
type: boolean
|
||||
objectCount:
|
||||
type: integer
|
||||
totalSizeInGB:
|
||||
type: number
|
||||
functions:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
slug:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
realtime:
|
||||
type: object
|
||||
description: Optional; present when realtime is configured
|
||||
properties:
|
||||
channels:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
pattern:
|
||||
type: string
|
||||
deployments:
|
||||
type: object
|
||||
description: Optional; present on cloud-hosted projects only
|
||||
properties:
|
||||
customSlug:
|
||||
type: string
|
||||
nullable: true
|
||||
version:
|
||||
type: string
|
||||
example:
|
||||
auth:
|
||||
oAuthProviders: ["google", "github"]
|
||||
requireEmailVerification: true
|
||||
disableSignup: false
|
||||
database:
|
||||
tables:
|
||||
- tableName: "users"
|
||||
recordCount: 150
|
||||
- tableName: "posts"
|
||||
recordCount: 1200
|
||||
totalSizeInGB: 0.3
|
||||
storage:
|
||||
buckets:
|
||||
- name: "avatars"
|
||||
public: true
|
||||
objectCount: 42
|
||||
totalSizeInGB: 0.5
|
||||
functions:
|
||||
- slug: "hello-world"
|
||||
name: "hello-world"
|
||||
status: "active"
|
||||
description: "Test function"
|
||||
version: "2.0.0"
|
||||
text/markdown:
|
||||
schema:
|
||||
type: string
|
||||
example: |
|
||||
# Project Metadata
|
||||
> v2.0.0
|
||||
|
||||
## Auth
|
||||
- **OAuth providers**: google, github
|
||||
- **Email verification**: required
|
||||
- **Signup**: enabled
|
||||
'400':
|
||||
description: Invalid format parameter
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'401':
|
||||
description: Unauthorized — missing or invalid credentials
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: Forbidden — authenticated but not an admin
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/metadata/database:
|
||||
get:
|
||||
summary: Get database metadata
|
||||
description: Get database statistics and table information for dashboard
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
responses:
|
||||
'200':
|
||||
description: Database metadata
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
tables:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
recordCount:
|
||||
type: integer
|
||||
totalTables:
|
||||
type: integer
|
||||
totalRecords:
|
||||
type: integer
|
||||
databaseSize:
|
||||
type: string
|
||||
lastUpdated:
|
||||
type: string
|
||||
format: date-time
|
||||
example:
|
||||
tables:
|
||||
- name: "posts"
|
||||
recordCount: 5678
|
||||
- name: "comments"
|
||||
recordCount: 9012
|
||||
totalTables: 15
|
||||
totalRecords: 16924
|
||||
databaseSize: "125 MB"
|
||||
lastUpdated: "2024-01-21T10:30:00Z"
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "UNAUTHORIZED"
|
||||
message: "Invalid or missing authentication"
|
||||
statusCode: 401
|
||||
nextAction: "Please provide valid authentication credentials"
|
||||
|
||||
/api/metadata/api-key:
|
||||
get:
|
||||
summary: Get API key
|
||||
description: Admin-only endpoint to retrieve the API key
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: API key
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
apiKey:
|
||||
type: string
|
||||
example:
|
||||
apiKey: "ins_1234567890abcdef1234567890abcdef"
|
||||
'403':
|
||||
description: Forbidden
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INSUFFICIENT_PERMISSIONS"
|
||||
message: "Only admin users can access API keys"
|
||||
statusCode: 403
|
||||
nextAction: "Contact an administrator for API key access"
|
||||
|
||||
/api/metadata/anon-key:
|
||||
get:
|
||||
summary: Get anon key
|
||||
description: >-
|
||||
Admin-only endpoint to retrieve the project's opaque anon key
|
||||
(`anon_...`). The anon key is a non-secret client identifier that maps
|
||||
requests to the `anon` role - safe to embed in frontend bundles; row
|
||||
level security is the security boundary. Rotatable via
|
||||
POST /api/secrets/anon-key/rotate.
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Anon key
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
anonKey:
|
||||
type: string
|
||||
example:
|
||||
anonKey: "anon_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd"
|
||||
'403':
|
||||
description: Forbidden
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: Anon key not initialized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
apiKey:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Key
|
||||
|
||||
schemas:
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
- message
|
||||
- statusCode
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error code for programmatic handling
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message
|
||||
statusCode:
|
||||
type: integer
|
||||
description: HTTP status code
|
||||
nextAction:
|
||||
type: string
|
||||
description: Suggested action to resolve the error
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,833 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Realtime API
|
||||
version: 1.0.0
|
||||
description: Realtime channel management, message history, permissions, and retention API. Live pub/sub uses Socket.IO.
|
||||
|
||||
tags:
|
||||
- name: Channels
|
||||
description: Configure realtime channel patterns, webhooks, and availability.
|
||||
- name: Messages
|
||||
description: Inspect realtime message history and delivery stats.
|
||||
- name: Permissions
|
||||
description: Manage helper endpoints for realtime RLS examples.
|
||||
- name: Configuration
|
||||
description: Manage realtime retention settings.
|
||||
|
||||
paths:
|
||||
/api/realtime/channels:
|
||||
get:
|
||||
summary: List All Channels
|
||||
description: Retrieve all configured realtime channels
|
||||
tags:
|
||||
- Channels
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
responses:
|
||||
'200':
|
||||
description: List of channels
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Channel'
|
||||
example:
|
||||
- id: "550e8400-e29b-41d4-a716-446655440000"
|
||||
pattern: "order:%"
|
||||
description: "Order updates channel"
|
||||
webhookUrls: ["https://example.com/webhook"]
|
||||
enabled: true
|
||||
createdAt: "2024-01-15T10:30:00Z"
|
||||
updatedAt: "2024-01-15T10:30:00Z"
|
||||
- id: "550e8400-e29b-41d4-a716-446655440001"
|
||||
pattern: "chat:%"
|
||||
description: "Chat room messages"
|
||||
webhookUrls: null
|
||||
enabled: true
|
||||
createdAt: "2024-01-16T11:00:00Z"
|
||||
updatedAt: "2024-01-16T11:00:00Z"
|
||||
|
||||
post:
|
||||
summary: Create Channel
|
||||
description: Create a new realtime channel with a pattern for subscription matching
|
||||
tags:
|
||||
- Channels
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateChannelRequest'
|
||||
example:
|
||||
pattern: "order:%"
|
||||
description: "Order updates channel"
|
||||
webhookUrls: ["https://example.com/webhook"]
|
||||
enabled: true
|
||||
responses:
|
||||
'201':
|
||||
description: Channel created successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Channel'
|
||||
example:
|
||||
id: "550e8400-e29b-41d4-a716-446655440000"
|
||||
pattern: "order:%"
|
||||
description: "Order updates channel"
|
||||
webhookUrls: ["https://example.com/webhook"]
|
||||
enabled: true
|
||||
createdAt: "2024-01-15T10:30:00Z"
|
||||
updatedAt: "2024-01-15T10:30:00Z"
|
||||
'400':
|
||||
description: Invalid input
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_INPUT"
|
||||
message: "pattern: Channel pattern is required"
|
||||
statusCode: 400
|
||||
|
||||
/api/realtime/channels/{id}:
|
||||
get:
|
||||
summary: Get Channel by ID
|
||||
description: Retrieve a specific channel by its UUID
|
||||
tags:
|
||||
- Channels
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
example: "550e8400-e29b-41d4-a716-446655440000"
|
||||
responses:
|
||||
'200':
|
||||
description: Channel details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Channel'
|
||||
example:
|
||||
id: "550e8400-e29b-41d4-a716-446655440000"
|
||||
pattern: "order:%"
|
||||
description: "Order updates channel"
|
||||
webhookUrls: ["https://example.com/webhook"]
|
||||
enabled: true
|
||||
createdAt: "2024-01-15T10:30:00Z"
|
||||
updatedAt: "2024-01-15T10:30:00Z"
|
||||
'404':
|
||||
description: Channel not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "NOT_FOUND"
|
||||
message: "Channel not found"
|
||||
statusCode: 404
|
||||
|
||||
put:
|
||||
summary: Update Channel
|
||||
description: Update an existing channel's configuration
|
||||
tags:
|
||||
- Channels
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
example: "550e8400-e29b-41d4-a716-446655440000"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateChannelRequest'
|
||||
example:
|
||||
description: "Updated order channel description"
|
||||
enabled: false
|
||||
responses:
|
||||
'200':
|
||||
description: Channel updated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Channel'
|
||||
example:
|
||||
id: "550e8400-e29b-41d4-a716-446655440000"
|
||||
pattern: "order:%"
|
||||
description: "Updated order channel description"
|
||||
webhookUrls: ["https://example.com/webhook"]
|
||||
enabled: false
|
||||
createdAt: "2024-01-15T10:30:00Z"
|
||||
updatedAt: "2024-01-17T14:00:00Z"
|
||||
'400':
|
||||
description: Invalid input
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_INPUT"
|
||||
message: "webhookUrls.0: Invalid url"
|
||||
statusCode: 400
|
||||
'404':
|
||||
description: Channel not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "NOT_FOUND"
|
||||
message: "Channel not found"
|
||||
statusCode: 404
|
||||
|
||||
delete:
|
||||
summary: Delete Channel
|
||||
description: Delete a channel definition. Existing message history is preserved with null channelId values.
|
||||
tags:
|
||||
- Channels
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
example: "550e8400-e29b-41d4-a716-446655440000"
|
||||
responses:
|
||||
'200':
|
||||
description: Channel deleted successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
example:
|
||||
message: "Channel deleted"
|
||||
'404':
|
||||
description: Channel not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "NOT_FOUND"
|
||||
message: "Channel not found"
|
||||
statusCode: 404
|
||||
|
||||
/api/realtime/messages:
|
||||
get:
|
||||
summary: List Messages
|
||||
description: Retrieve message history with optional filters
|
||||
tags:
|
||||
- Messages
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: channelId
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Filter messages by channel ID
|
||||
example: "550e8400-e29b-41d4-a716-446655440000"
|
||||
- name: eventName
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Filter messages by event name
|
||||
example: "order.created"
|
||||
- name: limit
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
default: 100
|
||||
description: Maximum number of messages to return
|
||||
- name: offset
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
default: 0
|
||||
description: Number of messages to skip
|
||||
responses:
|
||||
'200':
|
||||
description: List of messages
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
example:
|
||||
- id: "660e8400-e29b-41d4-a716-446655440000"
|
||||
eventName: "order.created"
|
||||
channelId: "550e8400-e29b-41d4-a716-446655440000"
|
||||
channelName: "order:123"
|
||||
payload:
|
||||
orderId: "123"
|
||||
status: "pending"
|
||||
senderType: "user"
|
||||
senderId: "770e8400-e29b-41d4-a716-446655440000"
|
||||
wsAudienceCount: 5
|
||||
whAudienceCount: 1
|
||||
whDeliveredCount: 1
|
||||
createdAt: "2024-01-15T10:30:00Z"
|
||||
'400':
|
||||
description: Invalid input
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_INPUT"
|
||||
message: "channelId: Invalid uuid"
|
||||
statusCode: 400
|
||||
delete:
|
||||
summary: Clear Messages
|
||||
description: Permanently delete all stored realtime messages
|
||||
tags:
|
||||
- Messages
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
responses:
|
||||
'200':
|
||||
description: Messages cleared
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ClearMessagesResponse'
|
||||
example:
|
||||
deleted: 42
|
||||
|
||||
/api/realtime/messages/stats:
|
||||
get:
|
||||
summary: Get Message Statistics
|
||||
description: Retrieve aggregated statistics about messages
|
||||
tags:
|
||||
- Messages
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: channelId
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Filter stats by channel ID
|
||||
example: "550e8400-e29b-41d4-a716-446655440000"
|
||||
- name: since
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Filter stats since this timestamp
|
||||
example: "2024-01-01T00:00:00Z"
|
||||
responses:
|
||||
'200':
|
||||
description: Message statistics
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MessageStats'
|
||||
example:
|
||||
totalMessages: 1250
|
||||
whDeliveryRate: 0.98
|
||||
topEvents:
|
||||
- eventName: "order.created"
|
||||
count: 450
|
||||
- eventName: "order.updated"
|
||||
count: 380
|
||||
- eventName: "order.completed"
|
||||
count: 220
|
||||
retentionDays: null
|
||||
'400':
|
||||
description: Invalid input
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_INPUT"
|
||||
message: "since: Invalid datetime"
|
||||
statusCode: 400
|
||||
|
||||
/api/realtime/permissions:
|
||||
get:
|
||||
summary: Get Realtime Permissions
|
||||
description: Retrieve RLS policies for subscribe (channels) and publish (messages) operations
|
||||
tags:
|
||||
- Permissions
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
responses:
|
||||
'200':
|
||||
description: Realtime RLS permissions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RealtimePermissions'
|
||||
example:
|
||||
subscribe:
|
||||
policies:
|
||||
- policyName: "allow_authenticated_subscribe"
|
||||
tableName: "channels"
|
||||
command: "SELECT"
|
||||
roles: ["authenticated"]
|
||||
using: "enabled = true"
|
||||
withCheck: null
|
||||
publish:
|
||||
policies:
|
||||
- policyName: "allow_authenticated_publish"
|
||||
tableName: "messages"
|
||||
command: "INSERT"
|
||||
roles: ["authenticated"]
|
||||
using: null
|
||||
withCheck: "true"
|
||||
|
||||
/api/realtime/config:
|
||||
get:
|
||||
summary: Get Realtime Config
|
||||
description: Retrieve realtime message retention configuration
|
||||
tags:
|
||||
- Configuration
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
responses:
|
||||
'200':
|
||||
description: Realtime configuration
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RealtimeConfig'
|
||||
example:
|
||||
retentionDays: null
|
||||
patch:
|
||||
summary: Update Realtime Config
|
||||
description: Update realtime message retention configuration
|
||||
tags:
|
||||
- Configuration
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateRealtimeConfigRequest'
|
||||
examples:
|
||||
retainFor90Days:
|
||||
value:
|
||||
retentionDays: 90
|
||||
keepForever:
|
||||
value:
|
||||
retentionDays: null
|
||||
responses:
|
||||
'200':
|
||||
description: Realtime configuration updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- message
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
example:
|
||||
message: "Retention config updated successfully"
|
||||
'400':
|
||||
description: Invalid input
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_INPUT"
|
||||
message: "retentionDays: Number must be greater than 0"
|
||||
statusCode: 400
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
apiKey:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Key
|
||||
|
||||
schemas:
|
||||
Channel:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- pattern
|
||||
- enabled
|
||||
- createdAt
|
||||
- updatedAt
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Unique identifier for the channel
|
||||
example: "550e8400-e29b-41d4-a716-446655440000"
|
||||
pattern:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Channel pattern for subscription matching. Uses SQL LIKE wildcards, for example "order:%".
|
||||
example: "order:%"
|
||||
description:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Human-readable description of the channel
|
||||
example: "Order updates channel"
|
||||
webhookUrls:
|
||||
type: array
|
||||
nullable: true
|
||||
items:
|
||||
type: string
|
||||
format: uri
|
||||
description: URLs to receive webhook notifications for messages on this channel
|
||||
example: ["https://example.com/webhook"]
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the channel is currently active
|
||||
example: true
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when the channel was created
|
||||
example: "2024-01-15T10:30:00Z"
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when the channel was last updated
|
||||
example: "2024-01-15T10:30:00Z"
|
||||
|
||||
CreateChannelRequest:
|
||||
type: object
|
||||
required:
|
||||
- pattern
|
||||
properties:
|
||||
pattern:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Channel pattern for subscription matching. Uses SQL LIKE wildcards, for example "order:%".
|
||||
example: "order:%"
|
||||
description:
|
||||
type: string
|
||||
description: Human-readable description of the channel
|
||||
example: "Order updates channel"
|
||||
webhookUrls:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uri
|
||||
description: URLs to receive webhook notifications
|
||||
example: ["https://example.com/webhook"]
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Whether the channel should be active upon creation
|
||||
example: true
|
||||
|
||||
UpdateChannelRequest:
|
||||
type: object
|
||||
properties:
|
||||
pattern:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Updated channel pattern
|
||||
example: "order:%"
|
||||
description:
|
||||
type: string
|
||||
description: Updated description
|
||||
example: "Updated order channel"
|
||||
webhookUrls:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uri
|
||||
description: Updated webhook URLs
|
||||
example: ["https://example.com/webhook"]
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Updated enabled status
|
||||
example: false
|
||||
|
||||
Message:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- eventName
|
||||
- channelName
|
||||
- payload
|
||||
- senderType
|
||||
- wsAudienceCount
|
||||
- whAudienceCount
|
||||
- whDeliveredCount
|
||||
- createdAt
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Unique identifier for the message
|
||||
example: "660e8400-e29b-41d4-a716-446655440000"
|
||||
eventName:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Name of the event
|
||||
example: "order.created"
|
||||
channelId:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: ID of the channel this message belongs to
|
||||
example: "550e8400-e29b-41d4-a716-446655440000"
|
||||
channelName:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Resolved channel name (instance of the pattern)
|
||||
example: "order:123"
|
||||
payload:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
description: Message payload data
|
||||
example:
|
||||
orderId: "123"
|
||||
status: "pending"
|
||||
senderType:
|
||||
type: string
|
||||
enum: [system, user]
|
||||
description: Type of sender that published the message
|
||||
example: "user"
|
||||
senderId:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: ID of the user who sent the message (null for system messages)
|
||||
example: "770e8400-e29b-41d4-a716-446655440000"
|
||||
wsAudienceCount:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Number of WebSocket clients who received the message
|
||||
example: 5
|
||||
whAudienceCount:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Number of webhooks that should receive the message
|
||||
example: 1
|
||||
whDeliveredCount:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Number of webhooks that successfully received the message
|
||||
example: 1
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when the message was created
|
||||
example: "2024-01-15T10:30:00Z"
|
||||
|
||||
ClearMessagesResponse:
|
||||
type: object
|
||||
required:
|
||||
- deleted
|
||||
properties:
|
||||
deleted:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Number of realtime messages deleted
|
||||
example: 42
|
||||
|
||||
MessageStats:
|
||||
type: object
|
||||
required:
|
||||
- totalMessages
|
||||
- whDeliveryRate
|
||||
- topEvents
|
||||
- retentionDays
|
||||
properties:
|
||||
totalMessages:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Total number of messages
|
||||
example: 1250
|
||||
whDeliveryRate:
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
description: Webhook delivery success rate (0-1)
|
||||
example: 0.98
|
||||
topEvents:
|
||||
type: array
|
||||
description: Most frequent event types
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- eventName
|
||||
- count
|
||||
properties:
|
||||
eventName:
|
||||
type: string
|
||||
description: Name of the event
|
||||
example: "order.created"
|
||||
count:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Number of occurrences
|
||||
example: 450
|
||||
retentionDays:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
description: Number of days messages are retained. Null means messages are kept indefinitely.
|
||||
example: null
|
||||
|
||||
RealtimeConfig:
|
||||
type: object
|
||||
required:
|
||||
- retentionDays
|
||||
properties:
|
||||
retentionDays:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
description: Number of days messages are retained. Null means messages are kept indefinitely.
|
||||
example: null
|
||||
|
||||
UpdateRealtimeConfigRequest:
|
||||
type: object
|
||||
required:
|
||||
- retentionDays
|
||||
properties:
|
||||
retentionDays:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
description: Number of days messages are retained. Null means messages are kept indefinitely.
|
||||
example: 90
|
||||
|
||||
RlsPolicy:
|
||||
type: object
|
||||
required:
|
||||
- policyName
|
||||
- tableName
|
||||
- command
|
||||
- roles
|
||||
properties:
|
||||
policyName:
|
||||
type: string
|
||||
description: Name of the RLS policy
|
||||
example: "allow_authenticated_subscribe"
|
||||
tableName:
|
||||
type: string
|
||||
description: Table the policy applies to
|
||||
example: "channels"
|
||||
command:
|
||||
type: string
|
||||
description: SQL command the policy applies to (SELECT, INSERT, UPDATE, DELETE)
|
||||
example: "SELECT"
|
||||
roles:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Database roles the policy applies to
|
||||
example: ["authenticated"]
|
||||
using:
|
||||
type: string
|
||||
nullable: true
|
||||
description: USING clause for row-level filtering
|
||||
example: "enabled = true"
|
||||
withCheck:
|
||||
type: string
|
||||
nullable: true
|
||||
description: WITH CHECK clause for row-level validation
|
||||
example: null
|
||||
|
||||
RealtimePermissions:
|
||||
type: object
|
||||
required:
|
||||
- subscribe
|
||||
- publish
|
||||
properties:
|
||||
subscribe:
|
||||
type: object
|
||||
required:
|
||||
- policies
|
||||
properties:
|
||||
policies:
|
||||
type: array
|
||||
description: RLS policies for channel subscriptions
|
||||
items:
|
||||
$ref: '#/components/schemas/RlsPolicy'
|
||||
publish:
|
||||
type: object
|
||||
required:
|
||||
- policies
|
||||
properties:
|
||||
policies:
|
||||
type: array
|
||||
description: RLS policies for message publishing
|
||||
items:
|
||||
$ref: '#/components/schemas/RlsPolicy'
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
- message
|
||||
- statusCode
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error code for programmatic handling
|
||||
example: "INVALID_INPUT"
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message
|
||||
example: "Invalid request"
|
||||
statusCode:
|
||||
type: integer
|
||||
description: HTTP status code
|
||||
example: 400
|
||||
nextActions:
|
||||
type: string
|
||||
description: Suggested action to resolve the error
|
||||
example: "Check your request parameters"
|
||||
@@ -0,0 +1,382 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Records API
|
||||
version: 2.0.0
|
||||
description: PostgREST-style database record operations
|
||||
|
||||
paths:
|
||||
/api/database/records/{tableName}:
|
||||
get:
|
||||
summary: Query Records
|
||||
description: Query records from a table with filtering, sorting, and pagination
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: tableName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Name of the table to query
|
||||
example: posts
|
||||
- name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 100
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
description: Maximum number of records to return
|
||||
- name: offset
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 0
|
||||
minimum: 0
|
||||
description: Number of records to skip for pagination
|
||||
- name: order
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
description: Sort order (e.g., "createdAt.desc", "name.asc")
|
||||
example: createdAt.desc
|
||||
- name: select
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
description: Comma-separated list of columns to return
|
||||
example: id,title,content
|
||||
# PostgREST-style filters
|
||||
- name: field
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
description: Filter by field value (e.g., "?status=eq.active", "?age=gt.18")
|
||||
example: status=eq.active
|
||||
responses:
|
||||
'200':
|
||||
description: List of records
|
||||
headers:
|
||||
X-Total-Count:
|
||||
schema:
|
||||
type: integer
|
||||
description: Total number of records matching the query
|
||||
Content-Range:
|
||||
schema:
|
||||
type: string
|
||||
description: Range of records returned (e.g., "0-99/1234")
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
example:
|
||||
- id: "248373e1-0aea-45ce-8844-5ef259203749"
|
||||
title: "Getting Started with InsForge"
|
||||
content: "This is a guide to help you get started..."
|
||||
createdAt: "2025-07-18T05:37:24.338Z"
|
||||
updatedAt: "2025-07-18T05:37:24.338Z"
|
||||
- id: "348373e1-0aea-45ce-8844-5ef259203750"
|
||||
title: "Advanced Database Queries"
|
||||
content: "Learn how to write complex queries..."
|
||||
createdAt: "2025-07-19T08:15:10.123Z"
|
||||
updatedAt: "2025-07-19T08:15:10.123Z"
|
||||
'404':
|
||||
description: Table not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "TABLE_NOT_FOUND"
|
||||
message: "Table 'nonexistent' does not exist"
|
||||
statusCode: 404
|
||||
nextActions: "Check table name and try again"
|
||||
'400':
|
||||
description: Invalid query parameters
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_QUERY"
|
||||
message: "Invalid filter syntax"
|
||||
statusCode: 400
|
||||
nextActions: "Check PostgREST filter documentation"
|
||||
|
||||
post:
|
||||
summary: Create Records
|
||||
description: Create one or more records. Request body MUST be an array.
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: tableName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Name of the table
|
||||
example: posts
|
||||
- name: Prefer
|
||||
in: header
|
||||
schema:
|
||||
type: string
|
||||
enum: ["return=representation"]
|
||||
description: Include to return created records in response (otherwise returns empty array)
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
minItems: 1
|
||||
example:
|
||||
- title: "My First Post"
|
||||
content: "Hello world! This is my first post."
|
||||
published: true
|
||||
responses:
|
||||
'201':
|
||||
description: Records created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
examples:
|
||||
without_prefer:
|
||||
summary: Response without Prefer header
|
||||
value: []
|
||||
with_prefer:
|
||||
summary: "Response with Prefer: return=representation"
|
||||
value:
|
||||
- id: "248373e1-0aea-45ce-8844-5ef259203749"
|
||||
title: "My First Post"
|
||||
content: "Hello world! This is my first post."
|
||||
published: true
|
||||
createdAt: "2025-07-18T05:37:24.338Z"
|
||||
updatedAt: "2025-07-18T05:37:24.338Z"
|
||||
'400':
|
||||
description: Invalid input
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "VALIDATION_ERROR"
|
||||
message: "Invalid field type: expected boolean for 'published'"
|
||||
statusCode: 400
|
||||
nextActions: "Ensure field types match the table schema"
|
||||
'404':
|
||||
description: Table not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "TABLE_NOT_FOUND"
|
||||
message: "Table 'nonexistent' does not exist"
|
||||
statusCode: 404
|
||||
nextActions: "Create the table first"
|
||||
|
||||
patch:
|
||||
summary: Update Records
|
||||
description: Update records matching query filters
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: tableName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Name of the table
|
||||
example: posts
|
||||
- name: id
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
description: Filter records by ID (e.g., "?id=eq.123e4567-e89b-12d3-a456-426614174000")
|
||||
example: eq.123e4567-e89b-12d3-a456-426614174000
|
||||
- name: Prefer
|
||||
in: header
|
||||
schema:
|
||||
type: string
|
||||
enum: ["return=representation"]
|
||||
description: Include to return updated records in response
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
example:
|
||||
title: "Updated Post Title"
|
||||
content: "This content has been updated."
|
||||
responses:
|
||||
'200':
|
||||
description: Records updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
examples:
|
||||
without_prefer:
|
||||
summary: Response without Prefer header
|
||||
value: []
|
||||
with_prefer:
|
||||
summary: "Response with Prefer: return=representation"
|
||||
value:
|
||||
- id: "123e4567-e89b-12d3-a456-426614174000"
|
||||
title: "Updated Post Title"
|
||||
content: "This content has been updated."
|
||||
createdAt: "2025-01-01T00:00:00Z"
|
||||
updatedAt: "2025-01-21T11:00:00Z"
|
||||
'404':
|
||||
description: No records found to update
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
example: []
|
||||
'400':
|
||||
description: Invalid input
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "VALIDATION_ERROR"
|
||||
message: "Invalid field type"
|
||||
statusCode: 400
|
||||
nextActions: "Check field types match table schema"
|
||||
|
||||
delete:
|
||||
summary: Delete Records
|
||||
description: Delete records matching query filters
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: tableName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Name of the table
|
||||
example: posts
|
||||
- name: id
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
description: Filter records by ID (e.g., "?id=eq.123e4567-e89b-12d3-a456-426614174000")
|
||||
example: eq.123e4567-e89b-12d3-a456-426614174000
|
||||
- name: Prefer
|
||||
in: header
|
||||
schema:
|
||||
type: string
|
||||
enum: ["return=representation"]
|
||||
description: Include to return deleted records in response
|
||||
responses:
|
||||
'204':
|
||||
description: Records deleted (without Prefer header)
|
||||
'200':
|
||||
description: Records deleted (with Prefer header)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
examples:
|
||||
records_deleted:
|
||||
summary: Records were deleted
|
||||
value:
|
||||
- id: "123e4567-e89b-12d3-a456-426614174000"
|
||||
title: "Deleted Post"
|
||||
createdAt: "2025-01-01T00:00:00Z"
|
||||
updatedAt: "2025-01-21T11:00:00Z"
|
||||
no_records:
|
||||
summary: No records found to delete
|
||||
value: []
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
apiKey:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: x-api-key
|
||||
|
||||
schemas:
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
- message
|
||||
- statusCode
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error code for programmatic handling
|
||||
example: "VALIDATION_ERROR"
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message
|
||||
example: "Invalid input data"
|
||||
statusCode:
|
||||
type: integer
|
||||
description: HTTP status code
|
||||
example: 400
|
||||
nextActions:
|
||||
type: string
|
||||
description: Suggested action to resolve the error
|
||||
example: "Check the request body format"
|
||||
|
||||
Record:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Auto-generated UUID
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when record was created
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when record was last updated
|
||||
additionalProperties: true
|
||||
description: Base record structure with system fields
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
@@ -0,0 +1,433 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Secrets API
|
||||
version: 1.0.0
|
||||
description: Secure secrets management for storing API keys, tokens, and sensitive configuration
|
||||
|
||||
paths:
|
||||
/api/secrets:
|
||||
get:
|
||||
summary: List all secrets
|
||||
description: Returns metadata for all secrets (without values)
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: List of secret metadata
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
secrets:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SecretMetadata'
|
||||
example:
|
||||
secrets:
|
||||
- id: "123e4567-e89b-12d3-a456-426614174000"
|
||||
key: "STRIPE_API_KEY"
|
||||
isActive: true
|
||||
isReserved: false
|
||||
createdAt: "2024-01-21T10:30:00Z"
|
||||
updatedAt: "2024-01-21T10:30:00Z"
|
||||
expiresAt: null
|
||||
- id: "223e4567-e89b-12d3-a456-426614174001"
|
||||
key: "OPENAI_API_KEY"
|
||||
isActive: true
|
||||
isReserved: true
|
||||
createdAt: "2024-01-20T09:15:00Z"
|
||||
updatedAt: "2024-01-20T09:15:00Z"
|
||||
expiresAt: "2025-01-20T09:15:00Z"
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'403':
|
||||
description: Forbidden - Admin only
|
||||
|
||||
post:
|
||||
summary: Create a new secret
|
||||
description: Create a new encrypted secret with a unique key
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- key
|
||||
- value
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
pattern: '^[A-Z0-9_]+$'
|
||||
description: Unique key identifier (uppercase letters, numbers, underscores only)
|
||||
example: "STRIPE_API_KEY"
|
||||
value:
|
||||
type: string
|
||||
description: Secret value to be encrypted
|
||||
example: "sk_live_..."
|
||||
isReserved:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Whether the secret is protected from deletion
|
||||
expiresAt:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
description: Optional expiration date for the secret
|
||||
responses:
|
||||
'201':
|
||||
description: Secret created successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
example:
|
||||
success: true
|
||||
message: "Secret STRIPE_API_KEY has been created successfully"
|
||||
id: "123e4567-e89b-12d3-a456-426614174000"
|
||||
'400':
|
||||
description: Invalid input
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
examples:
|
||||
missingFields:
|
||||
value:
|
||||
error: "INVALID_INPUT"
|
||||
message: "Both key and value are required"
|
||||
statusCode: 400
|
||||
invalidKeyFormat:
|
||||
value:
|
||||
error: "INVALID_INPUT"
|
||||
message: "Invalid key format. Use uppercase letters, numbers, and underscores only (e.g., STRIPE_API_KEY)"
|
||||
statusCode: 400
|
||||
'409':
|
||||
description: Secret already exists
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_INPUT"
|
||||
message: "Secret already exists: STRIPE_API_KEY"
|
||||
statusCode: 409
|
||||
|
||||
/api/secrets/{key}:
|
||||
get:
|
||||
summary: Get secret value
|
||||
description: Retrieve the decrypted value of a specific secret by its key
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: key
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[A-Z0-9_]+$'
|
||||
description: Secret key identifier
|
||||
example: "STRIPE_API_KEY"
|
||||
responses:
|
||||
'200':
|
||||
description: Secret value retrieved
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
example:
|
||||
key: "STRIPE_API_KEY"
|
||||
value: "sk_live_..."
|
||||
'404':
|
||||
description: Secret not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "NOT_FOUND"
|
||||
message: "Secret not found: INVALID_KEY"
|
||||
statusCode: 404
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'403':
|
||||
description: Forbidden - Admin only
|
||||
|
||||
put:
|
||||
summary: Update secret
|
||||
description: Update an existing secret's value or metadata
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: key
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[A-Z0-9_]+$'
|
||||
description: Secret key identifier
|
||||
example: "STRIPE_API_KEY"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
value:
|
||||
type: string
|
||||
description: New secret value (will be encrypted)
|
||||
isActive:
|
||||
type: boolean
|
||||
description: Whether the secret is active
|
||||
isReserved:
|
||||
type: boolean
|
||||
description: Whether the secret is protected from deletion
|
||||
expiresAt:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
description: Expiration date (null to remove expiration)
|
||||
responses:
|
||||
'200':
|
||||
description: Secret updated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
example:
|
||||
success: true
|
||||
message: "Secret STRIPE_API_KEY has been updated successfully"
|
||||
'404':
|
||||
description: Secret not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "NOT_FOUND"
|
||||
message: "Secret not found: INVALID_KEY"
|
||||
statusCode: 404
|
||||
'500':
|
||||
description: Failed to update secret
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INTERNAL_ERROR"
|
||||
message: "Failed to update secret: STRIPE_API_KEY"
|
||||
statusCode: 500
|
||||
|
||||
delete:
|
||||
summary: Delete secret
|
||||
description: Mark a secret as inactive (soft delete). Cannot delete reserved secrets.
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: key
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[A-Z0-9_]+$'
|
||||
description: Secret key identifier
|
||||
example: "STRIPE_API_KEY"
|
||||
responses:
|
||||
'200':
|
||||
description: Secret deleted successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
example:
|
||||
success: true
|
||||
message: "Secret STRIPE_API_KEY has been deleted successfully"
|
||||
'403':
|
||||
description: Cannot delete reserved secret
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "FORBIDDEN"
|
||||
message: "Cannot delete reserved secret: OPENAI_API_KEY"
|
||||
statusCode: 403
|
||||
'404':
|
||||
description: Secret not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "NOT_FOUND"
|
||||
message: "Secret not found: INVALID_KEY"
|
||||
statusCode: 404
|
||||
|
||||
/api/secrets/anon-key/rotate:
|
||||
post:
|
||||
summary: Rotate anon key
|
||||
description: >-
|
||||
Rotate the project's opaque anon key (`anon_...`). A new key is
|
||||
generated and returned; the old key stays valid for the grace period
|
||||
(default 168 hours / 7 days, max 720) so already-deployed frontends
|
||||
and mobile binaries keep working while the new key ships. Admin only.
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
gracePeriodHours:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 720
|
||||
default: 168
|
||||
description: How long the old key remains valid after rotation
|
||||
responses:
|
||||
'200':
|
||||
description: Anon key rotated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
anonKey:
|
||||
type: string
|
||||
description: The new anon key
|
||||
oldKeyExpiresAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: When the previous key stops being accepted
|
||||
example:
|
||||
success: true
|
||||
message: "Anon key rotated successfully. Old key will remain valid during grace period."
|
||||
anonKey: "anon_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd"
|
||||
oldKeyExpiresAt: "2026-06-18T00:00:00.000Z"
|
||||
'400':
|
||||
description: Invalid grace period
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: Forbidden - admin access required
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
|
||||
schemas:
|
||||
SecretMetadata:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Unique identifier for the secret
|
||||
key:
|
||||
type: string
|
||||
description: Unique key identifier (uppercase with underscores)
|
||||
example: "STRIPE_API_KEY"
|
||||
isActive:
|
||||
type: boolean
|
||||
description: Whether the secret is currently active
|
||||
isReserved:
|
||||
type: boolean
|
||||
description: Whether the secret is protected from deletion
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: When the secret was created
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: When the secret was last updated
|
||||
expiresAt:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
description: When the secret expires (null if no expiration)
|
||||
required:
|
||||
- id
|
||||
- key
|
||||
- isActive
|
||||
- isReserved
|
||||
- createdAt
|
||||
- updatedAt
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
- message
|
||||
- statusCode
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error code for programmatic handling
|
||||
example: "INVALID_INPUT"
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message
|
||||
example: "Invalid input data"
|
||||
statusCode:
|
||||
type: integer
|
||||
description: HTTP status code
|
||||
example: 400
|
||||
nextActions:
|
||||
type: string
|
||||
description: Suggested action to resolve the error
|
||||
example: "Check the request body format"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,604 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Insforge Tables API
|
||||
version: 1.0.0
|
||||
|
||||
paths:
|
||||
/api/database/tables:
|
||||
get:
|
||||
summary: List Tables
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
responses:
|
||||
'200':
|
||||
description: List of table names
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example:
|
||||
- "posts"
|
||||
- "comments"
|
||||
- "categories"
|
||||
|
||||
post:
|
||||
summary: Create Table
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- tableName
|
||||
- columns
|
||||
properties:
|
||||
tableName:
|
||||
type: string
|
||||
columns:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
- nullable
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
enum: [string, datetime, integer, float, boolean, uuid, json, file]
|
||||
nullable:
|
||||
type: boolean
|
||||
unique:
|
||||
type: boolean
|
||||
defaultValue:
|
||||
type: string
|
||||
foreignKeys:
|
||||
type: array
|
||||
description: Table-level foreign key constraints
|
||||
items:
|
||||
$ref: '#/components/schemas/ForeignKeyConstraint'
|
||||
rlsEnabled:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Enable Row Level Security on the table
|
||||
responses:
|
||||
'201':
|
||||
description: Table created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
table_name:
|
||||
type: string
|
||||
example:
|
||||
message: "Table created successfully"
|
||||
tableName: "posts"
|
||||
'400':
|
||||
description: Bad request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "VALIDATION_ERROR"
|
||||
message: "Table name already exists"
|
||||
statusCode: 400
|
||||
nextActions: "Choose a different table name"
|
||||
'422':
|
||||
description: Unprocessable entity
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "INVALID_FIELD_TYPE"
|
||||
message: "Invalid field type: 'text'. Valid types are: string, integer, float, boolean, datetime, uuid, json, file"
|
||||
statusCode: 422
|
||||
nextActions: "Use one of the valid field types"
|
||||
|
||||
/api/database/tables/{tableName}/schema:
|
||||
get:
|
||||
summary: Get Table Schema
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: tableName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Table schema
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
table_name:
|
||||
type: string
|
||||
columns:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
nullable:
|
||||
type: boolean
|
||||
unique:
|
||||
type: boolean
|
||||
default:
|
||||
type: string
|
||||
nullable: true
|
||||
isPrimaryKey:
|
||||
type: boolean
|
||||
foreignKeys:
|
||||
type: array
|
||||
description: Table-level foreign key constraints (one entry per constraint)
|
||||
items:
|
||||
$ref: '#/components/schemas/ForeignKeyConstraint'
|
||||
example:
|
||||
tableName: "posts"
|
||||
columns:
|
||||
- name: "id"
|
||||
type: "uuid"
|
||||
nullable: false
|
||||
unique: true
|
||||
default: "gen_random_uuid()"
|
||||
isPrimaryKey: true
|
||||
- name: "title"
|
||||
type: "string"
|
||||
nullable: false
|
||||
unique: false
|
||||
default: null
|
||||
isPrimaryKey: false
|
||||
- name: "userId"
|
||||
type: "uuid"
|
||||
nullable: false
|
||||
unique: false
|
||||
default: null
|
||||
isPrimaryKey: false
|
||||
foreignKeys:
|
||||
- constraintName: "fk_userId_auth_users_id"
|
||||
referenceTable: "auth.users"
|
||||
referenceColumns:
|
||||
- sourceColumn: "userId"
|
||||
referenceColumn: "id"
|
||||
onDelete: "CASCADE"
|
||||
onUpdate: "CASCADE"
|
||||
'404':
|
||||
description: Table not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "TABLE_NOT_FOUND"
|
||||
message: "Table 'nonexistent' does not exist"
|
||||
statusCode: 404
|
||||
nextActions: "Check table name and try again"
|
||||
|
||||
patch:
|
||||
summary: Update Table Schema
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: tableName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
addColumns:
|
||||
type: array
|
||||
description: Add new columns to the table
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- columnName
|
||||
- type
|
||||
properties:
|
||||
columnName:
|
||||
type: string
|
||||
description: Name of the new column
|
||||
type:
|
||||
type: string
|
||||
enum: [string, integer, float, boolean, datetime, uuid, json, file]
|
||||
description: Data type of the column
|
||||
isNullable:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Whether the column allows NULL values
|
||||
isUnique:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Whether the column values must be unique
|
||||
defaultValue:
|
||||
type: string
|
||||
description: Default value for the column
|
||||
dropColumns:
|
||||
type: array
|
||||
description: Remove columns from the table
|
||||
items:
|
||||
type: string
|
||||
description: Name of the column to drop
|
||||
updateColumns:
|
||||
type: array
|
||||
description: Modify existing columns (rename or change default)
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- columnName
|
||||
properties:
|
||||
columnName:
|
||||
type: string
|
||||
description: Current name of the column
|
||||
newColumnName:
|
||||
type: string
|
||||
description: New name for the column (optional)
|
||||
minLength: 1
|
||||
maxLength: 64
|
||||
defaultValue:
|
||||
type: string
|
||||
description: New default value for the column (optional)
|
||||
addForeignKeys:
|
||||
type: array
|
||||
description: Add foreign key constraints (one entry per constraint; composite keys list multiple column mappings)
|
||||
items:
|
||||
$ref: '#/components/schemas/ForeignKeyConstraint'
|
||||
dropForeignKeys:
|
||||
type: array
|
||||
description: Constraint names of foreign keys to drop
|
||||
items:
|
||||
type: string
|
||||
description: Name of the foreign key constraint to drop
|
||||
renameTable:
|
||||
type: object
|
||||
description: Rename the table
|
||||
required:
|
||||
- newTableName
|
||||
properties:
|
||||
newTableName:
|
||||
type: string
|
||||
description: New name for the table
|
||||
minLength: 1
|
||||
maxLength: 64
|
||||
responses:
|
||||
'200':
|
||||
description: Table schema updated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
description: Success message
|
||||
tableName:
|
||||
type: string
|
||||
description: Name of the updated table
|
||||
operations:
|
||||
type: array
|
||||
description: List of operations performed
|
||||
items:
|
||||
type: string
|
||||
example:
|
||||
message: "Table schema updated successfully"
|
||||
tableName: "posts"
|
||||
operations:
|
||||
- "added 2 columns"
|
||||
- "dropped 1 columns"
|
||||
- "renamed 1 columns"
|
||||
- "added 1 foreign keys"
|
||||
- "dropped 1 foreign keys"
|
||||
'400':
|
||||
description: Bad request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
examples:
|
||||
columnExists:
|
||||
value:
|
||||
error: "COLUMN_EXISTS"
|
||||
message: "Column 'title' already exists in table 'posts'"
|
||||
statusCode: 400
|
||||
nextActions: "Choose a different column name or drop it first"
|
||||
columnNotFound:
|
||||
value:
|
||||
error: "COLUMN_NOT_FOUND"
|
||||
message: "Column 'nonexistent' not found in table 'posts'"
|
||||
statusCode: 400
|
||||
nextActions: "Check column name with GET /api/tables/{tableName}/schema"
|
||||
foreignKeyExists:
|
||||
value:
|
||||
error: "FOREIGN_KEY_EXISTS"
|
||||
message: "Foreign key on column 'user_id' already exists"
|
||||
statusCode: 400
|
||||
nextActions: "Drop the existing foreign key first or use a different column"
|
||||
'404':
|
||||
description: Table not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "TABLE_NOT_FOUND"
|
||||
message: "Table 'nonexistent' does not exist"
|
||||
statusCode: 404
|
||||
nextActions: "Check table name and try again"
|
||||
|
||||
/api/database/tables/{tableName}:
|
||||
delete:
|
||||
summary: Delete Table
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
parameters:
|
||||
- name: tableName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Table deleted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
table_name:
|
||||
type: string
|
||||
example:
|
||||
message: "Table deleted successfully"
|
||||
tableName: "posts"
|
||||
'404':
|
||||
description: Table not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "TABLE_NOT_FOUND"
|
||||
message: "Table 'nonexistent' does not exist"
|
||||
statusCode: 404
|
||||
nextActions: "Check table name and try again"
|
||||
|
||||
/api/database/migrations:
|
||||
get:
|
||||
summary: List Database Migrations
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
responses:
|
||||
'200':
|
||||
description: List successful custom migrations
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- migrations
|
||||
properties:
|
||||
migrations:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Migration'
|
||||
example:
|
||||
migrations:
|
||||
- version: "20260416170500"
|
||||
name: "create_posts_table"
|
||||
statements:
|
||||
- "CREATE TABLE posts (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL);"
|
||||
createdAt: "2026-04-16T17:05:00.000Z"
|
||||
|
||||
post:
|
||||
summary: Create and Execute Database Migration
|
||||
tags:
|
||||
- Admin
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKey: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- version
|
||||
- name
|
||||
- sql
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
description: Numeric migration version. Accepts Drizzle-style sequential prefixes (e.g. `0001`) or a `YYYYMMDDHHmmss` timestamp. Versions are compared numerically.
|
||||
pattern: '^\d{1,64}$'
|
||||
maxLength: 64
|
||||
name:
|
||||
type: string
|
||||
description: Migration name
|
||||
minLength: 1
|
||||
sql:
|
||||
type: string
|
||||
description: SQL text to parse and execute immediately
|
||||
minLength: 1
|
||||
example:
|
||||
version: "20260416170500"
|
||||
name: "create_posts_table"
|
||||
sql: "CREATE TABLE posts (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL);"
|
||||
responses:
|
||||
'201':
|
||||
description: Migration executed and recorded successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Migration'
|
||||
- type: object
|
||||
required:
|
||||
- message
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
example:
|
||||
version: "20260416170500"
|
||||
name: "create_posts_table"
|
||||
statements:
|
||||
- "CREATE TABLE posts (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL);"
|
||||
createdAt: "2026-04-16T17:05:00.000Z"
|
||||
message: "Migration executed successfully"
|
||||
'400':
|
||||
description: Invalid migration
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
examples:
|
||||
invalidPayload:
|
||||
value:
|
||||
error: "INVALID_INPUT"
|
||||
message: "sql: Migration SQL is required"
|
||||
statusCode: 400
|
||||
transactionStatement:
|
||||
value:
|
||||
error: "DATABASE_FORBIDDEN"
|
||||
message: "Custom migrations cannot manage their own transactions."
|
||||
statusCode: 400
|
||||
'409':
|
||||
description: Migration version already exists or is not newer than the latest applied migration
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
error: "ALREADY_EXISTS"
|
||||
message: "Migration version already exists."
|
||||
statusCode: 409
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
apiKey:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Key
|
||||
|
||||
schemas:
|
||||
ForeignKeyConstraint:
|
||||
type: object
|
||||
description: A table-level foreign key constraint. One entry per constraint; composite keys list multiple column mappings.
|
||||
required:
|
||||
- referenceTable
|
||||
- referenceColumns
|
||||
- onDelete
|
||||
- onUpdate
|
||||
properties:
|
||||
constraintName:
|
||||
type: string
|
||||
description: Constraint name. Returned when reading the schema; derived by the backend on create.
|
||||
referenceTable:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Table being referenced
|
||||
referenceColumns:
|
||||
type: array
|
||||
minItems: 1
|
||||
description: Ordered (source → reference) column pairs
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- sourceColumn
|
||||
- referenceColumn
|
||||
properties:
|
||||
sourceColumn:
|
||||
type: string
|
||||
description: Column in the current table
|
||||
referenceColumn:
|
||||
type: string
|
||||
description: Column in the referenced table
|
||||
onDelete:
|
||||
type: string
|
||||
enum: [CASCADE, SET NULL, SET DEFAULT, NO ACTION, RESTRICT]
|
||||
onUpdate:
|
||||
type: string
|
||||
enum: [CASCADE, SET NULL, SET DEFAULT, NO ACTION, RESTRICT]
|
||||
Migration:
|
||||
type: object
|
||||
required:
|
||||
- version
|
||||
- name
|
||||
- statements
|
||||
- createdAt
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
pattern: '^\d{1,64}$'
|
||||
name:
|
||||
type: string
|
||||
statements:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
- message
|
||||
- statusCode
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error code for programmatic handling
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message
|
||||
statusCode:
|
||||
type: integer
|
||||
description: HTTP status code
|
||||
nextActions:
|
||||
type: string
|
||||
description: Suggested action to resolve the error
|
||||
Reference in New Issue
Block a user