@tulipes/cli 0.2.0 → 0.3.0-rc.2
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/dist/config-check.js +1 -1
- package/dist/env-check.d.ts +5 -3
- package/dist/env-check.js +26 -19
- package/dist/env-check.js.map +1 -1
- package/dist/main.js +83 -19
- package/dist/main.js.map +1 -1
- package/dist/migrate-0.12-env.d.ts +36 -0
- package/dist/migrate-0.12-env.js +250 -0
- package/dist/migrate-0.12-env.js.map +1 -0
- package/dist/migrate-0.12-scaffold.d.ts +1 -0
- package/dist/migrate-0.12-scaffold.js +197 -0
- package/dist/migrate-0.12-scaffold.js.map +1 -0
- package/dist/migrate-0.12.d.ts +47 -0
- package/dist/migrate-0.12.js +210 -0
- package/dist/migrate-0.12.js.map +1 -0
- package/dist/migrate.d.ts +3 -1
- package/dist/migrate.js +11 -5
- package/dist/migrate.js.map +1 -1
- package/dist/new-module.d.ts +7 -0
- package/dist/new-module.js +35 -16
- package/dist/new-module.js.map +1 -1
- package/dist/roles.d.ts +15 -0
- package/dist/roles.js +54 -0
- package/dist/roles.js.map +1 -0
- package/dist/routes.d.ts +7 -2
- package/dist/routes.js +19 -10
- package/dist/routes.js.map +1 -1
- package/dist/spec.d.ts +2 -0
- package/dist/spec.js +13 -11
- package/dist/spec.js.map +1 -1
- package/dist/sync.js +66 -53
- package/dist/sync.js.map +1 -1
- package/package.json +4 -4
- package/templates/AGENTS.md +35 -23
- package/templates/app/.envs/.env.development +2 -1
- package/templates/app/.envs/.env.production +2 -2
- package/templates/app/.envs/.env.staging +2 -2
- package/templates/app/README.md +44 -29
- package/templates/app/config/app.config.ts +60 -7
- package/templates/app/docs/agent-manifest.json +8 -681
- package/templates/app/modules/agent/controllers/{agent.controllers.ts → web.agent.controllers.ts} +1 -1
- package/templates/app/modules/agent/routes/{agent.routes.ts → web.agent.routes.ts} +4 -4
- package/templates/app/modules/auth/bootstrap/credentials.bootstrap.ts +20 -12
- package/templates/app/modules/auth/bootstrap/seed-admin.bootstrap.ts +4 -2
- package/templates/app/modules/auth/controllers/{auth.controllers.ts → web.auth.controllers.ts} +1 -1
- package/templates/app/modules/auth/helpers/passport.ts +1 -1
- package/templates/app/modules/auth/helpers/session-cache.ts +3 -3
- package/templates/app/modules/auth/helpers/tokens.ts +3 -5
- package/templates/app/modules/auth/module.acl.ts +31 -9
- package/templates/app/modules/auth/module.config.ts +5 -3
- package/templates/app/modules/auth/package.json +10 -10
- package/templates/app/modules/auth/routes/{auth.routes.ts → web.auth.routes.ts} +15 -10
- package/templates/app/modules/auth/tests/auth.test.ts +1 -1
- package/templates/app/modules/config/controllers/{config.controllers.ts → bo.config.controllers.ts} +3 -2
- package/templates/app/modules/config/helpers/audit.ts +1 -1
- package/templates/app/modules/config/helpers/describe.ts +35 -26
- package/templates/app/modules/config/module.acl.ts +1 -1
- package/templates/app/modules/config/routes/{config.routes.ts → bo.config.routes.ts} +5 -5
- package/templates/app/modules/config/tests/config.test.ts +14 -6
- package/templates/app/modules/core/helpers/cache/index.ts +8 -7
- package/templates/app/modules/core/helpers/lazy.ts +3 -3
- package/templates/app/modules/core/helpers/logger/index.ts +24 -16
- package/templates/app/modules/core/module.config.ts +5 -3
- package/templates/app/modules/core/package.json +46 -19
- package/templates/app/modules/core/routes/core.routes.ts +4 -4
- package/templates/app/modules/hello/agent/hello.agent.ts +2 -2
- package/templates/app/modules/hello/controllers/{hello.controllers.ts → web/hello.controllers.ts} +1 -1
- package/templates/app/modules/hello/controllers/{greetings.controllers.ts → web/hello.greetings.controllers.ts} +2 -2
- package/templates/app/modules/hello/module.acl.ts +12 -7
- package/templates/app/modules/hello/queues/hello.queues.ts +1 -1
- package/templates/app/modules/hello/routes/{greetings.routes.ts → web/hello.greetings.routes.ts} +4 -4
- package/templates/app/modules/hello/routes/{hello.routes.ts → web/hello.routes.ts} +14 -8
- package/templates/app/modules/security/helpers/rate-limit.ts +3 -2
- package/templates/app/modules/security/routes/security.routes.ts +4 -4
- package/templates/app/modules/settings/bootstrap/log-streams.bootstrap.ts +2 -2
- package/templates/app/modules/settings/controllers/{settings.controllers.ts → bo.settings.controllers.ts} +7 -6
- package/templates/app/modules/settings/helpers/log-stream.ts +6 -5
- package/templates/app/modules/settings/routes/{settings.routes.ts → bo.settings.routes.ts} +3 -3
- package/templates/app/modules/settings/tests/settings.test.ts +21 -25
- package/templates/app/modules/users/controllers/{users.controllers.ts → bo.users.controllers.ts} +5 -55
- package/templates/{overlays/no-queues/modules/users/controllers/users.controllers.ts → app/modules/users/controllers/web.users.controllers.ts} +4 -42
- package/templates/app/modules/users/module.acl.ts +4 -4
- package/templates/app/modules/users/queues/users.queues.ts +1 -1
- package/templates/app/modules/users/routes/bo.users.routes.ts +39 -0
- package/templates/app/modules/users/routes/web.users.routes.ts +45 -0
- package/templates/app/nginx/production.conf +1 -1
- package/templates/app/scripts/deploy-nginx.ts +3 -2
- package/templates/app/tests/support.ts +4 -3
- package/templates/claude/skills/tulipes-agent-tool/SKILL.md +6 -4
- package/templates/claude/skills/tulipes-boot-errors/SKILL.md +13 -7
- package/templates/claude/skills/tulipes-config/SKILL.md +6 -4
- package/templates/claude/skills/tulipes-endpoint/SKILL.md +34 -15
- package/templates/claude/skills/tulipes-env-variable/SKILL.md +62 -37
- package/templates/claude/skills/tulipes-i18n/SKILL.md +20 -1
- package/templates/claude/skills/tulipes-module/SKILL.md +10 -5
- package/templates/claude/skills/tulipes-permissions/SKILL.md +123 -35
- package/templates/claude/skills/tulipes-queue/SKILL.md +3 -2
- package/templates/claude/skills/tulipes-settings/SKILL.md +9 -8
- package/templates/claude/skills/tulipes-spec/SKILL.md +12 -2
- package/templates/minimal/config/app.config.ts +13 -3
- package/templates/minimal/modules/agent/routes/{agent.routes.ts → web.agent.routes.ts} +2 -2
- package/templates/minimal/modules/core/module.acl.ts +7 -1
- package/templates/minimal/modules/core/package.json +4 -4
- package/templates/minimal/modules/hello/module.acl.ts +1 -1
- package/templates/minimal/modules/hello/routes/{health.routes.ts → web.hello.health.routes.ts} +1 -1
- package/templates/minimal/modules/hello/routes/{hello.routes.ts → web.hello.routes.ts} +1 -1
- package/templates/overlays/no-queues/modules/hello/controllers/{greetings.controllers.ts → web/hello.greetings.controllers.ts} +1 -1
- package/templates/overlays/no-queues/modules/users/controllers/bo.users.controllers.ts +57 -0
- package/templates/versions.json +8 -8
- package/templates/app/modules/users/routes/users.routes.ts +0 -60
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { forgetUser } from "@app/auth";
|
|
2
|
+
import type { Request, RequestHandler, Response } from "express";
|
|
3
|
+
import type { Ctx } from "@tulipes/core/boot";
|
|
4
|
+
import { HttpError } from "@tulipes/core/http";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The users back office — the users:bo:* endpoints, granted to admin
|
|
8
|
+
* through its users:* grant. Same shape as the web controller beside it:
|
|
9
|
+
* one class holding the context, one handler per method, failures thrown
|
|
10
|
+
* as i18n keys.
|
|
11
|
+
*/
|
|
12
|
+
export class BoUsersController {
|
|
13
|
+
readonly #ctx: Ctx;
|
|
14
|
+
|
|
15
|
+
constructor(ctx: Ctx) {
|
|
16
|
+
this.#ctx = ctx;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Soft delete: the row stays in mongo but leaves every ordinary query. */
|
|
20
|
+
softDelete(): RequestHandler {
|
|
21
|
+
return async (req: Request, res: Response) => {
|
|
22
|
+
const user = await this.#ctx.models!.get("User").findOne({ email: req.params.email });
|
|
23
|
+
if (!user) throw new HttpError(404, "notFound");
|
|
24
|
+
|
|
25
|
+
// The account leaves every query, so its sessions resolve to nobody on
|
|
26
|
+
// the cold path; the cached identities would not know, so they go too.
|
|
27
|
+
await forgetUser(this.#ctx, String(user._id));
|
|
28
|
+
await (user as unknown as { softDelete(): Promise<unknown> }).softDelete();
|
|
29
|
+
res.respond({ data: { email: req.params.email, soft_deleted: true } });
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
listDeleted(): RequestHandler {
|
|
34
|
+
return async (_req: Request, res: Response) => {
|
|
35
|
+
const deleted = await this.#ctx.models!
|
|
36
|
+
.get("User")
|
|
37
|
+
.find({ is_deleted: true })
|
|
38
|
+
.setOptions({ withDeleted: true } as Record<string, unknown>)
|
|
39
|
+
.select("email role -_id")
|
|
40
|
+
.lean();
|
|
41
|
+
|
|
42
|
+
res.respond({ data: deleted });
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
create(): RequestHandler {
|
|
47
|
+
return async (req: Request, res: Response) => {
|
|
48
|
+
const { email } = (req.body ?? {}) as { email?: string };
|
|
49
|
+
// `field` is what puts this against the input in the envelope.
|
|
50
|
+
if (!email) throw new HttpError(422, "emailRequired", { field: "email" });
|
|
51
|
+
|
|
52
|
+
await this.#ctx.models!.get("User").create({ email });
|
|
53
|
+
|
|
54
|
+
res.respond({ status: 201, data: { email } });
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
package/templates/versions.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"pnpm": "10.28.0",
|
|
3
|
-
"@tulipes/core": "0.
|
|
4
|
-
"@tulipes/cli": "0.
|
|
5
|
-
"@tulipes/spec": "0.
|
|
6
|
-
"@tulipes/mongoose": "0.2.
|
|
7
|
-
"@tulipes/bullmq": "0.2.
|
|
8
|
-
"@tulipes/socket.io": "0.1.2",
|
|
9
|
-
"@tulipes/testing": "0.1.
|
|
10
|
-
"@tulipes/mcp": "0.1.
|
|
3
|
+
"@tulipes/core": "0.12.0-rc.2",
|
|
4
|
+
"@tulipes/cli": "0.3.0-rc.2",
|
|
5
|
+
"@tulipes/spec": "0.4.0-rc.2",
|
|
6
|
+
"@tulipes/mongoose": "0.2.1-rc.2",
|
|
7
|
+
"@tulipes/bullmq": "0.2.1-rc.2",
|
|
8
|
+
"@tulipes/socket.io": "0.1.3-rc.2",
|
|
9
|
+
"@tulipes/testing": "0.1.1-rc.2",
|
|
10
|
+
"@tulipes/mcp": "0.1.1-rc.2"
|
|
11
11
|
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { Router } from "express";
|
|
2
|
-
import type { BackendCtx } from "@tulipes/core/boot";
|
|
3
|
-
import { z } from "zod/v4";
|
|
4
|
-
|
|
5
|
-
import { UsersController } from "../controllers/users.controllers.ts";
|
|
6
|
-
import { createBody, emailParam, listQuery, userShape } from "../schemas/users.schemas.ts";
|
|
7
|
-
|
|
8
|
-
export default function usersRoutes(ctx: BackendCtx): void {
|
|
9
|
-
const { rai, routes } = ctx;
|
|
10
|
-
const router = Router();
|
|
11
|
-
const users = new UsersController(ctx);
|
|
12
|
-
|
|
13
|
-
router.get("/", rai({
|
|
14
|
-
id: "users:read",
|
|
15
|
-
name: "List users",
|
|
16
|
-
description: "Page through accounts, clamped by the shared bounds",
|
|
17
|
-
// Validated on the way in, and described on the way out: one
|
|
18
|
-
// declaration, so the spec cannot drift from what is enforced.
|
|
19
|
-
query: listQuery,
|
|
20
|
-
returns: z.array(userShape),
|
|
21
|
-
}), users.list());
|
|
22
|
-
|
|
23
|
-
router.delete("/:email", rai({
|
|
24
|
-
id: "users:delete",
|
|
25
|
-
name: "Soft-delete a user",
|
|
26
|
-
description: "Flags the account; ordinary queries stop returning it",
|
|
27
|
-
params: emailParam,
|
|
28
|
-
}), users.softDelete());
|
|
29
|
-
|
|
30
|
-
router.get("/deleted", rai({
|
|
31
|
-
id: "users:read-deleted",
|
|
32
|
-
name: "List soft-deleted users",
|
|
33
|
-
}), users.listDeleted());
|
|
34
|
-
|
|
35
|
-
router.post("/", rai({
|
|
36
|
-
id: "users:create",
|
|
37
|
-
status: 201,
|
|
38
|
-
name: "Create a user",
|
|
39
|
-
description: "Adds the account and queues its welcome email",
|
|
40
|
-
body: createBody,
|
|
41
|
-
}), users.create());
|
|
42
|
-
|
|
43
|
-
router.get("/ping", rai({
|
|
44
|
-
id: "users:ping",
|
|
45
|
-
name: "Users module status",
|
|
46
|
-
}), users.ping());
|
|
47
|
-
|
|
48
|
-
router.get("/missing", rai({
|
|
49
|
-
id: "users:demo-missing",
|
|
50
|
-
name: "HttpError demo",
|
|
51
|
-
}), users.demoMissing());
|
|
52
|
-
|
|
53
|
-
router.get("/boom", rai({
|
|
54
|
-
id: "users:demo-boom",
|
|
55
|
-
name: "Unhandled error demo",
|
|
56
|
-
}), users.demoBoom());
|
|
57
|
-
|
|
58
|
-
// Deliberately outside the API prefix, to show a mount of its own.
|
|
59
|
-
routes.mount(router, "/users");
|
|
60
|
-
}
|