zyket 1.0.53 → 1.0.55
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 +16 -0
- package/claude-plugin/.claude-plugin/commands/event.md +29 -0
- package/claude-plugin/.claude-plugin/commands/guard.md +37 -0
- package/claude-plugin/.claude-plugin/commands/handler.md +37 -0
- package/claude-plugin/.claude-plugin/commands/middleware.md +28 -0
- package/claude-plugin/.claude-plugin/commands/model.md +40 -0
- package/claude-plugin/.claude-plugin/commands/route.md +42 -0
- package/claude-plugin/.claude-plugin/commands/schedule.md +38 -0
- package/claude-plugin/.claude-plugin/commands/service.md +59 -0
- package/claude-plugin/.claude-plugin/commands/worker.md +39 -0
- package/claude-plugin/.claude-plugin/plugin.json +133 -0
- package/claude-plugin/.claude-plugin/skills/auth.md +128 -0
- package/claude-plugin/.claude-plugin/skills/database.md +132 -0
- package/claude-plugin/.claude-plugin/skills/events.md +63 -0
- package/claude-plugin/.claude-plugin/skills/extensions.md +75 -0
- package/claude-plugin/.claude-plugin/skills/middlewares.md +97 -0
- package/claude-plugin/.claude-plugin/skills/overview.md +109 -0
- package/claude-plugin/.claude-plugin/skills/routes.md +141 -0
- package/claude-plugin/.claude-plugin/skills/scheduler.md +60 -0
- package/claude-plugin/.claude-plugin/skills/services.md +152 -0
- package/claude-plugin/.claude-plugin/skills/socketio.md +110 -0
- package/claude-plugin/.claude-plugin/skills/workers.md +88 -0
- package/claude-plugin/README.md +54 -0
- package/index.js +3 -1
- package/package.json +1 -1
- package/src/services/express/Express.js +2 -0
- package/src/services/express/RedirectResponse.js +9 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zyket",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "PemaK"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "A collection of Claude plugins for building Zyket apps",
|
|
8
|
+
"version": "1.0.8"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "zyket",
|
|
13
|
+
"source": "./claude-plugin"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Create a Zyket Event
|
|
2
|
+
|
|
3
|
+
Create a new event file in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Events live in `src/events/`. The filename (without `.js`) is the event name used in `emit()`.
|
|
8
|
+
|
|
9
|
+
### Template
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const { Event } = require("zyket");
|
|
13
|
+
|
|
14
|
+
module.exports = class $EventName$Event extends Event {
|
|
15
|
+
async handle({ container, payload }) {
|
|
16
|
+
container.get("logger").info("$EventName$ event fired", payload);
|
|
17
|
+
// Your event logic here
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Steps:
|
|
23
|
+
1. Create `src/events/$event-name$.js`.
|
|
24
|
+
2. Implement the handler logic based on the user's description.
|
|
25
|
+
3. Show the user how to emit it:
|
|
26
|
+
```js
|
|
27
|
+
container.get("events").emitAsync("$event-name$", { ...payload });
|
|
28
|
+
```
|
|
29
|
+
4. Remind the user to set `DISABLE_EVENTS=false` in `.env`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Create a Zyket Socket.IO Guard
|
|
2
|
+
|
|
3
|
+
Create a new Socket.IO guard file in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Guards live in `src/guards/`. The filename (without `.js`) is the guard name used in handler `guards` arrays. Guards run before an event handler or connection and can block execution by throwing an error.
|
|
8
|
+
|
|
9
|
+
### Template
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const { Guard } = require("zyket");
|
|
13
|
+
|
|
14
|
+
module.exports = class $Name$Guard extends Guard {
|
|
15
|
+
async handle({ container, socket, io }) {
|
|
16
|
+
// Validate / authorize the socket
|
|
17
|
+
// Throw an error to block the event or connection
|
|
18
|
+
// e.g.:
|
|
19
|
+
const token = socket.handshake.auth?.token;
|
|
20
|
+
if (!token) throw new Error("Unauthorized");
|
|
21
|
+
|
|
22
|
+
// Attach data to socket for handlers to use
|
|
23
|
+
// socket.data.user = verifiedUser;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Steps:
|
|
29
|
+
1. Create `src/guards/$name$.js`.
|
|
30
|
+
2. Implement the authorization logic.
|
|
31
|
+
3. Show how to attach it to a handler:
|
|
32
|
+
```js
|
|
33
|
+
module.exports = class MyHandler extends Handler {
|
|
34
|
+
guards = ["$name$"];
|
|
35
|
+
// ...
|
|
36
|
+
};
|
|
37
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Create a Zyket Socket.IO Handler
|
|
2
|
+
|
|
3
|
+
Create a new Socket.IO event handler file in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Handlers live in `src/handlers/`. The filename (without `.js`) is the Socket.IO event name. The special file `connection.js` is the connection handler.
|
|
8
|
+
|
|
9
|
+
### Event Handler Template
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const { Handler } = require("zyket");
|
|
13
|
+
|
|
14
|
+
module.exports = class $EventName$Handler extends Handler {
|
|
15
|
+
guards = []; // optional: list guard filenames (without .js) to run first
|
|
16
|
+
|
|
17
|
+
async handle({ container, socket, data, io }) {
|
|
18
|
+
container.get("logger").info(`$EventName$ from ${socket.id}`, data);
|
|
19
|
+
|
|
20
|
+
// Your event handling logic here
|
|
21
|
+
|
|
22
|
+
// Return value is sent back via ack callback (if client uses acks)
|
|
23
|
+
return { success: true };
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Steps:
|
|
29
|
+
1. Create `src/handlers/$event-name$.js`.
|
|
30
|
+
2. Add guard names to the `guards` array if authorization is needed.
|
|
31
|
+
3. Implement the event handling logic.
|
|
32
|
+
4. Optionally show the client-side code to emit the event:
|
|
33
|
+
```js
|
|
34
|
+
socket.emit("$event-name$", { ...data }, (response) => {
|
|
35
|
+
console.log(response); // { success: true }
|
|
36
|
+
});
|
|
37
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Create a Zyket Middleware
|
|
2
|
+
|
|
3
|
+
Create a new Express middleware file in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Create a middleware class inside `src/middlewares/`. Middlewares must extend `Middleware` from `zyket` and implement the `handle` method.
|
|
8
|
+
|
|
9
|
+
### Template
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const { Middleware } = require("zyket");
|
|
13
|
+
|
|
14
|
+
module.exports = class $ClassName$Middleware extends Middleware {
|
|
15
|
+
async handle({ container, request, response, next }) {
|
|
16
|
+
// Your logic here
|
|
17
|
+
// Call next() to allow the request to continue
|
|
18
|
+
// Or return response.status(401).json({ success: false, message: "..." }) to block
|
|
19
|
+
next();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Steps:
|
|
25
|
+
1. Create `src/middlewares/$name$.js` where `$name$` describes the purpose.
|
|
26
|
+
2. Implement the validation/authentication/transformation logic.
|
|
27
|
+
3. Call `next()` to pass the request through, or send a response to block.
|
|
28
|
+
4. Show the user how to attach it to a route.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Create a Zyket Sequelize Model
|
|
2
|
+
|
|
3
|
+
Create a new Sequelize model file in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Models live in `src/models/`. Each file must export a **function** (not a class) that receives `{ sequelize, Sequelize, container }` and returns a Sequelize model instance.
|
|
8
|
+
|
|
9
|
+
### Template
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const { DataTypes } = require("sequelize");
|
|
13
|
+
|
|
14
|
+
module.exports = ({ sequelize, Sequelize, container }) => {
|
|
15
|
+
const $ModelName$ = sequelize.define("$ModelName$", {
|
|
16
|
+
id: {
|
|
17
|
+
type: DataTypes.INTEGER,
|
|
18
|
+
primaryKey: true,
|
|
19
|
+
autoIncrement: true,
|
|
20
|
+
},
|
|
21
|
+
// Add your fields here
|
|
22
|
+
}, {
|
|
23
|
+
tableName: "$table_name$",
|
|
24
|
+
timestamps: true,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
$ModelName$.associate = (models) => {
|
|
28
|
+
// Define associations here
|
|
29
|
+
// $ModelName$.belongsTo(models.User, { foreignKey: "userId" });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return $ModelName$;
|
|
33
|
+
};
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Steps:
|
|
37
|
+
1. Create `src/models/$ModelName$.js`.
|
|
38
|
+
2. Define all columns based on the user's description.
|
|
39
|
+
3. Add associations if the model relates to other models.
|
|
40
|
+
4. Optionally create a migration file in `src/models/migrations/`.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Create a Zyket Route
|
|
2
|
+
|
|
3
|
+
Create a new HTTP route file in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Given a route path or description from the user, create the appropriate file inside `src/routes/` following Zyket's file-based routing conventions.
|
|
8
|
+
|
|
9
|
+
### File location rules:
|
|
10
|
+
- `/users` → `src/routes/users.js`
|
|
11
|
+
- `/users/:id` → `src/routes/users/[id].js`
|
|
12
|
+
- `/users/:id/posts` → `src/routes/users/[id]/posts.js`
|
|
13
|
+
- `/` (root) → `src/routes/index.js`
|
|
14
|
+
|
|
15
|
+
### Template
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
const { Route } = require("zyket");
|
|
19
|
+
|
|
20
|
+
module.exports = class $ClassName$Route extends Route {
|
|
21
|
+
// Add middlewares if needed:
|
|
22
|
+
// middlewares = {
|
|
23
|
+
// get: [new SomeMiddleware()],
|
|
24
|
+
// post: [new SomeMiddleware()],
|
|
25
|
+
// };
|
|
26
|
+
|
|
27
|
+
async get({ container, request }) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async post({ container, request }) {
|
|
32
|
+
return { status: 201 };
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Steps:
|
|
38
|
+
1. Determine the correct file path based on the route the user wants.
|
|
39
|
+
2. Identify which HTTP methods are needed (get, post, put, delete).
|
|
40
|
+
3. Implement each method based on the user's described functionality.
|
|
41
|
+
4. Add middlewares if the route needs authentication or validation.
|
|
42
|
+
5. Use `container.get("database")` to access models, `container.get("cache")` for caching, etc.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Create a Zyket Schedule
|
|
2
|
+
|
|
3
|
+
Create a new cron-based scheduled task in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Schedulers live in `src/schedulers/`. Each file exports a class extending `Schedule` from `zyket`. The `time` property must be a valid `node-cron` expression.
|
|
8
|
+
|
|
9
|
+
### Template
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const { Schedule } = require("zyket");
|
|
13
|
+
|
|
14
|
+
module.exports = class $Name$Schedule extends Schedule {
|
|
15
|
+
time = "$cron-expression$"; // e.g. "0 * * * *" for every hour
|
|
16
|
+
|
|
17
|
+
async handle({ container }) {
|
|
18
|
+
container.get("logger").info("Running $Name$ schedule");
|
|
19
|
+
// Your scheduled logic here
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Common cron expressions
|
|
25
|
+
|
|
26
|
+
| Pattern | Meaning |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `* * * * *` | Every minute |
|
|
29
|
+
| `0 * * * *` | Every hour |
|
|
30
|
+
| `0 0 * * *` | Every day at midnight |
|
|
31
|
+
| `0 9 * * 1` | Every Monday at 9:00 |
|
|
32
|
+
| `*/5 * * * *` | Every 5 minutes |
|
|
33
|
+
|
|
34
|
+
### Steps:
|
|
35
|
+
1. Create `src/schedulers/$name$.js`.
|
|
36
|
+
2. Set `time` to the appropriate cron expression.
|
|
37
|
+
3. Implement the recurring task logic.
|
|
38
|
+
4. Remind the user to set `DISABLE_SCHEDULER=false` in `.env`.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Create a Zyket Custom Service
|
|
2
|
+
|
|
3
|
+
Create a new custom service in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Custom services extend the `Service` base class and are registered in the `Kernel` constructor. They are booted once at startup and accessible anywhere via `container.get("serviceName")`.
|
|
8
|
+
|
|
9
|
+
### Template
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
// src/services/$name$.js
|
|
13
|
+
const { Service } = require("zyket");
|
|
14
|
+
|
|
15
|
+
module.exports = class $Name$Service extends Service {
|
|
16
|
+
#container;
|
|
17
|
+
|
|
18
|
+
constructor(container) {
|
|
19
|
+
super("$name$"); // Must match the registration name
|
|
20
|
+
this.#container = container;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async boot() {
|
|
24
|
+
// Initialize any connections, clients, etc.
|
|
25
|
+
this.#container.get("logger").info("$Name$Service booted");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Add your service methods here
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Registration
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
// index.js
|
|
36
|
+
const { Kernel } = require("zyket");
|
|
37
|
+
const $Name$Service = require("./src/services/$name$");
|
|
38
|
+
|
|
39
|
+
const kernel = new Kernel({
|
|
40
|
+
services: [
|
|
41
|
+
["$name$", $Name$Service, ["@service_container"]],
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
kernel.boot();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Usage
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
const service = container.get("$name$");
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Steps:
|
|
55
|
+
1. Create `src/services/$name$.js`.
|
|
56
|
+
2. Implement the `boot()` method for initialization.
|
|
57
|
+
3. Add the service methods.
|
|
58
|
+
4. Register it in `index.js` with a unique name.
|
|
59
|
+
5. Use `"@service_container"` in the args array to inject the DI container.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Create a Zyket BullMQ Worker
|
|
2
|
+
|
|
3
|
+
Create a new BullMQ worker file in the Zyket project.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Workers live in `src/workers/`. Each file must export a class extending `Worker` from `zyket`.
|
|
8
|
+
|
|
9
|
+
### Template
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const { Worker } = require("zyket");
|
|
13
|
+
|
|
14
|
+
module.exports = class $WorkerName$Worker extends Worker {
|
|
15
|
+
queueName = "$queue-name$"; // Must match a name in the QUEUES env var
|
|
16
|
+
|
|
17
|
+
async handle({ container, job }) {
|
|
18
|
+
const data = job.data;
|
|
19
|
+
container.get("logger").info(`Processing job ${job.id}`, data);
|
|
20
|
+
|
|
21
|
+
// Your job logic here
|
|
22
|
+
|
|
23
|
+
return { success: true };
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Steps:
|
|
29
|
+
1. Create `src/workers/$worker-name$.js`.
|
|
30
|
+
2. Set `queueName` to the appropriate queue name.
|
|
31
|
+
3. Implement the job processing logic.
|
|
32
|
+
4. Remind the user to:
|
|
33
|
+
- Add `DISABLE_BULLMQ=false` to `.env`
|
|
34
|
+
- Add the queue name to `QUEUES` in `.env` (e.g. `QUEUES=emails,notifications`)
|
|
35
|
+
- Set `CACHE_URL` in `.env`
|
|
36
|
+
5. Show how to dispatch a job:
|
|
37
|
+
```js
|
|
38
|
+
await container.get("queues").addJob("$queue-name$", "job-name", { ...data });
|
|
39
|
+
```
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zyket",
|
|
3
|
+
"description": "Comprehensive skills for building Zyket backends.",
|
|
4
|
+
"version": "1.0.8",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "PemaK"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://zyket.com",
|
|
9
|
+
"repository": "https://github.com/PemaK/zyket-claude",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"zyket",
|
|
12
|
+
"backend",
|
|
13
|
+
"admin",
|
|
14
|
+
"node.js",
|
|
15
|
+
"headless",
|
|
16
|
+
"api",
|
|
17
|
+
"database",
|
|
18
|
+
"authentication",
|
|
19
|
+
"authorization",
|
|
20
|
+
"file management",
|
|
21
|
+
"email",
|
|
22
|
+
"notifications",
|
|
23
|
+
"analytics",
|
|
24
|
+
"logging",
|
|
25
|
+
"testing",
|
|
26
|
+
"deployment",
|
|
27
|
+
"security"
|
|
28
|
+
],
|
|
29
|
+
"skills": [
|
|
30
|
+
{
|
|
31
|
+
"name": "zyket-overview",
|
|
32
|
+
"description": "Overview of the Zyket framework, its architecture and project structure",
|
|
33
|
+
"file": "skills/overview.md"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "zyket-routes",
|
|
37
|
+
"description": "How to create and configure Express HTTP routes in Zyket",
|
|
38
|
+
"file": "skills/routes.md"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "zyket-middlewares",
|
|
42
|
+
"description": "How to create Express middlewares in Zyket",
|
|
43
|
+
"file": "skills/middlewares.md"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "zyket-database",
|
|
47
|
+
"description": "How to define Sequelize models and run database migrations in Zyket",
|
|
48
|
+
"file": "skills/database.md"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "zyket-events",
|
|
52
|
+
"description": "How to create and emit events in Zyket",
|
|
53
|
+
"file": "skills/events.md"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "zyket-workers",
|
|
57
|
+
"description": "How to create BullMQ workers and manage job queues in Zyket",
|
|
58
|
+
"file": "skills/workers.md"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "zyket-scheduler",
|
|
62
|
+
"description": "How to create cron-based scheduled tasks in Zyket",
|
|
63
|
+
"file": "skills/scheduler.md"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "zyket-socketio",
|
|
67
|
+
"description": "How to create Socket.IO handlers and guards in Zyket",
|
|
68
|
+
"file": "skills/socketio.md"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "zyket-auth",
|
|
72
|
+
"description": "How to set up authentication using better-auth in Zyket",
|
|
73
|
+
"file": "skills/auth.md"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "zyket-services",
|
|
77
|
+
"description": "How to use built-in services (cache, S3, logger) and create custom services in Zyket",
|
|
78
|
+
"file": "skills/services.md"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "zyket-extensions",
|
|
82
|
+
"description": "How to create and use extensions in Zyket",
|
|
83
|
+
"file": "skills/extensions.md"
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"commands": [
|
|
87
|
+
{
|
|
88
|
+
"name": "route",
|
|
89
|
+
"description": "Create a new HTTP route",
|
|
90
|
+
"file": "commands/route.md"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "middleware",
|
|
94
|
+
"description": "Create a new Express middleware",
|
|
95
|
+
"file": "commands/middleware.md"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "model",
|
|
99
|
+
"description": "Create a new Sequelize model",
|
|
100
|
+
"file": "commands/model.md"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "event",
|
|
104
|
+
"description": "Create a new event",
|
|
105
|
+
"file": "commands/event.md"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "worker",
|
|
109
|
+
"description": "Create a new BullMQ worker",
|
|
110
|
+
"file": "commands/worker.md"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "schedule",
|
|
114
|
+
"description": "Create a new cron schedule",
|
|
115
|
+
"file": "commands/schedule.md"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "handler",
|
|
119
|
+
"description": "Create a new Socket.IO event handler",
|
|
120
|
+
"file": "commands/handler.md"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "guard",
|
|
124
|
+
"description": "Create a new Socket.IO guard",
|
|
125
|
+
"file": "commands/guard.md"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "service",
|
|
129
|
+
"description": "Create a new custom service",
|
|
130
|
+
"file": "commands/service.md"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Zyket – Authentication (better-auth)
|
|
2
|
+
|
|
3
|
+
Zyket uses [better-auth](https://better-auth.com) for authentication. It only supports **PostgreSQL** as the database dialect.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
```env
|
|
8
|
+
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
|
|
9
|
+
DATABASE_DIALECT=postgresql
|
|
10
|
+
CACHE_URL=redis://localhost:6379 # required for session secondary storage
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Creating an Auth Service
|
|
14
|
+
|
|
15
|
+
Create a custom service that extends `AuthService`:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
// src/services/auth.js
|
|
19
|
+
const { AuthService } = require("zyket");
|
|
20
|
+
|
|
21
|
+
module.exports = class MyAuth extends AuthService {
|
|
22
|
+
// Add extra better-auth plugins
|
|
23
|
+
get plugins() {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Add social providers (Google, GitHub, etc.)
|
|
28
|
+
get socialProviders() {
|
|
29
|
+
return {
|
|
30
|
+
google: {
|
|
31
|
+
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
32
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Extra fields on the user table
|
|
38
|
+
get userAdditionalFields() {
|
|
39
|
+
return {
|
|
40
|
+
role: {
|
|
41
|
+
type: "string",
|
|
42
|
+
defaultValue: "user",
|
|
43
|
+
input: false, // cannot be set by user on sign-up
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Email hooks (implement or throw to disable)
|
|
49
|
+
async sendResetPasswordEmail({ user, url, token }, request) {
|
|
50
|
+
// Send email with the reset URL
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async sendVerificationEmail({ user, url, token }, request) {
|
|
54
|
+
// Send email with the verification URL
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async sendInvitationEmail(data) {
|
|
58
|
+
// Send org invitation email
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async allowUserToCreateOrganization(user) {
|
|
62
|
+
return user.role === "admin"; // only admins can create orgs
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Registering the Auth Service
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
// index.js
|
|
71
|
+
const { Kernel } = require("zyket");
|
|
72
|
+
const MyAuth = require("./src/services/auth");
|
|
73
|
+
|
|
74
|
+
const kernel = new Kernel({
|
|
75
|
+
services: [
|
|
76
|
+
["auth", MyAuth, ["@service_container"]],
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
kernel.boot();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Endpoints Provided
|
|
84
|
+
|
|
85
|
+
All better-auth REST endpoints are mounted at `/api/auth/*`. This includes:
|
|
86
|
+
|
|
87
|
+
- `POST /api/auth/sign-up/email`
|
|
88
|
+
- `POST /api/auth/sign-in/email`
|
|
89
|
+
- `POST /api/auth/sign-out`
|
|
90
|
+
- `POST /api/auth/forget-password`
|
|
91
|
+
- `POST /api/auth/reset-password`
|
|
92
|
+
- `GET /api/auth/session`
|
|
93
|
+
- `GET /api/auth/verify-email`
|
|
94
|
+
- Social provider OAuth flows (`/api/auth/sign-in/:provider`)
|
|
95
|
+
- Admin routes (`/api/auth/admin/...`)
|
|
96
|
+
- Organization routes (`/api/auth/organization/...`)
|
|
97
|
+
|
|
98
|
+
## Verifying Sessions in Routes
|
|
99
|
+
|
|
100
|
+
Use the `auth` property on the service or use `better-auth/node` token utilities:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
// src/middlewares/auth.js
|
|
104
|
+
const { Middleware } = require("zyket");
|
|
105
|
+
|
|
106
|
+
module.exports = class AuthMiddleware extends Middleware {
|
|
107
|
+
async handle({ container, request, response, next }) {
|
|
108
|
+
const auth = container.get("auth").client;
|
|
109
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
110
|
+
if (!session) {
|
|
111
|
+
return response.status(401).json({ success: false, message: "Unauthorized" });
|
|
112
|
+
}
|
|
113
|
+
request.user = session.user;
|
|
114
|
+
next();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Built-in Plugins Included
|
|
120
|
+
|
|
121
|
+
The base `AuthService` always includes:
|
|
122
|
+
- `admin` – admin panel routes + `banUser`, `unbanUser`, `listUsers`, etc.
|
|
123
|
+
- `bearer` – Bearer token support (for API use)
|
|
124
|
+
- `organization` – multi-tenancy / organisations
|
|
125
|
+
|
|
126
|
+
## Cookie Configuration
|
|
127
|
+
|
|
128
|
+
Cookies are configured for cross-subdomain, `SameSite=none`, `Secure=true` by default (suitable for separate frontend/backend domains).
|