zyket 1.2.20 → 1.2.22
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/AGENTS.md +28 -6
- package/README.md +1 -1
- package/bin/cli.js +4 -1
- package/package.json +22 -22
- package/plugin/skills/build/SKILL.md +10 -4
- package/src/kernel/HTTPServer.js +12 -0
- package/src/services/auth/index.js +73 -6
- package/src/services/express/Express.js +6 -7
- package/src/services/index.js +6 -1
- package/src/templates/api-rest/README.md +15 -2
- package/src/templates/api-rest/index.js +2 -2
- package/src/templates/api-rest/src/services/auth/auth.js +3 -2
- package/src/templates/realtime-chat/README.md +14 -1
- package/src/templates/realtime-chat/src/services/auth/auth.js +3 -1
- package/src/templates/saas-multitenant/README.md +14 -1
- package/src/templates/saas-multitenant/src/services/auth/auth.js +3 -1
- package/src/utils/EnvManager.js +5 -0
package/AGENTS.md
CHANGED
|
@@ -20,7 +20,7 @@ npx zyket init api-rest # default | api-rest | saas-multitenant | realtim
|
|
|
20
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
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npx @better-auth/cli migrate
|
|
23
|
+
npx @better-auth/cli migrate --config src/services/auth/auth.js
|
|
24
24
|
node index.js
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -49,10 +49,10 @@ kernel.boot(clearConsole = true, secretsPath = "<cwd>/.env")
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Boot order:
|
|
52
|
-
1. Load `.env` (creates one with defaults if missing).
|
|
53
|
-
2. Start the HTTP server on `PORT` (default 3000).
|
|
52
|
+
1. Load `.env` (creates one with defaults if missing, including a random `AUTH_SECRET`).
|
|
53
|
+
2. Start the HTTP server on `PORT` (default 3000). Until the Express service attaches, requests are answered `503 {"success":false,"message":"Server is starting"}` with `Retry-After: 1` — so a health check during boot gets an answer instead of hanging. With `DISABLE_EXPRESS=true` every HTTP request keeps getting that 503.
|
|
54
54
|
3. Register **core services** (auto-selected from env, see §4) then your `services`.
|
|
55
|
-
4. Call `boot({ httpServer })` on every service in order.
|
|
55
|
+
4. Call `boot({ httpServer })` on every service in order. `express` boots before `socketio` on purpose: engine.io wraps the existing request listener, so reversing them breaks the socket.io polling transport.
|
|
56
56
|
5. Load each `extensions` instance via `extension.load(container)` (after services).
|
|
57
57
|
|
|
58
58
|
`kernel.container` exposes the DI container (`container.get('name')`, `container.has('name')`).
|
|
@@ -291,7 +291,29 @@ module.exports = class CustomAuthService extends AuthService {
|
|
|
291
291
|
};
|
|
292
292
|
```
|
|
293
293
|
|
|
294
|
-
Requirements: `DATABASE_DIALECT` must be `sqlite` or `postgresql`.
|
|
294
|
+
Requirements: `DATABASE_DIALECT` must be `sqlite` or `postgresql`.
|
|
295
|
+
|
|
296
|
+
### Creating / updating the auth tables
|
|
297
|
+
|
|
298
|
+
`AuthService.migrate()` runs whatever better-auth migrations are still pending — creating missing tables and adding missing columns — using the **installed** better-auth, so it always matches the schema your app expects:
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
kernel.boot().then(async () => {
|
|
302
|
+
const { pending, tablesCreated, fieldsAdded } = await kernel.container.get('auth').migrate();
|
|
303
|
+
// { pending: 5, tablesCreated: ['user','account','organization',…], fieldsAdded: [] }
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
| Call | Effect |
|
|
308
|
+
|------|--------|
|
|
309
|
+
| `await auth.migrate()` | applies pending migrations; returns `{ pending, tablesCreated, fieldsAdded }` |
|
|
310
|
+
| `await auth.migrate({ dryRun: true })` | applies nothing; adds `sql` (the compiled statements) to the return value |
|
|
311
|
+
|
|
312
|
+
Run it after adding a plugin, an `additionalFields` entry, or on a fresh database. It is idempotent — `pending: 0` when there is nothing to do — but it is **not** automatic: nothing migrates on boot unless you call it. `sqlite`/`postgresql` only (it throws otherwise).
|
|
313
|
+
|
|
314
|
+
The external CLI still works — `npx @better-auth/cli migrate --config src/services/auth/auth.js` — and `--config` is required, because the CLI only auto-discovers `auth.js` under `src/{auth,lib,server,utils}/`, never under `src/services/`. Prefer `migrate()`: the CLI bundles its own better-auth copy, so it can disagree with your installed version about the schema (it currently creates a `verification` table that better-auth 1.6 no longer uses).
|
|
315
|
+
|
|
316
|
+
**No `session` / `verification` tables?** That's expected. The `auth` service always wires better-auth's `secondaryStorage` to the `cache` service, and better-auth then keeps sessions and verification tokens there instead of in SQL. With the default in-memory cache that means **sessions do not survive a restart and are not shared between instances** — set `CACHE_URL=redis://…` for durable, shared sessions, or opt back into SQL with `session: { storeSessionInDatabase: true }` / `verification: { storeInDatabase: true }` in a subclass.
|
|
295
317
|
|
|
296
318
|
### Protecting routes & sockets
|
|
297
319
|
|
|
@@ -402,6 +424,6 @@ See `SECURITY.md` for the full audit. Essentials before production:
|
|
|
402
424
|
3. **Access data** — `const { Thing } = container.get('database').models;`.
|
|
403
425
|
4. **Realtime (optional)** — `src/handlers/<event>.js` with `guards = ["auth"]`; broadcast with `io`.
|
|
404
426
|
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`.
|
|
427
|
+
6. **Run** — `npx @better-auth/cli migrate --config src/services/auth/auth.js` (first time) then `node index.js`.
|
|
406
428
|
|
|
407
429
|
To scaffold any component quickly, use the `generate` skill (`/zyket:generate route things/[id]`).
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ npx zyket init api-rest # default | api-rest | saas-multitenant | realtime-
|
|
|
19
19
|
`init` scaffolds the template, generates a `.env`, writes `package.json` with the right dependencies, and installs them. Templates that ship authentication need their tables created once:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
npx @better-auth/cli migrate
|
|
22
|
+
npx @better-auth/cli migrate --config src/services/auth/auth.js
|
|
23
23
|
node index.js
|
|
24
24
|
```
|
|
25
25
|
|
package/bin/cli.js
CHANGED
|
@@ -285,7 +285,10 @@ function npmInstall() {
|
|
|
285
285
|
// Templates that ship auth need their tables created first.
|
|
286
286
|
const usesAuth = fs.existsSync(path.join(process.cwd(), 'src', 'services', 'auth'));
|
|
287
287
|
console.log('\nNext steps:');
|
|
288
|
-
if (usesAuth)
|
|
288
|
+
if (usesAuth) {
|
|
289
|
+
console.log(' npx @better-auth/cli migrate --config src/services/auth/auth.js # create the auth tables');
|
|
290
|
+
console.log(" # or, from inside the app: await container.get('auth').migrate()");
|
|
291
|
+
}
|
|
289
292
|
console.log(' node index.js\n');
|
|
290
293
|
if (fs.existsSync(path.join(process.cwd(), 'README.md'))) {
|
|
291
294
|
console.log('See README.md for template-specific details.\n');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zyket",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.22",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -13,45 +13,45 @@
|
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"description": "",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@bull-board/express": "^
|
|
17
|
-
"@tailwindcss/vite": "^4.
|
|
18
|
-
"@vitejs/plugin-react": "^6.0.
|
|
19
|
-
"better-auth": "^1.
|
|
16
|
+
"@bull-board/express": "^8.3.0",
|
|
17
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
18
|
+
"@vitejs/plugin-react": "^6.0.4",
|
|
19
|
+
"better-auth": "^1.6.25",
|
|
20
20
|
"better-sqlite3": "^12.8.0",
|
|
21
|
-
"bullmq": "^5.
|
|
21
|
+
"bullmq": "^5.81.2",
|
|
22
22
|
"colors": "^1.4.0",
|
|
23
23
|
"cors": "^2.8.6",
|
|
24
|
-
"dotenv": "^17.
|
|
24
|
+
"dotenv": "^17.4.2",
|
|
25
25
|
"express": "^5.2.1",
|
|
26
26
|
"express-basic-auth": "^1.2.1",
|
|
27
27
|
"fast-glob": "^3.3.3",
|
|
28
|
-
"mariadb": "^3.5.
|
|
28
|
+
"mariadb": "^3.5.3",
|
|
29
29
|
"minio": "^8.0.7",
|
|
30
|
-
"multer": "^2.
|
|
31
|
-
"node-cron": "^4.
|
|
32
|
-
"node-dependency-injection": "^
|
|
33
|
-
"pg": "^8.
|
|
30
|
+
"multer": "^2.2.0",
|
|
31
|
+
"node-cron": "^4.6.0",
|
|
32
|
+
"node-dependency-injection": "^4.2.2",
|
|
33
|
+
"pg": "^8.22.0",
|
|
34
34
|
"prompts": "^2.4.2",
|
|
35
35
|
"prop-types": "^15.8.1",
|
|
36
|
-
"react": "^19.2.
|
|
37
|
-
"react-dom": "^19.2.
|
|
38
|
-
"react-router-dom": "^7.
|
|
39
|
-
"redis": "^
|
|
36
|
+
"react": "^19.2.8",
|
|
37
|
+
"react-dom": "^19.2.8",
|
|
38
|
+
"react-router-dom": "^7.18.1",
|
|
39
|
+
"redis": "^6.1.0",
|
|
40
40
|
"sequelize": "^6.37.8",
|
|
41
41
|
"socket.io": "^4.8.3",
|
|
42
42
|
"socket.io-client": "^4.8.3",
|
|
43
43
|
"sqlite3": "^6.0.1",
|
|
44
|
-
"swagger-jsdoc": "^6.
|
|
44
|
+
"swagger-jsdoc": "^6.3.0",
|
|
45
45
|
"swagger-ui-express": "^5.0.1",
|
|
46
|
-
"tailwindcss": "^4.
|
|
46
|
+
"tailwindcss": "^4.3.3",
|
|
47
47
|
"typescript": "^5.9.3",
|
|
48
|
-
"umzug": "^3.8.
|
|
49
|
-
"vite": "^8.
|
|
50
|
-
"zustand": "^5.0.
|
|
48
|
+
"umzug": "^3.8.3",
|
|
49
|
+
"vite": "^8.1.5",
|
|
50
|
+
"zustand": "^5.0.14"
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
53
53
|
"@socket.io/redis-adapter": "^8.3.0",
|
|
54
|
-
"ioredis": "^5.
|
|
54
|
+
"ioredis": "^5.11.1"
|
|
55
55
|
},
|
|
56
56
|
"overrides": {
|
|
57
57
|
"ajv": "^8.17.1",
|
|
@@ -35,9 +35,13 @@ Use this skill to take a user from zero to a running Zyket application, or to ex
|
|
|
35
35
|
```
|
|
36
36
|
This copies the template, merges its `.env.example` onto sensible defaults, writes `package.json` with the right dependencies, and installs them.
|
|
37
37
|
|
|
38
|
-
3. **If the template uses auth** (`src/services/auth` exists), create the tables
|
|
38
|
+
3. **If the template uses auth** (`src/services/auth` exists), create the tables. From inside the app (preferred — uses the installed better-auth):
|
|
39
|
+
```js
|
|
40
|
+
kernel.boot().then(() => kernel.container.get('auth').migrate());
|
|
41
|
+
```
|
|
42
|
+
Or with the CLI, which needs `--config` because it never scans `src/services/`:
|
|
39
43
|
```bash
|
|
40
|
-
npx @better-auth/cli migrate
|
|
44
|
+
npx @better-auth/cli migrate --config src/services/auth/auth.js
|
|
41
45
|
```
|
|
42
46
|
|
|
43
47
|
4. **Run:** `node index.js`. The API is on `PORT` (3000); a frontend, if any, on `VITE_PORT` (5173).
|
|
@@ -61,7 +65,7 @@ Flip the env flag, then add the component. Confirm details in `AGENTS.md §3–4
|
|
|
61
65
|
|
|
62
66
|
1. Register the auth service in `index.js`: `["auth", require("./src/services/auth"), ["@service_container"]]`.
|
|
63
67
|
2. Subclass `AuthService` (see `AGENTS.md §6`) to set `organizationEnabled`, `requireEmailVerification`, email senders, etc.
|
|
64
|
-
3. Require `DATABASE_DIALECT` = `sqlite` or `postgresql`;
|
|
68
|
+
3. Require `DATABASE_DIALECT` = `sqlite` or `postgresql`; create/update the tables with `await container.get('auth').migrate()` (see `AGENTS.md §6`) or the CLI with `--config src/services/auth/auth.js`. Re-run it after adding a plugin or an additional field.
|
|
65
69
|
4. Protect routes with `new RequireAuthMiddleware()` (or `['admin']` / `RequireAdminMiddleware`) and sockets with the `AuthGuard` (`module.exports = require('zyket').AuthGuard` in `src/guards/auth.js`; enforce connect-time auth inside `src/handlers/connection.js` via `container.get('auth').getSession(socket.handshake.headers)`).
|
|
66
70
|
|
|
67
71
|
### D. Building a feature (recipe)
|
|
@@ -79,7 +83,9 @@ Delegate individual component scaffolding to the **`generate`** skill (e.g. `/zy
|
|
|
79
83
|
- **PATCH routes don't dispatch** — only `get/post/put/delete` methods are wired.
|
|
80
84
|
- **Connection guards don't block** — do connect-time auth inside `connection.js` (throw to disconnect).
|
|
81
85
|
- **`bullmq`/`scheduler`/`events` default ON when the `DISABLE_*` var is absent**, but the generated `.env` ships them as `true`. Set them to `false` explicitly to enable.
|
|
82
|
-
- **Auth fails to boot** if `AUTH_SECRET` is missing or a known placeholder — let the framework generate it.
|
|
86
|
+
- **Auth fails to boot** if `AUTH_SECRET` is missing or a known placeholder — let the framework generate it (it lands in `.env` when the file is created).
|
|
87
|
+
- **`better-auth migrate` says "No configuration file found"** — the CLI only auto-discovers `auth.js` under `src/{auth,lib,server,utils}/`, never under `src/services/`. Always pass `--config src/services/auth/auth.js`, or call `container.get('auth').migrate()` instead.
|
|
88
|
+
- **No `session`/`verification` table after migrating** — expected: `secondaryStorage` is wired to the `cache`, so better-auth keeps them there. With the default in-memory cache, sessions die on restart; set `CACHE_URL=redis://…` in production.
|
|
83
89
|
- **Frontend can't resolve `vite`/`react`/`better-auth`** — the project (not just zyket) must declare those deps; `npx zyket init` does this automatically for templates with a frontend.
|
|
84
90
|
- **Boot crashes with `Cannot read properties of undefined (reading 'BarBarToken')`** — a TypeScript 7 got hoisted into the tree, which `node-dependency-injection`'s bundled `typescript-estree@5` can't read. Zyket pins `typescript` to `^5` and `npx zyket init` writes a matching `overrides` block; on a project predating that, add `"overrides": { "typescript": "^5.9.3" }` to `package.json` and reinstall.
|
|
85
91
|
|
package/src/kernel/HTTPServer.js
CHANGED
|
@@ -19,6 +19,18 @@ module.exports = class HTTPServer {
|
|
|
19
19
|
}
|
|
20
20
|
console.log(`HTTP Server listening on port ${this.#port}`)
|
|
21
21
|
|
|
22
|
+
// The socket accepts connections from this point on, but Express only
|
|
23
|
+
// attaches its request listener once its service boots. Without a
|
|
24
|
+
// placeholder, anything arriving in between (health checks, an eager
|
|
25
|
+
// client) would be accepted and then never answered — the request hangs
|
|
26
|
+
// forever and the socket leaks. Answer 503 until Express takes over; it
|
|
27
|
+
// replaces this listener when it boots. With DISABLE_EXPRESS=true the
|
|
28
|
+
// placeholder stays, which is also the honest answer.
|
|
29
|
+
this.#server.on('request', (request, response) => {
|
|
30
|
+
response.writeHead(503, { 'Content-Type': 'application/json', 'Retry-After': '1' })
|
|
31
|
+
response.end(JSON.stringify({ success: false, message: 'Server is starting' }))
|
|
32
|
+
})
|
|
33
|
+
|
|
22
34
|
this.#server.listen(this.#port, () => {})
|
|
23
35
|
}
|
|
24
36
|
|
|
@@ -8,6 +8,7 @@ const crypto = require("crypto");
|
|
|
8
8
|
|
|
9
9
|
module.exports = class AuthService extends Service {
|
|
10
10
|
#container;
|
|
11
|
+
#authOptions;
|
|
11
12
|
client;
|
|
12
13
|
|
|
13
14
|
constructor(container) {
|
|
@@ -131,7 +132,77 @@ module.exports = class AuthService extends Service {
|
|
|
131
132
|
throw new Error("allowUserToCreateOrganization method not implemented");
|
|
132
133
|
}
|
|
133
134
|
|
|
135
|
+
// Run the better-auth migrations that are still pending: create missing
|
|
136
|
+
// tables and add missing columns. Same work as
|
|
137
|
+
// `npx @better-auth/cli migrate --config src/services/auth/auth.js`, but from
|
|
138
|
+
// inside the app — so adding a plugin (two-factor, jwt, …) or a new
|
|
139
|
+
// additional field only needs `await container.get('auth').migrate()`.
|
|
140
|
+
//
|
|
141
|
+
// Pass `{ dryRun: true }` to get the SQL back without touching the database.
|
|
142
|
+
// Returns { pending, tablesCreated, fieldsAdded, sql } so callers can log,
|
|
143
|
+
// assert, or refuse to boot on a dirty schema.
|
|
144
|
+
async migrate({ dryRun = false } = {}) {
|
|
145
|
+
if (!['postgresql', 'sqlite'].includes(process.env.DATABASE_DIALECT)) {
|
|
146
|
+
throw new Error('AuthService.migrate() only supports PostgreSQL and SQLite as database dialects');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const logger = this.#logger();
|
|
150
|
+
// ESM-only subpath; import() keeps this working regardless of Node's
|
|
151
|
+
// require(esm) support.
|
|
152
|
+
const { getMigrations } = await import('better-auth/db/migration');
|
|
153
|
+
const { toBeCreated, toBeAdded, runMigrations, compileMigrations } = await getMigrations(this.authOptions);
|
|
154
|
+
|
|
155
|
+
const tablesCreated = toBeCreated.map((table) => table.table);
|
|
156
|
+
const fieldsAdded = toBeAdded.flatMap(
|
|
157
|
+
(table) => Object.keys(table.fields).map((field) => `${table.table}.${field}`)
|
|
158
|
+
);
|
|
159
|
+
const pending = tablesCreated.length + fieldsAdded.length;
|
|
160
|
+
|
|
161
|
+
if (pending === 0) {
|
|
162
|
+
logger.info('Auth schema is up to date, nothing to migrate');
|
|
163
|
+
return { pending: 0, tablesCreated: [], fieldsAdded: [], sql: dryRun ? '' : undefined };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const summary = [
|
|
167
|
+
tablesCreated.length ? `tables: ${tablesCreated.join(', ')}` : null,
|
|
168
|
+
fieldsAdded.length ? `fields: ${fieldsAdded.join(', ')}` : null,
|
|
169
|
+
].filter(Boolean).join(' | ');
|
|
170
|
+
|
|
171
|
+
if (dryRun) {
|
|
172
|
+
const sql = await compileMigrations();
|
|
173
|
+
logger.info(`Auth migrations pending (dry run, nothing applied) — ${summary}`);
|
|
174
|
+
return { pending, tablesCreated, fieldsAdded, sql };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
await runMigrations();
|
|
178
|
+
logger.info(`Auth migrations applied — ${summary}`);
|
|
179
|
+
return { pending, tablesCreated, fieldsAdded };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// The logger is unavailable when the service is built outside a booted
|
|
183
|
+
// kernel (e.g. the better-auth CLI loading src/services/auth/auth.js), and
|
|
184
|
+
// the DI container throws on unknown services rather than returning null.
|
|
185
|
+
#logger() {
|
|
186
|
+
try {
|
|
187
|
+
return this.#container?.get('logger') || console;
|
|
188
|
+
} catch {
|
|
189
|
+
return console;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Built once and reused: every access to `auth` would otherwise open another
|
|
194
|
+
// SQLite handle / pg Pool. `migrate()` needs the very same options object,
|
|
195
|
+
// since that is what defines the expected schema.
|
|
196
|
+
get authOptions() {
|
|
197
|
+
if (!this.#authOptions) this.#authOptions = this.#buildAuthOptions();
|
|
198
|
+
return this.#authOptions;
|
|
199
|
+
}
|
|
200
|
+
|
|
134
201
|
get auth() {
|
|
202
|
+
return betterAuth(this.authOptions);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
#buildAuthOptions() {
|
|
135
206
|
const cache = this.#container.get('cache');
|
|
136
207
|
|
|
137
208
|
// Environment-aware cookie security:
|
|
@@ -144,7 +215,7 @@ module.exports = class AuthService extends Service {
|
|
|
144
215
|
const cookieSameSite = crossDomain ? 'none' : 'lax';
|
|
145
216
|
const cookieSecure = crossDomain || isProduction;
|
|
146
217
|
|
|
147
|
-
return
|
|
218
|
+
return {
|
|
148
219
|
hooks: this.hooks,
|
|
149
220
|
plugins: [
|
|
150
221
|
admin(),
|
|
@@ -215,10 +286,6 @@ module.exports = class AuthService extends Service {
|
|
|
215
286
|
baseURL: process.env.BETTER_AUTH_URL || 'http://localhost:3000',
|
|
216
287
|
secret: this.#requireAuthSecret(),
|
|
217
288
|
trustedOrigins: process.env.TRUSTED_ORIGINS?.split(',') || ['http://localhost:5173', 'http://localhost:6632']
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
get client() {
|
|
222
|
-
return this.client;
|
|
289
|
+
};
|
|
223
290
|
}
|
|
224
291
|
}
|
|
@@ -111,7 +111,9 @@ module.exports = class Express extends Service {
|
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
-
// Attach Express to HTTP server
|
|
114
|
+
// Attach Express to the HTTP server, replacing the kernel's "still
|
|
115
|
+
// booting" placeholder. This happens exactly once: the Express router is
|
|
116
|
+
// mutable, so routes registered later need no re-attachment.
|
|
115
117
|
this.#httpServer.removeAllListeners("request");
|
|
116
118
|
this.#httpServer.on("request", this.#app);
|
|
117
119
|
|
|
@@ -166,16 +168,13 @@ module.exports = class Express extends Service {
|
|
|
166
168
|
});
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
// No re-attachment: the app is already the server's request listener and
|
|
172
|
+
// its router picks up these routes immediately. Re-attaching would wipe
|
|
173
|
+
// engine.io's wrapper and kill the socket.io polling transport.
|
|
172
174
|
}
|
|
173
175
|
|
|
174
176
|
async regiterRawAllRoutes(path, handler) {
|
|
175
177
|
this.#app.all(path, handler);
|
|
176
|
-
|
|
177
|
-
this.#httpServer.removeAllListeners("request");
|
|
178
|
-
this.#httpServer.on("request", this.#app);
|
|
179
178
|
}
|
|
180
179
|
|
|
181
180
|
async #loadCorsOrCreateDefault() {
|
package/src/services/index.js
CHANGED
|
@@ -25,7 +25,12 @@ module.exports = [
|
|
|
25
25
|
s3Activated ? ["s3", S3, ["@service_container", process.env.S3_ENDPOINT, process.env.S3_PORT, process.env.S3_USE_SSL === "true", process.env.S3_ACCESS_KEY, process.env.S3_SECRET_KEY, process.env.S3_PUBLIC_BUCKETS, process.env.S3_PRIVATE_BUCKETS]] : null,
|
|
26
26
|
schedulerActivated ? ["scheduler", Scheduler, ["@service_container"]] : null,
|
|
27
27
|
bullmqActivated ? ["bullmq", require("./bullmq"), ["@service_container"]] : null,
|
|
28
|
+
// Express must boot BEFORE socketio: engine.io's attach() wraps whatever
|
|
29
|
+
// "request" listener is already on the HTTP server and delegates non-socket
|
|
30
|
+
// requests to it. Booting it the other way round means Express replaces
|
|
31
|
+
// engine.io's listener and the polling transport (the default first hop for
|
|
32
|
+
// every socket.io client) breaks with "xhr poll error".
|
|
33
|
+
expressActivated ? ["express", Express, ["@service_container"]] : null,
|
|
28
34
|
socketActivated ? ["socketio", SocketIO, ["@service_container"]] : null,
|
|
29
35
|
viteActivated ? ["vite", require("./vite"), ["@service_container", process.env.VITE_ROOT, Number(process.env.VITE_PORT) || 5173]] : null,
|
|
30
|
-
expressActivated ? ["express", Express, ["@service_container"]] : null,
|
|
31
36
|
].filter(Boolean);
|
|
@@ -23,12 +23,25 @@ Auth endpoints are mounted by better-auth under `/api/auth/*`.
|
|
|
23
23
|
## Setup
|
|
24
24
|
```bash
|
|
25
25
|
npm install
|
|
26
|
-
# 1) Create the better-auth tables
|
|
27
|
-
npx @better-auth/cli migrate
|
|
26
|
+
# 1) Create the better-auth tables
|
|
27
|
+
npx @better-auth/cli migrate --config src/services/auth/auth.js
|
|
28
28
|
# 2) Run
|
|
29
29
|
node index.js
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
Instead of the CLI you can migrate from inside the app — it uses the
|
|
33
|
+
better-auth version this project actually installs:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
kernel.boot().then(() => kernel.container.get('auth').migrate());
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Re-run it after adding a better-auth plugin or an `additionalFields` entry.
|
|
40
|
+
There is no `session`/`verification` table: `secondaryStorage` is wired to the
|
|
41
|
+
cache service, so better-auth keeps those there (set `CACHE_URL=redis://…` if
|
|
42
|
+
sessions must survive a restart).
|
|
43
|
+
|
|
44
|
+
|
|
32
45
|
The `Task` table is created automatically via `sequelize.sync()` on boot.
|
|
33
46
|
|
|
34
47
|
## Auth quick test
|
|
@@ -9,8 +9,8 @@ const kernel = new Kernel({
|
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
kernel.boot().then(async () => {
|
|
12
|
-
// Create the example tables (Task). better-auth tables
|
|
13
|
-
// better-auth CLI
|
|
12
|
+
// Create the example tables (Task). The better-auth tables come from
|
|
13
|
+
// `container.get('auth').migrate()` or the better-auth CLI (see README).
|
|
14
14
|
await kernel.container.get('database').sync();
|
|
15
15
|
kernel.container.get('logger').info('api-rest template ready — try GET /tasks');
|
|
16
16
|
}).catch((error) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Your auth configuration for the better-auth CLI. The CLI does not scan
|
|
2
|
+
// src/services/, so point it here explicitly:
|
|
3
|
+
// npx @better-auth/cli generate|migrate --config src/services/auth/auth.js
|
|
3
4
|
const AuthService = require('./index.js');
|
|
4
5
|
|
|
5
6
|
const auth = new AuthService({
|
|
@@ -14,9 +14,22 @@ connect or send messages.
|
|
|
14
14
|
## Setup
|
|
15
15
|
```bash
|
|
16
16
|
npm install
|
|
17
|
-
npx @better-auth/cli migrate
|
|
17
|
+
npx @better-auth/cli migrate --config src/services/auth/auth.js
|
|
18
18
|
node index.js
|
|
19
19
|
```
|
|
20
|
+
|
|
21
|
+
Instead of the CLI you can migrate from inside the app — it uses the
|
|
22
|
+
better-auth version this project actually installs:
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
kernel.boot().then(() => kernel.container.get('auth').migrate());
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Re-run it after adding a better-auth plugin or an `additionalFields` entry.
|
|
29
|
+
There is no `session`/`verification` table: `secondaryStorage` is wired to the
|
|
30
|
+
cache service, so better-auth keeps those there (set `CACHE_URL=redis://…` if
|
|
31
|
+
sessions must survive a restart).
|
|
32
|
+
|
|
20
33
|
This starts the API + websocket server on `:3000` and the Vite frontend on `:5173`.
|
|
21
34
|
Open http://localhost:5173, register a user, and start chatting (open two
|
|
22
35
|
browsers/users to see real-time delivery).
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Your auth configuration for the better-auth CLI. The CLI does not scan
|
|
2
|
+
// src/services/, so point it here explicitly:
|
|
3
|
+
// npx @better-auth/cli generate|migrate --config src/services/auth/auth.js
|
|
2
4
|
const AuthService = require('./index.js');
|
|
3
5
|
|
|
4
6
|
const auth = new AuthService({
|
|
@@ -26,9 +26,22 @@ routes, not on re-implementing what better-auth already exposes.
|
|
|
26
26
|
## Setup
|
|
27
27
|
```bash
|
|
28
28
|
npm install
|
|
29
|
-
npx @better-auth/cli migrate # creates user/
|
|
29
|
+
npx @better-auth/cli migrate --config src/services/auth/auth.js # creates the user/account/organization/member tables
|
|
30
30
|
node index.js
|
|
31
31
|
```
|
|
32
|
+
|
|
33
|
+
Instead of the CLI you can migrate from inside the app — it uses the
|
|
34
|
+
better-auth version this project actually installs:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
kernel.boot().then(() => kernel.container.get('auth').migrate());
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Re-run it after adding a better-auth plugin or an `additionalFields` entry.
|
|
41
|
+
There is no `session`/`verification` table: `secondaryStorage` is wired to the
|
|
42
|
+
cache service, so better-auth keeps those there (set `CACHE_URL=redis://…` if
|
|
43
|
+
sessions must survive a restart).
|
|
44
|
+
|
|
32
45
|
This starts the API on `:3000` and the Vite dashboard on `:5173`. Open
|
|
33
46
|
http://localhost:5173, sign up, create an organization, set it active, and add
|
|
34
47
|
projects (scoped to that organization).
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Your auth configuration for the better-auth CLI. The CLI does not scan
|
|
2
|
+
// src/services/, so point it here explicitly:
|
|
3
|
+
// npx @better-auth/cli generate|migrate --config src/services/auth/auth.js
|
|
2
4
|
const AuthService = require('./index.js');
|
|
3
5
|
|
|
4
6
|
const auth = new AuthService({
|
package/src/utils/EnvManager.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
+
const crypto = require('crypto');
|
|
2
3
|
|
|
3
4
|
module.exports = class EnvManager {
|
|
4
5
|
static load(secretsPath) {
|
|
@@ -43,6 +44,10 @@ module.exports = class EnvManager {
|
|
|
43
44
|
VITE_ROOT: './frontend',
|
|
44
45
|
VITE_PORT: 5173,
|
|
45
46
|
DISABLE_VITE: true,
|
|
47
|
+
// Generated here, not on first boot: the better-auth CLI reads .env when
|
|
48
|
+
// it loads your auth config, so `migrate` has to work before the app has
|
|
49
|
+
// ever run. A per-project random value, never a shared placeholder.
|
|
50
|
+
AUTH_SECRET: crypto.randomBytes(32).toString('hex'),
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
return header + Object.entries(envsToCreate).reduce((acc, [key, value]) => {
|