teamshare-bridge 0.22.7 → 0.22.8
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 +383 -383
- package/dist/bridge/daemon.js +43 -20
- package/dist/bridge/daemon.js.map +1 -1
- package/dist/bridge/install-verify.js +8 -8
- package/dist/bridge/spawn.d.ts +14 -0
- package/dist/bridge/spawn.js +7 -1
- package/dist/bridge/spawn.js.map +1 -1
- package/dist/bridge/wake-handler.d.ts +8 -15
- package/dist/bridge/wake-handler.js +109 -14
- package/dist/bridge/wake-handler.js.map +1 -1
- package/dist/bridge/wake-outbox.js +7 -0
- package/dist/bridge/wake-outbox.js.map +1 -1
- package/dist/cli/commands/plan.js +4 -0
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/lib/browser/human-input-guard.js +98 -98
- package/dist/lib/browser/screen-change.js +60 -60
- package/dist/lib/llm/usage-reader.js +4 -4
- package/dist/lib/workspace/classify.d.ts +29 -0
- package/dist/lib/workspace/classify.js +62 -0
- package/dist/lib/workspace/classify.js.map +1 -1
- package/dist/lib/workspace/presence.d.ts +31 -0
- package/dist/lib/workspace/presence.js +35 -1
- package/dist/lib/workspace/presence.js.map +1 -1
- package/dist/lib/workspace/shim-runner.js +23 -0
- package/dist/lib/workspace/shim-runner.js.map +1 -1
- package/package.json +70 -70
- package/skills/THIRD_PARTY_NOTICES.md +26 -26
- package/skills/architect/LICENSE +21 -21
- package/skills/codebase-design/LICENSE +21 -21
- package/skills/diagnosing-bugs/LICENSE +21 -21
- package/skills/domain-modeling/LICENSE +21 -21
- package/skills/improve-codebase-architecture/LICENSE +21 -21
- package/skills/recover/LICENSE +21 -21
- package/skills/review/LICENSE +21 -21
- package/skills/tdd/LICENSE +21 -21
- package/skills/teamshare-auth-rbac/SKILL.md +78 -78
- package/skills/teamshare-cdd/SKILL.md +205 -205
- package/skills/teamshare-modularity/SKILL.md +146 -146
- package/skills/teamshare-orchestrator-ask/SKILL.md +77 -77
- package/skills/teamshare-scaffold/SKILL.md +116 -116
- package/templates/backend/.env.example.tpl +13 -13
- package/templates/backend/nest-cli.json.tpl +8 -8
- package/templates/backend/package.json.tpl +41 -41
- package/templates/backend/prisma/schema.prisma.tpl +39 -39
- package/templates/backend/src/app.module.ts.tpl +24 -24
- package/templates/backend/src/common/filters/all-exceptions.filter.ts.tpl +59 -59
- package/templates/backend/src/common/guards/jwt-auth.guard.ts.tpl +27 -27
- package/templates/backend/src/common/guards/rbac.guard.ts.tpl +48 -48
- package/templates/backend/src/common/interceptors/transform.interceptor.ts.tpl +44 -44
- package/templates/backend/src/common/pipes/zod-validation.pipe.ts.tpl +29 -29
- package/templates/backend/src/common/swagger/envelope.schemas.ts.tpl +53 -53
- package/templates/backend/src/common/swagger/error.schemas.ts.tpl +62 -62
- package/templates/backend/src/main.ts.tpl +27 -27
- package/templates/backend/src/modules/example/example.controller.ts.tpl +102 -102
- package/templates/backend/src/modules/example/example.dto.ts.tpl +14 -14
- package/templates/backend/src/modules/example/example.module.ts.tpl +10 -10
- package/templates/backend/src/modules/example/example.service.ts.tpl +45 -45
- package/templates/backend/tsconfig.json.tpl +26 -26
- package/templates/common/.gitignore.tpl +42 -42
- package/templates/common/AGENTS.md.tpl +58 -58
- package/templates/common/docs/adr/README.md +25 -25
- package/templates/common/docs/api-contract.md.tpl +72 -72
- package/templates/web/next.config.ts.tpl +7 -7
- package/templates/web/package.json.tpl +37 -37
- package/templates/web/src/app/globals.css.tpl +48 -48
- package/templates/web/src/app/layout.tsx.tpl +25 -25
- package/templates/web/src/components/shared/app-button.tsx.tpl +59 -59
- package/templates/web/src/components/shared/app-empty-state.tsx.tpl +39 -39
- package/templates/web/src/components/shared/app-list-toolbar.tsx.tpl +63 -63
- package/templates/web/src/components/shared/app-skeleton.tsx.tpl +14 -14
- package/templates/web/src/components/shared/app-table.tsx.tpl +128 -128
- package/templates/web/src/components/shared/index.ts.tpl +5 -5
- package/templates/web/src/hooks/use-list-state.ts.tpl +72 -72
- package/templates/web/src/hooks/use-url-tab.ts.tpl +25 -25
- package/templates/web/src/lib/api/client.ts.tpl +123 -123
- package/templates/web/src/lib/providers.tsx.tpl +35 -35
- package/templates/web/src/lib/query-keys.ts.tpl +21 -21
- package/templates/web/src/lib/utils.ts.tpl +7 -7
- package/templates/web/src/lib/validation/schemas.ts.tpl +15 -15
- package/templates/web/src/modules/example/views/example-list-view.tsx.tpl +97 -97
- package/templates/web/tsconfig.json.tpl +23 -23
- package/tools/workspace-arbitration-check.mjs +389 -389
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: teamshare-scaffold
|
|
3
|
-
description: "Enforce TeamShare standards when scaffolding new codebases, auditing existing ones, or cleaning up code. Covers stack decisions, folder layouts, view recipes, RBAC guards, loading states, safety patterns (no lint-disable unless worse), and the audit report format."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# teamshare-scaffold
|
|
7
|
-
|
|
8
|
-
Standards enforcement for TeamShare codebases. Use when scaffolding a new repo, auditing an existing codebase, or cleaning up code.
|
|
9
|
-
|
|
10
|
-
## Stack Decisions
|
|
11
|
-
|
|
12
|
-
| Layer | Stack |
|
|
13
|
-
|-------|-------|
|
|
14
|
-
| Web | Next.js App Router + TS + Tailwind v4 + shadcn (radix-nova) + TS* wrappers |
|
|
15
|
-
| Icons | iconsax-react (variant+color always, no emoji) |
|
|
16
|
-
| Tables | @tanstack/react-table v8 (NOT v9) |
|
|
17
|
-
| Forms | TSForm + zodResolver + zod schemas mirroring backend |
|
|
18
|
-
| State | useListState + useUrlTab (URL-driven) |
|
|
19
|
-
| Backend | NestJS 11 + Prisma 6 + zod-first DTOs + envelope |
|
|
20
|
-
| Mobile | Expo SDK 57 + NativeWind v4 + reusables |
|
|
21
|
-
|
|
1
|
+
---
|
|
2
|
+
name: teamshare-scaffold
|
|
3
|
+
description: "Enforce TeamShare standards when scaffolding new codebases, auditing existing ones, or cleaning up code. Covers stack decisions, folder layouts, view recipes, RBAC guards, loading states, safety patterns (no lint-disable unless worse), and the audit report format."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# teamshare-scaffold
|
|
7
|
+
|
|
8
|
+
Standards enforcement for TeamShare codebases. Use when scaffolding a new repo, auditing an existing codebase, or cleaning up code.
|
|
9
|
+
|
|
10
|
+
## Stack Decisions
|
|
11
|
+
|
|
12
|
+
| Layer | Stack |
|
|
13
|
+
|-------|-------|
|
|
14
|
+
| Web | Next.js App Router + TS + Tailwind v4 + shadcn (radix-nova) + TS* wrappers |
|
|
15
|
+
| Icons | iconsax-react (variant+color always, no emoji) |
|
|
16
|
+
| Tables | @tanstack/react-table v8 (NOT v9) |
|
|
17
|
+
| Forms | TSForm + zodResolver + zod schemas mirroring backend |
|
|
18
|
+
| State | useListState + useUrlTab (URL-driven) |
|
|
19
|
+
| Backend | NestJS 11 + Prisma 6 + zod-first DTOs + envelope |
|
|
20
|
+
| Mobile | Expo SDK 57 + NativeWind v4 + reusables |
|
|
21
|
+
|
|
22
22
|
## Frontend Layout
|
|
23
23
|
|
|
24
24
|
- `src/components/ui/` = VENDOR shadcn. DO NOT EDIT. DO NOT IMPORT from app code.
|
|
25
25
|
- `src/components/shared/` = Shared wrappers (AppButton, AppTable, etc). Barrel index.ts.
|
|
26
26
|
- `src/modules/<feature>/{views,components,columns,hooks,utils}`
|
|
27
27
|
- `src/lib/api/<feature>.ts` + `src/lib/query-keys.ts` + `src/lib/validation/schemas.ts`
|
|
28
|
-
- Pages stay thin. Views do the work.
|
|
29
|
-
|
|
30
|
-
## Backend Layout
|
|
31
|
-
|
|
32
|
-
- `src/modules/<feature>/{controller,service,dto,module}`
|
|
33
|
-
- `src/common/{guards,filters,interceptors,pipes,swagger,constants,utils,prisma,logging}`
|
|
34
|
-
- Prisma schema.prisma is canonical. `npx prisma migrate deploy` only.
|
|
35
|
-
|
|
28
|
+
- Pages stay thin. Views do the work.
|
|
29
|
+
|
|
30
|
+
## Backend Layout
|
|
31
|
+
|
|
32
|
+
- `src/modules/<feature>/{controller,service,dto,module}`
|
|
33
|
+
- `src/common/{guards,filters,interceptors,pipes,swagger,constants,utils,prisma,logging}`
|
|
34
|
+
- Prisma schema.prisma is canonical. `npx prisma migrate deploy` only.
|
|
35
|
+
|
|
36
36
|
## View Recipe (every list view)
|
|
37
37
|
|
|
38
38
|
1. `useListState({ pageSize })` - URL-driven
|
|
@@ -43,91 +43,91 @@ Standards enforcement for TeamShare codebases. Use when scaffolding a new repo,
|
|
|
43
43
|
6. Toolbar: `AppListToolbar` with actions slot (create button always visible)
|
|
44
44
|
7. Mutations: `useMutation` + toast success/error + `queryClient.invalidateQueries`
|
|
45
45
|
|
|
46
|
-
Every view MUST handle loading, error, and empty states. Never show white screen.
|
|
47
|
-
|
|
48
|
-
## RBAC Guard Table
|
|
49
|
-
|
|
50
|
-
| Who | Guard stack |
|
|
51
|
-
|-----|-------------|
|
|
52
|
-
| Human only | `@Permissions('entity.action')` |
|
|
53
|
-
| Agent + human | `@Public()` + `AgentsDualAuthGuard` + `@ApiKeyScopes` |
|
|
54
|
-
| Truly public | `@Public()` only |
|
|
55
|
-
| MCP | `ApiKeyGuard` + `canUseTool()` |
|
|
56
|
-
|
|
57
|
-
Never combine @Permissions with AgentsDualAuthGuard on same route.
|
|
58
|
-
|
|
59
|
-
Three enforcement layers: capability + key scope + project access. Never less than two.
|
|
60
|
-
|
|
61
|
-
## IconSax Rules
|
|
62
|
-
|
|
63
|
-
Every icon instance MUST pass `variant` and `color` props. Default variant="Outline".
|
|
64
|
-
- Bold for active nav
|
|
65
|
-
- TwoTone for empty-state illustrations
|
|
66
|
-
- Broken for destructive confirmations
|
|
67
|
-
- Sizes: 16/20/24/32
|
|
68
|
-
|
|
69
|
-
## Safety Patterns
|
|
70
|
-
|
|
71
|
-
1. **eslint-disable only when fix is worse.** Add `// eslint-disable-next-line <rule> -- <reason>`. Inventoried for revert.
|
|
72
|
-
2. **Never hand-edit generated/ or vendor ui/.**
|
|
73
|
-
3. **Build/typecheck verified.** Failed fixes reverted + noted.
|
|
74
|
-
4. **Cleanup in worktrees/branches.**
|
|
75
|
-
5. **Secrets never in code/logs/messages.**
|
|
76
|
-
6. **Existing repos: additive fixes only.** Never restructure.
|
|
77
|
-
7. **Max 120 chars per line.**
|
|
78
|
-
|
|
79
|
-
## Audit Report Format
|
|
80
|
-
|
|
81
|
-
When running an audit, produce findings in this structure:
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
# Audit Report — [repo-name]
|
|
85
|
-
|
|
86
|
-
**Date:** YYYY-MM-DD
|
|
87
|
-
**Scope:** [files scanned]
|
|
88
|
-
|
|
89
|
-
## Critical / Security
|
|
90
|
-
- [finding] [file:line] [evidence] [suggested fix]
|
|
91
|
-
|
|
92
|
-
## Modularity
|
|
93
|
-
- [finding] [file:line] [evidence] [suggested fix]
|
|
94
|
-
|
|
95
|
-
## Quality
|
|
96
|
-
- [finding] [file:line] [evidence] [suggested fix]
|
|
97
|
-
|
|
98
|
-
## Advisory (existing code)
|
|
99
|
-
- [finding] [file:line] [note]
|
|
100
|
-
|
|
101
|
-
## Summary
|
|
102
|
-
- Total findings: N (Critical: X, Security: Y, Modularity: Z, Quality: W)
|
|
103
|
-
- Gate status: build [pass/fail], typecheck [pass/fail]
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Severity levels: Critical (security, data loss), High (broken patterns, missing guards),
|
|
107
|
-
Medium (quality, missing states), Low (style, naming), Advisory (existing code deviations).
|
|
108
|
-
|
|
109
|
-
## Scaffold Task Flow
|
|
110
|
-
|
|
111
|
-
When asked to scaffold a new codebase:
|
|
112
|
-
1. Ask questions first (stack, auth provider, DB, package manager) if --ask
|
|
113
|
-
2. Write plan.md before generating
|
|
114
|
-
3. Create folder tree from template
|
|
115
|
-
4. Write AGENTS.md + docs/ skeleton
|
|
116
|
-
5. Generate module skeletons with full guard stack + loading states
|
|
117
|
-
6. Run build/typecheck gates
|
|
118
|
-
7. Write summary.md with what was built
|
|
119
|
-
|
|
120
|
-
When asked to audit an existing codebase:
|
|
121
|
-
1. Scan all dimensions (security, modularity, rbac, errors, quality, ux, docs, gates)
|
|
122
|
-
2. Existing code deviations = Advisory only (never force restructure)
|
|
123
|
-
3. New code deviations = High/Critical findings
|
|
124
|
-
4. Produce detailed markdown report with file:line evidence
|
|
125
|
-
5. Suggest fixes for each finding
|
|
126
|
-
|
|
127
|
-
When asked to clean up code:
|
|
128
|
-
1. Run audit first
|
|
129
|
-
2. Group findings into fix tasks
|
|
130
|
-
3. Apply mechanical fixes (line length, naming, imports)
|
|
131
|
-
4. For complex fixes: ask_human or route to orchestrator
|
|
132
|
-
5. Every fix verified by build/typecheck
|
|
46
|
+
Every view MUST handle loading, error, and empty states. Never show white screen.
|
|
47
|
+
|
|
48
|
+
## RBAC Guard Table
|
|
49
|
+
|
|
50
|
+
| Who | Guard stack |
|
|
51
|
+
|-----|-------------|
|
|
52
|
+
| Human only | `@Permissions('entity.action')` |
|
|
53
|
+
| Agent + human | `@Public()` + `AgentsDualAuthGuard` + `@ApiKeyScopes` |
|
|
54
|
+
| Truly public | `@Public()` only |
|
|
55
|
+
| MCP | `ApiKeyGuard` + `canUseTool()` |
|
|
56
|
+
|
|
57
|
+
Never combine @Permissions with AgentsDualAuthGuard on same route.
|
|
58
|
+
|
|
59
|
+
Three enforcement layers: capability + key scope + project access. Never less than two.
|
|
60
|
+
|
|
61
|
+
## IconSax Rules
|
|
62
|
+
|
|
63
|
+
Every icon instance MUST pass `variant` and `color` props. Default variant="Outline".
|
|
64
|
+
- Bold for active nav
|
|
65
|
+
- TwoTone for empty-state illustrations
|
|
66
|
+
- Broken for destructive confirmations
|
|
67
|
+
- Sizes: 16/20/24/32
|
|
68
|
+
|
|
69
|
+
## Safety Patterns
|
|
70
|
+
|
|
71
|
+
1. **eslint-disable only when fix is worse.** Add `// eslint-disable-next-line <rule> -- <reason>`. Inventoried for revert.
|
|
72
|
+
2. **Never hand-edit generated/ or vendor ui/.**
|
|
73
|
+
3. **Build/typecheck verified.** Failed fixes reverted + noted.
|
|
74
|
+
4. **Cleanup in worktrees/branches.**
|
|
75
|
+
5. **Secrets never in code/logs/messages.**
|
|
76
|
+
6. **Existing repos: additive fixes only.** Never restructure.
|
|
77
|
+
7. **Max 120 chars per line.**
|
|
78
|
+
|
|
79
|
+
## Audit Report Format
|
|
80
|
+
|
|
81
|
+
When running an audit, produce findings in this structure:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
# Audit Report — [repo-name]
|
|
85
|
+
|
|
86
|
+
**Date:** YYYY-MM-DD
|
|
87
|
+
**Scope:** [files scanned]
|
|
88
|
+
|
|
89
|
+
## Critical / Security
|
|
90
|
+
- [finding] [file:line] [evidence] [suggested fix]
|
|
91
|
+
|
|
92
|
+
## Modularity
|
|
93
|
+
- [finding] [file:line] [evidence] [suggested fix]
|
|
94
|
+
|
|
95
|
+
## Quality
|
|
96
|
+
- [finding] [file:line] [evidence] [suggested fix]
|
|
97
|
+
|
|
98
|
+
## Advisory (existing code)
|
|
99
|
+
- [finding] [file:line] [note]
|
|
100
|
+
|
|
101
|
+
## Summary
|
|
102
|
+
- Total findings: N (Critical: X, Security: Y, Modularity: Z, Quality: W)
|
|
103
|
+
- Gate status: build [pass/fail], typecheck [pass/fail]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Severity levels: Critical (security, data loss), High (broken patterns, missing guards),
|
|
107
|
+
Medium (quality, missing states), Low (style, naming), Advisory (existing code deviations).
|
|
108
|
+
|
|
109
|
+
## Scaffold Task Flow
|
|
110
|
+
|
|
111
|
+
When asked to scaffold a new codebase:
|
|
112
|
+
1. Ask questions first (stack, auth provider, DB, package manager) if --ask
|
|
113
|
+
2. Write plan.md before generating
|
|
114
|
+
3. Create folder tree from template
|
|
115
|
+
4. Write AGENTS.md + docs/ skeleton
|
|
116
|
+
5. Generate module skeletons with full guard stack + loading states
|
|
117
|
+
6. Run build/typecheck gates
|
|
118
|
+
7. Write summary.md with what was built
|
|
119
|
+
|
|
120
|
+
When asked to audit an existing codebase:
|
|
121
|
+
1. Scan all dimensions (security, modularity, rbac, errors, quality, ux, docs, gates)
|
|
122
|
+
2. Existing code deviations = Advisory only (never force restructure)
|
|
123
|
+
3. New code deviations = High/Critical findings
|
|
124
|
+
4. Produce detailed markdown report with file:line evidence
|
|
125
|
+
5. Suggest fixes for each finding
|
|
126
|
+
|
|
127
|
+
When asked to clean up code:
|
|
128
|
+
1. Run audit first
|
|
129
|
+
2. Group findings into fix tasks
|
|
130
|
+
3. Apply mechanical fixes (line length, naming, imports)
|
|
131
|
+
4. For complex fixes: ask_human or route to orchestrator
|
|
132
|
+
5. Every fix verified by build/typecheck
|
|
133
133
|
6. Failed fixes reverted + noted in report
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
# Database
|
|
2
|
-
DATABASE_URL="postgresql://user:password@localhost:5432/{{slug}}?schema=public"
|
|
3
|
-
|
|
4
|
-
# JWT
|
|
5
|
-
JWT_SECRET="change-me-to-a-random-secret"
|
|
6
|
-
JWT_EXPIRES_IN="7d"
|
|
7
|
-
|
|
8
|
-
# Server
|
|
9
|
-
PORT={{apiPort}}
|
|
10
|
-
NODE_ENV=development
|
|
11
|
-
|
|
12
|
-
# CORS
|
|
13
|
-
CORS_ORIGIN="http://localhost:{{port}}"
|
|
1
|
+
# Database
|
|
2
|
+
DATABASE_URL="postgresql://user:password@localhost:5432/{{slug}}?schema=public"
|
|
3
|
+
|
|
4
|
+
# JWT
|
|
5
|
+
JWT_SECRET="change-me-to-a-random-secret"
|
|
6
|
+
JWT_EXPIRES_IN="7d"
|
|
7
|
+
|
|
8
|
+
# Server
|
|
9
|
+
PORT={{apiPort}}
|
|
10
|
+
NODE_ENV=development
|
|
11
|
+
|
|
12
|
+
# CORS
|
|
13
|
+
CORS_ORIGIN="http://localhost:{{port}}"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json.schemastore.org/nest-cli",
|
|
3
|
-
"collection": "@nestjs/schematics",
|
|
4
|
-
"sourceRoot": "src",
|
|
5
|
-
"compilerOptions": {
|
|
6
|
-
"deleteOutDir": true
|
|
7
|
-
}
|
|
8
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/nest-cli",
|
|
3
|
+
"collection": "@nestjs/schematics",
|
|
4
|
+
"sourceRoot": "src",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"deleteOutDir": true
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{packageName}}",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"description": "{{description}}",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "nest build",
|
|
8
|
-
"start": "nest start",
|
|
9
|
-
"start:dev": "nest start --watch",
|
|
10
|
-
"prisma:generate": "prisma generate",
|
|
11
|
-
"prisma:migrate": "prisma migrate dev",
|
|
12
|
-
"lint": "eslint src"
|
|
13
|
-
},
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"@nestjs/common": "^11.0.0",
|
|
16
|
-
"@nestjs/config": "^4.0.0",
|
|
17
|
-
"@nestjs/core": "^11.0.0",
|
|
18
|
-
"@nestjs/passport": "^11.0.0",
|
|
19
|
-
"@nestjs/platform-express": "^11.0.0",
|
|
20
|
-
"@nestjs/swagger": "^11.0.0",
|
|
21
|
-
"@prisma/client": "^6.0.0",
|
|
22
|
-
"argon2": "^0.41.0",
|
|
23
|
-
"class-transformer": "^0.5.1",
|
|
24
|
-
"class-validator": "^0.14.0",
|
|
25
|
-
"passport": "^0.7.0",
|
|
26
|
-
"passport-jwt": "^4.0.1",
|
|
27
|
-
"prisma-zod-generator": "^0.9.0",
|
|
28
|
-
"reflect-metadata": "^0.2.0",
|
|
29
|
-
"rxjs": "^7.8.0",
|
|
30
|
-
"socket.io": "^4.8.0",
|
|
31
|
-
"zod": "^3.23.0"
|
|
32
|
-
},
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"@nestjs/cli": "^11.0.0",
|
|
35
|
-
"@nestjs/schematics": "^11.0.0",
|
|
36
|
-
"@types/node": "^22.0.0",
|
|
37
|
-
"@types/passport-jwt": "^4.0.0",
|
|
38
|
-
"prisma": "^6.0.0",
|
|
39
|
-
"typescript": "^5.6.0"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "{{description}}",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "nest build",
|
|
8
|
+
"start": "nest start",
|
|
9
|
+
"start:dev": "nest start --watch",
|
|
10
|
+
"prisma:generate": "prisma generate",
|
|
11
|
+
"prisma:migrate": "prisma migrate dev",
|
|
12
|
+
"lint": "eslint src"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@nestjs/common": "^11.0.0",
|
|
16
|
+
"@nestjs/config": "^4.0.0",
|
|
17
|
+
"@nestjs/core": "^11.0.0",
|
|
18
|
+
"@nestjs/passport": "^11.0.0",
|
|
19
|
+
"@nestjs/platform-express": "^11.0.0",
|
|
20
|
+
"@nestjs/swagger": "^11.0.0",
|
|
21
|
+
"@prisma/client": "^6.0.0",
|
|
22
|
+
"argon2": "^0.41.0",
|
|
23
|
+
"class-transformer": "^0.5.1",
|
|
24
|
+
"class-validator": "^0.14.0",
|
|
25
|
+
"passport": "^0.7.0",
|
|
26
|
+
"passport-jwt": "^4.0.1",
|
|
27
|
+
"prisma-zod-generator": "^0.9.0",
|
|
28
|
+
"reflect-metadata": "^0.2.0",
|
|
29
|
+
"rxjs": "^7.8.0",
|
|
30
|
+
"socket.io": "^4.8.0",
|
|
31
|
+
"zod": "^3.23.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@nestjs/cli": "^11.0.0",
|
|
35
|
+
"@nestjs/schematics": "^11.0.0",
|
|
36
|
+
"@types/node": "^22.0.0",
|
|
37
|
+
"@types/passport-jwt": "^4.0.0",
|
|
38
|
+
"prisma": "^6.0.0",
|
|
39
|
+
"typescript": "^5.6.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
generator client {
|
|
2
|
-
provider = "prisma-client-js"
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
generator zod {
|
|
6
|
-
provider = "prisma-zod-generator"
|
|
7
|
-
output = "../src/generated/zod"
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
datasource db {
|
|
11
|
-
provider = "postgresql"
|
|
12
|
-
url = env("DATABASE_URL")
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
model User {
|
|
16
|
-
id String @id @default(uuid()) @db.Uuid
|
|
17
|
-
email String @unique
|
|
18
|
-
name String?
|
|
19
|
-
password String
|
|
20
|
-
createdAt DateTime @default(now()) @map("created_at")
|
|
21
|
-
updatedAt DateTime @updatedAt @map("updated_at")
|
|
22
|
-
|
|
23
|
-
apiKey ApiKey[]
|
|
24
|
-
|
|
25
|
-
@@map("users")
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
model ApiKey {
|
|
29
|
-
id String @id @default(uuid()) @db.Uuid
|
|
30
|
-
key String @unique
|
|
31
|
-
name String
|
|
32
|
-
userId String @map("user_id") @db.Uuid
|
|
33
|
-
createdAt DateTime @default(now()) @map("created_at")
|
|
34
|
-
lastUsed DateTime? @map("last_used")
|
|
35
|
-
|
|
36
|
-
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
37
|
-
|
|
38
|
-
@@map("api_keys")
|
|
39
|
-
}
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
generator zod {
|
|
6
|
+
provider = "prisma-zod-generator"
|
|
7
|
+
output = "../src/generated/zod"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
datasource db {
|
|
11
|
+
provider = "postgresql"
|
|
12
|
+
url = env("DATABASE_URL")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
model User {
|
|
16
|
+
id String @id @default(uuid()) @db.Uuid
|
|
17
|
+
email String @unique
|
|
18
|
+
name String?
|
|
19
|
+
password String
|
|
20
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
21
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
22
|
+
|
|
23
|
+
apiKey ApiKey[]
|
|
24
|
+
|
|
25
|
+
@@map("users")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
model ApiKey {
|
|
29
|
+
id String @id @default(uuid()) @db.Uuid
|
|
30
|
+
key String @unique
|
|
31
|
+
name String
|
|
32
|
+
userId String @map("user_id") @db.Uuid
|
|
33
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
34
|
+
lastUsed DateTime? @map("last_used")
|
|
35
|
+
|
|
36
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
37
|
+
|
|
38
|
+
@@map("api_keys")
|
|
39
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { Module } from "@nestjs/common";
|
|
2
|
-
import { ConfigModule } from "@nestjs/config";
|
|
3
|
-
import { APP_GUARD, APP_FILTER, APP_INTERCEPTOR } from "@nestjs/core";
|
|
4
|
-
import { AuthGuard } from "./common/guards/jwt-auth.guard";
|
|
5
|
-
import { RbacGuard } from "./common/guards/rbac.guard";
|
|
6
|
-
import { AllExceptionsFilter } from "./common/filters/all-exceptions.filter";
|
|
7
|
-
import { TransformInterceptor } from "./common/interceptors/transform.interceptor";
|
|
8
|
-
import { PrismaModule } from "./prisma/prisma.module";
|
|
9
|
-
import { ExampleModule } from "./modules/example/example.module";
|
|
10
|
-
|
|
11
|
-
@Module({
|
|
12
|
-
imports: [
|
|
13
|
-
ConfigModule.forRoot({ isGlobal: true }),
|
|
14
|
-
PrismaModule,
|
|
15
|
-
ExampleModule,
|
|
16
|
-
],
|
|
17
|
-
providers: [
|
|
18
|
-
{ provide: APP_GUARD, useClass: AuthGuard },
|
|
19
|
-
{ provide: APP_GUARD, useClass: RbacGuard },
|
|
20
|
-
{ provide: APP_FILTER, useClass: AllExceptionsFilter },
|
|
21
|
-
{ provide: APP_INTERCEPTOR, useClass: TransformInterceptor },
|
|
22
|
-
],
|
|
23
|
-
})
|
|
24
|
-
export class AppModule {}
|
|
1
|
+
import { Module } from "@nestjs/common";
|
|
2
|
+
import { ConfigModule } from "@nestjs/config";
|
|
3
|
+
import { APP_GUARD, APP_FILTER, APP_INTERCEPTOR } from "@nestjs/core";
|
|
4
|
+
import { AuthGuard } from "./common/guards/jwt-auth.guard";
|
|
5
|
+
import { RbacGuard } from "./common/guards/rbac.guard";
|
|
6
|
+
import { AllExceptionsFilter } from "./common/filters/all-exceptions.filter";
|
|
7
|
+
import { TransformInterceptor } from "./common/interceptors/transform.interceptor";
|
|
8
|
+
import { PrismaModule } from "./prisma/prisma.module";
|
|
9
|
+
import { ExampleModule } from "./modules/example/example.module";
|
|
10
|
+
|
|
11
|
+
@Module({
|
|
12
|
+
imports: [
|
|
13
|
+
ConfigModule.forRoot({ isGlobal: true }),
|
|
14
|
+
PrismaModule,
|
|
15
|
+
ExampleModule,
|
|
16
|
+
],
|
|
17
|
+
providers: [
|
|
18
|
+
{ provide: APP_GUARD, useClass: AuthGuard },
|
|
19
|
+
{ provide: APP_GUARD, useClass: RbacGuard },
|
|
20
|
+
{ provide: APP_FILTER, useClass: AllExceptionsFilter },
|
|
21
|
+
{ provide: APP_INTERCEPTOR, useClass: TransformInterceptor },
|
|
22
|
+
],
|
|
23
|
+
})
|
|
24
|
+
export class AppModule {}
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ExceptionFilter,
|
|
3
|
-
Catch,
|
|
4
|
-
ArgumentsHost,
|
|
5
|
-
HttpException,
|
|
6
|
-
HttpStatus,
|
|
7
|
-
} from "@nestjs/common";
|
|
8
|
-
import { Response } from "express";
|
|
9
|
-
|
|
10
|
-
@Catch()
|
|
11
|
-
export class AllExceptionsFilter implements ExceptionFilter {
|
|
12
|
-
catch(exception: unknown, host: ArgumentsHost) {
|
|
13
|
-
const ctx = host.switchToHttp();
|
|
14
|
-
const response = ctx.getResponse<Response>();
|
|
15
|
-
|
|
16
|
-
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
17
|
-
let code = "INTERNAL_ERROR";
|
|
18
|
-
let message = "An unexpected error occurred";
|
|
19
|
-
let details: unknown = undefined;
|
|
20
|
-
|
|
21
|
-
if (exception instanceof HttpException) {
|
|
22
|
-
status = exception.getStatus();
|
|
23
|
-
const res = exception.getResponse();
|
|
24
|
-
if (typeof res === "string") {
|
|
25
|
-
message = res;
|
|
26
|
-
} else if (typeof res === "object" && res !== null) {
|
|
27
|
-
const obj = res as Record<string, unknown>;
|
|
28
|
-
message = (obj.message as string) ?? message;
|
|
29
|
-
code = (obj.error as string) ?? code;
|
|
30
|
-
details = obj.details;
|
|
31
|
-
}
|
|
32
|
-
code = this.codeFromStatus(status);
|
|
33
|
-
} else if (exception instanceof Error) {
|
|
34
|
-
message = exception.message;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
response.status(status).json({
|
|
38
|
-
success: false,
|
|
39
|
-
error: {
|
|
40
|
-
code,
|
|
41
|
-
message,
|
|
42
|
-
details,
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
private codeFromStatus(status: number): string {
|
|
48
|
-
const map: Record<number, string> = {
|
|
49
|
-
400: "BAD_REQUEST",
|
|
50
|
-
401: "UNAUTHORIZED",
|
|
51
|
-
403: "FORBIDDEN",
|
|
52
|
-
404: "NOT_FOUND",
|
|
53
|
-
409: "CONFLICT",
|
|
54
|
-
422: "UNPROCESSABLE_ENTITY",
|
|
55
|
-
429: "TOO_MANY_REQUESTS",
|
|
56
|
-
};
|
|
57
|
-
return map[status] ?? "INTERNAL_ERROR";
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
ExceptionFilter,
|
|
3
|
+
Catch,
|
|
4
|
+
ArgumentsHost,
|
|
5
|
+
HttpException,
|
|
6
|
+
HttpStatus,
|
|
7
|
+
} from "@nestjs/common";
|
|
8
|
+
import { Response } from "express";
|
|
9
|
+
|
|
10
|
+
@Catch()
|
|
11
|
+
export class AllExceptionsFilter implements ExceptionFilter {
|
|
12
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
13
|
+
const ctx = host.switchToHttp();
|
|
14
|
+
const response = ctx.getResponse<Response>();
|
|
15
|
+
|
|
16
|
+
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
17
|
+
let code = "INTERNAL_ERROR";
|
|
18
|
+
let message = "An unexpected error occurred";
|
|
19
|
+
let details: unknown = undefined;
|
|
20
|
+
|
|
21
|
+
if (exception instanceof HttpException) {
|
|
22
|
+
status = exception.getStatus();
|
|
23
|
+
const res = exception.getResponse();
|
|
24
|
+
if (typeof res === "string") {
|
|
25
|
+
message = res;
|
|
26
|
+
} else if (typeof res === "object" && res !== null) {
|
|
27
|
+
const obj = res as Record<string, unknown>;
|
|
28
|
+
message = (obj.message as string) ?? message;
|
|
29
|
+
code = (obj.error as string) ?? code;
|
|
30
|
+
details = obj.details;
|
|
31
|
+
}
|
|
32
|
+
code = this.codeFromStatus(status);
|
|
33
|
+
} else if (exception instanceof Error) {
|
|
34
|
+
message = exception.message;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
response.status(status).json({
|
|
38
|
+
success: false,
|
|
39
|
+
error: {
|
|
40
|
+
code,
|
|
41
|
+
message,
|
|
42
|
+
details,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private codeFromStatus(status: number): string {
|
|
48
|
+
const map: Record<number, string> = {
|
|
49
|
+
400: "BAD_REQUEST",
|
|
50
|
+
401: "UNAUTHORIZED",
|
|
51
|
+
403: "FORBIDDEN",
|
|
52
|
+
404: "NOT_FOUND",
|
|
53
|
+
409: "CONFLICT",
|
|
54
|
+
422: "UNPROCESSABLE_ENTITY",
|
|
55
|
+
429: "TOO_MANY_REQUESTS",
|
|
56
|
+
};
|
|
57
|
+
return map[status] ?? "INTERNAL_ERROR";
|
|
58
|
+
}
|
|
59
|
+
}
|