zyket 1.2.18 → 1.2.20
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/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +407 -0
- package/README.md +175 -279
- package/SECURITY.md +40 -0
- package/bin/cli.js +310 -202
- package/index.js +4 -3
- package/package.json +7 -1
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/skills/build/SKILL.md +88 -0
- package/src/extensions/bullboard/index.js +14 -3
- package/src/extensions/interactive-storage/index.js +25 -9
- package/src/extensions/interactive-storage/routes/delete-folder.js +13 -1
- package/src/extensions/interactive-storage/routes/delete.js +14 -3
- package/src/extensions/interactive-storage/routes/download.js +22 -3
- package/src/extensions/interactive-storage/routes/info.js +13 -0
- package/src/services/auth/index.js +46 -28
- package/src/services/express/Express.js +32 -15
- package/src/services/express/RequireAdminMiddleware.js +14 -0
- package/src/services/express/RequireAuthMiddleware.js +46 -0
- package/src/services/express/index.js +7 -5
- package/src/services/socketio/AuthGuard.js +33 -0
- package/src/services/socketio/SocketIO.js +3 -1
- package/src/services/socketio/index.js +2 -1
- package/src/services/template-manager/index.js +1 -0
- package/src/templates/api-rest/.env.example +24 -0
- package/src/templates/api-rest/README.md +50 -0
- package/src/templates/api-rest/index.js +18 -0
- package/src/templates/api-rest/src/models/Task.js +18 -0
- package/src/templates/api-rest/src/routes/tasks/[id].js +42 -0
- package/src/templates/api-rest/src/routes/tasks/index.js +26 -0
- package/src/templates/api-rest/src/services/auth/auth.js +9 -0
- package/src/templates/api-rest/src/services/auth/index.js +23 -0
- package/src/templates/realtime-chat/.env.example +26 -0
- package/src/templates/realtime-chat/README.md +38 -0
- package/src/templates/realtime-chat/frontend/.env.example +3 -0
- package/src/templates/realtime-chat/frontend/index.html +12 -0
- package/src/templates/realtime-chat/frontend/main.jsx +18 -0
- package/src/templates/realtime-chat/frontend/src/hooks/useAuth.jsx +27 -0
- package/src/templates/realtime-chat/frontend/src/hooks/useChatSocket.jsx +29 -0
- package/src/templates/realtime-chat/frontend/src/middlewares/LoggedMiddleware.jsx +12 -0
- package/src/templates/realtime-chat/frontend/src/middlewares/NotLoggedMiddleware.jsx +12 -0
- package/src/templates/realtime-chat/frontend/src/store/storeAuth.jsx +11 -0
- package/src/templates/realtime-chat/frontend/src/views/AuthView.jsx +70 -0
- package/src/templates/realtime-chat/frontend/src/views/ChatView.jsx +69 -0
- package/src/templates/realtime-chat/frontend/styles.css +1 -0
- package/src/templates/realtime-chat/frontend/vite.config.js +7 -0
- package/src/templates/realtime-chat/index.js +14 -0
- package/src/templates/realtime-chat/src/guards/auth.js +3 -0
- package/src/templates/realtime-chat/src/handlers/connection.js +23 -0
- package/src/templates/realtime-chat/src/handlers/message.js +29 -0
- package/src/templates/realtime-chat/src/services/auth/auth.js +8 -0
- package/src/templates/realtime-chat/src/services/auth/index.js +19 -0
- package/src/templates/saas-multitenant/.env.example +22 -0
- package/src/templates/saas-multitenant/README.md +71 -0
- package/src/templates/saas-multitenant/frontend/.env.example +3 -0
- package/src/templates/saas-multitenant/frontend/index.html +12 -0
- package/src/templates/saas-multitenant/frontend/main.jsx +18 -0
- package/src/templates/saas-multitenant/frontend/src/hooks/useAuth.jsx +27 -0
- package/src/templates/saas-multitenant/frontend/src/hooks/useProjects.jsx +41 -0
- package/src/templates/saas-multitenant/frontend/src/middlewares/LoggedMiddleware.jsx +12 -0
- package/src/templates/saas-multitenant/frontend/src/middlewares/NotLoggedMiddleware.jsx +12 -0
- package/src/templates/saas-multitenant/frontend/src/store/storeAuth.jsx +13 -0
- package/src/templates/saas-multitenant/frontend/src/views/AuthView.jsx +70 -0
- package/src/templates/saas-multitenant/frontend/src/views/DashboardView.jsx +131 -0
- package/src/templates/saas-multitenant/frontend/styles.css +1 -0
- package/src/templates/saas-multitenant/frontend/vite.config.js +7 -0
- package/src/templates/saas-multitenant/index.js +14 -0
- package/src/templates/saas-multitenant/src/middlewares/RequireOrganization.js +22 -0
- package/src/templates/saas-multitenant/src/models/Project.js +17 -0
- package/src/templates/saas-multitenant/src/routes/admin/stats.js +15 -0
- package/src/templates/saas-multitenant/src/routes/projects/index.js +34 -0
- package/src/templates/saas-multitenant/src/services/auth/auth.js +8 -0
- package/src/templates/saas-multitenant/src/services/auth/index.js +43 -0
- package/src/utils/EnvManager.js +23 -0
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "zyket",
|
|
12
12
|
"source": "./plugin",
|
|
13
|
-
"description": "
|
|
14
|
-
"version": "1.
|
|
13
|
+
"description": "Build and scaffold Zyket apps — initialize from templates, wire services/auth/security (build), and generate components like routes, handlers, guards, services, and workers (generate).",
|
|
14
|
+
"version": "1.1.0"
|
|
15
15
|
}
|
|
16
16
|
]
|
|
17
17
|
}
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
# AGENTS.md — Zyket Framework Reference
|
|
2
|
+
|
|
3
|
+
> A complete, accurate reference for AI agents building applications with **Zyket**.
|
|
4
|
+
> Zyket is a service-oriented Node.js framework (Express 5 + Socket.IO + better-auth + Sequelize/BullMQ/MinIO) inspired by Symfony's architecture. Components are auto-discovered from the filesystem; services are wired through a dependency-injection container.
|
|
5
|
+
|
|
6
|
+
This document is the source of truth. When an installed copy exists, read `node_modules/zyket/AGENTS.md`. Always confirm a base class API against the installed source before relying on it.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Quick start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
mkdir my-app && cd my-app
|
|
14
|
+
npm install zyket
|
|
15
|
+
npx zyket init # interactive: pick a template
|
|
16
|
+
# or non-interactive:
|
|
17
|
+
npx zyket init api-rest # default | api-rest | saas-multitenant | realtime-chat
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`init` scaffolds the chosen template's files, generates a `.env` (defaults merged with the template's `.env.example`), writes a `package.json` with the right dependencies, and runs `npm install`. Templates that ship auth need their tables created once:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx @better-auth/cli migrate
|
|
24
|
+
node index.js
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Minimal manual boot:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
// index.js
|
|
31
|
+
const { Kernel } = require("zyket");
|
|
32
|
+
|
|
33
|
+
const kernel = new Kernel({
|
|
34
|
+
services: [
|
|
35
|
+
["auth", require("./src/services/auth"), ["@service_container"]],
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
kernel.boot().then(() => console.log("Booted")).catch(console.error);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2. The Kernel
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
new Kernel({ services: [], extensions: [] })
|
|
48
|
+
kernel.boot(clearConsole = true, secretsPath = "<cwd>/.env")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Boot order:
|
|
52
|
+
1. Load `.env` (creates one with defaults if missing).
|
|
53
|
+
2. Start the HTTP server on `PORT` (default 3000).
|
|
54
|
+
3. Register **core services** (auto-selected from env, see §4) then your `services`.
|
|
55
|
+
4. Call `boot({ httpServer })` on every service in order.
|
|
56
|
+
5. Load each `extensions` instance via `extension.load(container)` (after services).
|
|
57
|
+
|
|
58
|
+
`kernel.container` exposes the DI container (`container.get('name')`, `container.has('name')`).
|
|
59
|
+
|
|
60
|
+
**Service registration tuple:** `[name, ClassOrFactory, args]`. Special arg placeholders:
|
|
61
|
+
|
|
62
|
+
| Placeholder | Injected value |
|
|
63
|
+
|-------------|----------------|
|
|
64
|
+
| `"@service_container"` | the DI container |
|
|
65
|
+
| `"@onConnection"` | the kernel's socket-connection callback |
|
|
66
|
+
|
|
67
|
+
Auth is **not** a core service — register it yourself (templates do).
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 3. Environment variables
|
|
72
|
+
|
|
73
|
+
The generated `.env` (`src/utils/EnvManager.js`) plus auth/vite variables:
|
|
74
|
+
|
|
75
|
+
| Variable | Default | Purpose |
|
|
76
|
+
|----------|---------|---------|
|
|
77
|
+
| `DEBUG` | `true` | Enable debug-level logs |
|
|
78
|
+
| `PORT` | `3000` | HTTP/WS port |
|
|
79
|
+
| `NODE_ENV` | — | `production` enables secure cookies, Vite preview build |
|
|
80
|
+
| `HTTP_JSON_LIMIT` | `10mb` | Express JSON body limit |
|
|
81
|
+
| `SOCKET_MAX_HTTP_BUFFER_SIZE` | `10485760` | Socket.IO max payload bytes |
|
|
82
|
+
| `DISABLE_EXPRESS` | `false` | Turn off the HTTP/REST service |
|
|
83
|
+
| `DISABLE_SOCKET` | `true` | Turn off Socket.IO |
|
|
84
|
+
| `DISABLE_VITE` | `true` | Turn off the Vite frontend |
|
|
85
|
+
| `DISABLE_EVENTS` | `true` | Turn off the events service |
|
|
86
|
+
| `DISABLE_BULLMQ` | `true` | Turn off BullMQ queues/workers |
|
|
87
|
+
| `DISABLE_SCHEDULER` | `true` | Turn off the cron scheduler |
|
|
88
|
+
| `DISABLE_LOGGER` | `false` | Silence the logger |
|
|
89
|
+
| `DISABLE_SWAGGER` | — | `true` removes the `/docs` UI |
|
|
90
|
+
| `SWAGGER_PASSWORD` / `SWAGGER_USER` | — / `admin` | HTTP Basic auth for `/docs` |
|
|
91
|
+
| `SWAGGER_PATH` | `/docs` | Swagger UI mount path |
|
|
92
|
+
| `DATABASE_URL` | `./database.sqlite` | DB connection / sqlite path. Activates the `database` service when set |
|
|
93
|
+
| `DATABASE_DIALECT` | `sqlite` | `sqlite`, `postgresql`, `mariadb`… (auth supports only `sqlite`/`postgresql`) |
|
|
94
|
+
| `CACHE_URL` | `` | `redis://…` for Redis, empty/`memory` for in-memory |
|
|
95
|
+
| `REDIS_URL` | — | Enables the Socket.IO Redis adapter (multi-instance) |
|
|
96
|
+
| `S3_ENDPOINT` `S3_PORT` `S3_USE_SSL` `S3_ACCESS_KEY` `S3_SECRET_KEY` | — | MinIO/S3. The `s3` service activates when endpoint+keys are set |
|
|
97
|
+
| `S3_PUBLIC_BUCKETS` / `S3_PRIVATE_BUCKETS` | — | Comma lists; public buckets get a read-all policy |
|
|
98
|
+
| `LOG_DIRECTORY` | `./logs` | Log file directory |
|
|
99
|
+
| `QUEUES` | `` | Comma list of BullMQ queue names to create |
|
|
100
|
+
| `VITE_ROOT` | `./frontend` | Frontend project root |
|
|
101
|
+
| `VITE_PORT` | `5173` | Vite dev/preview port |
|
|
102
|
+
| `VITE_API_BASE` | `http://localhost:3000` | Frontend → backend base URL |
|
|
103
|
+
| **Auth** (added by the auth service) | | |
|
|
104
|
+
| `AUTH_SECRET` | random 32-byte hex | Session signing secret. **Boot fails if missing/insecure** |
|
|
105
|
+
| `BETTER_AUTH_URL` | `http://localhost:3000` | Public base URL of the auth server |
|
|
106
|
+
| `TRUSTED_ORIGINS` | `http://localhost:5173,http://localhost:3000` | Comma list of allowed origins (CORS + CSRF) |
|
|
107
|
+
| `AUTH_CROSS_DOMAIN` | `false` | `true` → cross-site cookies (`sameSite=none; secure`, needs HTTPS) |
|
|
108
|
+
| `BULLBOARD_ADMIN_PASSWORD` | — | Required to expose the BullBoard dashboard extension |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 4. Services
|
|
113
|
+
|
|
114
|
+
Core services are auto-registered based on env (`src/services/index.js`). Access any with `container.get('<name>')`.
|
|
115
|
+
|
|
116
|
+
| Name | Activated when | Key API |
|
|
117
|
+
|------|----------------|---------|
|
|
118
|
+
| `logger` | always (silent if `DISABLE_LOGGER=true`) | `info/warn/error/debug(msg)` |
|
|
119
|
+
| `template-manager` | always | internal (templates) |
|
|
120
|
+
| `events` | `DISABLE_EVENTS !== 'true'` | `emit(name, payload)`, `emitAsync(name, payload, timeout)` |
|
|
121
|
+
| `database` | `DATABASE_URL` set | `models`, `sequelize`, `Op`, `Sequelize`, `sync()`, `runMigrations(path)`, `loadModel(fn)` |
|
|
122
|
+
| `cache` | always | `get/set/del/keys/expire`, `client` (redis or null) |
|
|
123
|
+
| `s3` | `S3_ENDPOINT` + access + secret | `saveFile/getFile/removeFile/ensureBucket/listBuckets`, `client` (MinIO) |
|
|
124
|
+
| `scheduler` | `DISABLE_SCHEDULER !== 'true'` | discovers `src/schedulers/*` |
|
|
125
|
+
| `bullmq` | `DISABLE_BULLMQ !== 'true'` | `queues`, `addJob(queue, name, data, opts, waitForCompletion)` |
|
|
126
|
+
| `socketio` | `DISABLE_SOCKET !== 'true'` | `io`, `sockets` |
|
|
127
|
+
| `vite` | `VITE_ROOT` set + `DISABLE_VITE !== 'true'` | serves `frontend/` |
|
|
128
|
+
| `express` | `DISABLE_EXPRESS !== 'true'` | `app()`, `registerRoutes(routes)`, `regiterRawAllRoutes(path, handler)` |
|
|
129
|
+
| `auth` | **manual registration** | `client` (better-auth), `getSession(headers)` |
|
|
130
|
+
|
|
131
|
+
> Note: `bullmq`, `scheduler`, `events` default to ON when their `DISABLE_*` var is absent, but the generated `.env` ships them as `true` (off). Set the relevant `DISABLE_*=false` to enable.
|
|
132
|
+
|
|
133
|
+
### Custom service
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
const { Service } = require("zyket");
|
|
137
|
+
|
|
138
|
+
module.exports = class MyService extends Service {
|
|
139
|
+
#container;
|
|
140
|
+
constructor(container) { super("my-service"); this.#container = container; }
|
|
141
|
+
async boot({ httpServer } = {}) { /* init */ }
|
|
142
|
+
doThing() { /* ... */ }
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Register: `services: [["my-service", MyService, ["@service_container"]]]`.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 5. Auto-discovered components
|
|
151
|
+
|
|
152
|
+
These are discovered from folders under `src/` — the **filename is the identity**. Use kebab-case, `module.exports = class extends Base`, and do not add constructor params (the loader passes only the derived name).
|
|
153
|
+
|
|
154
|
+
| Component | Folder | Base | Identity from filename |
|
|
155
|
+
|-----------|--------|------|------------------------|
|
|
156
|
+
| Route | `src/routes/` | `Route` | URL path |
|
|
157
|
+
| Middleware | `src/middlewares/` | `Middleware` | referenced manually |
|
|
158
|
+
| Handler | `src/handlers/` | `Handler` | socket event name |
|
|
159
|
+
| Guard | `src/guards/` | `Guard` | guard name |
|
|
160
|
+
| Event | `src/events/` | `Event` | event name |
|
|
161
|
+
| Scheduler | `src/schedulers/` | `Schedule` | label |
|
|
162
|
+
| Worker | `src/workers/` | `Worker` | label |
|
|
163
|
+
| Model | `src/models/` | factory fn | model name |
|
|
164
|
+
|
|
165
|
+
### Routes (`src/routes/**.js`)
|
|
166
|
+
|
|
167
|
+
File path → URL: `index.js`→`/`, `users.js`→`/users`, `users/[id].js`→`/users/:id`. Methods: **`get`, `post`, `put`, `delete`** (PATCH is not dispatched).
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
const { Route, RequireAuthMiddleware } = require("zyket");
|
|
171
|
+
|
|
172
|
+
module.exports = class extends Route {
|
|
173
|
+
middlewares = { post: [new RequireAuthMiddleware()] }; // per-method
|
|
174
|
+
|
|
175
|
+
async get({ container, request, response }) {
|
|
176
|
+
return { items: [] }; // → { success: true, items: [] }
|
|
177
|
+
}
|
|
178
|
+
async post({ container, request }) {
|
|
179
|
+
if (!request.body.name) return { success: false, message: "name required", status: 400 };
|
|
180
|
+
return { created: true, status: 201 };
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Return conventions: object → `{ success: true, ...obj }` JSON; `status` key sets HTTP code; `Buffer` → file download; `new RedirectResponse(url)` → redirect. Thrown errors → `500 { success:false, message:"Internal Server Error" }` (details only in logs).
|
|
186
|
+
|
|
187
|
+
### Middlewares (`src/middlewares/*.js`)
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
const { Middleware } = require("zyket");
|
|
191
|
+
module.exports = class extends Middleware {
|
|
192
|
+
async handle({ container, request, response, next }) {
|
|
193
|
+
// respond to block, or call next() to continue
|
|
194
|
+
next();
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Socket.IO handlers (`src/handlers/*.js`) & guards (`src/guards/*.js`)
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
// src/handlers/message.js → listens to the "message" event
|
|
203
|
+
const { Handler } = require("zyket");
|
|
204
|
+
module.exports = class extends Handler {
|
|
205
|
+
guards = ["auth"]; // run before handle; throw to block
|
|
206
|
+
async handle({ container, socket, data, io }) {
|
|
207
|
+
io.to("room").emit("message", data);
|
|
208
|
+
return { ok: true }; // returned to the client ack callback
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
// src/guards/auth.js
|
|
215
|
+
const { Guard } = require("zyket");
|
|
216
|
+
module.exports = class extends Guard {
|
|
217
|
+
async handle({ container, socket, io }) {
|
|
218
|
+
if (!ok) throw new Error("Unauthorized"); // blocks the event
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
> `connection.js` is special (runs on connect). **Connection-level guards are not awaited and do not reliably block** — enforce connect-time auth inside `connection.js` itself (throwing there disconnects the socket).
|
|
224
|
+
|
|
225
|
+
### Events (`src/events/*.js`)
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
const { Event } = require("zyket");
|
|
229
|
+
module.exports = class extends Event {
|
|
230
|
+
async handle({ container, payload }) { /* ... */ }
|
|
231
|
+
};
|
|
232
|
+
// elsewhere: container.get('events').emit('user-registered', { id })
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Scheduler (`src/schedulers/*.js`)
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
const { Schedule } = require("zyket");
|
|
239
|
+
module.exports = class extends Schedule {
|
|
240
|
+
time = "*/5 * * * *"; // node-cron expression
|
|
241
|
+
async handle({ container }) { /* ... */ }
|
|
242
|
+
};
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Worker (`src/workers/*.js`) — BullMQ
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
const { Worker } = require("zyket");
|
|
249
|
+
module.exports = class extends Worker {
|
|
250
|
+
queueName = "emails"; // must match a name in QUEUES
|
|
251
|
+
instances = 1; // number | array | async fn
|
|
252
|
+
options = { concurrency: 5 }; // BullMQ worker options | fn
|
|
253
|
+
async handle({ container, job, instance, index }) { /* process job.data */ }
|
|
254
|
+
};
|
|
255
|
+
// enqueue: container.get('bullmq').addJob('emails', 'welcome', { to })
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Model (`src/models/*.js`) — Sequelize factory
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
module.exports = ({ sequelize, Sequelize, container }) => {
|
|
262
|
+
const User = sequelize.define("User", {
|
|
263
|
+
email: { type: Sequelize.STRING, allowNull: false },
|
|
264
|
+
});
|
|
265
|
+
User.associate = (models) => { /* User.hasMany(models.Post) */ };
|
|
266
|
+
return User;
|
|
267
|
+
};
|
|
268
|
+
// access: container.get('database').models.User ; create tables: .sync()
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 6. Authentication (better-auth)
|
|
274
|
+
|
|
275
|
+
The `auth` service mounts better-auth at `/api/auth/*` and provides sessions, the **admin** plugin, the **bearer** plugin, and (optionally) the **organization** plugin. Customize by subclassing `AuthService`:
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
const { AuthService } = require("zyket");
|
|
279
|
+
|
|
280
|
+
module.exports = class CustomAuthService extends AuthService {
|
|
281
|
+
get organizationEnabled() { return true; } // multi-tenant
|
|
282
|
+
get requireEmailVerification() { return false; }
|
|
283
|
+
get plugins() { return []; } // extra better-auth plugins
|
|
284
|
+
get socialProviders() { return {}; }
|
|
285
|
+
get userAdditionalFields() { return {}; }
|
|
286
|
+
get organizationAdditionalFields() { return {}; }
|
|
287
|
+
async allowUserToCreateOrganization(user) { return true; }
|
|
288
|
+
async sendVerificationEmail({ user, url, token }) { /* email */ }
|
|
289
|
+
async sendResetPasswordEmail({ user, url, token }) { /* email */ }
|
|
290
|
+
async sendInvitationEmail(data) { /* org invite */ }
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Requirements: `DATABASE_DIALECT` must be `sqlite` or `postgresql`. Run `npx @better-auth/cli migrate` to create auth tables (it reads `src/services/auth/auth.js`).
|
|
295
|
+
|
|
296
|
+
### Protecting routes & sockets
|
|
297
|
+
|
|
298
|
+
| Helper | Use |
|
|
299
|
+
|--------|-----|
|
|
300
|
+
| `RequireAuthMiddleware` | route middleware; requires a session. `new RequireAuthMiddleware(['admin'])` to gate by role. Sets `request.user` / `request.session` |
|
|
301
|
+
| `RequireAdminMiddleware` | route middleware; requires `role === 'admin'` |
|
|
302
|
+
| `AuthGuard` | socket guard; rejects events without a session. Use `module.exports = require('zyket').AuthGuard` in `src/guards/auth.js` |
|
|
303
|
+
| `container.get('auth').getSession(headers)` | resolve a session from raw headers (e.g. `socket.handshake.headers`) without importing better-auth |
|
|
304
|
+
|
|
305
|
+
### Cookies (environment-aware)
|
|
306
|
+
|
|
307
|
+
Default: `sameSite=lax`, `secure` only in production → works over `http://localhost` when frontend (`:5173`) and API (`:3000`) are same-site. For front/back on **different domains**, set `AUTH_CROSS_DOMAIN=true` (HTTPS required) → `sameSite=none; secure` + cross-subdomain cookies.
|
|
308
|
+
|
|
309
|
+
### Frontend auth client
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
import { createAuthClient } from "better-auth/react";
|
|
313
|
+
import { organizationClient, adminClient } from "better-auth/client/plugins";
|
|
314
|
+
|
|
315
|
+
export const client = createAuthClient({
|
|
316
|
+
baseURL: `${import.meta.env.VITE_API_BASE || "http://localhost:3000"}/api/auth/`,
|
|
317
|
+
plugins: [organizationClient(), adminClient()],
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## 7. Extensions
|
|
324
|
+
|
|
325
|
+
Post-boot plugins with full container access. Register instances in the Kernel.
|
|
326
|
+
|
|
327
|
+
```js
|
|
328
|
+
const { Kernel, BullBoardExtension, InteractiveStorageExtension } = require("zyket");
|
|
329
|
+
|
|
330
|
+
new Kernel({
|
|
331
|
+
extensions: [
|
|
332
|
+
new BullBoardExtension({ path: "/bullboard", middlewares: [] }), // needs BULLBOARD_ADMIN_PASSWORD or middlewares, else it won't mount
|
|
333
|
+
new InteractiveStorageExtension({ path: "/storage", bucketName: "dropbox", middlewares: [/* auth */], maxDeleteBatch: 100 }),
|
|
334
|
+
],
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Custom extension:
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
const { Extension } = require("zyket");
|
|
342
|
+
module.exports = class extends Extension {
|
|
343
|
+
constructor(options = {}) { super("MyExtension"); this.options = options; }
|
|
344
|
+
async load(container) {
|
|
345
|
+
if (!container.get("express")) return container.get("logger").warn("no express");
|
|
346
|
+
container.get("express").registerRoutes([/* Route instances */]);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 8. Frontend (Vite + React)
|
|
354
|
+
|
|
355
|
+
When `VITE_ROOT` is set and `DISABLE_VITE=false`, Zyket runs Vite (dev server, or a built preview server in production) for the `frontend/` app. Stack: React 19 + react-router-dom + zustand + Tailwind v4 + better-auth client. The frontend talks to the backend at `VITE_API_BASE`. A project with a frontend must declare its own frontend dependencies (the CLI does this automatically per template).
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 9. Templates
|
|
360
|
+
|
|
361
|
+
| Template | Stack | Use for |
|
|
362
|
+
|----------|-------|---------|
|
|
363
|
+
| `default` | backend + (optional) React frontend | general starter |
|
|
364
|
+
| `api-rest` | backend only, auth + CRUD (`Task`) | clean REST APIs |
|
|
365
|
+
| `saas-multitenant` | orgs/roles + React dashboard, tenant-scoped `Project` | multi-tenant SaaS |
|
|
366
|
+
| `realtime-chat` | Socket.IO + React chat UI, session auth | real-time apps |
|
|
367
|
+
|
|
368
|
+
Each template is self-contained (`index.js`, `.env.example`, `README.md`, and frontend where relevant). `npx zyket init <template>` scaffolds it.
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 10. CLI
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
npx zyket # interactive menu
|
|
376
|
+
npx zyket init # choose a template, scaffold + install + (default) run
|
|
377
|
+
npx zyket init <template> # scaffold a specific template
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
`init` keeps existing files (non-destructive). For non-default templates it prints next steps (auth migrate + run) instead of auto-starting.
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## 11. Security checklist
|
|
385
|
+
|
|
386
|
+
See `SECURITY.md` for the full audit. Essentials before production:
|
|
387
|
+
|
|
388
|
+
- Keep `.env` out of git; never reuse `AUTH_SECRET` (it's generated per project; boot fails on a known/empty value).
|
|
389
|
+
- Protect every non-public route/socket with `RequireAuthMiddleware` / `AuthGuard`.
|
|
390
|
+
- Set `BULLBOARD_ADMIN_PASSWORD` (or pass `middlewares`) — the dashboard won't mount without auth.
|
|
391
|
+
- Set `SWAGGER_PASSWORD` (or `DISABLE_SWAGGER=true`) in production.
|
|
392
|
+
- Lock `TRUSTED_ORIGINS`; set `AUTH_CROSS_DOMAIN=true` only for real cross-domain HTTPS deployments.
|
|
393
|
+
- Add rate limiting and `helmet` (not bundled).
|
|
394
|
+
- For the storage extension, always pass auth `middlewares`; tune `maxFileSize` / `maxDeleteBatch`.
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## 12. Build-a-feature recipe
|
|
399
|
+
|
|
400
|
+
1. **Model** — `src/models/Thing.js` (Sequelize factory). Create tables with `container.get('database').sync()` in `index.js` after boot, or migrations.
|
|
401
|
+
2. **Routes** — `src/routes/things/index.js` (GET list, POST create) and `src/routes/things/[id].js` (GET/PUT/DELETE). Gate with `new RequireAuthMiddleware()`.
|
|
402
|
+
3. **Access data** — `const { Thing } = container.get('database').models;`.
|
|
403
|
+
4. **Realtime (optional)** — `src/handlers/<event>.js` with `guards = ["auth"]`; broadcast with `io`.
|
|
404
|
+
5. **Background (optional)** — add the queue to `QUEUES`, `DISABLE_BULLMQ=false`, add `src/workers/<name>.js`, enqueue with `container.get('bullmq').addJob(...)`.
|
|
405
|
+
6. **Run** — `npx @better-auth/cli migrate` (first time) then `node index.js`.
|
|
406
|
+
|
|
407
|
+
To scaffold any component quickly, use the `generate` skill (`/zyket:generate route things/[id]`).
|