create-nexwire 4.0.0 → 4.1.1
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 +5 -4
- package/express/README.md +13 -7
- package/express/package.json +2 -2
- package/express/src/config/session.ts +1 -1
- package/express/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
- package/express/src/database/migrations/sqlite/meta/_journal.json +1 -1
- package/express/src/database/schema.ts +3 -3
- package/express/src/framework/cache/cache.ts +82 -81
- package/express/src/framework/database/seed.ts +1 -1
- package/express/src/framework/facade.ts +1 -24
- package/express/src/framework/http/app.ts +5 -5
- package/express/src/framework/http/openapi.ts +1 -1
- package/express/src/framework/http/router.ts +3 -18
- package/express/src/framework/http/types.ts +19 -0
- package/express/src/framework/http/validation.ts +18 -18
- package/express/src/framework/maker-cli/middleware/core.mjs +41 -47
- package/express/src/framework/maker-cli/module/core.mjs +920 -684
- package/express/src/framework/maker-cli/module/index.mjs +202 -140
- package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
- package/express/src/framework/maker-cli/stubs/controller/openapi.ts.stub +40 -14
- package/express/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +40 -15
- package/express/src/framework/maker-cli/stubs/controller/plain.ts.stub +42 -16
- package/express/src/framework/maker-cli/stubs/controller/standalone.ts.stub +47 -0
- package/express/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
- package/express/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
- package/express/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
- package/express/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/notification/route.api.ts.stub +50 -50
- package/express/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +11 -11
- package/express/src/framework/maker-cli/stubs/route/api.ts.stub +93 -79
- package/express/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -10
- package/express/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +113 -0
- package/express/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +37 -0
- package/express/src/framework/maker-cli/stubs/{controller/schema.plain.ts.stub → schema/name.plain.ts.stub} +13 -13
- package/express/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub} +32 -32
- package/express/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
- package/express/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
- package/express/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
- package/express/src/framework/maker-cli/utils/naming.mjs +56 -13
- package/express/src/framework/modules/discover.ts +57 -54
- package/express/src/framework/notification/index.ts +1 -1
- package/express/src/framework/realtime/broadcast.ts +46 -46
- package/express/src/framework/server.ts +2 -1
- package/express/src/framework/session/session.ts +102 -98
- package/express/src/framework/support/mail.ts +48 -48
- package/express/src/framework/support/status-codes.ts +75 -0
- package/express/src/middlewares/auth-middleware.ts +1 -1
- package/express/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
- package/express/src/modules/auth/__tests__/login.test.ts +171 -0
- package/express/src/modules/auth/__tests__/logout.test.ts +60 -0
- package/express/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
- package/express/src/modules/auth/__tests__/me.test.ts +91 -0
- package/express/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
- package/express/src/modules/auth/__tests__/register.test.ts +291 -0
- package/express/src/modules/auth/__tests__/resetpass.test.ts +206 -0
- package/express/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
- package/express/src/modules/auth/controllers/auth.ts +254 -0
- package/express/src/modules/auth/controllers/{role.controller.ts → role.ts} +25 -15
- package/{hono/src/modules/auth/database/models/notifications.ts → express/src/modules/auth/database/models/notification.ts} +1 -1
- package/express/src/modules/auth/database/models/role.ts +1 -1
- package/express/src/modules/auth/database/models/user.ts +1 -1
- package/express/src/modules/auth/facade.ts +12 -0
- package/express/src/modules/auth/{controllers/auth.helpers.ts → helpers/auth.ts} +3 -4
- package/express/src/modules/auth/jobs/registeruser.ts +23 -23
- package/express/src/modules/auth/routes/{api.ts → index.ts} +150 -150
- package/express/src/modules/auth/routes/role.ts +33 -33
- package/{hono/src/modules/auth/controllers/auth.schema.ts → express/src/modules/auth/schemas/auth.ts} +102 -102
- package/express/src/modules/auth/services/forgetpassword.ts +66 -0
- package/express/src/modules/auth/services/login.ts +63 -0
- package/express/src/modules/auth/services/logout.ts +25 -0
- package/express/src/modules/auth/services/logoutalldevices.ts +19 -0
- package/express/src/modules/auth/services/me.ts +31 -0
- package/express/src/modules/auth/services/refreshtoken.ts +110 -0
- package/express/src/modules/auth/services/register.ts +141 -0
- package/express/src/modules/auth/services/resetpassword.ts +69 -0
- package/express/src/modules/auth/services/verifyemail.ts +77 -0
- package/express/src/modules/auth/types/auth.ts +31 -0
- package/express/src/resources/src/assets/images/logo.png +0 -0
- package/express/src/resources/src/pages/auth/forgetPassword.vue +1 -0
- package/express/src/resources/src/pages/auth/login.vue +1 -1
- package/express/src/storage/database/nexwire.sqlite +0 -0
- package/hono/README.md +13 -7
- package/hono/package.json +1 -1
- package/hono/src/config/session.ts +1 -1
- package/hono/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
- package/hono/src/database/migrations/sqlite/meta/_journal.json +1 -1
- package/hono/src/database/schema.ts +3 -3
- package/hono/src/framework/cache/cache.ts +82 -81
- package/hono/src/framework/database/seed.ts +1 -1
- package/hono/src/framework/http/app.ts +5 -5
- package/hono/src/framework/http/validation.ts +21 -21
- package/hono/src/framework/maker-cli/middleware/core.mjs +40 -46
- package/hono/src/framework/maker-cli/module/core.mjs +401 -168
- package/hono/src/framework/maker-cli/module/index.mjs +73 -11
- package/hono/src/framework/maker-cli/stubs/controller/openapi.ts.stub +28 -16
- package/hono/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +28 -17
- package/hono/src/framework/maker-cli/stubs/controller/plain.ts.stub +28 -16
- package/hono/src/framework/maker-cli/stubs/controller/standalone.ts.stub +56 -0
- package/hono/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
- package/hono/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
- package/hono/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
- package/hono/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/notification/route.api.ts.stub +49 -49
- package/hono/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +11 -11
- package/hono/src/framework/maker-cli/stubs/route/api.ts.stub +93 -79
- package/hono/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -10
- package/hono/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +112 -0
- package/hono/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +37 -0
- package/hono/src/framework/maker-cli/stubs/{controller/schema.plain.ts.stub → schema/name.plain.ts.stub} +13 -13
- package/hono/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub} +32 -32
- package/hono/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
- package/hono/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
- package/hono/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
- package/hono/src/framework/maker-cli/utils/naming.mjs +56 -13
- package/hono/src/framework/modules/discover.ts +57 -54
- package/hono/src/framework/notification/index.ts +1 -1
- package/hono/src/framework/realtime/broadcast.ts +46 -46
- package/hono/src/framework/server.ts +1 -1
- package/hono/src/framework/session/session.ts +121 -116
- package/hono/src/middlewares/auth-middleware.ts +1 -1
- package/hono/src/modules/auth/__tests__/forgetpass.test.ts +170 -0
- package/hono/src/modules/auth/__tests__/login.test.ts +154 -0
- package/hono/src/modules/auth/__tests__/logout.test.ts +53 -0
- package/hono/src/modules/auth/__tests__/logoutalldevices.test.ts +34 -0
- package/hono/src/modules/auth/__tests__/me.test.ts +87 -0
- package/hono/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
- package/hono/src/modules/auth/__tests__/register.test.ts +258 -0
- package/hono/src/modules/auth/__tests__/resetpass.test.ts +214 -0
- package/hono/src/modules/auth/__tests__/verifyemail.test.ts +189 -0
- package/hono/src/modules/auth/controllers/auth.ts +238 -0
- package/hono/src/modules/auth/controllers/{role.controller.ts → role.ts} +25 -25
- package/{express/src/modules/auth/database/models/notifications.ts → hono/src/modules/auth/database/models/notification.ts} +1 -1
- package/hono/src/modules/auth/database/models/role.ts +1 -1
- package/hono/src/modules/auth/database/models/user.ts +1 -1
- package/hono/src/modules/auth/facade.ts +12 -0
- package/hono/src/modules/auth/{controllers/auth.helpers.ts → helpers/auth.ts} +5 -6
- package/hono/src/modules/auth/jobs/registeruser.ts +23 -23
- package/hono/src/modules/auth/routes/{api.ts → index.ts} +150 -150
- package/hono/src/modules/auth/routes/role.ts +33 -33
- package/{express/src/modules/auth/controllers/auth.schema.ts → hono/src/modules/auth/schemas/auth.ts} +102 -102
- package/hono/src/modules/auth/services/forgetpassword.ts +58 -0
- package/hono/src/modules/auth/services/login.ts +61 -0
- package/hono/src/modules/auth/services/logout.ts +24 -0
- package/hono/src/modules/auth/services/logoutalldevices.ts +19 -0
- package/hono/src/modules/auth/services/me.ts +31 -0
- package/hono/src/modules/auth/services/refreshtoken.ts +110 -0
- package/hono/src/modules/auth/services/register.ts +132 -0
- package/hono/src/modules/auth/services/resetpassword.ts +69 -0
- package/hono/src/modules/auth/services/verifyemail.ts +77 -0
- package/hono/src/modules/auth/types/auth.ts +31 -0
- package/hono/src/resources/src/assets/images/logo.png +0 -0
- package/hono/src/storage/database/nexwire.sqlite +0 -0
- package/package.json +1 -1
- package/express/src/framework/maker-cli/stubs/example/console.ts.stub +0 -34
- package/express/src/framework/maker-cli/stubs/example/controller.ts.stub +0 -418
- package/express/src/framework/maker-cli/stubs/example/job.ts.stub +0 -75
- package/express/src/framework/maker-cli/stubs/example/route.api.ts.stub +0 -207
- package/express/src/framework/maker-cli/stubs/example/route.plain.ts.stub +0 -18
- package/express/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +0 -42
- package/express/src/framework/maker-cli/stubs/example/schema.ts.stub +0 -42
- package/express/src/modules/auth/controllers/auth.controller.ts +0 -358
- package/hono/src/framework/maker-cli/stubs/example/console.ts.stub +0 -34
- package/hono/src/framework/maker-cli/stubs/example/controller.ts.stub +0 -505
- package/hono/src/framework/maker-cli/stubs/example/job.ts.stub +0 -75
- package/hono/src/framework/maker-cli/stubs/example/route.api.ts.stub +0 -207
- package/hono/src/framework/maker-cli/stubs/example/route.plain.ts.stub +0 -18
- package/hono/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +0 -42
- package/hono/src/framework/maker-cli/stubs/example/schema.ts.stub +0 -42
- package/hono/src/modules/auth/controllers/auth.controller.ts +0 -413
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Request, Response } from "express";
|
|
2
|
+
import { createRoute, group, HttpStatusCodes, jsonContent, z } from "@/framework/facade.js";
|
|
3
|
+
import { authMiddleware } from "@/middlewares/auth-middleware.js";
|
|
4
|
+
import { requireRole } from "@/middlewares/role-middleware.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* {{module}} routes for {{controller}}.
|
|
8
|
+
*
|
|
9
|
+
* Wire each route to a handler:
|
|
10
|
+
* import { index, show, store, update, destroy } from "@/modules/{{module}}/controllers/{{controller}}.js";
|
|
11
|
+
* export default group().api(indexRoute, index).api(showRoute, show) ...
|
|
12
|
+
*
|
|
13
|
+
* Wire each route to its validation schema:
|
|
14
|
+
* import {
|
|
15
|
+
* Create{{ClassName}}Schema,
|
|
16
|
+
* {{ClassName}}IdParamsSchema,
|
|
17
|
+
* {{ClassName}}ListResponseSchema,
|
|
18
|
+
* {{ClassName}}MessageSchema,
|
|
19
|
+
* {{ClassName}}ResponseSchema,
|
|
20
|
+
* Update{{ClassName}}Schema
|
|
21
|
+
* } from "@/modules/{{module}}/schemas/{{controller}}.js";
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const indexRoute = createRoute({
|
|
25
|
+
path: "/",
|
|
26
|
+
method: "get",
|
|
27
|
+
tags: ["{{ModuleClass}}"],
|
|
28
|
+
// request: {
|
|
29
|
+
// query: {{ClassName}}ListQuerySchema
|
|
30
|
+
// },
|
|
31
|
+
responses: {
|
|
32
|
+
[HttpStatusCodes.OK]: jsonContent(
|
|
33
|
+
z.object({ message: z.string(), data: z.array(z.object({ id: z.number(), name: z.string() })) }),
|
|
34
|
+
"{{module}} list"
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const showRoute = createRoute({
|
|
40
|
+
path: "/{id}",
|
|
41
|
+
method: "get",
|
|
42
|
+
tags: ["{{ModuleClass}}"],
|
|
43
|
+
// request: {
|
|
44
|
+
// params: {{ClassName}}IdParamsSchema
|
|
45
|
+
// },
|
|
46
|
+
responses: {
|
|
47
|
+
[HttpStatusCodes.OK]: jsonContent(
|
|
48
|
+
z.object({ message: z.string(), data: z.object({ id: z.number(), name: z.string() }) }),
|
|
49
|
+
"{{module}} item"
|
|
50
|
+
),
|
|
51
|
+
[HttpStatusCodes.NOT_FOUND]: jsonContent(z.object({ message: z.string() }), "Not found")
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const storeRoute = createRoute({
|
|
56
|
+
path: "/",
|
|
57
|
+
method: "post",
|
|
58
|
+
tags: ["{{ModuleClass}}"],
|
|
59
|
+
// request: {
|
|
60
|
+
// body: jsonContent(Create{{ClassName}}Schema, "Create {{module}} payload")
|
|
61
|
+
// },
|
|
62
|
+
responses: {
|
|
63
|
+
[HttpStatusCodes.CREATED]: jsonContent(z.object({ message: z.string() }), "Created")
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const updateRoute = createRoute({
|
|
68
|
+
path: "/{id}",
|
|
69
|
+
method: "put",
|
|
70
|
+
tags: ["{{ModuleClass}}"],
|
|
71
|
+
// request: {
|
|
72
|
+
// params: {{ClassName}}IdParamsSchema,
|
|
73
|
+
// body: jsonContent(Update{{ClassName}}Schema, "Update {{module}} payload")
|
|
74
|
+
// },
|
|
75
|
+
responses: {
|
|
76
|
+
[HttpStatusCodes.OK]: jsonContent(z.object({ message: z.string() }), "Updated"),
|
|
77
|
+
[HttpStatusCodes.NOT_FOUND]: jsonContent(z.object({ message: z.string() }), "Not found")
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const destroyRoute = createRoute({
|
|
82
|
+
path: "/{id}",
|
|
83
|
+
method: "delete",
|
|
84
|
+
tags: ["{{ModuleClass}}"],
|
|
85
|
+
// request: {
|
|
86
|
+
// params: {{ClassName}}IdParamsSchema
|
|
87
|
+
// },
|
|
88
|
+
responses: {
|
|
89
|
+
[HttpStatusCodes.OK]: jsonContent(z.object({ message: z.string() }), "Deleted"),
|
|
90
|
+
[HttpStatusCodes.NOT_FOUND]: jsonContent(z.object({ message: z.string() }), "Not found")
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export default group()
|
|
95
|
+
.api(indexRoute, (_req: Request, res: Response) => res.json({ message: "{{module}} list not implemented", data: [] }))
|
|
96
|
+
.api(showRoute, (_req: Request, res: Response) =>res.json({ message: "{{module}} item not implemented", data: { id: 0, name: "" } }))
|
|
97
|
+
.api(storeRoute, (_req: Request, res: Response) =>res.status(HttpStatusCodes.CREATED).json({ message: "{{module}} create not implemented" }))
|
|
98
|
+
.api(updateRoute, (_req: Request, res: Response) => res.json({ message: "{{module}} update not implemented" }))
|
|
99
|
+
.api(destroyRoute, (_req: Request, res: Response) => res.json({ message: "{{module}} delete not implemented" }));
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
// Gate the whole group (authMiddleware resolves the session once for every route):
|
|
103
|
+
// export default group(authMiddleware).api(indexRoute, index) ...
|
|
104
|
+
//
|
|
105
|
+
// Or gate a single route by role. requireRole reads the role authMiddleware set,
|
|
106
|
+
// so authMiddleware must run first:
|
|
107
|
+
// group().api(destroyRoute, [authMiddleware, requireRole("admin")], destroy)
|
|
108
|
+
//
|
|
109
|
+
// Or group all authMiddleware and set single requireRole middleware
|
|
110
|
+
// so authMiddleware must run on all chain and requireRole single on per route:
|
|
111
|
+
// group(authMiddleware).api(destroyRoute, [requireRole("admin")], destroy)
|
|
112
|
+
//
|
|
113
|
+
// Delete the imports above when you do not need them.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Request, Response } from "express";
|
|
2
|
+
import { group, HttpStatusCodes } from "@/framework/facade.js";
|
|
3
|
+
import { authMiddleware } from "@/middlewares/auth-middleware.js";
|
|
4
|
+
import { requireRole } from "@/middlewares/role-middleware.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* {{module}} routes for {{controller}}.
|
|
8
|
+
*
|
|
9
|
+
* Wire each handler to a controller:
|
|
10
|
+
* import { index, show, store, update, destroy } from "@/modules/{{module}}/controllers/{{controller}}.js";
|
|
11
|
+
* export default group().get("/", index).get("/:id", show) ...
|
|
12
|
+
*
|
|
13
|
+
* Add validation with the validate() helper:
|
|
14
|
+
* import { validate } from "@/framework/facade.js";
|
|
15
|
+
* import { {{ClassName}}IdParamsSchema } from "@/modules/{{module}}/schemas/{{controller}}.js";
|
|
16
|
+
* const params = await validate({{ClassName}}IdParamsSchema, req.params);
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export default group()
|
|
20
|
+
.get("/", (_req: Request, res: Response) => res.json({ message: "{{module}} list not implemented", data: [] }))
|
|
21
|
+
.get("/:id", (_req: Request, res: Response) =>res.json({ message: "{{module}} item not implemented", data: { id: 0, name: "" } }))
|
|
22
|
+
.post("/", (_req: Request, res: Response) =>res.status(HttpStatusCodes.CREATED).json({ message: "{{module}} create not implemented" }))
|
|
23
|
+
.put("/:id", (_req: Request, res: Response) => res.json({ message: "{{module}} update not implemented" }))
|
|
24
|
+
.delete("/:id", (_req: Request, res: Response) => res.json({ message: "{{module}} delete not implemented" }));
|
|
25
|
+
|
|
26
|
+
// Gate the whole group (authMiddleware resolves the session once for every route):
|
|
27
|
+
// export default group(authMiddleware).api(indexRoute, index) ...
|
|
28
|
+
//
|
|
29
|
+
// Or gate a single route by role. requireRole reads the role authMiddleware set,
|
|
30
|
+
// so authMiddleware must run first:
|
|
31
|
+
// group().api(destroyRoute, [authMiddleware, requireRole("admin")], destroy)
|
|
32
|
+
//
|
|
33
|
+
// Or group all authMiddleware and set single requireRole middleware
|
|
34
|
+
// so authMiddleware must run on all chain and requireRole single on per route:
|
|
35
|
+
// group(authMiddleware).api(destroyRoute, [requireRole("admin")], destroy)
|
|
36
|
+
//
|
|
37
|
+
// Delete the imports above when you do not need them.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { z } from "@/framework/facade.js";
|
|
2
|
-
|
|
3
|
-
export const Create{{ClassName}}Schema = z.object({
|
|
4
|
-
name: z.string().min(1)
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
export const Update{{ClassName}}Schema = z.object({
|
|
8
|
-
name: z.string().min(1)
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
export const {{ClassName}}IdParamsSchema = z.object({
|
|
12
|
-
id: z.coerce.number().int().positive()
|
|
13
|
-
});
|
|
1
|
+
import { z } from "@/framework/facade.js";
|
|
2
|
+
|
|
3
|
+
export const Create{{ClassName}}Schema = z.object({
|
|
4
|
+
name: z.string().min(1)
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
export const Update{{ClassName}}Schema = z.object({
|
|
8
|
+
name: z.string().min(1)
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const {{ClassName}}IdParamsSchema = z.object({
|
|
12
|
+
id: z.coerce.number().int().positive()
|
|
13
|
+
});
|
|
14
14
|
|
package/express/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub}
RENAMED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { z } from "@/framework/facade.js";
|
|
2
|
-
|
|
3
|
-
export const {{ClassName}}ItemSchema = z.object({
|
|
4
|
-
id: z.number(),
|
|
5
|
-
name: z.string()
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
export const Create{{ClassName}}Schema = z.object({
|
|
9
|
-
name: z.string().min(1)
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export const Update{{ClassName}}Schema = z.object({
|
|
13
|
-
name: z.string().min(1)
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
export const {{ClassName}}IdParamsSchema = z.object({
|
|
17
|
-
id: z.coerce.number().int().positive()
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
export const {{ClassName}}ListResponseSchema = z.object({
|
|
21
|
-
message: z.string(),
|
|
22
|
-
data: z.array({{ClassName}}ItemSchema)
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
export const {{ClassName}}ResponseSchema = z.object({
|
|
26
|
-
message: z.string(),
|
|
27
|
-
data: {{ClassName}}ItemSchema
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
export const {{ClassName}}MessageSchema = z.object({
|
|
31
|
-
message: z.string()
|
|
32
|
-
});
|
|
1
|
+
import { z } from "@/framework/facade.js";
|
|
2
|
+
|
|
3
|
+
export const {{ClassName}}ItemSchema = z.object({
|
|
4
|
+
id: z.number(),
|
|
5
|
+
name: z.string()
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const Create{{ClassName}}Schema = z.object({
|
|
9
|
+
name: z.string().min(1)
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const Update{{ClassName}}Schema = z.object({
|
|
13
|
+
name: z.string().min(1)
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const {{ClassName}}IdParamsSchema = z.object({
|
|
17
|
+
id: z.coerce.number().int().positive()
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const {{ClassName}}ListResponseSchema = z.object({
|
|
21
|
+
message: z.string(),
|
|
22
|
+
data: z.array({{ClassName}}ItemSchema)
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const {{ClassName}}ResponseSchema = z.object({
|
|
26
|
+
message: z.string(),
|
|
27
|
+
data: {{ClassName}}ItemSchema
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const {{ClassName}}MessageSchema = z.object({
|
|
31
|
+
message: z.string()
|
|
32
|
+
});
|
|
33
33
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { db } from "@/framework/facade.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Why: Returns every {{controller}} row for list screens.
|
|
6
|
+
* When: Called by the controller's index() handler.
|
|
7
|
+
* Where: GET /{{controller}} route.
|
|
8
|
+
*
|
|
9
|
+
* TODO: import your model and query it, for example:
|
|
10
|
+
* import { {{tableVariable}} } from "@/modules/{{module}}/database/models/{{controller}}.js";
|
|
11
|
+
* return db.select().from({{tableVariable}});
|
|
12
|
+
*/
|
|
13
|
+
export const {{controller}}Service = {
|
|
14
|
+
/**
|
|
15
|
+
* Why: CRUD service for {{controller}}.
|
|
16
|
+
* When: Consumed directly by the controller as `{{controller}}Service.list()`, etc.
|
|
17
|
+
* Where: Wired into the module's controller handlers.
|
|
18
|
+
*/
|
|
19
|
+
list: async () => {
|
|
20
|
+
void db;
|
|
21
|
+
return [];
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Why: Returns one {{controller}} row by primary key.
|
|
26
|
+
* When: Called by the controller's show/update/destroy handlers.
|
|
27
|
+
* Where: Reused across GET/PUT/DELETE /{{controller}}/{id} routes.
|
|
28
|
+
*
|
|
29
|
+
* TODO: query the model by id and return the row, or null when missing.
|
|
30
|
+
*/
|
|
31
|
+
findOne: async (_id: number) => {
|
|
32
|
+
return null;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Why: Persists a new {{controller}} row.
|
|
37
|
+
* When: Called by the controller's store() handler.
|
|
38
|
+
* Where: POST /{{controller}} route.
|
|
39
|
+
*
|
|
40
|
+
* TODO: insert the validated payload and return the created row.
|
|
41
|
+
*/
|
|
42
|
+
create: async (_data: Record<string, unknown>) => {
|
|
43
|
+
return null;
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Why: Applies a partial change to one {{controller}} row.
|
|
48
|
+
* When: Called by the controller's update() handler.
|
|
49
|
+
* Where: PUT/PATCH /{{controller}}/{id} route.
|
|
50
|
+
*
|
|
51
|
+
* TODO: update by id, refresh updatedAt, and return the row, or null when missing.
|
|
52
|
+
*/
|
|
53
|
+
update: async (_id: number, _data: Record<string, unknown>) => {
|
|
54
|
+
return null;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Why: Removes one {{controller}} row.
|
|
59
|
+
* When: Called by the controller's destroy() handler.
|
|
60
|
+
* Where: DELETE /{{controller}}/{id} route.
|
|
61
|
+
*
|
|
62
|
+
* TODO: delete by id and return the removed row, or null when missing.
|
|
63
|
+
*/
|
|
64
|
+
remove: async (_id: number) => {
|
|
65
|
+
return null;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { db } from "@/framework/facade.js";
|
|
3
|
+
import { {{tableVariable}} } from "@/modules/{{module}}/database/models/{{controller}}.js";
|
|
4
|
+
|
|
5
|
+
export const {{controller}}Service = {
|
|
6
|
+
/**
|
|
7
|
+
* Why: Returns every {{controller}} row for list screens.
|
|
8
|
+
* When: Called by the controller's index() handler.
|
|
9
|
+
* Where: GET /{{controller}} route.
|
|
10
|
+
*/
|
|
11
|
+
list: async () => {
|
|
12
|
+
return db.select().from({{tableVariable}});
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Why: Returns one {{controller}} row by primary key.
|
|
17
|
+
* When: Called by the controller's show/update/destroy handlers.
|
|
18
|
+
* Where: Reused across GET/PUT/DELETE /{{controller}}/{id} routes.
|
|
19
|
+
*/
|
|
20
|
+
findOne: async (id: number) => {
|
|
21
|
+
const [row] = await db
|
|
22
|
+
.select()
|
|
23
|
+
.from({{tableVariable}})
|
|
24
|
+
.where(eq({{tableVariable}}.id, id))
|
|
25
|
+
.limit(1);
|
|
26
|
+
return row ?? null;
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Why: Persists a new {{controller}} row.
|
|
31
|
+
* When: Called by the controller's store() handler.
|
|
32
|
+
* Where: POST /{{controller}} route.
|
|
33
|
+
*/
|
|
34
|
+
create: async (data: Record<string, unknown>) => {
|
|
35
|
+
const [row] = await db.insert({{tableVariable}}).values(data).returning();
|
|
36
|
+
return row;
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Why: Applies a partial change to one {{controller}} row.
|
|
41
|
+
* When: Called by the controller's update() handler.
|
|
42
|
+
* Where: PUT/PATCH /{{controller}}/{id} route.
|
|
43
|
+
*/
|
|
44
|
+
update: async (id: number, data: Record<string, unknown>) => {
|
|
45
|
+
const [row] = await db
|
|
46
|
+
.update({{tableVariable}})
|
|
47
|
+
.set(data)
|
|
48
|
+
.where(eq({{tableVariable}}.id, id))
|
|
49
|
+
.returning();
|
|
50
|
+
return row ?? null;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Why: Removes one {{controller}} row.
|
|
55
|
+
* When: Called by the controller's destroy() handler.
|
|
56
|
+
* Where: DELETE /{{controller}}/{id} route.
|
|
57
|
+
*/
|
|
58
|
+
remove: async (id: number) => {
|
|
59
|
+
const [row] = await db
|
|
60
|
+
.delete({{tableVariable}})
|
|
61
|
+
.where(eq({{tableVariable}}.id, id))
|
|
62
|
+
.returning();
|
|
63
|
+
return row ?? null;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why: Shared TypeScript types for the {{module}} module.
|
|
3
|
+
* When: Needed by controllers, services, or helpers that must agree on a shape.
|
|
4
|
+
* Where: src/modules/{{module}}/types/{{name}}.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Why: Base shape for every {{name}} record. When: Used as a constraint or return type. Where: This file. */
|
|
8
|
+
export interface {{Name}}Record {
|
|
9
|
+
id: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Why: Result shape for a single-item response. When: Returned by show-style handlers. Where: This file. */
|
|
13
|
+
export interface {{Name}}Result {
|
|
14
|
+
message: string;
|
|
15
|
+
data: {{Name}}Record;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Why: Result shape for list responses. When: Returned by index-style handlers. Where: This file. */
|
|
19
|
+
export interface {{Name}}ListResult {
|
|
20
|
+
message: string;
|
|
21
|
+
data: {{Name}}Record[];
|
|
22
|
+
}
|
|
@@ -1,13 +1,56 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A name is safe when it can be used as a folder name, a URL segment, a table
|
|
3
|
+
* name and a TypeScript identifier without further quoting.
|
|
4
|
+
*
|
|
5
|
+
* Kebab-case, snake_case and camelCase all pass: `pascal` already folds `-` and
|
|
6
|
+
* `_`, and names are lowercased before this test. What fails is anything that
|
|
7
|
+
* would produce uncompilable code - spaces and dots break the generated
|
|
8
|
+
* `@/modules/...` import, a leading digit breaks the identifier.
|
|
9
|
+
*/
|
|
10
|
+
const NAME_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Best-effort readable repair of a rejected name, used only in error text.
|
|
14
|
+
* Returns "" when no safe suggestion exists (e.g. a leading digit cannot be
|
|
15
|
+
* reordered without guessing the developer's intent).
|
|
16
|
+
*/
|
|
17
|
+
function suggestName(value) {
|
|
18
|
+
const cleaned = value
|
|
19
|
+
.trim()
|
|
20
|
+
.toLowerCase()
|
|
21
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
22
|
+
.replace(/-{2,}/g, "-")
|
|
23
|
+
.replace(/^[-_]+|[-_]+$/g, "");
|
|
24
|
+
if (!cleaned || !/^[a-z]/.test(cleaned)) return "";
|
|
25
|
+
return cleaned;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validate that a name value exists and is safe to use.
|
|
30
|
+
* Throws with the exact command to run instead when the value is unusable.
|
|
31
|
+
*/
|
|
32
|
+
export function assertName(value, label) {
|
|
33
|
+
if (!value) {
|
|
34
|
+
throw new Error(`${label} is required.`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const name = value.trim().toLowerCase();
|
|
38
|
+
if (NAME_PATTERN.test(name)) return name;
|
|
39
|
+
|
|
40
|
+
const suggestion = suggestName(value);
|
|
41
|
+
const reason = /^[0-9]/.test(name)
|
|
42
|
+
? "must start with a lowercase letter"
|
|
43
|
+
: "may only contain lowercase letters, numbers, dashes and underscores";
|
|
44
|
+
throw new Error(`Invalid ${label.toLowerCase()}: "${value}" - a name ${reason}.${suggestion ? ` Use "${suggestion}" instead.` : ""}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Convert a kebab-case or snake_case string to PascalCase. */
|
|
48
|
+
export function pascal(input) {
|
|
49
|
+
return input.replace(/(^\w|[-_]\w)/g, (part) => part.replace(/[-_]/g, "").toUpperCase());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Convert a kebab-case or snake_case string to camelCase. */
|
|
53
|
+
export function camelCase(input) {
|
|
54
|
+
const p = pascal(input);
|
|
55
|
+
return p.charAt(0).toLowerCase() + p.slice(1);
|
|
56
|
+
}
|
|
@@ -1,54 +1,57 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
-
import { glob } from "glob";
|
|
5
|
-
|
|
6
|
-
function resolveModulesPath() {
|
|
7
|
-
const cwd = process.cwd();
|
|
8
|
-
const srcModules = path.resolve(cwd, "src/modules");
|
|
9
|
-
const distModules = path.resolve(cwd, "dist/src/modules");
|
|
10
|
-
const entry = String(process.argv[1] || "");
|
|
11
|
-
const runningDist = /[\\/]dist[\\/]/.test(entry);
|
|
12
|
-
|
|
13
|
-
if (runningDist && fs.existsSync(distModules)) return distModules;
|
|
14
|
-
return srcModules;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const modulesPath = resolveModulesPath();
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Why: Finds module files by glob from active src/dist modules path.
|
|
21
|
-
* When: Framework needs module routes/jobs/seeders/schedules discovery.
|
|
22
|
-
* Where: Route registrar, queue boot, scheduler boot, seeding.
|
|
23
|
-
* How: Runs glob with absolute paths and stable sorting.
|
|
24
|
-
*/
|
|
25
|
-
export async function discoverModuleFiles(pattern: string) {
|
|
26
|
-
const files = await glob(pattern, {
|
|
27
|
-
cwd: modulesPath,
|
|
28
|
-
absolute: true,
|
|
29
|
-
nodir: true,
|
|
30
|
-
windowsPathsNoEscape: true
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return files.sort();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Why: Derives module
|
|
38
|
-
* When: Mounting route prefixes and grouping module assets.
|
|
39
|
-
* Where: Route registration logic.
|
|
40
|
-
* How:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { glob } from "glob";
|
|
5
|
+
|
|
6
|
+
function resolveModulesPath() {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const srcModules = path.resolve(cwd, "src/modules");
|
|
9
|
+
const distModules = path.resolve(cwd, "dist/src/modules");
|
|
10
|
+
const entry = String(process.argv[1] || "");
|
|
11
|
+
const runningDist = /[\\/]dist[\\/]/.test(entry);
|
|
12
|
+
|
|
13
|
+
if (runningDist && fs.existsSync(distModules)) return distModules;
|
|
14
|
+
return srcModules;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const modulesPath = resolveModulesPath();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Why: Finds module files by glob from active src/dist modules path.
|
|
21
|
+
* When: Framework needs module routes/jobs/seeders/schedules discovery.
|
|
22
|
+
* Where: Route registrar, queue boot, scheduler boot, seeding.
|
|
23
|
+
* How: Runs glob with absolute paths and stable sorting.
|
|
24
|
+
*/
|
|
25
|
+
export async function discoverModuleFiles(pattern: string) {
|
|
26
|
+
const files = await glob(pattern, {
|
|
27
|
+
cwd: modulesPath,
|
|
28
|
+
absolute: true,
|
|
29
|
+
nodir: true,
|
|
30
|
+
windowsPathsNoEscape: true
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return files.sort();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Why: Derives the module name from an absolute discovered file path.
|
|
38
|
+
* When: Mounting route prefixes and grouping module assets.
|
|
39
|
+
* Where: Route registration logic.
|
|
40
|
+
* How: Strips the artifact folder (e.g. `routes/`), keeping any panel segment so
|
|
41
|
+
* `admin/post/routes/index.ts` resolves to `admin/post` rather than `admin`.
|
|
42
|
+
* Returns POSIX separators because the result is used as a URL prefix.
|
|
43
|
+
*/
|
|
44
|
+
export function moduleNameFromPath(filePath: string) {
|
|
45
|
+
const relative = path.relative(modulesPath, filePath);
|
|
46
|
+
return path.dirname(path.dirname(relative)).split(path.sep).join("/");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Why: Dynamically imports a discovered module file.
|
|
51
|
+
* When: Bootstrapping pluggable module artifacts.
|
|
52
|
+
* Where: Discovery consumers across framework.
|
|
53
|
+
* How: Converts file path to file URL and imports it.
|
|
54
|
+
*/
|
|
55
|
+
export async function importFile(filePath: string) {
|
|
56
|
+
return await import(pathToFileURL(filePath).href);
|
|
57
|
+
}
|
|
@@ -3,7 +3,7 @@ import { db } from "@/framework/database/connection.js";
|
|
|
3
3
|
import { dispatchEvent } from "@/framework/events/dispatcher.js";
|
|
4
4
|
import { redisClientIfReady } from "@/framework/redis/client.js";
|
|
5
5
|
import { mail } from "@/framework/support/mail.js";
|
|
6
|
-
import { notifications } from "@/modules/auth/database/models/
|
|
6
|
+
import { notifications } from "@/modules/auth/database/models/notification.js";
|
|
7
7
|
import { users } from "@/modules/auth/database/models/user.js";
|
|
8
8
|
|
|
9
9
|
/** Extra JSON metadata attached to the notification (e.g. entity IDs, links). */
|