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 CHANGED
@@ -1,1361 +1,66 @@
1
1
  ![banner](https://raw.githubusercontent.com/Grigary-C-Antony/Rotifex/master/banner-1.png)
2
2
 
3
- # Rotifex
3
+ <div align="center">
4
4
 
5
- > open the documentaion at [Docs](https://rotifex-docs.vercel.app/)
5
+ [![npm version](https://img.shields.io/npm/v/rotifex?color=4f6ef7&label=npm)](https://www.npmjs.com/package/rotifex)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](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
- **Response `200`:**
11
+ [Documentation](https://rotifex-docs.vercel.app/) · [Full Reference](README-final.md) · [npm](https://www.npmjs.com/package/rotifex)
1197
12
 
1198
- ```json
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
- #### `GET /admin/api/agents`
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
- Delete an agent permanently.
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
- **Response:** `204 No Content`
21
+ Think Supabase or Firebase, but running entirely on your own machine.
1267
22
 
1268
23
  ---
1269
24
 
1270
- ## 4. Authentication
25
+ ## Key Features
1271
26
 
1272
- ### Flow
1273
-
1274
- ```
1275
- Client Rotifex Server
1276
- | |
1277
- |-- POST /auth/register ----------> | Hash password, create user
1278
- |<- { user } ---------------------- |
1279
- | |
1280
- |-- POST /auth/login -------------> | Verify password
1281
- |<- { accessToken, refreshToken }-- |
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
- ## 5. Models / Data Structures
1320
-
1321
- ### User
40
+ ## Quick Start
1322
41
 
1323
- Built-in model managed by the auth system. Not configurable via the schema engine.
42
+ **Install and run:**
1324
43
 
1325
- | Field | Type | Required | Notes |
1326
- | --------------- | ----------------- | -------- | ----------------------------------- |
1327
- | `id` | string (UUID) | Auto | Primary key |
1328
- | `email` | string | Yes | Unique |
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
- ### \_files (File Metadata)
51
+ ```bash
52
+ npx rotifex start
53
+ ```
1338
54
 
1339
- Internal table managed by `StorageManager`. Not in `schema.json`.
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
- | Field | Type | Notes |
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
- ### Custom Models (schema.json)
1355
-
1356
- All custom models automatically receive `id`, `created_at`, and `updated_at`.
61
+ ## How It Works
1357
62
 
1358
- **Example `schema.json`:**
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
- # Register
1798
- curl -X POST http://localhost:3000/auth/register \
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 '{"email":"jane@example.com","password":"secure123"}'
83
+ -d '{"name":"Widget","price":9.99,"in_stock":true}'
1806
84
 
1807
- # Store token
1808
- export TOKEN="<accessToken from response>"
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
- # 1. Create model (admin role required)
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 '{"price":14.99}'
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
- ### Uploading and Downloading Files
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
- # Get a signed URL for the private file
1870
- curl http://localhost:3000/files/<id>/signed-url \
1871
- -H "Authorization: Bearer $TOKEN"
1872
-
1873
- # Download using the signed URL
1874
- curl "<signed-url>" -o document.pdf
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
- ### Calling an AI Model
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
- ### Creating and Running an AI Agent
1907
-
1908
- ```bash
1909
- # 1. Create an agent (admin required)
1910
- curl -X POST http://localhost:3000/admin/api/agents \
1911
- -H "Content-Type: application/json" \
1912
- -H "Authorization: Bearer $TOKEN" \
1913
- -d '{
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
- ### Refreshing an Expired Token
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
- ## 13. Notes for Documentation Generators
1942
-
1943
- ### Docusaurus
134
+ ## Admin Dashboard
1944
135
 
1945
- - Place this file in `docs/` as `intro.md`.
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
- ### Mintlify
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
- - Each `##` section maps to a separate `.mdx` page.
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
- ### GitBook
148
+ ## Full Documentation
1958
149
 
1959
- - Import this file directly. GitBook renders all standard Markdown including tables, code blocks, and nested lists.
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
- ### Swagger / OpenAPI Mapping
152
+ **[README-final.md](README-final.md)**
1963
153
 
1964
- This document contains enough information to produce a complete `openapi.yaml`. Use this mapping:
154
+ Or visit the hosted docs at [rotifex-docs.vercel.app](https://rotifex-docs.vercel.app/)
1965
155
 
1966
- | Section | OpenAPI Component |
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
- ### General Formatting Notes
158
+ ## License
1979
159
 
1980
- - All code blocks specify a language (`bash`, `json`, `nginx`) for syntax highlighting.
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