flet-box 1.0.3 → 1.0.5
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 +10 -0
- package/bin/cli.js +15 -0
- package/bin/commands/checkApi.js +761 -0
- package/bin/commands/create.js +77 -5
- package/bin/utils/dtsProps.js +191 -0
- package/bin/utils/moduleExports.js +165 -0
- package/bin/utils/neutralise.js +64 -0
- package/bin/utils/templates.js +366 -27
- package/docs/CONTRIBUTING.md +156 -0
- package/docs/README.md +68 -0
- package/docs/arquitectura.md +164 -0
- package/docs/cli/README.md +542 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/app-templates.md +197 -0
- package/docs/guides/compatibility.md +95 -0
- package/docs/guides/embedding.md +284 -0
- package/docs/guides/first-app.md +208 -0
- package/docs/guides/frontend-services.md +171 -0
- package/docs/guides/minification-and-protection.md +260 -0
- package/docs/guides/mobile-and-platforms.md +439 -0
- package/docs/guides/online-updates.md +196 -0
- package/docs/guides/router.md +282 -0
- package/docs/guides/state.md +283 -0
- package/docs/guides/syntax-philosophy.md +150 -0
- package/docs/guides/utilities.md +241 -0
- package/docs/guides/widget-structure.md +163 -0
- package/docs/server/README.md +68 -0
- package/docs/server/START_HERE.md +121 -0
- package/docs/server/api.md +225 -0
- package/docs/server/data-services.md +300 -0
- package/docs/server/deployment.md +113 -0
- package/docs/server/security.md +196 -0
- package/docs/services/README.md +79 -0
- package/docs/services/RamStore.md +84 -0
- package/docs/services/Session.md +84 -0
- package/docs/services/Storage.md +88 -0
- package/docs/services/http.md +86 -0
- package/docs/tools/README.md +138 -0
- package/docs/tools/animation.md +87 -0
- package/docs/tools/async-ids-log.md +80 -0
- package/docs/tools/device.md +132 -0
- package/docs/tools/introspection.md +67 -0
- package/docs/tools/lists.md +96 -0
- package/docs/tools/markdown.md +79 -0
- package/docs/tools/reactivity.md +288 -0
- package/docs/tools/state.md +103 -0
- package/docs/tools/styling.md +92 -0
- package/docs/tools/text-and-time.md +89 -0
- package/docs/tools/theme.md +102 -0
- package/docs/tools/validation.md +75 -0
- package/docs/tools/visual-effects.md +84 -0
- package/docs/widget/Accordion.md +144 -0
- package/docs/widget/AdMob.md +144 -0
- package/docs/widget/AdSense.md +133 -0
- package/docs/widget/AdaptiveScaffold.md +105 -0
- package/docs/widget/AlertDialog.md +103 -0
- package/docs/widget/AnimatedBox.md +130 -0
- package/docs/widget/AnimatedText.md +123 -0
- package/docs/widget/AppBar.md +118 -0
- package/docs/widget/Audio.md +135 -0
- package/docs/widget/Avatar.md +119 -0
- package/docs/widget/Badge.md +128 -0
- package/docs/widget/BottomNavigation.md +116 -0
- package/docs/widget/BottomSheet.md +125 -0
- package/docs/widget/Button.md +137 -0
- package/docs/widget/COMMON_PROPS.md +150 -0
- package/docs/widget/Card.md +125 -0
- package/docs/widget/Carousel.md +151 -0
- package/docs/widget/Chart.md +151 -0
- package/docs/widget/Checkbox.md +118 -0
- package/docs/widget/Chip.md +126 -0
- package/docs/widget/CircularBar.md +168 -0
- package/docs/widget/CircularChart.md +164 -0
- package/docs/widget/CodeViewer.md +130 -0
- package/docs/widget/CollapsibleSideBar.md +112 -0
- package/docs/widget/Column.md +129 -0
- package/docs/widget/Container.md +136 -0
- package/docs/widget/DataTable.md +150 -0
- package/docs/widget/Divider.md +109 -0
- package/docs/widget/DraggBox.md +129 -0
- package/docs/widget/Drawer.md +135 -0
- package/docs/widget/DrawerItem.md +98 -0
- package/docs/widget/Dropdown.md +139 -0
- package/docs/widget/DroppBox.md +152 -0
- package/docs/widget/FloatingActionButton.md +120 -0
- package/docs/widget/GridView.md +140 -0
- package/docs/widget/Icon.md +117 -0
- package/docs/widget/Image.md +125 -0
- package/docs/widget/Input.md +151 -0
- package/docs/widget/Inspector.md +92 -0
- package/docs/widget/InstallButton.md +128 -0
- package/docs/widget/ListTile.md +132 -0
- package/docs/widget/ListView.md +138 -0
- package/docs/widget/Markdown.md +175 -0
- package/docs/widget/MatrixRain.md +122 -0
- package/docs/widget/Modal.md +129 -0
- package/docs/widget/Pagination.md +135 -0
- package/docs/widget/ParallaxBox.md +128 -0
- package/docs/widget/ProgressBar.md +130 -0
- package/docs/widget/QRCode.md +117 -0
- package/docs/widget/README.md +188 -0
- package/docs/widget/Radio.md +123 -0
- package/docs/widget/Rating.md +131 -0
- package/docs/widget/Row.md +131 -0
- package/docs/widget/START_HERE.md +146 -0
- package/docs/widget/Scaffold.md +152 -0
- package/docs/widget/Skeleton.md +130 -0
- package/docs/widget/Slider.md +146 -0
- package/docs/widget/SnackBar.md +102 -0
- package/docs/widget/Stack.md +128 -0
- package/docs/widget/Stepper.md +145 -0
- package/docs/widget/Switch.md +118 -0
- package/docs/widget/Tabs.md +124 -0
- package/docs/widget/Text.md +133 -0
- package/docs/widget/Tooltip.md +145 -0
- package/docs/widget/TreeView.md +176 -0
- package/docs/widget/Video.md +127 -0
- package/docs/widget/_template.md +61 -0
- package/package.json +4 -2
- package/src/animations/AnimatedBox.js +32 -1
- package/src/animations/AnimatedText.js +7 -3
- package/src/animations/MatrixRain.js +37 -12
- package/src/animations/ParallaxBox.js +16 -3
- package/src/animations/animate.js +187 -58
- package/src/animations/index.js +1 -0
- package/src/core/runApp.js +13 -1
- package/src/create-index.sh +1 -1
- package/src/index.d.ts +227 -91
- package/src/index.js +1 -0
- package/src/navigations/AdaptiveScaffold.js +11 -2
- package/src/navigations/AppBar.js +8 -2
- package/src/navigations/BottomNavigation.js +24 -5
- package/src/navigations/DrawerItem.js +16 -3
- package/src/navigations/Scaffold.js +8 -1
- package/src/navigations/Tabs.js +40 -8
- package/src/tools/dimensions.js +11 -3
- package/src/tools/useState.js +139 -101
- package/src/utils/getWidgetProps.js +5 -3
- package/src/widget-factory/assignProps.js +30 -11
- package/src/widget-factory/processProps.js +17 -5
- package/src/widget-factory/reactiveValue.js +172 -0
- package/src/widget-factory/translateProps.js +777 -901
- package/src/widget-factory/warnings.js +15 -0
- package/src/widget-factory/widgetFactory.js +27 -0
- package/src/widgets/Accordion.js +6 -5
- package/src/widgets/AlertDialog.js +7 -5
- package/src/widgets/Audio.js +30 -8
- package/src/widgets/BottomSheet.js +24 -1
- package/src/widgets/Chart.js +4 -1
- package/src/widgets/Checkbox.js +16 -8
- package/src/widgets/CircularBar.js +12 -1
- package/src/widgets/CircularChart.js +4 -1
- package/src/widgets/CodeViewer.js +26 -8
- package/src/widgets/DataTable.js +11 -2
- package/src/widgets/Dropdown.js +5 -1
- package/src/widgets/FloatingActionButton.js +4 -7
- package/src/widgets/Image.js +7 -2
- package/src/widgets/Input.js +119 -6
- package/src/widgets/Inspector.js +16 -10
- package/src/widgets/ListView.js +80 -30
- package/src/widgets/Markdown.js +53 -8
- package/src/widgets/Modal.js +24 -1
- package/src/widgets/Pagination.js +6 -3
- package/src/widgets/ProgressBar.js +16 -13
- package/src/widgets/QRCode.js +9 -2
- package/src/widgets/Radio.js +11 -8
- package/src/widgets/Slider.js +26 -4
- package/src/widgets/SnackBar.js +20 -2
- package/src/widgets/Stack.js +36 -3
- package/src/widgets/Stepper.js +4 -3
- package/src/widgets/Switch.js +8 -5
- package/src/widgets/TreeView.js +3 -2
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# API and routes
|
|
2
|
+
|
|
3
|
+
## `Api()`
|
|
4
|
+
|
|
5
|
+
`Api()` turns a readable route object into the internal route map used by the server.
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { Api } from "flet-box-server";
|
|
9
|
+
|
|
10
|
+
const routes = Api({
|
|
11
|
+
"/products": {
|
|
12
|
+
GET: {
|
|
13
|
+
handler: ({ query }) => ({
|
|
14
|
+
items: [],
|
|
15
|
+
search: query.search || null,
|
|
16
|
+
}),
|
|
17
|
+
tag: "products",
|
|
18
|
+
description: "List products",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Supported methods are `GET`, `POST`, `PUT`, `DELETE`, and `PATCH`.
|
|
25
|
+
|
|
26
|
+
## Handler styles
|
|
27
|
+
|
|
28
|
+
A short handler can be a function:
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
"/health": {
|
|
32
|
+
GET: () => ({ ok: true }),
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
A detailed handler can include middleware, metadata, schemas, and examples:
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
"/tasks": {
|
|
40
|
+
POST: {
|
|
41
|
+
handler: ({ title, completed }) => ({
|
|
42
|
+
id: 1,
|
|
43
|
+
title,
|
|
44
|
+
completed,
|
|
45
|
+
}),
|
|
46
|
+
middleware: [],
|
|
47
|
+
tag: "tasks",
|
|
48
|
+
description: "Create a task",
|
|
49
|
+
example: {
|
|
50
|
+
title: "Read the docs",
|
|
51
|
+
completed: false,
|
|
52
|
+
},
|
|
53
|
+
responseExample: {
|
|
54
|
+
id: 1,
|
|
55
|
+
title: "Read the docs",
|
|
56
|
+
completed: false,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## URL parameters
|
|
63
|
+
|
|
64
|
+
Use `:name` in a route path:
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
"/users/:id": {
|
|
68
|
+
GET: ({ id }) => ({
|
|
69
|
+
userId: id,
|
|
70
|
+
}),
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The server matches `/users/42` and provides `id: "42"` to the handler.
|
|
75
|
+
|
|
76
|
+
## Query parameters
|
|
77
|
+
|
|
78
|
+
Query values are available through `query` or `req.query`:
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
"/search": {
|
|
82
|
+
GET: ({ query }) => ({
|
|
83
|
+
term: query.term || "",
|
|
84
|
+
page: Number(query.page || 1),
|
|
85
|
+
}),
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Request example:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
curl "http://localhost:3000/search?term=widgets&page=2"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Request body
|
|
96
|
+
|
|
97
|
+
For JSON requests, body fields are spread into the handler context:
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
"/profile": {
|
|
101
|
+
PUT: {
|
|
102
|
+
handler: ({ name, email }) => ({ name, email }),
|
|
103
|
+
example: {
|
|
104
|
+
name: "Ada",
|
|
105
|
+
email: "ada@example.com",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
You can also access the original body through `req.body`.
|
|
112
|
+
|
|
113
|
+
## Automatic request schemas
|
|
114
|
+
|
|
115
|
+
When a method has an `example`, the server calls `inferSchema()` and stores the generated schema in the route documentation.
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
example: {
|
|
119
|
+
name: "Ada",
|
|
120
|
+
age: 30,
|
|
121
|
+
active: true,
|
|
122
|
+
tags: ["developer"],
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Use real JavaScript values in examples. For example, `age: 30`, not `age: thirty`.
|
|
127
|
+
|
|
128
|
+
The inferred schema detects strings, numbers, booleans, arrays, objects, email-like values, and URL-like values.
|
|
129
|
+
|
|
130
|
+
## Manual schemas
|
|
131
|
+
|
|
132
|
+
Use `schemas` when the inferred schema is not enough:
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
"/users": {
|
|
136
|
+
POST: {
|
|
137
|
+
handler: ({ body }) => body,
|
|
138
|
+
schemas: {
|
|
139
|
+
CreateUser: {
|
|
140
|
+
type: "object",
|
|
141
|
+
properties: {
|
|
142
|
+
name: { type: "string", minLength: 2 },
|
|
143
|
+
email: { type: "email" },
|
|
144
|
+
},
|
|
145
|
+
required: ["name", "email"],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
For active validation, use `validate: true` in the server options or add validation middleware explicitly.
|
|
153
|
+
|
|
154
|
+
## Response examples
|
|
155
|
+
|
|
156
|
+
`responseExample` creates response documentation and a success response entry:
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
responseExample: {
|
|
160
|
+
id: 1,
|
|
161
|
+
name: "Ada",
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Middleware
|
|
166
|
+
|
|
167
|
+
Middleware uses the Express-style `(req, res, next)` shape:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
const logRequest = (req, res, next) => {
|
|
171
|
+
console.log(req.method, req.url);
|
|
172
|
+
next();
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const routes = Api({
|
|
176
|
+
"/health": {
|
|
177
|
+
GET: {
|
|
178
|
+
handler: () => ({ ok: true }),
|
|
179
|
+
middleware: [logRequest],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Generated documentation
|
|
186
|
+
|
|
187
|
+
Enable docs when starting the server:
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
runServer(routes, { docs: true });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The server exposes:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
GET /docs
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The response groups endpoints by tag and includes methods, descriptions, schemas, responses, and generated curl, Python, and JavaScript examples.
|
|
200
|
+
|
|
201
|
+
## HTTP responses
|
|
202
|
+
|
|
203
|
+
Returning an object sends a JSON response with status `200`:
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
"/status": {
|
|
207
|
+
GET: () => ({ status: "ok" }),
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
For errors inside middleware, use:
|
|
212
|
+
|
|
213
|
+
```javascript
|
|
214
|
+
return res.error("Unauthorized", 401);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Route checklist
|
|
218
|
+
|
|
219
|
+
- Use a clear path.
|
|
220
|
+
- Use the correct HTTP method.
|
|
221
|
+
- Add `description` and `tag`.
|
|
222
|
+
- Add `example` for body-based methods.
|
|
223
|
+
- Add `responseExample` for useful response documentation.
|
|
224
|
+
- Add authentication and validation where needed.
|
|
225
|
+
- Do not expose secrets in examples or responses.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# Database and services
|
|
2
|
+
|
|
3
|
+
## Where should your data live?
|
|
4
|
+
|
|
5
|
+
A browser cannot run every database engine. The Node `better-sqlite3` library used by the server tools is a **native module** — it only runs on the server. Before choosing storage, decide where the data lives.
|
|
6
|
+
|
|
7
|
+
### Option 1 — Browser only (no API)
|
|
8
|
+
|
|
9
|
+
Data stays on the visitor's device. No server and no network calls.
|
|
10
|
+
|
|
11
|
+
- **localStorage** — ships with the framework as `src/services/Storage.js` (`saveData`, `getData`, `updateData`, `deleteData`, ...). Synchronous, ~5 MB per origin, string values; the helpers serialize objects for you.
|
|
12
|
+
- **IndexedDB** — asynchronous and much larger (gigabytes). Good for big records or offline files. Not bundled; call it directly or wrap it in a small helper.
|
|
13
|
+
- **SQLite in the browser** — sql.js or sqlite-wasm run real SQL as WebAssembly. Not bundled.
|
|
14
|
+
|
|
15
|
+
Choose this for drafts, per-device settings, offline caches, or single-user prototypes.
|
|
16
|
+
|
|
17
|
+
### Option 2 — SQLite API server
|
|
18
|
+
|
|
19
|
+
Data lives on your server and the UI talks to it over HTTP through the [API server section](#api-server-for-sqlite) below:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
UI widget (httpGet / httpPost / httpPut / httpDelete)
|
|
23
|
+
↓ HTTPS + JSON
|
|
24
|
+
Server routes (Api + runServer)
|
|
25
|
+
↓
|
|
26
|
+
SQLite (better-sqlite3 wrapper)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Choose this when data is shared between users or devices, must survive a browser reset, or is read by other systems.
|
|
30
|
+
|
|
31
|
+
### Option 3 — Hybrid
|
|
32
|
+
|
|
33
|
+
Use browser storage for offline cache and preferences, and sync with the API when the app is online.
|
|
34
|
+
|
|
35
|
+
## SQLite database
|
|
36
|
+
|
|
37
|
+
The current `Database` helper supports SQLite through the `SQLite` wrapper.
|
|
38
|
+
|
|
39
|
+
> **Import rule:** the package root exports the core only. Every tool is a deep import (`flet-box-server/tools/<name>.js`) so that loading one never pulls in puppeteer, twilio, xlsx or the optional Google Cloud packages.
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
import { Database } from "flet-box-server/tools/Database.js";
|
|
43
|
+
|
|
44
|
+
const db = Database({
|
|
45
|
+
type: "sqlite",
|
|
46
|
+
path: "data/app.db",
|
|
47
|
+
tables: {
|
|
48
|
+
users: {
|
|
49
|
+
id: "INTEGER PRIMARY KEY AUTOINCREMENT",
|
|
50
|
+
name: "TEXT",
|
|
51
|
+
email: "TEXT",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use the generated table API:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
const result = db.users.insert({
|
|
61
|
+
name: "Ada",
|
|
62
|
+
email: "ada@example.com",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const users = db.users.readAll();
|
|
66
|
+
const ada = db.users.readWhere(["email", "ada@example.com"], true);
|
|
67
|
+
db.users.update({ name: "Ada Lovelace" }, ["email", "ada@example.com"]);
|
|
68
|
+
db.users.delete(["email", "ada@example.com"]);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The lower-level SQLite class also provides table creation, updates, deletes, structure inspection, and reads.
|
|
72
|
+
|
|
73
|
+
## API server for SQLite
|
|
74
|
+
|
|
75
|
+
Wire the SQLite wrapper into the route server so the UI can read and write data safely over HTTP. Every wrapper method returns `[status, messageOrData]` — `true` when the operation succeeded.
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
// server/index.js
|
|
79
|
+
import { Api, runServer } from "flet-box-server/core/index.js";
|
|
80
|
+
import { SQLite } from "flet-box-server/tools/betterSqlite.js";
|
|
81
|
+
|
|
82
|
+
const db = new SQLite("data/app.db");
|
|
83
|
+
db.createTable("users", {
|
|
84
|
+
id: "INTEGER PRIMARY KEY AUTOINCREMENT",
|
|
85
|
+
name: "TEXT",
|
|
86
|
+
email: "TEXT",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const routes = Api({
|
|
90
|
+
"/api/users": {
|
|
91
|
+
GET: () => {
|
|
92
|
+
const [ok, rows] = db.readAll("users");
|
|
93
|
+
return { ok, items: ok ? rows : [] };
|
|
94
|
+
},
|
|
95
|
+
POST: ({ body }) => {
|
|
96
|
+
const [ok, msg] = db.insert("users", {
|
|
97
|
+
name: body.name,
|
|
98
|
+
email: body.email,
|
|
99
|
+
});
|
|
100
|
+
return ok ? { ok } : { ok, error: msg };
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
"/api/users/:id": {
|
|
104
|
+
PUT: ({ id, body }) => {
|
|
105
|
+
const [ok, msg] = db.update("users", { email: body.email }, ["id", id]);
|
|
106
|
+
return ok ? { ok } : { ok, error: msg };
|
|
107
|
+
},
|
|
108
|
+
DELETE: ({ id }) => {
|
|
109
|
+
const [ok, msg] = db.delete("users", ["id", id]);
|
|
110
|
+
return ok ? { ok } : { ok, error: msg };
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
runServer(routes, { port: 3000, docs: true });
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Consume the API from the UI with the framework HTTP client:
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
// app.js (FletBox UI)
|
|
122
|
+
import { httpGet, httpPost, httpPut, httpDelete } from "flet-box";
|
|
123
|
+
|
|
124
|
+
const list = await httpGet("http://localhost:3000/api/users");
|
|
125
|
+
const created = await httpPost("http://localhost:3000/api/users", {
|
|
126
|
+
body: { name: "Ada", email: "ada@example.com" },
|
|
127
|
+
});
|
|
128
|
+
const updated = await httpPut("http://localhost:3000/api/users/1", {
|
|
129
|
+
body: { email: "ada@lovelace.dev" },
|
|
130
|
+
});
|
|
131
|
+
const removed = await httpDelete("http://localhost:3000/api/users/1");
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Start the server, then open the auto-generated docs at `http://localhost:3000/docs` to browse and test the endpoints.
|
|
135
|
+
|
|
136
|
+
## Redis cache
|
|
137
|
+
|
|
138
|
+
Configure Redis with `REDIS_URL`:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
REDIS_URL=redis://localhost:6379
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use the cache helpers:
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
import { connectRedis, cacheSet, cacheGet, cacheDel } from "flet-box-server/tools/cache.js";
|
|
148
|
+
|
|
149
|
+
connectRedis();
|
|
150
|
+
await cacheSet("user:1", { name: "Ada" }, 3600);
|
|
151
|
+
const user = await cacheGet("user:1");
|
|
152
|
+
await cacheDel("user:1");
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## File storage
|
|
156
|
+
|
|
157
|
+
The local storage module uses `STORAGE_PATH` or `./storage`:
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
import { uploadFile, downloadFile, listFiles, deleteFile } from "flet-box-server/tools/storage.js";
|
|
161
|
+
|
|
162
|
+
const file = await uploadFile("avatars", "profile.jpg", imageBuffer);
|
|
163
|
+
const stream = await downloadFile(file.path);
|
|
164
|
+
const files = await listFiles("avatars");
|
|
165
|
+
await deleteFile(file.path);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Validate file names, sizes, and content types before storing uploads.
|
|
169
|
+
|
|
170
|
+
## Email
|
|
171
|
+
|
|
172
|
+
Configure SMTP credentials through environment variables in production. A simple send looks like:
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
import { sendEmail } from "flet-box-server/tools/email.js";
|
|
176
|
+
|
|
177
|
+
await sendEmail({
|
|
178
|
+
sendTo: "user@example.com",
|
|
179
|
+
title: "Welcome",
|
|
180
|
+
personalCode: 123456,
|
|
181
|
+
userName: "Ada",
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Never commit passwords or application tokens in source code.
|
|
186
|
+
|
|
187
|
+
## SMS
|
|
188
|
+
|
|
189
|
+
Configure Twilio:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
TWILIO_ACCOUNT_SID=...
|
|
193
|
+
TWILIO_AUTH_TOKEN=...
|
|
194
|
+
TWILIO_PHONE_NUMBER=...
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
import { sendSMS, sendVerificationCode, verifyPhone } from "flet-box-server/tools/sms.js";
|
|
199
|
+
|
|
200
|
+
await sendSMS("+123456789", "Your code is 123456");
|
|
201
|
+
const code = await sendVerificationCode("+123456789");
|
|
202
|
+
const valid = await verifyPhone("+123456789", code);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The default verification store is in memory. Use a shared persistent store when scaling.
|
|
206
|
+
|
|
207
|
+
## PDF
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
import { generatePDF, savePDF, generateInvoice } from "flet-box-server/tools/pdf.js";
|
|
211
|
+
|
|
212
|
+
const pdf = await generatePDF("<h1>Hello</h1>", { format: "A4" });
|
|
213
|
+
await savePDF(pdf, "output.pdf");
|
|
214
|
+
|
|
215
|
+
const invoice = await generateInvoice({
|
|
216
|
+
number: "INV-001",
|
|
217
|
+
date: "2026-01-01",
|
|
218
|
+
client: "Ada Lovelace",
|
|
219
|
+
items: [{ description: "Consulting", quantity: 2, price: 50 }],
|
|
220
|
+
total: 100,
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
PDF generation uses Puppeteer and may require additional system configuration in deployment environments.
|
|
225
|
+
|
|
226
|
+
## CSV and Excel
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
import { readCSV, writeCSV, readExcel, writeExcel } from "flet-box-server/tools/csv.js";
|
|
230
|
+
|
|
231
|
+
const users = await readCSV("users.csv");
|
|
232
|
+
await writeCSV("copy.csv", users);
|
|
233
|
+
const workbookRows = await readExcel("users.xlsx");
|
|
234
|
+
await writeExcel("copy.xlsx", workbookRows);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Queues
|
|
238
|
+
|
|
239
|
+
Queues use Redis and Bull:
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
import { createQueue, addJob, processQueue } from "flet-box-server/tools/queue.js";
|
|
243
|
+
|
|
244
|
+
createQueue("emails", { attempts: 3, backoff: 5000 });
|
|
245
|
+
|
|
246
|
+
processQueue("emails", async (job) => {
|
|
247
|
+
await sendEmail(job.data);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
await addJob("emails", {
|
|
251
|
+
sendTo: "user@example.com",
|
|
252
|
+
title: "Welcome",
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Use queues for slow work that should not block an HTTP request.
|
|
257
|
+
|
|
258
|
+
## WebSockets
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
import { createWebSocketServer } from "flet-box-server/tools/webSocket.js";
|
|
262
|
+
|
|
263
|
+
const wss = createWebSocketServer(3001);
|
|
264
|
+
|
|
265
|
+
wss.onConnection((ws) => {
|
|
266
|
+
ws.send(JSON.stringify({ type: "connected" }));
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
wss.onMessage((ws, message) => {
|
|
270
|
+
wss.broadcast({ type: "message", data: message });
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The server supports connection, message, close, error, broadcast, and room operations.
|
|
275
|
+
|
|
276
|
+
## Speech and translation
|
|
277
|
+
|
|
278
|
+
Optional Google Cloud modules provide speech-to-text and text-to-speech. They are `optionalDependencies`, so install the ones you use (`npm install @google-cloud/speech @google-cloud/text-to-speech`); they require cloud credentials and should be configured outside source control.
|
|
279
|
+
|
|
280
|
+
```javascript
|
|
281
|
+
import { transcribeFile } from "flet-box-server/tools/speechToText.js";
|
|
282
|
+
import { speak } from "flet-box-server/tools/textToSpeech.js";
|
|
283
|
+
import { translate } from "flet-box-server/tools/translator.js";
|
|
284
|
+
|
|
285
|
+
const text = await transcribeFile("audio.mp3", { language: "en-US" });
|
|
286
|
+
const audio = await speak("Hello", "en");
|
|
287
|
+
const translated = await translate("hello", "es");
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Logging
|
|
291
|
+
|
|
292
|
+
```javascript
|
|
293
|
+
import { logger } from "flet-box-server/tools/logger.js";
|
|
294
|
+
|
|
295
|
+
logger.save("logs/app.log");
|
|
296
|
+
logger.info("Server started");
|
|
297
|
+
logger.warn("Slow request", { duration: 800 });
|
|
298
|
+
logger.error("Database error", { code: "DB_ERROR" });
|
|
299
|
+
logger.close();
|
|
300
|
+
```
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Deployment and operations
|
|
2
|
+
|
|
3
|
+
## Development server
|
|
4
|
+
|
|
5
|
+
Create a small entry file and run it with Node:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
node server.js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
For automatic restarts, use the package script or nodemon:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm run dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Environment variables
|
|
18
|
+
|
|
19
|
+
Use environment variables for secrets and deployment settings:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
PORT=3000
|
|
23
|
+
JWT_SECRET=replace-with-a-long-random-secret
|
|
24
|
+
CORS_ORIGINS=https://app.example.com
|
|
25
|
+
REDIS_URL=redis://localhost:6379
|
|
26
|
+
STORAGE_PATH=./storage
|
|
27
|
+
TWILIO_ACCOUNT_SID=...
|
|
28
|
+
TWILIO_AUTH_TOKEN=...
|
|
29
|
+
TWILIO_PHONE_NUMBER=...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Do not commit `.env` files containing secrets.
|
|
33
|
+
|
|
34
|
+
## Production server
|
|
35
|
+
|
|
36
|
+
Prefer the secure wrapper:
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import { Api, runSecureServer } from "flet-box-server";
|
|
40
|
+
|
|
41
|
+
const routes = Api({
|
|
42
|
+
"/health": {
|
|
43
|
+
GET: () => ({ status: "ok" }),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
runSecureServer(routes, {
|
|
48
|
+
port: Number(process.env.PORT || 3000),
|
|
49
|
+
docs: false,
|
|
50
|
+
rateLimit: { max: 100, windowMs: 60_000 },
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Set `docs: true` only when the generated documentation is safe to expose.
|
|
55
|
+
|
|
56
|
+
## Health check
|
|
57
|
+
|
|
58
|
+
Add a simple route for monitoring:
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
"/health": {
|
|
62
|
+
GET: () => ({
|
|
63
|
+
status: "ok",
|
|
64
|
+
timestamp: new Date().toISOString(),
|
|
65
|
+
}),
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Docker
|
|
70
|
+
|
|
71
|
+
A minimal Dockerfile:
|
|
72
|
+
|
|
73
|
+
```dockerfile
|
|
74
|
+
FROM node:20-alpine
|
|
75
|
+
WORKDIR /app
|
|
76
|
+
COPY package*.json ./
|
|
77
|
+
RUN npm ci --omit=dev
|
|
78
|
+
COPY . .
|
|
79
|
+
EXPOSE 3000
|
|
80
|
+
CMD ["node", "server.js"]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Build and run:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
docker build -t flet-box-server .
|
|
87
|
+
docker run --env-file .env -p 3000:3000 flet-box-server
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## HTTPS and proxies
|
|
91
|
+
|
|
92
|
+
Put HTTPS at a trusted reverse proxy such as Nginx, Caddy, or a cloud load balancer. Forward only the headers your proxy controls and configure CORS explicitly.
|
|
93
|
+
|
|
94
|
+
## Scaling
|
|
95
|
+
|
|
96
|
+
The default cache, rate limiter, verification store, and security incident list use memory. For multiple server processes, move shared state to Redis or another shared service.
|
|
97
|
+
|
|
98
|
+
Queues also require Redis to coordinate jobs between workers.
|
|
99
|
+
|
|
100
|
+
## Production checklist
|
|
101
|
+
|
|
102
|
+
- Use Node.js 18 or newer.
|
|
103
|
+
- Run `npm ci --omit=dev` in deployment.
|
|
104
|
+
- Set a strong `JWT_SECRET`.
|
|
105
|
+
- Remove hardcoded admin credentials.
|
|
106
|
+
- Disable or protect `/docs` when it exposes internal routes.
|
|
107
|
+
- Set explicit CORS origins.
|
|
108
|
+
- Use HTTPS.
|
|
109
|
+
- Enable validation and rate limiting.
|
|
110
|
+
- Configure Redis for shared cache, sessions, queues, and verification codes.
|
|
111
|
+
- Configure persistent storage and database backups.
|
|
112
|
+
- Monitor `/health`, errors, latency, and resource usage.
|
|
113
|
+
- Do not log passwords, tokens, or private personal data.
|