rotifex 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -1909
- package/bin/rotifex.js +2 -0
- package/config.default.json +29 -29
- package/package.json +73 -48
- package/src/ai/tools/registry.js +156 -156
- package/src/auth/auth.controller.js +168 -118
- package/src/auth/auth.routes.js +6 -4
- package/src/auth/auth.service.js +298 -182
- package/src/auth/jwt.middleware.js +7 -1
- package/src/commands/migrate.js +63 -62
- package/src/commands/resetAdmin.js +54 -0
- package/src/commands/start.js +45 -1
- package/src/db/adapters/base.js +96 -73
- package/src/db/adapters/sequelize.js +242 -0
- package/src/db/adapters/sqlite.js +106 -86
- package/src/db/connection.js +51 -45
- package/src/db/index.js +13 -12
- package/src/db/migrator.js +156 -142
- package/src/db/schema.js +57 -48
- package/src/engine/index.js +77 -76
- package/src/engine/routeFactory.js +122 -119
- package/src/engine/schemaLoader.js +75 -75
- package/src/engine/tableSync.js +68 -28
- package/src/lib/config.js +125 -120
- package/src/server/routes/admin.js +327 -276
- package/src/server/routes/files.js +188 -188
- package/src/server/routes/setup.js +71 -0
- package/src/server/server.js +157 -153
- package/src/storage/fileTable.js +10 -10
- package/src/storage/storageManager.js +18 -18
package/README.md
CHANGED
|
@@ -1,1361 +1,66 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/rotifex)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](LICENSE)
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm i rotifex
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Then start the server:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm start
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Or run directly without installing:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npx rotifex start
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Commands
|
|
28
|
-
|
|
29
|
-
| Command | Description |
|
|
30
|
-
| ------- | ------------------------------------ |
|
|
31
|
-
| `start` | Start the Rotifex development server |
|
|
32
|
-
|
|
33
|
-
## Table of Contents
|
|
34
|
-
|
|
35
|
-
1. [Application Overview](#1-application-overview)
|
|
36
|
-
2. [Feature Documentation](#2-feature-documentation)
|
|
37
|
-
3. [API Documentation](#3-api-documentation)
|
|
38
|
-
4. [Authentication](#4-authentication)
|
|
39
|
-
5. [Models / Data Structures](#5-models--data-structures)
|
|
40
|
-
6. [AI / LLM Integration](#6-ai--llm-integration)
|
|
41
|
-
7. [File Storage / Media Handling](#7-file-storage--media-handling)
|
|
42
|
-
8. [Admin Panel Features](#8-admin-panel-features)
|
|
43
|
-
9. [Error Handling](#9-error-handling)
|
|
44
|
-
10. [Environment Configuration](#10-environment-configuration)
|
|
45
|
-
11. [Deployment](#11-deployment)
|
|
46
|
-
12. [Example Workflows](#12-example-workflows)
|
|
47
|
-
13. [Notes for Documentation Generators](#13-notes-for-documentation-generators)
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## 1. Application Overview
|
|
52
|
-
|
|
53
|
-
### Description
|
|
54
|
-
|
|
55
|
-
**Rotifex** is a self-hosted backend-as-a-service platform. It lets developers define data models through a JSON schema file or a visual admin panel, and instantly get a full REST API — no code generation required. It ships with JWT authentication, file storage, AI/LLM integration, agent execution, and a built-in admin dashboard.
|
|
56
|
-
|
|
57
|
-
### Core Purpose
|
|
58
|
-
|
|
59
|
-
Rotifex eliminates boilerplate for backend development. Instead of writing CRUD endpoints, migrations, and auth logic manually, developers define a schema and Rotifex handles everything: table creation, route registration, validation, pagination, filtering, and sorting — live, without restarts.
|
|
60
|
-
|
|
61
|
-
### Key Capabilities
|
|
62
|
-
|
|
63
|
-
- **Schema-driven REST API** — define a model, get five CRUD endpoints instantly
|
|
64
|
-
- **Live schema updates** — add or remove models at runtime without restarting the server
|
|
65
|
-
- **JWT Authentication** — register, login, refresh tokens, role-based access control
|
|
66
|
-
- **File storage** — upload, download, manage public and private files with signed URLs
|
|
67
|
-
- **AI/LLM integration** — connect OpenAI, Anthropic, Gemini, and Ollama; generate and chat
|
|
68
|
-
- **AI Agents** — ReAct-loop agents with tools: calculator, web search, HTTP GET, DB query, datetime
|
|
69
|
-
- **Token usage tracking** — persistent per-provider token consumption logged to disk
|
|
70
|
-
- **Admin dashboard** — full SPA for managing schemas, users, files, AI providers, agents, logs, and settings
|
|
71
|
-
- **Rate limiting, CORS, structured logging** — production-ready out of the box
|
|
72
|
-
|
|
73
|
-
### Target Users
|
|
74
|
-
|
|
75
|
-
- Solo developers who need a backend quickly
|
|
76
|
-
- Startups prototyping a product without a dedicated backend engineer
|
|
77
|
-
- Internal tools teams who need structured data storage with an admin interface
|
|
78
|
-
- Developers integrating LLMs into their applications
|
|
79
|
-
|
|
80
|
-
### Architecture Overview
|
|
81
|
-
|
|
82
|
-
```
|
|
83
|
-
+-----------------------------------------------------+
|
|
84
|
-
| Rotifex Server |
|
|
85
|
-
| +----------+ +------------+ +-----------------+ |
|
|
86
|
-
| | Fastify | | Schema | | SQLite (via | |
|
|
87
|
-
| | HTTP | | Engine | | better-sqlite3)| |
|
|
88
|
-
| | Server | | (live) | | | |
|
|
89
|
-
| +----------+ +------------+ +-----------------+ |
|
|
90
|
-
| +----------+ +------------+ +-----------------+ |
|
|
91
|
-
| | JWT | | Storage | | AI / Agents | |
|
|
92
|
-
| | Auth | | Manager | | System | |
|
|
93
|
-
| +----------+ +------------+ +-----------------+ |
|
|
94
|
-
| +------------------------------------------------+ |
|
|
95
|
-
| | Admin SPA (React + Vite) | |
|
|
96
|
-
| +------------------------------------------------+ |
|
|
97
|
-
+-----------------------------------------------------+
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
- **Framework:** Fastify v5 (Node.js)
|
|
101
|
-
- **Database:** SQLite via `better-sqlite3`
|
|
102
|
-
- **Admin frontend:** React 19 + Vite, served as a static SPA from `/`
|
|
103
|
-
- **Config format:** JSON (`schema.json`, `ai.config.json`, `agents.config.json`)
|
|
104
|
-
- **Persistence:** `.env` for secrets, JSON files for AI/agent config, SQLite for all app data
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## 2. Feature Documentation
|
|
109
|
-
|
|
110
|
-
### 2.1 Dynamic REST Engine
|
|
111
|
-
|
|
112
|
-
**Description:** The core of Rotifex. Reads `schema.json`, creates SQLite tables, and registers five CRUD routes per model — all at startup and live when models are added via the admin API.
|
|
113
|
-
|
|
114
|
-
**Use case:** A developer defines a `Product` model with `name`, `price`, and `in_stock` fields. Rotifex immediately exposes `/api/products` with full CRUD, filtering, sorting, and pagination — no restart needed.
|
|
115
|
-
|
|
116
|
-
**How it works:**
|
|
117
|
-
|
|
118
|
-
1. `schema.json` is parsed by `schemaLoader.js` into normalized model definitions.
|
|
119
|
-
2. `tableSync.js` runs `CREATE TABLE IF NOT EXISTS` for each model.
|
|
120
|
-
3. `routeFactory.js` registers generic parametric routes (`/api/:table`) that resolve the model from an in-memory store at request time.
|
|
121
|
-
4. When a model is added/removed via the admin API, the in-memory store is updated and routes resolve immediately.
|
|
122
|
-
|
|
123
|
-
**Field types supported:**
|
|
124
|
-
|
|
125
|
-
| Schema Type | SQLite Type | Notes |
|
|
126
|
-
| ----------- | ----------- | ------------- |
|
|
127
|
-
| `string` | `TEXT` | |
|
|
128
|
-
| `number` | `REAL` | Float |
|
|
129
|
-
| `integer` | `INTEGER` | |
|
|
130
|
-
| `boolean` | `INTEGER` | Stored as 0/1 |
|
|
131
|
-
|
|
132
|
-
**Field definition formats:**
|
|
133
|
-
|
|
134
|
-
```json
|
|
135
|
-
// Shorthand
|
|
136
|
-
{ "name": "string" }
|
|
137
|
-
|
|
138
|
-
// Full form
|
|
139
|
-
{
|
|
140
|
-
"name": {
|
|
141
|
-
"type": "string",
|
|
142
|
-
"required": true,
|
|
143
|
-
"unique": false,
|
|
144
|
-
"default": "Unnamed"
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
**Auto-generated columns:** Every model automatically gets `id` (UUID), `created_at` (ISO 8601), and `updated_at` (ISO 8601).
|
|
150
|
-
|
|
151
|
-
**Table naming:** Model names are lowercased and pluralized. `Product` -> table `products`, route `/api/products`.
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
### 2.2 JWT Authentication
|
|
156
|
-
|
|
157
|
-
**Description:** Full authentication system with access tokens, refresh tokens, password hashing, and role-based access control.
|
|
158
|
-
|
|
159
|
-
**Use case:** Secure user registration and login for a Rotifex-backed application. Protect admin routes from regular users.
|
|
160
|
-
|
|
161
|
-
**How it works:**
|
|
162
|
-
|
|
163
|
-
1. `POST /auth/register` hashes the password with bcrypt and inserts a user row.
|
|
164
|
-
2. `POST /auth/login` verifies credentials and issues a short-lived access token (1 hour) and long-lived refresh token (30 days).
|
|
165
|
-
3. The JWT middleware runs on every request, verifies the `Authorization: Bearer` header, and injects `x-user-id` / `x-user-role` headers that downstream routes use for authorization.
|
|
166
|
-
4. `POST /auth/refresh` issues a new token pair without requiring the password.
|
|
167
|
-
|
|
168
|
-
**Roles:** `user` (default) and `admin`. Admin access is required for all `/admin/api/*` endpoints.
|
|
169
|
-
|
|
170
|
-
**Password rules:**
|
|
171
|
-
|
|
172
|
-
- Minimum 8 characters
|
|
173
|
-
- At least one letter
|
|
174
|
-
- At least one number
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
### 2.3 File Storage
|
|
179
|
-
|
|
180
|
-
**Description:** Upload, download, list, and delete files. Supports public and private visibility with HMAC-signed URLs for private file access.
|
|
181
|
-
|
|
182
|
-
**Use case:** Allow users to upload profile pictures, documents, or any binary files. Private files can only be accessed by their uploader or admins, or via a time-limited signed URL.
|
|
183
|
-
|
|
184
|
-
**How it works:**
|
|
185
|
-
|
|
186
|
-
1. Files are uploaded via multipart form to `POST /files/upload`.
|
|
187
|
-
2. The `StorageManager` validates MIME type and per-user storage quota.
|
|
188
|
-
3. Files are stored on disk with UUID-based names in separate `public/` and `private/` directories.
|
|
189
|
-
4. Metadata (name, MIME type, size, uploader, visibility) is recorded in the `_files` SQLite table.
|
|
190
|
-
5. Private files require a signed URL generated by `GET /files/:id/signed-url`. The URL includes an HMAC token and expiry timestamp; it is verified on download.
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
### 2.4 AI / LLM Integration
|
|
195
|
-
|
|
196
|
-
**Description:** Connect multiple LLM providers and use them for text generation and multi-turn chat from a unified API.
|
|
197
|
-
|
|
198
|
-
**Use case:** Add AI-powered features to your application — content generation, Q&A, summarization — using whichever provider you have access to.
|
|
199
|
-
|
|
200
|
-
**How it works:**
|
|
201
|
-
|
|
202
|
-
1. Providers (OpenAI, Anthropic, Gemini, Ollama) are configured via the admin panel; API keys are stored in `ai.config.json`.
|
|
203
|
-
2. `POST /api/ai/generate` and `POST /api/ai/chat` proxy requests to the selected provider using native `fetch`.
|
|
204
|
-
3. Each call records token usage to `ai.usage.json` for persistent tracking.
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
### 2.5 AI Agents
|
|
209
|
-
|
|
210
|
-
**Description:** Configurable AI agents that use a ReAct (Reasoning + Acting) loop to complete multi-step tasks using tools.
|
|
211
|
-
|
|
212
|
-
**Use case:** An agent tasked with "What's 15% of the current Bitcoin price?" will search the web for the price, then use the calculator tool to compute the result — all automatically.
|
|
213
|
-
|
|
214
|
-
**How it works:**
|
|
215
|
-
|
|
216
|
-
1. Agents are defined with a name, provider, model, system prompt, tool list, temperature, and iteration limits.
|
|
217
|
-
2. When run, the agent sends the task to the LLM with tool definitions.
|
|
218
|
-
3. If the LLM calls a tool, the tool is executed and the result is appended to the conversation.
|
|
219
|
-
4. The loop continues until the LLM returns a final answer or `maxIterations` is reached.
|
|
220
|
-
5. Steps (thinking, tool call, tool result, final answer) are returned for inspection.
|
|
221
|
-
|
|
222
|
-
**Available tools:**
|
|
223
|
-
|
|
224
|
-
| Tool | Description |
|
|
225
|
-
| ---------------- | ------------------------------------------------------------------------ |
|
|
226
|
-
| `get_datetime` | Returns current ISO 8601 datetime, UTC string, and Unix timestamp |
|
|
227
|
-
| `calculate` | Safely evaluates math expressions (`+`, `-`, `*`, `/`, `%`, parentheses) |
|
|
228
|
-
| `web_search` | DuckDuckGo instant-answer search (no API key required) |
|
|
229
|
-
| `http_get` | Makes HTTP GET requests to public URLs; returns up to 4000 chars of body |
|
|
230
|
-
| `database_query` | Runs read-only `SELECT` queries on the Rotifex SQLite database |
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
### 2.6 Admin Dashboard
|
|
235
|
-
|
|
236
|
-
**Description:** A React SPA served at `/` providing a visual interface for all administrative operations.
|
|
237
|
-
|
|
238
|
-
**Tabs:**
|
|
239
|
-
|
|
240
|
-
- **Dashboard** — stat cards (schemas, records, users, files, storage, connected LLMs, agents, uptime, status) and overview tables
|
|
241
|
-
- **Database Schemas** — create, view, and delete data models
|
|
242
|
-
- **User Management** — view and manage registered users
|
|
243
|
-
- **File Browser** — browse, preview, and delete uploaded files
|
|
244
|
-
- **AI Integration** — configure providers, playground for generate/chat, agent management, API docs
|
|
245
|
-
- **Server Logs** — real-time in-memory log viewer with level filtering
|
|
246
|
-
- **Settings** — edit environment variables (port, CORS, rate limits, JWT secrets, storage limits)
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
## 3. API Documentation
|
|
251
|
-
|
|
252
|
-
### Base URL
|
|
253
|
-
|
|
254
|
-
```
|
|
255
|
-
http://localhost:3000
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
All API responses follow the envelope format:
|
|
259
|
-
|
|
260
|
-
- **Success:** `{ "data": <payload>, "meta"?: <pagination> }`
|
|
261
|
-
- **Error:** `{ "error": "<type>", "message": "<detail>", "statusCode": <number> }`
|
|
262
|
-
|
|
263
|
-
---
|
|
264
|
-
|
|
265
|
-
### 3.1 Health
|
|
266
|
-
|
|
267
|
-
#### `GET /health`
|
|
268
|
-
|
|
269
|
-
Check server status.
|
|
270
|
-
|
|
271
|
-
**Auth:** None
|
|
272
|
-
|
|
273
|
-
**Response:**
|
|
274
|
-
|
|
275
|
-
```json
|
|
276
|
-
{
|
|
277
|
-
"status": "ok",
|
|
278
|
-
"uptime": 3600.5,
|
|
279
|
-
"timestamp": "2026-03-06T12:00:00.000Z"
|
|
280
|
-
}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
---
|
|
284
|
-
|
|
285
|
-
### 3.2 Authentication Endpoints
|
|
286
|
-
|
|
287
|
-
#### `POST /auth/register`
|
|
288
|
-
|
|
289
|
-
Register a new user.
|
|
290
|
-
|
|
291
|
-
**Auth:** None
|
|
292
|
-
|
|
293
|
-
**Request Body:**
|
|
294
|
-
|
|
295
|
-
```json
|
|
296
|
-
{
|
|
297
|
-
"email": "user@example.com",
|
|
298
|
-
"password": "secret123",
|
|
299
|
-
"display_name": "Jane Doe",
|
|
300
|
-
"role": "user"
|
|
301
|
-
}
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
| Field | Type | Required | Notes |
|
|
305
|
-
| -------------- | ------ | -------- | ------------------------------------------- |
|
|
306
|
-
| `email` | string | Yes | Must be a valid email |
|
|
307
|
-
| `password` | string | Yes | Min 8 chars, 1 letter, 1 number |
|
|
308
|
-
| `display_name` | string | No | Display name |
|
|
309
|
-
| `role` | string | No | `"user"` or `"admin"`. Defaults to `"user"` |
|
|
310
|
-
|
|
311
|
-
**Response `201`:**
|
|
312
|
-
|
|
313
|
-
```json
|
|
314
|
-
{
|
|
315
|
-
"data": {
|
|
316
|
-
"id": "uuid",
|
|
317
|
-
"email": "user@example.com",
|
|
318
|
-
"display_name": "Jane Doe",
|
|
319
|
-
"role": "user",
|
|
320
|
-
"created_at": "2026-03-06T12:00:00.000Z"
|
|
321
|
-
},
|
|
322
|
-
"message": "User registered successfully"
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
**Errors:**
|
|
327
|
-
|
|
328
|
-
| Code | Reason |
|
|
329
|
-
| ----- | ------------------------------------------------ |
|
|
330
|
-
| `400` | Validation failed (invalid email, weak password) |
|
|
331
|
-
| `409` | Email already in use |
|
|
332
|
-
|
|
333
|
-
---
|
|
334
|
-
|
|
335
|
-
#### `POST /auth/login`
|
|
336
|
-
|
|
337
|
-
Authenticate and receive tokens.
|
|
338
|
-
|
|
339
|
-
**Auth:** None
|
|
340
|
-
|
|
341
|
-
**Request Body:**
|
|
342
|
-
|
|
343
|
-
```json
|
|
344
|
-
{
|
|
345
|
-
"email": "user@example.com",
|
|
346
|
-
"password": "secret123"
|
|
347
|
-
}
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
**Response `200`:**
|
|
351
|
-
|
|
352
|
-
```json
|
|
353
|
-
{
|
|
354
|
-
"data": {
|
|
355
|
-
"accessToken": "<jwt>",
|
|
356
|
-
"refreshToken": "<jwt>",
|
|
357
|
-
"user": {
|
|
358
|
-
"id": "uuid",
|
|
359
|
-
"email": "user@example.com",
|
|
360
|
-
"display_name": "Jane Doe",
|
|
361
|
-
"role": "user"
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
**Errors:**
|
|
368
|
-
|
|
369
|
-
| Code | Reason |
|
|
370
|
-
| ----- | ------------------------- |
|
|
371
|
-
| `400` | Missing email or password |
|
|
372
|
-
| `401` | Invalid credentials |
|
|
373
|
-
|
|
374
|
-
---
|
|
375
|
-
|
|
376
|
-
#### `POST /auth/refresh`
|
|
377
|
-
|
|
378
|
-
Exchange a refresh token for a new token pair.
|
|
379
|
-
|
|
380
|
-
**Auth:** None
|
|
381
|
-
|
|
382
|
-
**Request Body:**
|
|
383
|
-
|
|
384
|
-
```json
|
|
385
|
-
{
|
|
386
|
-
"refreshToken": "<jwt>"
|
|
387
|
-
}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
**Response `200`:**
|
|
391
|
-
|
|
392
|
-
```json
|
|
393
|
-
{
|
|
394
|
-
"data": {
|
|
395
|
-
"accessToken": "<new-jwt>",
|
|
396
|
-
"refreshToken": "<new-jwt>"
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
**Errors:**
|
|
402
|
-
|
|
403
|
-
| Code | Reason |
|
|
404
|
-
| ----- | -------------------------------- |
|
|
405
|
-
| `400` | Missing refreshToken |
|
|
406
|
-
| `401` | Invalid or expired refresh token |
|
|
407
|
-
|
|
408
|
-
---
|
|
409
|
-
|
|
410
|
-
#### `GET /auth/me`
|
|
411
|
-
|
|
412
|
-
Return the currently authenticated user.
|
|
413
|
-
|
|
414
|
-
**Auth:** `Authorization: Bearer <accessToken>`
|
|
415
|
-
|
|
416
|
-
**Response `200`:**
|
|
417
|
-
|
|
418
|
-
```json
|
|
419
|
-
{
|
|
420
|
-
"data": {
|
|
421
|
-
"id": "uuid",
|
|
422
|
-
"email": "user@example.com",
|
|
423
|
-
"display_name": "Jane Doe",
|
|
424
|
-
"role": "user",
|
|
425
|
-
"created_at": "2026-03-06T12:00:00.000Z"
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
**Errors:**
|
|
431
|
-
|
|
432
|
-
| Code | Reason |
|
|
433
|
-
| ----- | ------------------------ |
|
|
434
|
-
| `401` | Missing or invalid token |
|
|
435
|
-
| `404` | User not found |
|
|
436
|
-
|
|
437
|
-
---
|
|
438
|
-
|
|
439
|
-
### 3.3 Dynamic CRUD Endpoints
|
|
440
|
-
|
|
441
|
-
These endpoints are available for every model defined in `schema.json`. Replace `:table` with the model's table name (e.g. `products` for a `Product` model).
|
|
442
|
-
|
|
443
|
-
#### `GET /api/:table`
|
|
444
|
-
|
|
445
|
-
List records with filtering, sorting, and pagination.
|
|
446
|
-
|
|
447
|
-
**Auth:** Optional
|
|
448
|
-
|
|
449
|
-
**Query Parameters:**
|
|
450
|
-
|
|
451
|
-
| Parameter | Type | Default | Description |
|
|
452
|
-
| --------- | ------- | ------------ | ---------------------------------------- |
|
|
453
|
-
| `page` | integer | `1` | Page number |
|
|
454
|
-
| `limit` | integer | `20` | Records per page (max 100) |
|
|
455
|
-
| `sort` | string | `created_at` | Field to sort by |
|
|
456
|
-
| `order` | string | `DESC` | `ASC` or `DESC` |
|
|
457
|
-
| `<field>` | any | — | Filter by exact value on any model field |
|
|
458
|
-
|
|
459
|
-
**Example:** `GET /api/products?sort=price&order=ASC&page=2&limit=10&in_stock=1`
|
|
460
|
-
|
|
461
|
-
**Response `200`:**
|
|
462
|
-
|
|
463
|
-
```json
|
|
464
|
-
{
|
|
465
|
-
"data": [
|
|
466
|
-
{
|
|
467
|
-
"id": "uuid",
|
|
468
|
-
"name": "Widget",
|
|
469
|
-
"price": 9.99,
|
|
470
|
-
"in_stock": 1,
|
|
471
|
-
"created_at": "2026-03-06T12:00:00.000Z",
|
|
472
|
-
"updated_at": "2026-03-06T12:00:00.000Z"
|
|
473
|
-
}
|
|
474
|
-
],
|
|
475
|
-
"meta": {
|
|
476
|
-
"total": 42,
|
|
477
|
-
"page": 2,
|
|
478
|
-
"limit": 10,
|
|
479
|
-
"pages": 5
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
#### `GET /api/:table/:id`
|
|
487
|
-
|
|
488
|
-
Get a single record by ID.
|
|
489
|
-
|
|
490
|
-
**Response `200`:**
|
|
491
|
-
|
|
492
|
-
```json
|
|
493
|
-
{
|
|
494
|
-
"data": {
|
|
495
|
-
"id": "uuid",
|
|
496
|
-
"name": "Widget",
|
|
497
|
-
"price": 9.99
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
**Errors:**
|
|
503
|
-
|
|
504
|
-
| Code | Reason |
|
|
505
|
-
| ----- | --------------------------------- |
|
|
506
|
-
| `404` | Record not found or unknown table |
|
|
507
|
-
|
|
508
|
-
---
|
|
509
|
-
|
|
510
|
-
#### `POST /api/:table`
|
|
511
|
-
|
|
512
|
-
Create a new record.
|
|
513
|
-
|
|
514
|
-
**Auth:** Optional
|
|
515
|
-
|
|
516
|
-
**Request Body:** JSON object matching the model's field definitions.
|
|
517
|
-
|
|
518
|
-
```json
|
|
519
|
-
{
|
|
520
|
-
"name": "Widget",
|
|
521
|
-
"price": 9.99,
|
|
522
|
-
"in_stock": true
|
|
523
|
-
}
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
**Response `201`:**
|
|
527
|
-
|
|
528
|
-
```json
|
|
529
|
-
{
|
|
530
|
-
"data": {
|
|
531
|
-
"id": "uuid",
|
|
532
|
-
"name": "Widget",
|
|
533
|
-
"price": 9.99,
|
|
534
|
-
"in_stock": 1,
|
|
535
|
-
"created_at": "2026-03-06T12:00:00.000Z",
|
|
536
|
-
"updated_at": "2026-03-06T12:00:00.000Z"
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
**Errors:**
|
|
542
|
-
|
|
543
|
-
| Code | Reason |
|
|
544
|
-
| ----- | ------------------------------------------------------- |
|
|
545
|
-
| `400` | Validation error (missing required fields, wrong types) |
|
|
546
|
-
| `404` | Unknown table |
|
|
547
|
-
|
|
548
|
-
---
|
|
549
|
-
|
|
550
|
-
#### `PUT /api/:table/:id`
|
|
551
|
-
|
|
552
|
-
Update a record (partial — only send fields to change).
|
|
553
|
-
|
|
554
|
-
**Request Body:**
|
|
555
|
-
|
|
556
|
-
```json
|
|
557
|
-
{
|
|
558
|
-
"price": 14.99
|
|
559
|
-
}
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
**Response `200`:**
|
|
563
|
-
|
|
564
|
-
```json
|
|
565
|
-
{
|
|
566
|
-
"data": {
|
|
567
|
-
"id": "uuid",
|
|
568
|
-
"name": "Widget",
|
|
569
|
-
"price": 14.99
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
**Errors:**
|
|
575
|
-
|
|
576
|
-
| Code | Reason |
|
|
577
|
-
| ----- | -------------------------------------- |
|
|
578
|
-
| `400` | Validation error or no fields provided |
|
|
579
|
-
| `404` | Record or table not found |
|
|
580
|
-
|
|
581
|
-
---
|
|
582
|
-
|
|
583
|
-
#### `DELETE /api/:table/:id`
|
|
584
|
-
|
|
585
|
-
Delete a record.
|
|
586
|
-
|
|
587
|
-
**Response:** `204 No Content`
|
|
588
|
-
|
|
589
|
-
**Errors:**
|
|
590
|
-
|
|
591
|
-
| Code | Reason |
|
|
592
|
-
| ----- | ------------------------- |
|
|
593
|
-
| `404` | Record or table not found |
|
|
594
|
-
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
### 3.4 File Endpoints
|
|
598
|
-
|
|
599
|
-
#### `POST /files/upload`
|
|
600
|
-
|
|
601
|
-
Upload a file.
|
|
602
|
-
|
|
603
|
-
**Auth:** Optional (identity from JWT `Authorization` header)
|
|
604
|
-
|
|
605
|
-
**Request:** `multipart/form-data`
|
|
606
|
-
|
|
607
|
-
| Field | Type | Required | Description |
|
|
608
|
-
| ------------ | ------ | -------- | ----------------------------------- |
|
|
609
|
-
| `file` | file | Yes | The file to upload |
|
|
610
|
-
| `visibility` | string | No | `"public"` (default) or `"private"` |
|
|
611
|
-
|
|
612
|
-
**Response `201`:**
|
|
613
|
-
|
|
614
|
-
```json
|
|
615
|
-
{
|
|
616
|
-
"data": {
|
|
617
|
-
"id": "uuid",
|
|
618
|
-
"original_name": "photo.jpg",
|
|
619
|
-
"stored_name": "uuid.jpg",
|
|
620
|
-
"mime_type": "image/jpeg",
|
|
621
|
-
"size_bytes": 204800,
|
|
622
|
-
"visibility": "public",
|
|
623
|
-
"uploader_id": "user-uuid",
|
|
624
|
-
"created_at": "2026-03-06T12:00:00.000Z"
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
**Errors:**
|
|
630
|
-
|
|
631
|
-
| Code | Reason |
|
|
632
|
-
| ----- | ----------------------------------------- |
|
|
633
|
-
| `400` | No file provided, invalid visibility |
|
|
634
|
-
| `413` | File exceeds size limit or per-user quota |
|
|
635
|
-
|
|
636
|
-
---
|
|
637
|
-
|
|
638
|
-
#### `GET /files`
|
|
639
|
-
|
|
640
|
-
List files. Admins see all files; other users see only their own.
|
|
641
|
-
|
|
642
|
-
**Response `200`:**
|
|
643
|
-
|
|
644
|
-
```json
|
|
645
|
-
{
|
|
646
|
-
"data": [
|
|
647
|
-
/* array of file metadata objects */
|
|
648
|
-
],
|
|
649
|
-
"meta": { "total": 5 }
|
|
650
|
-
}
|
|
651
|
-
```
|
|
652
|
-
|
|
653
|
-
---
|
|
654
|
-
|
|
655
|
-
#### `GET /files/:id`
|
|
656
|
-
|
|
657
|
-
Get metadata for a single file.
|
|
658
|
-
|
|
659
|
-
**Errors:**
|
|
660
|
-
|
|
661
|
-
| Code | Reason |
|
|
662
|
-
| ----- | --------------------------- |
|
|
663
|
-
| `403` | Not the owner and not admin |
|
|
664
|
-
| `404` | File not found |
|
|
665
|
-
|
|
666
|
-
---
|
|
667
|
-
|
|
668
|
-
#### `GET /files/:id/download`
|
|
669
|
-
|
|
670
|
-
Download a file. Public files are accessible directly. Private files require a signed URL.
|
|
671
|
-
|
|
672
|
-
**Query Parameters (private files only):**
|
|
673
|
-
|
|
674
|
-
| Parameter | Description |
|
|
675
|
-
| --------- | ---------------------------------- |
|
|
676
|
-
| `token` | HMAC token from the signed URL |
|
|
677
|
-
| `expires` | Unix timestamp from the signed URL |
|
|
678
|
-
|
|
679
|
-
**Response:** File stream with appropriate `Content-Type` and `Content-Disposition` headers.
|
|
680
|
-
|
|
681
|
-
**Errors:**
|
|
682
|
-
|
|
683
|
-
| Code | Reason |
|
|
684
|
-
| ----- | ---------------------------------------------- |
|
|
685
|
-
| `403` | Private file accessed without valid signed URL |
|
|
686
|
-
| `404` | File not found |
|
|
687
|
-
|
|
688
|
-
---
|
|
689
|
-
|
|
690
|
-
#### `GET /files/:id/signed-url`
|
|
691
|
-
|
|
692
|
-
Generate a time-limited signed URL for a private file.
|
|
693
|
-
|
|
694
|
-
**Auth:** Must be the file owner or admin.
|
|
695
|
-
|
|
696
|
-
**Response `200`:**
|
|
697
|
-
|
|
698
|
-
```json
|
|
699
|
-
{
|
|
700
|
-
"data": {
|
|
701
|
-
"url": "http://localhost:3000/files/uuid/download?token=abc123&expires=1741300000",
|
|
702
|
-
"expires": 1741300000
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
```
|
|
706
|
-
|
|
707
|
-
**Errors:**
|
|
708
|
-
|
|
709
|
-
| Code | Reason |
|
|
710
|
-
| ----- | --------------------------- |
|
|
711
|
-
| `400` | File is not private |
|
|
712
|
-
| `403` | Not the owner and not admin |
|
|
713
|
-
| `404` | File not found |
|
|
714
|
-
|
|
715
|
-
---
|
|
716
|
-
|
|
717
|
-
#### `DELETE /files/:id`
|
|
718
|
-
|
|
719
|
-
Delete a file from disk and database.
|
|
720
|
-
|
|
721
|
-
**Auth:** Must be the file owner or admin.
|
|
722
|
-
|
|
723
|
-
**Response:** `204 No Content`
|
|
724
|
-
|
|
725
|
-
---
|
|
726
|
-
|
|
727
|
-
### 3.5 AI Endpoints
|
|
728
|
-
|
|
729
|
-
#### `GET /api/ai/providers`
|
|
730
|
-
|
|
731
|
-
List all enabled AI providers (public).
|
|
732
|
-
|
|
733
|
-
**Response `200`:**
|
|
734
|
-
|
|
735
|
-
```json
|
|
736
|
-
{
|
|
737
|
-
"data": [
|
|
738
|
-
{
|
|
739
|
-
"id": "openai",
|
|
740
|
-
"label": "OpenAI",
|
|
741
|
-
"models": ["gpt-4o", "gpt-4o-mini"],
|
|
742
|
-
"defaultModel": "gpt-4o"
|
|
743
|
-
}
|
|
744
|
-
]
|
|
745
|
-
}
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
---
|
|
749
|
-
|
|
750
|
-
#### `GET /api/ai/models`
|
|
751
|
-
|
|
752
|
-
Flat list of all models across all enabled providers.
|
|
753
|
-
|
|
754
|
-
**Response `200`:**
|
|
755
|
-
|
|
756
|
-
```json
|
|
757
|
-
{
|
|
758
|
-
"data": [
|
|
759
|
-
{ "provider": "openai", "providerLabel": "OpenAI", "model": "gpt-4o" },
|
|
760
|
-
{
|
|
761
|
-
"provider": "anthropic",
|
|
762
|
-
"providerLabel": "Anthropic",
|
|
763
|
-
"model": "claude-sonnet-4-6"
|
|
764
|
-
}
|
|
765
|
-
]
|
|
766
|
-
}
|
|
767
|
-
```
|
|
768
|
-
|
|
769
|
-
---
|
|
770
|
-
|
|
771
|
-
#### `GET /api/ai/models/:provider`
|
|
772
|
-
|
|
773
|
-
Models for a specific provider.
|
|
774
|
-
|
|
775
|
-
**Response `200`:**
|
|
776
|
-
|
|
777
|
-
```json
|
|
778
|
-
{ "data": ["gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo"] }
|
|
779
|
-
```
|
|
780
|
-
|
|
781
|
-
---
|
|
782
|
-
|
|
783
|
-
#### `POST /api/ai/generate`
|
|
784
|
-
|
|
785
|
-
Generate a single text completion.
|
|
786
|
-
|
|
787
|
-
**Request Body:**
|
|
788
|
-
|
|
789
|
-
```json
|
|
790
|
-
{
|
|
791
|
-
"provider": "openai",
|
|
792
|
-
"model": "gpt-4o",
|
|
793
|
-
"prompt": "Explain quantum entanglement in one sentence.",
|
|
794
|
-
"system": "You are a physics professor.",
|
|
795
|
-
"maxTokens": 256,
|
|
796
|
-
"temperature": 0.7
|
|
797
|
-
}
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
| Field | Type | Required | Description |
|
|
801
|
-
| ------------- | ------- | -------- | ------------------------------------------------------- |
|
|
802
|
-
| `provider` | string | Yes | Provider ID (`openai`, `anthropic`, `gemini`, `ollama`) |
|
|
803
|
-
| `prompt` | string | Yes | The user prompt |
|
|
804
|
-
| `model` | string | No | Defaults to provider's `defaultModel` |
|
|
805
|
-
| `system` | string | No | System instruction |
|
|
806
|
-
| `maxTokens` | integer | No | Maximum tokens to generate |
|
|
807
|
-
| `temperature` | number | No | Sampling temperature (0-2) |
|
|
808
|
-
|
|
809
|
-
**Response `200`:**
|
|
810
|
-
|
|
811
|
-
```json
|
|
812
|
-
{
|
|
813
|
-
"data": {
|
|
814
|
-
"text": "Quantum entanglement is a phenomenon where two particles...",
|
|
815
|
-
"model": "gpt-4o",
|
|
816
|
-
"usage": {
|
|
817
|
-
"prompt_tokens": 42,
|
|
818
|
-
"completion_tokens": 28
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
```
|
|
823
|
-
|
|
824
|
-
---
|
|
825
|
-
|
|
826
|
-
#### `POST /api/ai/chat`
|
|
827
|
-
|
|
828
|
-
Multi-turn conversation.
|
|
829
|
-
|
|
830
|
-
**Request Body:**
|
|
831
|
-
|
|
832
|
-
```json
|
|
833
|
-
{
|
|
834
|
-
"provider": "anthropic",
|
|
835
|
-
"model": "claude-sonnet-4-6",
|
|
836
|
-
"messages": [
|
|
837
|
-
{ "role": "user", "content": "Hello!" },
|
|
838
|
-
{ "role": "assistant", "content": "Hi there! How can I help?" },
|
|
839
|
-
{ "role": "user", "content": "What is 2+2?" }
|
|
840
|
-
],
|
|
841
|
-
"system": "You are a helpful assistant.",
|
|
842
|
-
"maxTokens": 512
|
|
843
|
-
}
|
|
844
|
-
```
|
|
845
|
-
|
|
846
|
-
**Response `200`:**
|
|
847
|
-
|
|
848
|
-
```json
|
|
849
|
-
{
|
|
850
|
-
"data": {
|
|
851
|
-
"message": { "role": "assistant", "content": "2+2 equals 4." },
|
|
852
|
-
"model": "claude-sonnet-4-6",
|
|
853
|
-
"usage": {
|
|
854
|
-
"prompt_tokens": 60,
|
|
855
|
-
"completion_tokens": 12
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
```
|
|
860
|
-
|
|
861
|
-
---
|
|
862
|
-
|
|
863
|
-
### 3.6 Agent Endpoints
|
|
864
|
-
|
|
865
|
-
#### `GET /api/agents`
|
|
866
|
-
|
|
867
|
-
List all defined agents (public).
|
|
868
|
-
|
|
869
|
-
**Response `200`:**
|
|
870
|
-
|
|
871
|
-
```json
|
|
872
|
-
{
|
|
873
|
-
"data": [
|
|
874
|
-
{
|
|
875
|
-
"id": "uuid",
|
|
876
|
-
"name": "Research Assistant",
|
|
877
|
-
"description": "Searches the web and summarizes results",
|
|
878
|
-
"provider": "openai",
|
|
879
|
-
"model": "gpt-4o",
|
|
880
|
-
"tools": ["web_search", "calculate"],
|
|
881
|
-
"createdAt": "2026-03-06T12:00:00.000Z"
|
|
882
|
-
}
|
|
883
|
-
]
|
|
884
|
-
}
|
|
885
|
-
```
|
|
886
|
-
|
|
887
|
-
---
|
|
888
|
-
|
|
889
|
-
#### `GET /api/agents/tools`
|
|
890
|
-
|
|
891
|
-
List all available tools.
|
|
892
|
-
|
|
893
|
-
**Response `200`:**
|
|
894
|
-
|
|
895
|
-
```json
|
|
896
|
-
{
|
|
897
|
-
"data": [
|
|
898
|
-
{
|
|
899
|
-
"name": "calculate",
|
|
900
|
-
"description": "Safely evaluate a mathematical expression...",
|
|
901
|
-
"parameters": {
|
|
902
|
-
"expression": { "type": "string", "description": "A math expression" }
|
|
903
|
-
},
|
|
904
|
-
"required": ["expression"]
|
|
905
|
-
}
|
|
906
|
-
]
|
|
907
|
-
}
|
|
908
|
-
```
|
|
909
|
-
|
|
910
|
-
---
|
|
911
|
-
|
|
912
|
-
#### `POST /api/agents/:id/run`
|
|
913
|
-
|
|
914
|
-
Run an agent with a task input.
|
|
915
|
-
|
|
916
|
-
**Request Body:**
|
|
917
|
-
|
|
918
|
-
```json
|
|
919
|
-
{
|
|
920
|
-
"input": "What is 18% tip on a $47.50 bill?"
|
|
921
|
-
}
|
|
922
|
-
```
|
|
923
|
-
|
|
924
|
-
**Response `200`:**
|
|
925
|
-
|
|
926
|
-
```json
|
|
927
|
-
{
|
|
928
|
-
"data": {
|
|
929
|
-
"agentId": "uuid",
|
|
930
|
-
"agentName": "Math Helper",
|
|
931
|
-
"input": "What is 18% tip on a $47.50 bill?",
|
|
932
|
-
"output": "The 18% tip on a $47.50 bill is $8.55.",
|
|
933
|
-
"steps": [
|
|
934
|
-
{
|
|
935
|
-
"type": "thinking",
|
|
936
|
-
"content": "I need to calculate 18% of 47.50",
|
|
937
|
-
"iteration": 1
|
|
938
|
-
},
|
|
939
|
-
{
|
|
940
|
-
"type": "tool_call",
|
|
941
|
-
"tool": "calculate",
|
|
942
|
-
"args": { "expression": "47.50 * 0.18" },
|
|
943
|
-
"iteration": 1
|
|
944
|
-
},
|
|
945
|
-
{
|
|
946
|
-
"type": "tool_result",
|
|
947
|
-
"tool": "calculate",
|
|
948
|
-
"result": "{\"expression\":\"47.50 * 0.18\",\"result\":8.55}",
|
|
949
|
-
"iteration": 1
|
|
950
|
-
},
|
|
951
|
-
{
|
|
952
|
-
"type": "final_answer",
|
|
953
|
-
"content": "The 18% tip on a $47.50 bill is $8.55.",
|
|
954
|
-
"iteration": 2
|
|
955
|
-
}
|
|
956
|
-
],
|
|
957
|
-
"usage": {
|
|
958
|
-
"prompt_tokens": 320,
|
|
959
|
-
"completion_tokens": 85
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
```
|
|
964
|
-
|
|
965
|
-
**Errors:**
|
|
966
|
-
|
|
967
|
-
| Code | Reason |
|
|
968
|
-
| ----- | ------------------------ |
|
|
969
|
-
| `400` | Missing or empty `input` |
|
|
970
|
-
| `404` | Agent not found |
|
|
971
|
-
| `500` | LLM provider error |
|
|
972
|
-
|
|
973
|
-
---
|
|
974
|
-
|
|
975
|
-
### 3.7 Admin Endpoints
|
|
976
|
-
|
|
977
|
-
All admin endpoints require the `x-user-role: admin` header (automatically injected from JWT when role is `admin`).
|
|
978
|
-
|
|
979
|
-
#### `GET /admin/api/stats`
|
|
980
|
-
|
|
981
|
-
Dashboard statistics.
|
|
982
|
-
|
|
983
|
-
**Response `200`:**
|
|
984
|
-
|
|
985
|
-
```json
|
|
986
|
-
{
|
|
987
|
-
"data": {
|
|
988
|
-
"models": [{ "model": "Product", "table": "products", "count": 42 }],
|
|
989
|
-
"users": { "count": 15 },
|
|
990
|
-
"files": { "count": 8, "storageMB": 12.5 },
|
|
991
|
-
"uptime": 3600,
|
|
992
|
-
"ai": {
|
|
993
|
-
"connectedLLMs": 2,
|
|
994
|
-
"enabledLLMs": 2,
|
|
995
|
-
"providers": [{ "id": "openai", "label": "OpenAI", "hasKey": true }],
|
|
996
|
-
"agentsCount": 3,
|
|
997
|
-
"usage": {
|
|
998
|
-
"totalRequests": 120,
|
|
999
|
-
"totalInputTokens": 45000,
|
|
1000
|
-
"totalOutputTokens": 12000,
|
|
1001
|
-
"byProvider": {
|
|
1002
|
-
"openai": {
|
|
1003
|
-
"requests": 80,
|
|
1004
|
-
"inputTokens": 30000,
|
|
1005
|
-
"outputTokens": 8000
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
```
|
|
1013
|
-
|
|
1014
|
-
---
|
|
1015
|
-
|
|
1016
|
-
#### `GET /admin/api/schema`
|
|
1017
|
-
|
|
1018
|
-
Get all model definitions.
|
|
1019
|
-
|
|
1020
|
-
**Response `200`:**
|
|
1021
|
-
|
|
1022
|
-
```json
|
|
1023
|
-
{
|
|
1024
|
-
"data": {
|
|
1025
|
-
"Product": {
|
|
1026
|
-
"tableName": "products",
|
|
1027
|
-
"fields": [
|
|
1028
|
-
{ "name": "name", "type": "string", "required": true },
|
|
1029
|
-
{ "name": "price", "type": "number", "required": false }
|
|
1030
|
-
]
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
```
|
|
1035
|
-
|
|
1036
|
-
---
|
|
1037
|
-
|
|
1038
|
-
#### `POST /admin/api/schema`
|
|
1039
|
-
|
|
1040
|
-
Create a new model. Routes become active immediately — no restart required.
|
|
1041
|
-
|
|
1042
|
-
**Request Body:**
|
|
1043
|
-
|
|
1044
|
-
```json
|
|
1045
|
-
{
|
|
1046
|
-
"name": "Product",
|
|
1047
|
-
"fields": {
|
|
1048
|
-
"name": { "type": "string", "required": true },
|
|
1049
|
-
"price": "number",
|
|
1050
|
-
"in_stock": "boolean"
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
```
|
|
1054
|
-
|
|
1055
|
-
**Response `201`:**
|
|
1056
|
-
|
|
1057
|
-
```json
|
|
1058
|
-
{
|
|
1059
|
-
"data": { "name": "Product", "tableName": "products", "fields": [] },
|
|
1060
|
-
"message": "Model \"Product\" is live. Routes /products are active now."
|
|
1061
|
-
}
|
|
1062
|
-
```
|
|
1063
|
-
|
|
1064
|
-
**Errors:**
|
|
1065
|
-
|
|
1066
|
-
| Code | Reason |
|
|
1067
|
-
| ----- | ---------------------------------------------- |
|
|
1068
|
-
| `400` | Missing `name` or `fields`, reserved name used |
|
|
1069
|
-
| `409` | Model with that name already exists |
|
|
1070
|
-
|
|
1071
|
-
---
|
|
1072
|
-
|
|
1073
|
-
#### `DELETE /admin/api/schema/:name`
|
|
1074
|
-
|
|
1075
|
-
Remove a model. Routes are deactivated immediately; the DB table is preserved.
|
|
1076
|
-
|
|
1077
|
-
**Response:** `204 No Content`
|
|
1078
|
-
|
|
1079
|
-
**Errors:**
|
|
1080
|
-
|
|
1081
|
-
| Code | Reason |
|
|
1082
|
-
| ----- | ---------------------------------------------- |
|
|
1083
|
-
| `400` | Attempt to delete a system model (e.g. `User`) |
|
|
1084
|
-
| `404` | Model not found |
|
|
1085
|
-
|
|
1086
|
-
---
|
|
1087
|
-
|
|
1088
|
-
#### `GET /admin/api/logs`
|
|
1089
|
-
|
|
1090
|
-
Retrieve in-memory server logs.
|
|
1091
|
-
|
|
1092
|
-
**Query Parameters:**
|
|
1093
|
-
|
|
1094
|
-
| Parameter | Description |
|
|
1095
|
-
| --------- | ------------------------------------------------- |
|
|
1096
|
-
| `after` | Unix timestamp — only return logs after this time |
|
|
1097
|
-
| `level` | Filter by level: `info`, `warn`, `error`, `debug` |
|
|
1098
|
-
|
|
1099
|
-
**Response `200`:**
|
|
1100
|
-
|
|
1101
|
-
```json
|
|
1102
|
-
{
|
|
1103
|
-
"logs": [
|
|
1104
|
-
{
|
|
1105
|
-
"ts": 1741258800000,
|
|
1106
|
-
"level": "info",
|
|
1107
|
-
"msg": "GET /api/products 200 12ms"
|
|
1108
|
-
}
|
|
1109
|
-
]
|
|
1110
|
-
}
|
|
1111
|
-
```
|
|
1112
|
-
|
|
1113
|
-
---
|
|
1114
|
-
|
|
1115
|
-
#### `GET /admin/api/env`
|
|
1116
|
-
|
|
1117
|
-
Read current environment/config values.
|
|
1118
|
-
|
|
1119
|
-
**Response `200`:**
|
|
1120
|
-
|
|
1121
|
-
```json
|
|
1122
|
-
{
|
|
1123
|
-
"data": {
|
|
1124
|
-
"JWT_SECRET": "***",
|
|
1125
|
-
"ROTIFEX_PORT": "3000",
|
|
1126
|
-
"ROTIFEX_CORS_ORIGIN": "*"
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
```
|
|
1130
|
-
|
|
1131
|
-
---
|
|
1132
|
-
|
|
1133
|
-
#### `POST /admin/api/env`
|
|
1134
|
-
|
|
1135
|
-
Write environment variables to `.env`. Restart required for changes to take effect.
|
|
1136
|
-
|
|
1137
|
-
**Request Body:**
|
|
1138
|
-
|
|
1139
|
-
```json
|
|
1140
|
-
{
|
|
1141
|
-
"vars": {
|
|
1142
|
-
"ROTIFEX_PORT": "4000",
|
|
1143
|
-
"ROTIFEX_CORS_ORIGIN": "https://myapp.com"
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
```
|
|
1147
|
-
|
|
1148
|
-
**Response `200`:**
|
|
1149
|
-
|
|
1150
|
-
```json
|
|
1151
|
-
{
|
|
1152
|
-
"message": "Environment saved. Restart the server for changes to take effect."
|
|
1153
|
-
}
|
|
1154
|
-
```
|
|
1155
|
-
|
|
1156
|
-
---
|
|
1157
|
-
|
|
1158
|
-
#### `GET /admin/api/ai/providers`
|
|
1159
|
-
|
|
1160
|
-
Get all AI providers with masked API keys.
|
|
1161
|
-
|
|
1162
|
-
**Response `200`:**
|
|
1163
|
-
|
|
1164
|
-
```json
|
|
1165
|
-
{
|
|
1166
|
-
"data": [
|
|
1167
|
-
{
|
|
1168
|
-
"id": "openai",
|
|
1169
|
-
"label": "OpenAI",
|
|
1170
|
-
"enabled": true,
|
|
1171
|
-
"apiKey": "***abcd",
|
|
1172
|
-
"hasKey": true,
|
|
1173
|
-
"models": ["gpt-4o", "gpt-4o-mini"],
|
|
1174
|
-
"defaultModel": "gpt-4o"
|
|
1175
|
-
}
|
|
1176
|
-
]
|
|
1177
|
-
}
|
|
1178
|
-
```
|
|
1179
|
-
|
|
1180
|
-
---
|
|
1181
|
-
|
|
1182
|
-
#### `PUT /admin/api/ai/providers/:id`
|
|
1183
|
-
|
|
1184
|
-
Update a provider's configuration.
|
|
1185
|
-
|
|
1186
|
-
**Request Body:**
|
|
1187
|
-
|
|
1188
|
-
```json
|
|
1189
|
-
{
|
|
1190
|
-
"enabled": true,
|
|
1191
|
-
"apiKey": "sk-...",
|
|
1192
|
-
"defaultModel": "gpt-4o-mini"
|
|
1193
|
-
}
|
|
1194
|
-
```
|
|
9
|
+
**Self-hosted backend platform. Define a schema, get a full REST API instantly.**
|
|
1195
10
|
|
|
1196
|
-
|
|
11
|
+
[Documentation](https://rotifex-docs.vercel.app/) · [Full Reference](README-final.md) · [npm](https://www.npmjs.com/package/rotifex)
|
|
1197
12
|
|
|
1198
|
-
|
|
1199
|
-
{
|
|
1200
|
-
"data": {
|
|
1201
|
-
"id": "openai",
|
|
1202
|
-
"label": "OpenAI",
|
|
1203
|
-
"enabled": true,
|
|
1204
|
-
"hasKey": true
|
|
1205
|
-
},
|
|
1206
|
-
"message": "Provider \"openai\" updated."
|
|
1207
|
-
}
|
|
1208
|
-
```
|
|
13
|
+
</div>
|
|
1209
14
|
|
|
1210
15
|
---
|
|
1211
16
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
List all agents with full detail (including system prompt).
|
|
1215
|
-
|
|
1216
|
-
#### `GET /admin/api/agents/:id`
|
|
1217
|
-
|
|
1218
|
-
Get a single agent by ID.
|
|
1219
|
-
|
|
1220
|
-
#### `POST /admin/api/agents`
|
|
1221
|
-
|
|
1222
|
-
Create an agent.
|
|
1223
|
-
|
|
1224
|
-
**Request Body:**
|
|
1225
|
-
|
|
1226
|
-
```json
|
|
1227
|
-
{
|
|
1228
|
-
"name": "Research Assistant",
|
|
1229
|
-
"description": "Searches and summarizes web content",
|
|
1230
|
-
"provider": "openai",
|
|
1231
|
-
"model": "gpt-4o",
|
|
1232
|
-
"systemPrompt": "You are a research assistant. Use tools to answer questions accurately.",
|
|
1233
|
-
"tools": ["web_search", "calculate"],
|
|
1234
|
-
"temperature": 0.7,
|
|
1235
|
-
"maxTokens": 2048,
|
|
1236
|
-
"maxIterations": 10
|
|
1237
|
-
}
|
|
1238
|
-
```
|
|
1239
|
-
|
|
1240
|
-
**Response `201`:**
|
|
1241
|
-
|
|
1242
|
-
```json
|
|
1243
|
-
{
|
|
1244
|
-
"data": { "id": "uuid", "name": "Research Assistant", ... },
|
|
1245
|
-
"message": "Agent \"Research Assistant\" created."
|
|
1246
|
-
}
|
|
1247
|
-
```
|
|
1248
|
-
|
|
1249
|
-
#### `PUT /admin/api/agents/:id`
|
|
1250
|
-
|
|
1251
|
-
Update an agent (partial patch). Same body shape as POST.
|
|
1252
|
-
|
|
1253
|
-
**Response `200`:**
|
|
1254
|
-
|
|
1255
|
-
```json
|
|
1256
|
-
{
|
|
1257
|
-
"data": { "id": "uuid", "name": "Research Assistant", ... },
|
|
1258
|
-
"message": "Agent \"Research Assistant\" updated."
|
|
1259
|
-
}
|
|
1260
|
-
```
|
|
1261
|
-
|
|
1262
|
-
#### `DELETE /admin/api/agents/:id`
|
|
17
|
+
## What is Rotifex?
|
|
1263
18
|
|
|
1264
|
-
|
|
19
|
+
Rotifex is a self-hosted backend-as-a-service that turns a JSON schema into a production-ready REST API in seconds — no code generation, no restart. It ships with JWT authentication, file storage, AI/LLM integration, and a full admin dashboard.
|
|
1265
20
|
|
|
1266
|
-
|
|
21
|
+
Think Supabase or Firebase, but running entirely on your own machine.
|
|
1267
22
|
|
|
1268
23
|
---
|
|
1269
24
|
|
|
1270
|
-
##
|
|
25
|
+
## Key Features
|
|
1271
26
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
| |
|
|
1283
|
-
|-- GET /api/products |
|
|
1284
|
-
| Authorization: Bearer <token> > | Verify JWT, inject x-user-id/x-user-role
|
|
1285
|
-
|<- { data: [...] } --------------- |
|
|
1286
|
-
| |
|
|
1287
|
-
|-- POST /auth/refresh -----------> | Verify refresh token, issue new pair
|
|
1288
|
-
|<- { accessToken, refreshToken }-- |
|
|
1289
|
-
```
|
|
1290
|
-
|
|
1291
|
-
### Token Details
|
|
1292
|
-
|
|
1293
|
-
| Token | Algorithm | TTL | Secret Env Var |
|
|
1294
|
-
| ------------- | --------- | ------- | -------------------- |
|
|
1295
|
-
| Access Token | HS256 | 1 hour | `JWT_SECRET` |
|
|
1296
|
-
| Refresh Token | HS256 | 30 days | `JWT_REFRESH_SECRET` |
|
|
1297
|
-
|
|
1298
|
-
Secrets are auto-generated and saved to `.env` on first startup if not explicitly set.
|
|
1299
|
-
|
|
1300
|
-
### Required Headers
|
|
1301
|
-
|
|
1302
|
-
| Header | Value | Set By |
|
|
1303
|
-
| --------------- | ---------------------- | ------------------------------ |
|
|
1304
|
-
| `Authorization` | `Bearer <accessToken>` | Client |
|
|
1305
|
-
| `x-user-id` | User UUID | JWT middleware (auto-injected) |
|
|
1306
|
-
| `x-user-role` | `user` or `admin` | JWT middleware (auto-injected) |
|
|
1307
|
-
|
|
1308
|
-
### Permission Levels
|
|
1309
|
-
|
|
1310
|
-
| Role | Access |
|
|
1311
|
-
| ------- | --------------------------------------------- |
|
|
1312
|
-
| `user` | Public endpoints, own files, own data records |
|
|
1313
|
-
| `admin` | All endpoints including `/admin/api/*` |
|
|
1314
|
-
|
|
1315
|
-
> The JWT middleware skips all `/auth/*` routes. For `/auth/me`, the token is manually verified inside the handler.
|
|
27
|
+
| Feature | Description |
|
|
28
|
+
| --------------------- | ------------------------------------------------------------------------------ |
|
|
29
|
+
| **Schema-driven API** | Define models in JSON or the visual editor — get 5 CRUD endpoints instantly |
|
|
30
|
+
| **Live updates** | Add or remove models at runtime without restarting the server |
|
|
31
|
+
| **JWT Auth** | Register, login, refresh tokens, role-based access, secure first-run setup |
|
|
32
|
+
| **File Storage** | Public and private uploads with HMAC-signed URLs |
|
|
33
|
+
| **AI / LLM** | Connect OpenAI, Anthropic, Gemini, or Ollama from one unified API |
|
|
34
|
+
| **AI Agents** | ReAct-loop agents with tools: calculator, web search, HTTP, DB query, datetime |
|
|
35
|
+
| **Admin Dashboard** | React SPA for managing everything — schemas, users, files, AI, logs, settings |
|
|
36
|
+
| **Production-ready** | Rate limiting, CORS, structured logging, SQLite, auto-rotating JWT secrets |
|
|
1316
37
|
|
|
1317
38
|
---
|
|
1318
39
|
|
|
1319
|
-
##
|
|
1320
|
-
|
|
1321
|
-
### User
|
|
40
|
+
## Quick Start
|
|
1322
41
|
|
|
1323
|
-
|
|
42
|
+
**Install and run:**
|
|
1324
43
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
| `display_name` | string | No | |
|
|
1330
|
-
| `role` | string | Yes | `"user"` or `"admin"` |
|
|
1331
|
-
| `password_hash` | string | Internal | bcrypt hash — never returned by API |
|
|
1332
|
-
| `created_at` | string (ISO 8601) | Auto | |
|
|
1333
|
-
| `updated_at` | string (ISO 8601) | Auto | |
|
|
44
|
+
```bash
|
|
45
|
+
npm install rotifex
|
|
46
|
+
npm start
|
|
47
|
+
```
|
|
1334
48
|
|
|
1335
|
-
|
|
49
|
+
**Or without installing:**
|
|
1336
50
|
|
|
1337
|
-
|
|
51
|
+
```bash
|
|
52
|
+
npx rotifex start
|
|
53
|
+
```
|
|
1338
54
|
|
|
1339
|
-
|
|
55
|
+
Open `http://localhost:4994` in your browser. On first run you will see a setup screen — create your admin account and you are ready to go.
|
|
1340
56
|
|
|
1341
|
-
|
|
1342
|
-
| --------------- | ----------------- | ---------------------------------------- |
|
|
1343
|
-
| `id` | string (UUID) | Primary key |
|
|
1344
|
-
| `original_name` | string | Original filename from upload |
|
|
1345
|
-
| `stored_name` | string | `<uuid>.<ext>` — actual filename on disk |
|
|
1346
|
-
| `mime_type` | string | e.g. `image/jpeg` |
|
|
1347
|
-
| `size_bytes` | integer | File size in bytes |
|
|
1348
|
-
| `visibility` | string | `"public"` or `"private"` |
|
|
1349
|
-
| `uploader_id` | string | UUID of the uploading user |
|
|
1350
|
-
| `created_at` | string (ISO 8601) | |
|
|
57
|
+
> **Save your password.** There is no password recovery. If you ever lose access, run `rotifex reset-admin --yes` from the terminal.
|
|
1351
58
|
|
|
1352
59
|
---
|
|
1353
60
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
All custom models automatically receive `id`, `created_at`, and `updated_at`.
|
|
61
|
+
## How It Works
|
|
1357
62
|
|
|
1358
|
-
**
|
|
63
|
+
**1. Define your schema** (`schema.json` or the visual editor in the dashboard):
|
|
1359
64
|
|
|
1360
65
|
```json
|
|
1361
66
|
{
|
|
@@ -1363,621 +68,93 @@ All custom models automatically receive `id`, `created_at`, and `updated_at`.
|
|
|
1363
68
|
"fields": {
|
|
1364
69
|
"name": { "type": "string", "required": true },
|
|
1365
70
|
"price": "number",
|
|
1366
|
-
"in_stock": "boolean"
|
|
1367
|
-
"sku": { "type": "string", "unique": true }
|
|
1368
|
-
}
|
|
1369
|
-
},
|
|
1370
|
-
"Order": {
|
|
1371
|
-
"fields": {
|
|
1372
|
-
"product_id": { "type": "string", "required": true },
|
|
1373
|
-
"quantity": { "type": "integer", "required": true },
|
|
1374
|
-
"status": { "type": "string", "default": "pending" }
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
```
|
|
1379
|
-
|
|
1380
|
-
**Resulting routes:**
|
|
1381
|
-
|
|
1382
|
-
- `Product` -> table `products` -> `/api/products`, `/api/products/:id`
|
|
1383
|
-
- `Order` -> table `orders` -> `/api/orders`, `/api/orders/:id`
|
|
1384
|
-
|
|
1385
|
-
---
|
|
1386
|
-
|
|
1387
|
-
### Agent
|
|
1388
|
-
|
|
1389
|
-
Stored in `agents.config.json`.
|
|
1390
|
-
|
|
1391
|
-
| Field | Type | Default | Description |
|
|
1392
|
-
| --------------- | ----------------- | ------- | -------------------------------------------- |
|
|
1393
|
-
| `id` | string (UUID) | Auto | |
|
|
1394
|
-
| `name` | string | — | Display name |
|
|
1395
|
-
| `description` | string | `""` | Human-readable description |
|
|
1396
|
-
| `provider` | string | — | `openai`, `anthropic`, `gemini`, or `ollama` |
|
|
1397
|
-
| `model` | string | — | Model ID |
|
|
1398
|
-
| `systemPrompt` | string | `""` | Agent's system instruction |
|
|
1399
|
-
| `tools` | string[] | `[]` | List of tool names to enable |
|
|
1400
|
-
| `temperature` | number | `0.7` | Sampling temperature |
|
|
1401
|
-
| `maxTokens` | integer | `2048` | Max tokens per LLM call |
|
|
1402
|
-
| `maxIterations` | integer | `10` | Max tool-call loop iterations |
|
|
1403
|
-
| `createdAt` | string (ISO 8601) | Auto | |
|
|
1404
|
-
| `updatedAt` | string (ISO 8601) | Auto | |
|
|
1405
|
-
|
|
1406
|
-
---
|
|
1407
|
-
|
|
1408
|
-
### AI Provider (ai.config.json)
|
|
1409
|
-
|
|
1410
|
-
| Field | Type | Description |
|
|
1411
|
-
| -------------- | -------- | ---------------------------------- |
|
|
1412
|
-
| `label` | string | Display name |
|
|
1413
|
-
| `apiKey` | string | API key (empty string if not set) |
|
|
1414
|
-
| `enabled` | boolean | Whether the provider is active |
|
|
1415
|
-
| `models` | string[] | Available model IDs |
|
|
1416
|
-
| `defaultModel` | string | Default model ID |
|
|
1417
|
-
| `baseUrl` | string | Only for Ollama — local server URL |
|
|
1418
|
-
|
|
1419
|
-
---
|
|
1420
|
-
|
|
1421
|
-
### Token Usage (ai.usage.json)
|
|
1422
|
-
|
|
1423
|
-
```json
|
|
1424
|
-
{
|
|
1425
|
-
"totalRequests": 150,
|
|
1426
|
-
"totalInputTokens": 55000,
|
|
1427
|
-
"totalOutputTokens": 14000,
|
|
1428
|
-
"byProvider": {
|
|
1429
|
-
"openai": {
|
|
1430
|
-
"requests": 100,
|
|
1431
|
-
"inputTokens": 40000,
|
|
1432
|
-
"outputTokens": 10000
|
|
1433
|
-
},
|
|
1434
|
-
"anthropic": {
|
|
1435
|
-
"requests": 50,
|
|
1436
|
-
"inputTokens": 15000,
|
|
1437
|
-
"outputTokens": 4000
|
|
71
|
+
"in_stock": "boolean"
|
|
1438
72
|
}
|
|
1439
73
|
}
|
|
1440
74
|
}
|
|
1441
75
|
```
|
|
1442
76
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
## 6. AI / LLM Integration
|
|
1446
|
-
|
|
1447
|
-
### Supported Providers
|
|
1448
|
-
|
|
1449
|
-
| Provider ID | Label | Requires API Key | Default Models |
|
|
1450
|
-
| ----------- | -------------- | ---------------- | ------------------------------------------------------------- |
|
|
1451
|
-
| `openai` | OpenAI | Yes | gpt-4o, gpt-4o-mini, gpt-4-turbo, gpt-3.5-turbo |
|
|
1452
|
-
| `anthropic` | Anthropic | Yes | claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5-20251001 |
|
|
1453
|
-
| `gemini` | Google Gemini | Yes | gemini-2.0-flash, gemini-1.5-pro, gemini-1.5-flash |
|
|
1454
|
-
| `ollama` | Ollama (Local) | No | llama3.2, mistral, codellama, phi3 |
|
|
1455
|
-
|
|
1456
|
-
### Configuration
|
|
1457
|
-
|
|
1458
|
-
Providers are configured via:
|
|
1459
|
-
|
|
1460
|
-
1. **Admin panel -> AI Integration -> Providers tab:** Enable provider, paste API key, select default model.
|
|
1461
|
-
2. **Direct file edit:** Edit `ai.config.json` in the project root.
|
|
1462
|
-
|
|
1463
|
-
Ollama requires a running local Ollama server. Default base URL: `http://localhost:11434`.
|
|
1464
|
-
|
|
1465
|
-
### Token Usage Tracking
|
|
1466
|
-
|
|
1467
|
-
Every call to `POST /api/ai/generate`, `POST /api/ai/chat`, and `POST /api/agents/:id/run` records:
|
|
1468
|
-
|
|
1469
|
-
- Input token count
|
|
1470
|
-
- Output token count
|
|
1471
|
-
- Per-provider breakdown
|
|
1472
|
-
|
|
1473
|
-
Data is persisted to `ai.usage.json` and survives server restarts. Totals are visible in the admin dashboard.
|
|
1474
|
-
|
|
1475
|
-
### AI Playground (Admin Panel)
|
|
1476
|
-
|
|
1477
|
-
- **Generate mode:** Single prompt with provider, model, system prompt, temperature, max tokens controls. Shows token count after each request.
|
|
1478
|
-
- **Chat mode:** Multi-turn conversation with scrollable history and persistent context.
|
|
1479
|
-
- **Session totals:** Cumulative token counts for the current browser session with a reset button.
|
|
1480
|
-
|
|
1481
|
-
### Agent System
|
|
1482
|
-
|
|
1483
|
-
#### ReAct Loop
|
|
1484
|
-
|
|
1485
|
-
```
|
|
1486
|
-
User Input
|
|
1487
|
-
|
|
|
1488
|
-
v
|
|
1489
|
-
LLM (receives task + tool definitions)
|
|
1490
|
-
|
|
|
1491
|
-
+-- Tool call requested?
|
|
1492
|
-
| |
|
|
1493
|
-
| +-- Execute tool
|
|
1494
|
-
| +-- Append result to conversation
|
|
1495
|
-
| +-- Loop back to LLM
|
|
1496
|
-
|
|
|
1497
|
-
+-- Final answer -> return output + all steps to caller
|
|
1498
|
-
```
|
|
1499
|
-
|
|
1500
|
-
#### Agent Step Types
|
|
1501
|
-
|
|
1502
|
-
| Type | Description |
|
|
1503
|
-
| -------------- | ------------------------------------------- |
|
|
1504
|
-
| `thinking` | LLM reasoning text before invoking a tool |
|
|
1505
|
-
| `tool_call` | Name and arguments of the tool being called |
|
|
1506
|
-
| `tool_result` | Raw output from the tool execution |
|
|
1507
|
-
| `final_answer` | The LLM's conclusive response |
|
|
1508
|
-
|
|
1509
|
-
---
|
|
1510
|
-
|
|
1511
|
-
## 7. File Storage / Media Handling
|
|
1512
|
-
|
|
1513
|
-
### Upload Process
|
|
1514
|
-
|
|
1515
|
-
1. Client sends `POST /files/upload` as `multipart/form-data` with a `file` field and optional `visibility` field.
|
|
1516
|
-
2. Server reads and buffers the stream, validates MIME type against the allowed list.
|
|
1517
|
-
3. Checks the file size against `maxFileSizeMB` (default 10 MB).
|
|
1518
|
-
4. Checks the uploader's total storage against `maxStoragePerUserMB` (default 100 MB).
|
|
1519
|
-
5. Writes the file to disk as `<uuid><original-extension>` in the appropriate directory.
|
|
1520
|
-
6. Inserts metadata into the `_files` SQLite table.
|
|
1521
|
-
7. Returns the full file metadata record.
|
|
1522
|
-
|
|
1523
|
-
### Access URLs
|
|
1524
|
-
|
|
1525
|
-
| Visibility | Download URL | Auth Required |
|
|
1526
|
-
| ---------- | ---------------------------------------------------- | ------------------------ |
|
|
1527
|
-
| `public` | `/files/:id/download` | No |
|
|
1528
|
-
| `private` | `/files/:id/download?token=<hmac>&expires=<unix-ts>` | Signed URL (HMAC-SHA256) |
|
|
1529
|
-
|
|
1530
|
-
Signed URLs are generated via `GET /files/:id/signed-url`. The default TTL is 1 hour, configurable via `signedUrlTTLSeconds` in config.
|
|
1531
|
-
|
|
1532
|
-
### Storage Structure
|
|
1533
|
-
|
|
1534
|
-
```
|
|
1535
|
-
<project-root>/
|
|
1536
|
-
storage/
|
|
1537
|
-
public/ <- Publicly downloadable files
|
|
1538
|
-
<uuid>.jpg
|
|
1539
|
-
<uuid>.png
|
|
1540
|
-
private/ <- Signed-URL-only files
|
|
1541
|
-
<uuid>.pdf
|
|
1542
|
-
<uuid>.docx
|
|
1543
|
-
```
|
|
1544
|
-
|
|
1545
|
-
### Size and Quota Limits
|
|
1546
|
-
|
|
1547
|
-
| Setting | Environment Variable | Default |
|
|
1548
|
-
| -------------------- | ----------------------------------- | ------- |
|
|
1549
|
-
| Max file size | `ROTIFEX_STORAGE_MAX_FILE_SIZE_MB` | 10 MB |
|
|
1550
|
-
| Max storage per user | Config only (`maxStoragePerUserMB`) | 100 MB |
|
|
1551
|
-
|
|
1552
|
-
---
|
|
1553
|
-
|
|
1554
|
-
## 8. Admin Panel Features
|
|
1555
|
-
|
|
1556
|
-
### Dashboard
|
|
1557
|
-
|
|
1558
|
-
- **Stat cards:** Schemas, Total Records, Users, Files, Storage Used, Connected LLMs, Agents Created, Server Uptime, Server Status
|
|
1559
|
-
- **Schema Overview table:** model name, table name, record count per model
|
|
1560
|
-
- **Connected LLMs table:** provider name, request count, tokens in, tokens out, key status
|
|
1561
|
-
|
|
1562
|
-
### Database Schemas
|
|
1563
|
-
|
|
1564
|
-
- View all defined models with their field names, types, and constraints
|
|
1565
|
-
- Create a new model via a field builder UI (add field name + type pairs)
|
|
1566
|
-
- Attempting to create a model with an existing name returns a conflict error
|
|
1567
|
-
- Delete a model (routes deactivate immediately; underlying data table is preserved)
|
|
1568
|
-
|
|
1569
|
-
### User Management
|
|
1570
|
-
|
|
1571
|
-
- List all registered users: email, display name, role, creation date
|
|
1572
|
-
- Admin actions on user accounts
|
|
1573
|
-
|
|
1574
|
-
### File Browser
|
|
1575
|
-
|
|
1576
|
-
- Browse all files (admins see all; users see their own)
|
|
1577
|
-
- Preview images inline
|
|
1578
|
-
- Download any file
|
|
1579
|
-
- Delete files (removes from disk and database)
|
|
1580
|
-
|
|
1581
|
-
### AI Integration
|
|
1582
|
-
|
|
1583
|
-
**Providers tab:** Enable/disable providers, enter API keys (masked after save), set default model per provider.
|
|
1584
|
-
|
|
1585
|
-
**Playground tab:**
|
|
1586
|
-
|
|
1587
|
-
- Generate mode: prompt + provider/model/system/temp/maxTokens controls, token display after response
|
|
1588
|
-
- Chat mode: multi-turn conversation with message history
|
|
1589
|
-
|
|
1590
|
-
**Agents tab:**
|
|
1591
|
-
|
|
1592
|
-
- List all agents with name, provider, model, tools
|
|
1593
|
-
- Create agent form: name, description, provider, model, system prompt, tool checkboxes, temperature, max tokens, max iterations
|
|
1594
|
-
- Edit existing agents
|
|
1595
|
-
- Delete agents
|
|
1596
|
-
- Run agents interactively: enter a task, see reasoning steps in real time, view final output
|
|
1597
|
-
|
|
1598
|
-
**API Docs tab:** Built-in reference for all AI and agent endpoints.
|
|
1599
|
-
|
|
1600
|
-
### Server Logs
|
|
1601
|
-
|
|
1602
|
-
- In-memory ring buffer of structured log entries
|
|
1603
|
-
- Filter by level (`info`, `warn`, `error`, `debug`)
|
|
1604
|
-
- Timestamps and log messages displayed in a table
|
|
1605
|
-
|
|
1606
|
-
### Settings
|
|
1607
|
-
|
|
1608
|
-
Editable via admin panel — writes to `.env`:
|
|
1609
|
-
|
|
1610
|
-
| Variable | Description |
|
|
1611
|
-
| ----------------------------------- | ---------------------------- |
|
|
1612
|
-
| `JWT_SECRET` | Access token signing secret |
|
|
1613
|
-
| `JWT_REFRESH_SECRET` | Refresh token signing secret |
|
|
1614
|
-
| `ROTIFEX_PORT` | Server port |
|
|
1615
|
-
| `ROTIFEX_HOST` | Server bind host |
|
|
1616
|
-
| `ROTIFEX_CORS_ORIGIN` | Allowed CORS origin(s) |
|
|
1617
|
-
| `ROTIFEX_RATE_LIMIT_MAX` | Max requests per time window |
|
|
1618
|
-
| `ROTIFEX_LOG_LEVEL` | Log verbosity |
|
|
1619
|
-
| `ROTIFEX_STORAGE_MAX_FILE_SIZE_MB` | Max upload size in MB |
|
|
1620
|
-
| `ROTIFEX_STORAGE_SIGNED_URL_SECRET` | HMAC secret for signed URLs |
|
|
1621
|
-
|
|
1622
|
-
---
|
|
1623
|
-
|
|
1624
|
-
## 9. Error Handling
|
|
1625
|
-
|
|
1626
|
-
### Error Response Format
|
|
1627
|
-
|
|
1628
|
-
```json
|
|
1629
|
-
{
|
|
1630
|
-
"error": "Error Type",
|
|
1631
|
-
"message": "Human-readable description of what went wrong.",
|
|
1632
|
-
"statusCode": 400
|
|
1633
|
-
}
|
|
1634
|
-
```
|
|
1635
|
-
|
|
1636
|
-
Validation errors may return an array for `message`:
|
|
1637
|
-
|
|
1638
|
-
```json
|
|
1639
|
-
{
|
|
1640
|
-
"error": "Validation Error",
|
|
1641
|
-
"message": [{ "path": ["name"], "message": "Required" }],
|
|
1642
|
-
"statusCode": 400
|
|
1643
|
-
}
|
|
1644
|
-
```
|
|
1645
|
-
|
|
1646
|
-
### Common HTTP Error Codes
|
|
1647
|
-
|
|
1648
|
-
| Code | Meaning | Common Causes |
|
|
1649
|
-
| ----- | --------------------- | ------------------------------------------------------------------------------------- |
|
|
1650
|
-
| `400` | Bad Request | Missing required fields, invalid types, reserved model names, no fields to update |
|
|
1651
|
-
| `401` | Unauthorized | Missing, expired, or invalid JWT access token |
|
|
1652
|
-
| `403` | Forbidden | Non-admin accessing `/admin/api/*`, file access without ownership or valid signed URL |
|
|
1653
|
-
| `404` | Not Found | Unknown table name, record ID not found, agent not found, file not found |
|
|
1654
|
-
| `409` | Conflict | Email already registered, model name already exists |
|
|
1655
|
-
| `413` | Payload Too Large | File exceeds per-request size limit or per-user storage quota |
|
|
1656
|
-
| `500` | Internal Server Error | Unexpected server error, LLM provider failure |
|
|
1657
|
-
|
|
1658
|
-
---
|
|
1659
|
-
|
|
1660
|
-
## 10. Environment Configuration
|
|
1661
|
-
|
|
1662
|
-
### Configuration Priority (highest to lowest)
|
|
1663
|
-
|
|
1664
|
-
1. Shell environment variables
|
|
1665
|
-
2. CLI flags (`--port`, `--host`, `--verbose`)
|
|
1666
|
-
3. `config.json` (optional user overrides)
|
|
1667
|
-
4. `config.default.json` (shipped defaults)
|
|
1668
|
-
5. `.env` file (auto-loaded at startup)
|
|
1669
|
-
|
|
1670
|
-
### Environment Variables Reference
|
|
1671
|
-
|
|
1672
|
-
| Variable | Default | Description |
|
|
1673
|
-
| ----------------------------------- | --------- | -------------------------------------------- |
|
|
1674
|
-
| `ROTIFEX_PORT` | `3000` | TCP port |
|
|
1675
|
-
| `ROTIFEX_HOST` | `0.0.0.0` | Bind address |
|
|
1676
|
-
| `ROTIFEX_CORS_ORIGIN` | `*` | Allowed CORS origin |
|
|
1677
|
-
| `ROTIFEX_RATE_LIMIT_MAX` | `100` | Max requests per rate-limit window |
|
|
1678
|
-
| `ROTIFEX_LOG_LEVEL` | `info` | Log level (`info`, `debug`, `warn`, `error`) |
|
|
1679
|
-
| `ROTIFEX_STORAGE_MAX_FILE_SIZE_MB` | `10` | Max upload size in MB |
|
|
1680
|
-
| `ROTIFEX_STORAGE_SIGNED_URL_SECRET` | auto | HMAC secret for signed file URLs |
|
|
1681
|
-
| `JWT_SECRET` | auto | Access token signing secret |
|
|
1682
|
-
| `JWT_REFRESH_SECRET` | auto | Refresh token signing secret |
|
|
1683
|
-
|
|
1684
|
-
> `JWT_SECRET`, `JWT_REFRESH_SECRET`, and `ROTIFEX_STORAGE_SIGNED_URL_SECRET` are auto-generated on first startup if absent and saved to `.env`.
|
|
1685
|
-
|
|
1686
|
-
### Example `.env`
|
|
1687
|
-
|
|
1688
|
-
```env
|
|
1689
|
-
ROTIFEX_PORT=3000
|
|
1690
|
-
ROTIFEX_HOST=0.0.0.0
|
|
1691
|
-
ROTIFEX_CORS_ORIGIN=https://myapp.com
|
|
1692
|
-
ROTIFEX_RATE_LIMIT_MAX=200
|
|
1693
|
-
ROTIFEX_LOG_LEVEL=info
|
|
1694
|
-
ROTIFEX_STORAGE_MAX_FILE_SIZE_MB=25
|
|
1695
|
-
JWT_SECRET=replace-with-a-long-random-string
|
|
1696
|
-
JWT_REFRESH_SECRET=replace-with-another-long-random-string
|
|
1697
|
-
ROTIFEX_STORAGE_SIGNED_URL_SECRET=replace-with-yet-another-secret
|
|
1698
|
-
```
|
|
1699
|
-
|
|
1700
|
-
---
|
|
1701
|
-
|
|
1702
|
-
## 11. Deployment
|
|
1703
|
-
|
|
1704
|
-
### Requirements
|
|
1705
|
-
|
|
1706
|
-
- Node.js 18 or later
|
|
1707
|
-
- npm 9 or later
|
|
1708
|
-
|
|
1709
|
-
### Setup
|
|
1710
|
-
|
|
1711
|
-
```bash
|
|
1712
|
-
git clone <repo-url>
|
|
1713
|
-
cd rotifex
|
|
1714
|
-
npm install
|
|
1715
|
-
```
|
|
1716
|
-
|
|
1717
|
-
### Running in Development
|
|
1718
|
-
|
|
1719
|
-
```bash
|
|
1720
|
-
# Default port 3000
|
|
1721
|
-
npx rotifex start
|
|
1722
|
-
|
|
1723
|
-
# Custom port
|
|
1724
|
-
npx rotifex start --port 4000
|
|
1725
|
-
|
|
1726
|
-
# Custom host
|
|
1727
|
-
npx rotifex start --host 127.0.0.1
|
|
1728
|
-
|
|
1729
|
-
# Verbose (debug) logging
|
|
1730
|
-
npx rotifex start --verbose
|
|
1731
|
-
```
|
|
1732
|
-
|
|
1733
|
-
### Build the Admin Dashboard
|
|
1734
|
-
|
|
1735
|
-
```bash
|
|
1736
|
-
npm run build:admin
|
|
1737
|
-
```
|
|
1738
|
-
|
|
1739
|
-
Output is written to `admin/dist/`. The server automatically serves it at `/` when the directory exists.
|
|
1740
|
-
|
|
1741
|
-
### Production Setup
|
|
1742
|
-
|
|
1743
|
-
**1. Set secrets in environment:**
|
|
1744
|
-
|
|
1745
|
-
```bash
|
|
1746
|
-
export JWT_SECRET="$(openssl rand -hex 32)"
|
|
1747
|
-
export JWT_REFRESH_SECRET="$(openssl rand -hex 32)"
|
|
1748
|
-
export ROTIFEX_STORAGE_SIGNED_URL_SECRET="$(openssl rand -hex 32)"
|
|
1749
|
-
export ROTIFEX_CORS_ORIGIN="https://yourfrontend.com"
|
|
1750
|
-
```
|
|
1751
|
-
|
|
1752
|
-
**2. Build the admin dashboard:**
|
|
1753
|
-
|
|
1754
|
-
```bash
|
|
1755
|
-
npm run build:admin
|
|
1756
|
-
```
|
|
1757
|
-
|
|
1758
|
-
**3. Use a process manager:**
|
|
1759
|
-
|
|
1760
|
-
```bash
|
|
1761
|
-
npm install -g pm2
|
|
1762
|
-
pm2 start "npx rotifex start" --name rotifex
|
|
1763
|
-
pm2 save
|
|
1764
|
-
pm2 startup
|
|
1765
|
-
```
|
|
1766
|
-
|
|
1767
|
-
**4. Reverse proxy (Nginx example):**
|
|
1768
|
-
|
|
1769
|
-
```nginx
|
|
1770
|
-
server {
|
|
1771
|
-
listen 80;
|
|
1772
|
-
server_name api.yourapp.com;
|
|
1773
|
-
|
|
1774
|
-
location / {
|
|
1775
|
-
proxy_pass http://127.0.0.1:3000;
|
|
1776
|
-
proxy_set_header Host $host;
|
|
1777
|
-
proxy_set_header X-Real-IP $remote_addr;
|
|
1778
|
-
proxy_set_header X-Forwarded-Proto $scheme;
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
```
|
|
1782
|
-
|
|
1783
|
-
**5. Backup the database:**
|
|
1784
|
-
|
|
1785
|
-
```bash
|
|
1786
|
-
# SQLite database file — copy it regularly
|
|
1787
|
-
cp rotifex.db rotifex.db.backup
|
|
1788
|
-
```
|
|
1789
|
-
|
|
1790
|
-
---
|
|
1791
|
-
|
|
1792
|
-
## 12. Example Workflows
|
|
1793
|
-
|
|
1794
|
-
### Registering a User and Logging In
|
|
77
|
+
**2. Instant REST API** — no restart needed:
|
|
1795
78
|
|
|
1796
79
|
```bash
|
|
1797
|
-
#
|
|
1798
|
-
curl -X POST http://localhost:
|
|
1799
|
-
-H "Content-Type: application/json" \
|
|
1800
|
-
-d '{"email":"jane@example.com","password":"secure123","display_name":"Jane"}'
|
|
1801
|
-
|
|
1802
|
-
# Login — save the returned accessToken
|
|
1803
|
-
curl -X POST http://localhost:3000/auth/login \
|
|
80
|
+
# Create
|
|
81
|
+
curl -X POST http://localhost:4994/api/products \
|
|
1804
82
|
-H "Content-Type: application/json" \
|
|
1805
|
-
-d '{"
|
|
83
|
+
-d '{"name":"Widget","price":9.99,"in_stock":true}'
|
|
1806
84
|
|
|
1807
|
-
#
|
|
1808
|
-
|
|
85
|
+
# List with filter + sort
|
|
86
|
+
curl "http://localhost:4994/api/products?sort=price&order=ASC&in_stock=1"
|
|
1809
87
|
```
|
|
1810
88
|
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
### Defining a Model and Using the CRUD API
|
|
89
|
+
**3. Add AI** — configure a provider in the dashboard and call it:
|
|
1814
90
|
|
|
1815
91
|
```bash
|
|
1816
|
-
|
|
1817
|
-
curl -X POST http://localhost:3000/admin/api/schema \
|
|
1818
|
-
-H "Content-Type: application/json" \
|
|
1819
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
1820
|
-
-d '{
|
|
1821
|
-
"name": "Product",
|
|
1822
|
-
"fields": {
|
|
1823
|
-
"name": {"type": "string", "required": true},
|
|
1824
|
-
"price": "number",
|
|
1825
|
-
"in_stock": "boolean"
|
|
1826
|
-
}
|
|
1827
|
-
}'
|
|
1828
|
-
|
|
1829
|
-
# 2. Create a record
|
|
1830
|
-
curl -X POST http://localhost:3000/api/products \
|
|
1831
|
-
-H "Content-Type: application/json" \
|
|
1832
|
-
-d '{"name":"Widget","price":9.99,"in_stock":true}'
|
|
1833
|
-
|
|
1834
|
-
# 3. List with sort and filter
|
|
1835
|
-
curl "http://localhost:3000/api/products?sort=price&order=ASC&in_stock=1"
|
|
1836
|
-
|
|
1837
|
-
# 4. Get one
|
|
1838
|
-
curl http://localhost:3000/api/products/<id>
|
|
1839
|
-
|
|
1840
|
-
# 5. Update
|
|
1841
|
-
curl -X PUT http://localhost:3000/api/products/<id> \
|
|
92
|
+
curl -X POST http://localhost:4994/api/ai/generate \
|
|
1842
93
|
-H "Content-Type: application/json" \
|
|
1843
|
-
-d '{"
|
|
1844
|
-
|
|
1845
|
-
# 6. Delete
|
|
1846
|
-
curl -X DELETE http://localhost:3000/api/products/<id>
|
|
94
|
+
-d '{"provider":"openai","model":"gpt-4o","prompt":"Summarize this product catalog."}'
|
|
1847
95
|
```
|
|
1848
96
|
|
|
1849
97
|
---
|
|
1850
98
|
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
```bash
|
|
1854
|
-
# Upload a public file
|
|
1855
|
-
curl -X POST http://localhost:3000/files/upload \
|
|
1856
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
1857
|
-
-F "file=@/path/to/photo.jpg" \
|
|
1858
|
-
-F "visibility=public"
|
|
1859
|
-
|
|
1860
|
-
# Download public file (no auth needed)
|
|
1861
|
-
curl http://localhost:3000/files/<id>/download -o photo.jpg
|
|
1862
|
-
|
|
1863
|
-
# Upload a private file
|
|
1864
|
-
curl -X POST http://localhost:3000/files/upload \
|
|
1865
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
1866
|
-
-F "file=@/path/to/document.pdf" \
|
|
1867
|
-
-F "visibility=private"
|
|
99
|
+
## CLI Commands
|
|
1868
100
|
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
101
|
+
| Command | Description |
|
|
102
|
+
| --------------------------- | ------------------------------------------------- |
|
|
103
|
+
| `rotifex start` | Start the server (default port `4994`) |
|
|
104
|
+
| `rotifex start --port 4000` | Start on a custom port |
|
|
105
|
+
| `rotifex start --verbose` | Enable debug logging |
|
|
106
|
+
| `rotifex migrate` | Run pending database migrations |
|
|
107
|
+
| `rotifex reset-admin --yes` | Delete admin accounts and re-run first-time setup |
|
|
1876
108
|
|
|
1877
109
|
---
|
|
1878
110
|
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
```bash
|
|
1882
|
-
# Generate a completion
|
|
1883
|
-
curl -X POST http://localhost:3000/api/ai/generate \
|
|
1884
|
-
-H "Content-Type: application/json" \
|
|
1885
|
-
-d '{
|
|
1886
|
-
"provider": "openai",
|
|
1887
|
-
"model": "gpt-4o",
|
|
1888
|
-
"prompt": "Write a one-sentence tagline for a productivity app.",
|
|
1889
|
-
"maxTokens": 60
|
|
1890
|
-
}'
|
|
111
|
+
## Architecture
|
|
1891
112
|
|
|
1892
|
-
# Multi-turn chat
|
|
1893
|
-
curl -X POST http://localhost:3000/api/ai/chat \
|
|
1894
|
-
-H "Content-Type: application/json" \
|
|
1895
|
-
-d '{
|
|
1896
|
-
"provider": "anthropic",
|
|
1897
|
-
"model": "claude-sonnet-4-6",
|
|
1898
|
-
"messages": [
|
|
1899
|
-
{"role": "user", "content": "What is the capital of France?"}
|
|
1900
|
-
]
|
|
1901
|
-
}'
|
|
1902
113
|
```
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
"name": "Math Helper",
|
|
1915
|
-
"provider": "openai",
|
|
1916
|
-
"model": "gpt-4o",
|
|
1917
|
-
"systemPrompt": "You are a precise math assistant. Use the calculator tool for all computations.",
|
|
1918
|
-
"tools": ["calculate"],
|
|
1919
|
-
"temperature": 0.2,
|
|
1920
|
-
"maxIterations": 5
|
|
1921
|
-
}'
|
|
1922
|
-
|
|
1923
|
-
# 2. Run the agent
|
|
1924
|
-
curl -X POST http://localhost:3000/api/agents/<id>/run \
|
|
1925
|
-
-H "Content-Type: application/json" \
|
|
1926
|
-
-d '{"input": "What is (144 / 12) * 7.5 plus 33?"}'
|
|
114
|
+
┌─────────────────────────────────────────┐
|
|
115
|
+
│ Rotifex Server │
|
|
116
|
+
│ │
|
|
117
|
+
│ Fastify HTTP · Schema Engine (live) │
|
|
118
|
+
│ JWT Auth · SQLite (better-sqlite3│
|
|
119
|
+
│ File Storage · AI / Agent System │
|
|
120
|
+
│ │
|
|
121
|
+
│ ┌───────────────────────────────────┐ │
|
|
122
|
+
│ │ Admin SPA (React 19 + Vite) │ │
|
|
123
|
+
│ └───────────────────────────────────┘ │
|
|
124
|
+
└─────────────────────────────────────────┘
|
|
1927
125
|
```
|
|
1928
126
|
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
```bash
|
|
1934
|
-
curl -X POST http://localhost:3000/auth/refresh \
|
|
1935
|
-
-H "Content-Type: application/json" \
|
|
1936
|
-
-d '{"refreshToken":"<your-refresh-token>"}'
|
|
1937
|
-
```
|
|
127
|
+
- **Runtime:** Node.js ≥ 18, Fastify v5
|
|
128
|
+
- **Database:** SQLite via `better-sqlite3`
|
|
129
|
+
- **Frontend:** React 19 + Vite, served as a static SPA at `/`
|
|
130
|
+
- **AI providers:** OpenAI, Anthropic, Google Gemini, Ollama (local)
|
|
1938
131
|
|
|
1939
132
|
---
|
|
1940
133
|
|
|
1941
|
-
##
|
|
1942
|
-
|
|
1943
|
-
### Docusaurus
|
|
134
|
+
## Admin Dashboard
|
|
1944
135
|
|
|
1945
|
-
-
|
|
1946
|
-
- Split by `##` heading into separate files under `docs/api/`, `docs/guides/`, `docs/features/` for large doc sites.
|
|
1947
|
-
- Add `sidebar_label` and `sidebar_position` frontmatter to each split file.
|
|
1948
|
-
- All code blocks use fenced syntax compatible with Docusaurus's Prism highlighter.
|
|
1949
|
-
- Tables use standard GFM pipe syntax — no conversion needed.
|
|
136
|
+
The built-in SPA at `http://localhost:4994` gives you:
|
|
1950
137
|
|
|
1951
|
-
|
|
138
|
+
- **Dashboard** — live stats: schemas, records, users, files, storage, uptime
|
|
139
|
+
- **Database Schemas** — visual model builder, create and delete models on the fly
|
|
140
|
+
- **User Management** — list users, create accounts, reset passwords
|
|
141
|
+
- **File Browser** — browse, preview, download, and delete uploads
|
|
142
|
+
- **AI Integration** — configure providers, playground (generate + chat), agent builder
|
|
143
|
+
- **Server Logs** — real-time structured log viewer with level filtering
|
|
144
|
+
- **Settings** — edit all environment variables without touching files
|
|
1952
145
|
|
|
1953
|
-
|
|
1954
|
-
- API endpoint documentation can be supplemented by an `openapi.yaml` file generated from §3.
|
|
1955
|
-
- Use `<Note>`, `<Warning>`, and `<Tip>` components for callouts when converting to Mintlify MDX.
|
|
146
|
+
---
|
|
1956
147
|
|
|
1957
|
-
|
|
148
|
+
## Full Documentation
|
|
1958
149
|
|
|
1959
|
-
|
|
1960
|
-
- Use GitBook's `{% hint style="info" %}` blocks for the notes sections if converting to native GitBook format.
|
|
150
|
+
For the complete API reference, all endpoint details, deployment guides, example workflows, and configuration options see:
|
|
1961
151
|
|
|
1962
|
-
|
|
152
|
+
**[README-final.md](README-final.md)**
|
|
1963
153
|
|
|
1964
|
-
|
|
154
|
+
Or visit the hosted docs at [rotifex-docs.vercel.app](https://rotifex-docs.vercel.app/)
|
|
1965
155
|
|
|
1966
|
-
|
|
1967
|
-
| -------------------- | ------------------------------------------------ |
|
|
1968
|
-
| §3.2 Auth endpoints | `paths` under `/auth/*` |
|
|
1969
|
-
| §3.3 CRUD endpoints | `paths` under `/api/{table}` |
|
|
1970
|
-
| §3.4 File endpoints | `paths` under `/files/*` |
|
|
1971
|
-
| §3.5 AI endpoints | `paths` under `/api/ai/*` |
|
|
1972
|
-
| §3.6 Agent endpoints | `paths` under `/api/agents/*` |
|
|
1973
|
-
| §3.7 Admin endpoints | `paths` under `/admin/api/*` |
|
|
1974
|
-
| §5 Data Structures | `components/schemas` |
|
|
1975
|
-
| §9 Error codes | `components/responses` |
|
|
1976
|
-
| §4 Auth headers | `components/securitySchemes` (BearerAuth, HS256) |
|
|
156
|
+
---
|
|
1977
157
|
|
|
1978
|
-
|
|
158
|
+
## License
|
|
1979
159
|
|
|
1980
|
-
|
|
1981
|
-
- Headings use a strict hierarchy (`#` -> `##` -> `###` -> `####`) for correct TOC generation.
|
|
1982
|
-
- Internal anchor links use lowercase slugs compatible with GitHub, Docusaurus, Mintlify, and GitBook.
|
|
1983
|
-
- No HTML tags are used — the file is pure Markdown for maximum portability.
|
|
160
|
+
MIT
|