create-tigra 3.0.5 → 3.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 +4 -2
- package/bin/create-tigra.js +5 -19
- package/modules/email-verification/client/hooks/useVerification.ts +3 -3
- package/package.json +1 -1
- package/template/.agents/skills/security-audit/AI-AND-LLM.md +83 -0
- package/template/.agents/skills/security-audit/ATTACK-CLASSES.md +130 -0
- package/template/.agents/skills/security-audit/CLIENT-SIDE.md +83 -0
- package/template/.agents/skills/security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
- package/template/.agents/skills/security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
- package/template/.agents/skills/security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
- package/template/.agents/skills/security-audit/HUNTING.md +251 -0
- package/template/.agents/skills/security-audit/LICENSE +21 -0
- package/template/.agents/skills/security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
- package/template/.agents/skills/security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
- package/template/.agents/skills/security-audit/RECONNAISSANCE.md +156 -0
- package/template/.agents/skills/security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
- package/template/.agents/skills/security-audit/SKILL.md +192 -0
- package/template/.agents/skills/security-audit/SOURCE.md +5 -0
- package/template/.agents/skills/security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
- package/template/.agents/skills/security-audit/VALIDATION-AND-REPORTING.md +186 -0
- package/template/.agents/skills/security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
- package/template/.agents/skills/security-audit/report-schema.json +461 -0
- package/template/.agents/skills/security-audit/validate-coverage-ledger.cjs +872 -0
- package/template/.agents/skills/security-audit/validate-coverage-ledger.test.cjs +740 -0
- package/template/.agents/skills/security-audit/validate-findings.cjs +773 -0
- package/template/.agents/skills/security-audit/validate-findings.test.cjs +652 -0
- package/template/AGENTS.md +46 -0
- package/template/client/AGENTS.md +23 -0
- package/template/client/package-lock.json +410 -324
- package/template/client/package.json +3 -3
- package/template/client/src/app/(auth)/layout.tsx +9 -0
- package/template/client/src/app/(auth)/loading.tsx +7 -0
- package/template/client/src/app/(main)/layout.tsx +11 -0
- package/template/client/src/app/(main)/loading.tsx +7 -0
- package/template/client/src/app/globals.css +4 -0
- package/template/client/src/app/layout.tsx +9 -2
- package/template/client/src/app/loading.tsx +2 -6
- package/template/client/src/app/not-found.tsx +2 -3
- package/template/client/src/app/providers.tsx +6 -3
- package/template/client/src/components/common/AppLink.tsx +84 -0
- package/template/client/src/components/common/EmptyState.tsx +2 -2
- package/template/client/src/components/common/Pagination.tsx +3 -2
- package/template/client/src/components/common/RouteLoadingShell.tsx +21 -0
- package/template/client/src/components/common/SmoothNavigationProvider.tsx +151 -0
- package/template/client/src/components/layout/Header.tsx +12 -12
- package/template/client/src/features/admin/hooks/useAdminSessions.ts +2 -2
- package/template/client/src/features/admin/hooks/useAdminUsers.ts +3 -3
- package/template/client/src/features/auth/components/AuthInitializer.tsx +3 -2
- package/template/client/src/features/auth/components/LoginForm.tsx +3 -3
- package/template/client/src/features/auth/components/RegisterForm.tsx +3 -3
- package/template/client/src/features/auth/hooks/useAuth.ts +2 -2
- package/template/client/src/features/auth/hooks/usePasswordReset.ts +2 -2
- package/template/client/src/hooks/useAppRouter.ts +40 -0
- package/template/client/src/styles/fonts/inter-jetbrains.css +4 -2
- package/template/client/src/styles/themes/default.css +1 -1
- package/template/gitignore +0 -6
- package/template/server/AGENTS.md +28 -0
- package/template/server/package-lock.json +671 -522
- package/template/server/package.json +8 -8
- package/template/_claude/QUICK_REFERENCE.md +0 -193
- package/template/_claude/README.md +0 -53
- package/template/_claude/commands/create-client.md +0 -878
- package/template/_claude/commands/create-server.md +0 -388
- package/template/_claude/hooks/restrict-paths.sh +0 -51
- package/template/_claude/rules/client/01-project-structure.md +0 -147
- package/template/_claude/rules/client/02-components-and-types.md +0 -146
- package/template/_claude/rules/client/03-data-and-state.md +0 -195
- package/template/_claude/rules/client/04-design-system.md +0 -408
- package/template/_claude/rules/client/05-security.md +0 -55
- package/template/_claude/rules/client/06-ux-checklist.md +0 -111
- package/template/_claude/rules/client/07-deployment.md +0 -99
- package/template/_claude/rules/client/08-lockfile-cross-platform.md +0 -79
- package/template/_claude/rules/client/core.md +0 -46
- package/template/_claude/rules/global/completion-reports.md +0 -178
- package/template/_claude/rules/global/core.md +0 -104
- package/template/_claude/rules/global/investigation-before-conclusions.md +0 -57
- package/template/_claude/rules/server/core.md +0 -52
- package/template/_claude/rules/server/database.md +0 -124
- package/template/_claude/rules/server/deployment.md +0 -78
- package/template/_claude/rules/server/project-conventions.md +0 -254
- package/template/_claude/rules/server/response-handling.md +0 -144
- package/template/_claude/settings.json +0 -15
- package/template/_claude/skills/clean-ui/SKILL.md +0 -63
- package/template/_claude/skills/role/SKILL.md +0 -39
- package/template/_claude/skills/theme/SKILL.md +0 -109
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
> **SCOPE**: These rules apply specifically to the **server** directory.
|
|
2
|
-
|
|
3
|
-
# Deployment & Docker
|
|
4
|
-
|
|
5
|
-
This project is deployed via **Docker** on **Coolify** (or any Docker-based platform). The `Dockerfile` is the production deployment contract. Every code change must remain compatible with it.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Dockerfile Architecture
|
|
10
|
-
|
|
11
|
-
The server uses a **3-stage multi-stage build**:
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
Stage 1 (dependencies) → Installs prod-only node_modules (cached layer)
|
|
15
|
-
Stage 2 (builder) → Installs all deps, generates Prisma client, compiles TypeScript
|
|
16
|
-
Stage 3 (production) → Alpine + dumb-init, non-root user, copies dist + prod node_modules + Prisma
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
**Entry point**: the container `CMD` runs `npx prisma migrate deploy` and then starts `node dist/server.js` — pending migrations are applied automatically on every container boot (migrate-on-boot).
|
|
20
|
-
**Health check**: `GET /api/v1/live` — this endpoint MUST always exist and return 200.
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## When to Update the Dockerfile
|
|
25
|
-
|
|
26
|
-
| You did this... | Update Dockerfile? | What to change |
|
|
27
|
-
|---|---|---|
|
|
28
|
-
| Added a new npm dependency | No | Automatic — `npm ci` installs from `package.json` |
|
|
29
|
-
| Added a native/system dependency (e.g., `sharp`, `bcrypt`) | **Yes** | Add `apk add` in the production stage for required system libraries |
|
|
30
|
-
| Changed the build command or output directory | **Yes** | Update the `RUN npm run build` or `COPY` paths in stage 2/3 |
|
|
31
|
-
| Changed the entry point file (e.g., renamed `server.ts`) | **Yes** | Update the `CMD` to run `dist/<new-name>.js` (keep the `prisma migrate deploy` step before it) |
|
|
32
|
-
| Changed the default port | **Yes** | Update `EXPOSE` and the `HEALTHCHECK` port |
|
|
33
|
-
| Added/renamed a health check endpoint | **Yes** | Update the `HEALTHCHECK` URL path |
|
|
34
|
-
| Added files needed at runtime (e.g., templates, static assets) | **Yes** | Add a `COPY` line in stage 3 |
|
|
35
|
-
| Changed Prisma schema | No | Automatic — `npx prisma generate` runs in stage 2 |
|
|
36
|
-
| Added a new env var | Maybe | If it's needed at **build time**, add `ARG` + `ENV` in the builder stage |
|
|
37
|
-
| Added file upload functionality | **Yes** | Add a `VOLUME` directive or ensure the upload directory is writable |
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Critical Rules
|
|
42
|
-
|
|
43
|
-
1. **Health endpoint is sacred.** The route `GET /api/v1/live` must always exist and return HTTP 200. Coolify, Docker, and load balancers use it to determine if the container is alive. Never remove, rename, or gate it behind auth.
|
|
44
|
-
|
|
45
|
-
2. **Never break the build chain.** If you rename the build output directory, the entry file, or change `tsconfig.json` `outDir`, update the Dockerfile `COPY` paths and `CMD` accordingly.
|
|
46
|
-
|
|
47
|
-
3. **System dependencies must be explicit.** If a new npm package requires native binaries (e.g., `sharp` needs `libvips`, `bcrypt` needs `build-base`), add `apk add --no-cache <package>` in the production stage. The build will succeed locally but fail in Docker without this.
|
|
48
|
-
|
|
49
|
-
4. **Non-root user.** The app runs as `nodejs:nodejs` (UID 1001). Any files the app needs to write (uploads, logs) must be in directories owned by this user. Add `RUN mkdir -p /app/<dir> && chown nodejs:nodejs /app/<dir>` if needed.
|
|
50
|
-
|
|
51
|
-
5. **No secrets in the image.** Environment variables are injected at runtime via Coolify/Docker. Never hardcode secrets, never `COPY .env`, never use `ENV` for sensitive values. Only use `ARG`/`ENV` for non-secret build-time config.
|
|
52
|
-
|
|
53
|
-
6. **Keep `.dockerignore` in sync.** When adding new directories or file types that should NOT be in the Docker build context (test fixtures, docs, local scripts), add them to `.dockerignore`. When adding files that ARE needed at build time, make sure they're not ignored.
|
|
54
|
-
|
|
55
|
-
7. **Port consistency.** The default port is `8000`. If you change the port in `src/config/env.ts`, also update `EXPOSE` and the `HEALTHCHECK` in the Dockerfile.
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Coolify-Specific Notes
|
|
60
|
-
|
|
61
|
-
- **Environment variables**: Set in Coolify's UI, injected at container runtime. No `.env` file needed.
|
|
62
|
-
- **Build arguments**: For build-time vars, use Coolify's "Build Arguments" section → maps to `docker build --build-arg`.
|
|
63
|
-
- **Persistent storage**: For file uploads, mount a volume in Coolify to `/app/uploads`. Add to Dockerfile: `RUN mkdir -p /app/uploads && chown nodejs:nodejs /app/uploads` before `USER nodejs`.
|
|
64
|
-
- **Database migrations**: Applied automatically at container startup — the Dockerfile `CMD` runs `npx prisma migrate deploy` before starting the server, so no Coolify configuration is needed. (Alternative: remove the migrate step from the `CMD` and run `npx prisma migrate deploy` as a pre-deploy command in Coolify instead.)
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Files That Matter for Deployment
|
|
69
|
-
|
|
70
|
-
| File | Purpose | Must exist? |
|
|
71
|
-
|---|---|---|
|
|
72
|
-
| `Dockerfile` | Production build instructions | Yes |
|
|
73
|
-
| `.dockerignore` | Excludes files from Docker build context | Yes |
|
|
74
|
-
| `package.json` | Dependencies and build script | Yes |
|
|
75
|
-
| `tsconfig.json` | TypeScript compilation config | Yes |
|
|
76
|
-
| `prisma/schema.prisma` | Database schema (copied to runtime) | Yes |
|
|
77
|
-
| `prisma/migrations/` | Migration files (copied to runtime) | Yes |
|
|
78
|
-
| `src/server.ts` | Entry point (compiled to `dist/server.js`) | Yes |
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
> **SCOPE**: These rules apply specifically to the **server** directory.
|
|
2
|
-
|
|
3
|
-
# Project Conventions
|
|
4
|
-
|
|
5
|
-
## Stack
|
|
6
|
-
|
|
7
|
-
| Technology | Purpose |
|
|
8
|
-
|------------|---------|
|
|
9
|
-
| Node.js 20+ LTS | Runtime |
|
|
10
|
-
| Fastify | HTTP framework (plugins, route prefixes, centralized error handling) |
|
|
11
|
-
| TypeScript (strict) | Language — always type parameters and return types |
|
|
12
|
-
| MySQL 8.0+ | Primary database |
|
|
13
|
-
| Prisma 6.x | ORM, migrations, schema source of truth |
|
|
14
|
-
| Redis | Caching (frequently accessed data, lookups), rate limiting, background task signaling |
|
|
15
|
-
| Zod | Runtime validation and type inference |
|
|
16
|
-
| JWT (HS256/RS256) | Auth — minimal token payload (id, role), role-based access control |
|
|
17
|
-
| axios | Outbound HTTP client (external API calls) |
|
|
18
|
-
| PM2 + Nginx | Production deployment (cluster mode) |
|
|
19
|
-
| Jest / Vitest | Testing — deterministic, mock external APIs |
|
|
20
|
-
|
|
21
|
-
## Package Manager
|
|
22
|
-
|
|
23
|
-
Detect from lockfile: `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, else → npm.
|
|
24
|
-
|
|
25
|
-
## Folder Structure
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
src/
|
|
29
|
-
├── app.ts # Fastify instance and plugin registration
|
|
30
|
-
├── server.ts # listen() call only, no app logic
|
|
31
|
-
├── config/ # env, config, constants
|
|
32
|
-
├── libs/ # shared libraries (db, redis, logger, auth)
|
|
33
|
-
└── modules/<domain>/ # domain modules
|
|
34
|
-
├── <domain>.routes.ts
|
|
35
|
-
├── <domain>.controller.ts
|
|
36
|
-
├── <domain>.service.ts
|
|
37
|
-
├── <domain>.repo.ts
|
|
38
|
-
├── <domain>.schemas.ts
|
|
39
|
-
└── <domain>.types.ts (if needed)
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
New modules MUST follow `src/modules/<name>/` with the exact naming pattern above.
|
|
43
|
-
|
|
44
|
-
## API Routes
|
|
45
|
-
|
|
46
|
-
All routes prefixed with `/api/v1`. Grouped by domain: `/api/v1/<domain>/...`
|
|
47
|
-
|
|
48
|
-
Register routes as Fastify plugins in `<domain>.routes.ts`.
|
|
49
|
-
|
|
50
|
-
## Coding Style
|
|
51
|
-
|
|
52
|
-
- TypeScript `strict` mode ON
|
|
53
|
-
- `async/await` over `.then()`
|
|
54
|
-
- Named exports (except `src/app.ts` which default-exports the Fastify instance)
|
|
55
|
-
- Relative imports within a module (`./user.service`), aliased imports cross-module (`@modules/users/...`)
|
|
56
|
-
- Use `logger` from `src/libs/logger` — never `console.log`
|
|
57
|
-
|
|
58
|
-
## Layer Responsibilities
|
|
59
|
-
|
|
60
|
-
| Layer | MUST | MUST NOT |
|
|
61
|
-
|-------|------|----------|
|
|
62
|
-
| **Routes** | Register Fastify plugins, define HTTP method + path | Contain any logic |
|
|
63
|
-
| **Controllers** | Validate input (Zod), call services, return via `successResponse`/`paginatedResponse`, throw typed `AppError` | Business logic, direct DB access, manual error JSON, set HTTP status codes for errors |
|
|
64
|
-
| **Services** | All business logic, throw `AppError` subclasses, call repos for DB ops, be stateless | Touch Fastify (request/reply), format responses, return HTTP status codes |
|
|
65
|
-
| **Repositories** | Prisma queries, return raw data | Business logic, error formatting |
|
|
66
|
-
|
|
67
|
-
## Postman Collection
|
|
68
|
-
|
|
69
|
-
When API endpoints are created or modified, **always update the Postman collection** (`postman/collection.json`). If the collection does not exist, create it.
|
|
70
|
-
|
|
71
|
-
### Collection Structure
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
postman/
|
|
75
|
-
├── collection.json # Postman v2.1 collection
|
|
76
|
-
└── environment.json # Environment variables template
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Environment Variables
|
|
80
|
-
|
|
81
|
-
Use variables so requests are connected and portable:
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"variables": [
|
|
86
|
-
{ "key": "baseUrl", "value": "http://localhost:3000/api/v1" },
|
|
87
|
-
{ "key": "accessToken", "value": "" },
|
|
88
|
-
{ "key": "refreshToken", "value": "" },
|
|
89
|
-
{ "key": "userId", "value": "" }
|
|
90
|
-
]
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
- **`{{baseUrl}}`** — all request URLs use this prefix, never hardcoded hosts.
|
|
95
|
-
- **`{{accessToken}}`** / **`{{refreshToken}}`** — set automatically via login/register test scripts.
|
|
96
|
-
- **`{{userId}}`** and other IDs — captured from responses so subsequent requests can reference them.
|
|
97
|
-
|
|
98
|
-
### Auto-set Tokens via Test Scripts
|
|
99
|
-
|
|
100
|
-
The **Login** and **Register** requests MUST include a `Tests` script that stores tokens and user data into collection variables:
|
|
101
|
-
|
|
102
|
-
```javascript
|
|
103
|
-
if (pm.response.code === 200 || pm.response.code === 201) {
|
|
104
|
-
const res = pm.response.json();
|
|
105
|
-
pm.collectionVariables.set("accessToken", res.data.tokens.accessToken);
|
|
106
|
-
pm.collectionVariables.set("refreshToken", res.data.tokens.refreshToken);
|
|
107
|
-
pm.collectionVariables.set("userId", res.data.user.id);
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Auth Header
|
|
112
|
-
|
|
113
|
-
All authenticated requests use a collection-level or folder-level **Bearer Token** auth set to `{{accessToken}}`. Do not duplicate the auth header on every request — inherit from the parent folder.
|
|
114
|
-
|
|
115
|
-
### Folder Organization
|
|
116
|
-
|
|
117
|
-
Organize requests into folders matching domain modules:
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
Collection Root (Bearer Token: {{accessToken}})
|
|
121
|
-
├── Auth (No Auth)
|
|
122
|
-
│ ├── Register → POST {{baseUrl}}/auth/register
|
|
123
|
-
│ ├── Login → POST {{baseUrl}}/auth/login [sets tokens]
|
|
124
|
-
│ ├── Refresh Token → POST {{baseUrl}}/auth/refresh
|
|
125
|
-
│ └── Logout → POST {{baseUrl}}/auth/logout
|
|
126
|
-
├── Users
|
|
127
|
-
│ ├── Get Me → GET {{baseUrl}}/users/me
|
|
128
|
-
│ ├── Update Me → PATCH {{baseUrl}}/users/me
|
|
129
|
-
│ └── Delete Me → DELETE {{baseUrl}}/users/me
|
|
130
|
-
└── <Domain> → One folder per module
|
|
131
|
-
├── List → GET {{baseUrl}}/<domain>
|
|
132
|
-
├── Get by ID → GET {{baseUrl}}/<domain>/{{<domain>Id}}
|
|
133
|
-
├── Create → POST {{baseUrl}}/<domain>
|
|
134
|
-
├── Update → PATCH {{baseUrl}}/<domain>/{{<domain>Id}}
|
|
135
|
-
└── Delete → DELETE {{baseUrl}}/<domain>/{{<domain>Id}}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Rules
|
|
139
|
-
|
|
140
|
-
1. **Every route gets a request.** No endpoint should exist without a matching Postman request.
|
|
141
|
-
2. **Include example request bodies** for POST/PATCH/PUT with realistic placeholder data.
|
|
142
|
-
3. **Capture IDs from create responses** — add a `Tests` script that sets `{{<domain>Id}}` so Get/Update/Delete requests work without manual copy-paste.
|
|
143
|
-
4. **Auth folder uses "No Auth"** — login and register don't need tokens. All other folders inherit Bearer Token from the collection root.
|
|
144
|
-
5. **Keep it importable** — the collection must be valid Postman v2.1 JSON that anyone can import and run immediately after setting `baseUrl`.
|
|
145
|
-
|
|
146
|
-
## Security
|
|
147
|
-
|
|
148
|
-
- Validate all inputs with Zod schemas
|
|
149
|
-
- Never interpolate raw values into SQL — always use Prisma query builder
|
|
150
|
-
- Rate limit public endpoints
|
|
151
|
-
- Avoid N+1 queries — prefer joins or batched queries
|
|
152
|
-
|
|
153
|
-
## Outbound HTTP
|
|
154
|
-
|
|
155
|
-
Use `httpClient` from `src/libs/http.ts` for ALL outbound HTTP calls. Never use native `fetch`, `node:http`, or a new `axios.create()` at the call site — the singleton provides consistent logging, 30s timeout, and automatic error conversion.
|
|
156
|
-
|
|
157
|
-
### Import
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
import { httpClient } from '@libs/http.js';
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### Wrapping pattern (service layer)
|
|
164
|
-
|
|
165
|
-
Outbound HTTP calls belong in the **service layer**. The interceptor converts `AxiosError` to `InternalError` automatically — services only deal with `AppError` subclasses:
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
class WeatherService {
|
|
169
|
-
async getCurrentWeather(city: string): Promise<WeatherData> {
|
|
170
|
-
const response = await httpClient.get<WeatherApiResponse>(
|
|
171
|
-
`https://api.weather.example.com/v1/current`,
|
|
172
|
-
{ params: { q: city } },
|
|
173
|
-
);
|
|
174
|
-
return response.data.result;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
export const weatherService = new WeatherService();
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Auth headers
|
|
181
|
-
|
|
182
|
-
Do NOT add auth headers to the `httpClient` singleton — it is shared. Pass per-request headers at the call site:
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
await httpClient.post(url, body, {
|
|
186
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
187
|
-
});
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Fine-grained error mapping
|
|
191
|
-
|
|
192
|
-
The interceptor always throws `InternalError`. If you need a more specific error (e.g., a 404 from an external API should surface as `NotFoundError`), catch and rethrow:
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
try {
|
|
196
|
-
return await httpClient.get(url);
|
|
197
|
-
} catch {
|
|
198
|
-
throw new NotFoundError('External resource not found');
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Fixed-base-URL services
|
|
203
|
-
|
|
204
|
-
If a service always calls the same external API, create a private derived instance **inside the service file only** (never exported):
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
const apiClient = axios.create({ ...httpClient.defaults, baseURL: 'https://api.example.com/v2' });
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Rules
|
|
211
|
-
|
|
212
|
-
- Always use `httpClient` — never `fetch`, `node:http`, or inline `axios.create()`.
|
|
213
|
-
- Never add auth headers, cookies, or credentials to the singleton itself.
|
|
214
|
-
- Never log response bodies (may contain PII or secrets).
|
|
215
|
-
|
|
216
|
-
## File Storage
|
|
217
|
-
|
|
218
|
-
### Directory Structure Principles
|
|
219
|
-
|
|
220
|
-
Upload folders must be **scalable and manageable**. Never dump all files into a single flat directory (e.g., all product images under `/uploads/products/`). Instead, organize by **owner/entity ID** so each entity's files are isolated and easy to find, move, or delete.
|
|
221
|
-
|
|
222
|
-
**Pattern**: `uploads/<domain>/{entityId}/<media-type>/`
|
|
223
|
-
|
|
224
|
-
### User Uploads
|
|
225
|
-
|
|
226
|
-
Structure: `uploads/users/{userId}/<media-type>/`
|
|
227
|
-
|
|
228
|
-
| Media type | Path | Example |
|
|
229
|
-
|---|---|---|
|
|
230
|
-
| Avatar | `uploads/users/{userId}/avatar/` | `uploads/users/abc123/avatar/john-doe-avatar.webp` |
|
|
231
|
-
|
|
232
|
-
- All user media lives under `uploads/users/{userId}/` for easy per-user cleanup.
|
|
233
|
-
- On account purge, delete the entire `uploads/users/{userId}/` directory via `deleteUserMedia()`.
|
|
234
|
-
- Public URL pattern: `/uploads/users/{userId}/<media-type>/{filename}`
|
|
235
|
-
- New media types follow the same pattern: add a subfolder under the user directory.
|
|
236
|
-
|
|
237
|
-
### Domain Entity Uploads (Products, Articles, etc.)
|
|
238
|
-
|
|
239
|
-
Structure: `uploads/<domain>/{entityId}/<media-type>/`
|
|
240
|
-
|
|
241
|
-
| Domain | Media type | Path | Example |
|
|
242
|
-
|---|---|---|---|
|
|
243
|
-
| Products | Images | `uploads/products/{productId}/images/` | `uploads/products/prod-456/images/front-view.webp` |
|
|
244
|
-
| Products | Thumbnails | `uploads/products/{productId}/thumbnails/` | `uploads/products/prod-456/thumbnails/front-view-thumb.webp` |
|
|
245
|
-
| Articles | Cover | `uploads/articles/{articleId}/cover/` | `uploads/articles/art-789/cover/hero.webp` |
|
|
246
|
-
| Articles | Content images | `uploads/articles/{articleId}/content/` | `uploads/articles/art-789/content/diagram-1.webp` |
|
|
247
|
-
|
|
248
|
-
### Rules
|
|
249
|
-
|
|
250
|
-
1. **Never use flat directories** — `uploads/products/img1.jpg, img2.jpg, ...` is forbidden. Always namespace by entity ID.
|
|
251
|
-
2. **One folder per entity instance** — makes deletion, migration, and backup trivial (`rm -rf uploads/products/{id}/`).
|
|
252
|
-
3. **Separate media types into subfolders** — don't mix avatars, thumbnails, and full-size images in the same folder.
|
|
253
|
-
4. **Entity cleanup** — when an entity is deleted, remove its entire upload directory (e.g., `uploads/products/{productId}/`).
|
|
254
|
-
5. **Consistent naming** — use kebab-case for filenames, domain plural for top-level folders (`products`, `articles`, `users`).
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
> **SCOPE**: These rules apply specifically to the **server** directory.
|
|
2
|
-
|
|
3
|
-
# Response Handling
|
|
4
|
-
|
|
5
|
-
All API responses follow a unified contract. No exceptions.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Success Response
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"success": true,
|
|
14
|
-
"message": "Human-readable message",
|
|
15
|
-
"data": {}
|
|
16
|
-
}
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
- `data` can be an object, array, or null
|
|
20
|
-
- Controllers MUST use the shared `successResponse()` helper
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
// Allowed
|
|
24
|
-
return reply.send(successResponse("Item created successfully", item));
|
|
25
|
-
|
|
26
|
-
// Forbidden — never send raw values or custom shapes
|
|
27
|
-
reply.send(item);
|
|
28
|
-
reply.send({ data: item });
|
|
29
|
-
reply.send({ success: true, item });
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Error Response
|
|
35
|
-
|
|
36
|
-
```json
|
|
37
|
-
{
|
|
38
|
-
"success": false,
|
|
39
|
-
"error": {
|
|
40
|
-
"code": "MACHINE_READABLE_CODE",
|
|
41
|
-
"message": "Human-readable, user-safe message"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
- `code`: stable machine-readable string (e.g. `RESOURCE_NOT_FOUND`, `VALIDATION_FAILED`)
|
|
47
|
-
- Never expose internal details, stack traces, or SQL errors
|
|
48
|
-
- Controllers MUST NOT manually construct error responses
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## Paginated Response
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"success": true,
|
|
57
|
-
"message": "string",
|
|
58
|
-
"data": {
|
|
59
|
-
"items": [],
|
|
60
|
-
"pagination": {
|
|
61
|
-
"page": 1,
|
|
62
|
-
"limit": 10,
|
|
63
|
-
"totalItems": 237,
|
|
64
|
-
"totalPages": 24,
|
|
65
|
-
"hasNextPage": true,
|
|
66
|
-
"hasPreviousPage": false
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Controllers MUST use the shared `paginatedResponse()` helper:
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
function paginatedResponse<T>(message: string, items: T[], page: number, limit: number, totalItems: number)
|
|
76
|
-
|
|
77
|
-
// Allowed
|
|
78
|
-
return reply.send(paginatedResponse("Items retrieved successfully", items, page, limit, totalCount));
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Pagination Input Validation
|
|
82
|
-
|
|
83
|
-
All paginated endpoints MUST validate with this shared Zod schema:
|
|
84
|
-
|
|
85
|
-
```typescript
|
|
86
|
-
const PaginationSchema = z.object({
|
|
87
|
-
page: z.coerce.number().int().min(1).default(1),
|
|
88
|
-
limit: z.coerce.number().int().min(1).max(100).default(10)
|
|
89
|
-
});
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Service Return Pattern for Pagination
|
|
93
|
-
|
|
94
|
-
Services return `{ items, totalItems }`. Controllers build the pagination metadata.
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
// Service returns:
|
|
98
|
-
return { items, totalItems: count };
|
|
99
|
-
// Offset calculation: (page - 1) * limit
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Filters with Pagination
|
|
103
|
-
|
|
104
|
-
- Apply filters/sorting BEFORE `limit` and `offset`
|
|
105
|
-
- Count total items AFTER filters but BEFORE pagination
|
|
106
|
-
- Pages start at 1 (never 0)
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## Error Architecture
|
|
111
|
-
|
|
112
|
-
ONE global Fastify error handler via `fastify.setErrorHandler(...)`:
|
|
113
|
-
- Maps `AppError` subclasses to HTTP status codes
|
|
114
|
-
- Formats the unified error JSON structure
|
|
115
|
-
- Logs internal details server-side only — never sends to client
|
|
116
|
-
|
|
117
|
-
### AppError Subclasses
|
|
118
|
-
|
|
119
|
-
All errors MUST extend `AppError` (which defines `code`, `message`, `statusCode`). Only throw these typed subclasses — never raw `Error`, strings, or plain objects.
|
|
120
|
-
|
|
121
|
-
| Class | Status | Example Code |
|
|
122
|
-
|-------|--------|--------------|
|
|
123
|
-
| `BadRequestError` | 400 | `INVALID_INPUT` |
|
|
124
|
-
| `ValidationError` | 422 | `VALIDATION_FAILED` |
|
|
125
|
-
| `UnauthorizedError` | 401 | `UNAUTHORIZED` |
|
|
126
|
-
| `ForbiddenError` | 403 | `FORBIDDEN` |
|
|
127
|
-
| `NotFoundError` | 404 | `RESOURCE_NOT_FOUND` |
|
|
128
|
-
| `ConflictError` | 409 | `EMAIL_ALREADY_EXISTS` |
|
|
129
|
-
| `InternalError` | 500 | `INTERNAL_ERROR` |
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## Controller Response Rules
|
|
134
|
-
|
|
135
|
-
**ALWAYS:**
|
|
136
|
-
- Use `successResponse()` or `paginatedResponse()` for all responses
|
|
137
|
-
- Validate input with Zod before calling services
|
|
138
|
-
- Throw typed `AppError` subclasses on failure
|
|
139
|
-
|
|
140
|
-
**NEVER:**
|
|
141
|
-
- Send raw values or custom JSON shapes
|
|
142
|
-
- Set HTTP status codes for errors (global handler does this)
|
|
143
|
-
- Catch errors unless rethrowing as typed `AppError`
|
|
144
|
-
- Manually construct error response JSON
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: clean-ui
|
|
3
|
-
description: Remove the starter welcome page UI and replace with a blank canvas, preserving all client-side functionality (auth, hooks, services, store, middleware, utils)
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
The user wants to remove the starter/welcome UI from the scaffolded client and start with a blank page.
|
|
7
|
-
|
|
8
|
-
## What this skill does
|
|
9
|
-
|
|
10
|
-
Replaces the demo welcome page (`src/app/page.tsx`) with the absolute bare minimum — just a centered "Ready to build." text. **Nothing else is touched** — all functional infrastructure remains intact.
|
|
11
|
-
|
|
12
|
-
## What gets replaced
|
|
13
|
-
|
|
14
|
-
| File | Action | Reason |
|
|
15
|
-
|------|--------|--------|
|
|
16
|
-
| `src/app/page.tsx` | **Replace** with blank canvas | Remove ALL demo content — hero, ambient glow, GitHub links, buttons, everything |
|
|
17
|
-
|
|
18
|
-
## What is NOT touched (preserved as-is)
|
|
19
|
-
|
|
20
|
-
- `src/components/layout/` — Header, Footer, MainLayout
|
|
21
|
-
- `src/app/layout.tsx` — root layout with providers
|
|
22
|
-
- `src/app/providers.tsx` — Redux, React Query, themes, AuthInitializer
|
|
23
|
-
- `src/app/error.tsx`, `src/app/not-found.tsx`, `src/app/loading.tsx`
|
|
24
|
-
- `src/middleware.ts` — route protection
|
|
25
|
-
- `src/features/auth/**` — entire auth system
|
|
26
|
-
- `src/components/common/**` — ThemeToggle, EmptyState, LoadingSpinner, etc.
|
|
27
|
-
- `src/components/ui/**` — all shadcn/ui components
|
|
28
|
-
- `src/hooks/**`, `src/store/**`, `src/lib/**`, `src/styles/**`
|
|
29
|
-
- All config files
|
|
30
|
-
|
|
31
|
-
## Steps
|
|
32
|
-
|
|
33
|
-
1. Read `src/app/page.tsx` to confirm it exists.
|
|
34
|
-
2. Replace its contents with the clean page below. Use the **exact** template — do not add anything.
|
|
35
|
-
3. Confirm to the user what was done.
|
|
36
|
-
|
|
37
|
-
## Clean page template
|
|
38
|
-
|
|
39
|
-
Replace `src/app/page.tsx` with **exactly** this — no additions, no modifications:
|
|
40
|
-
|
|
41
|
-
```tsx
|
|
42
|
-
import type React from 'react';
|
|
43
|
-
|
|
44
|
-
export default function HomePage(): React.ReactElement {
|
|
45
|
-
return (
|
|
46
|
-
<main className="flex min-h-dvh items-center justify-center">
|
|
47
|
-
<p className="text-sm text-muted-foreground">Ready to build.</p>
|
|
48
|
-
</main>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
**CRITICAL**: Do NOT add anything beyond what is in the template above. No heading, no links, no buttons, no metadata, no imports beyond React. The entire point is a blank canvas.
|
|
54
|
-
|
|
55
|
-
## Response format
|
|
56
|
-
|
|
57
|
-
After completing, respond with:
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
Starter UI cleaned. `src/app/page.tsx` is now a blank canvas.
|
|
61
|
-
|
|
62
|
-
Everything else is untouched — auth, hooks, services, store, middleware, components, and design system are all intact.
|
|
63
|
-
```
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: role
|
|
3
|
-
description: Switch developer role to restrict Claude's access to frontend-only, backend-only, or full access
|
|
4
|
-
argument-hint: "[frontend | backend | fullstack]"
|
|
5
|
-
disable-model-invocation: true
|
|
6
|
-
allowed-tools: Read, Write
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
The user wants to switch their developer role. The argument is: $ARGUMENTS
|
|
10
|
-
|
|
11
|
-
Read the file `.developer-role` in the project root to see the current role.
|
|
12
|
-
|
|
13
|
-
## Rules
|
|
14
|
-
|
|
15
|
-
- If the argument is `frontend`, `backend`, or `fullstack` — write that word to `.developer-role` and confirm the switch.
|
|
16
|
-
- If no argument is given — read `.developer-role` and tell the user their current role, then ask which role they want.
|
|
17
|
-
- If the argument is not one of the three valid roles — tell the user the valid options.
|
|
18
|
-
|
|
19
|
-
## What each role does
|
|
20
|
-
|
|
21
|
-
Claude can always READ all files for full project context. Only WRITE access is restricted.
|
|
22
|
-
|
|
23
|
-
| Role | Can edit |
|
|
24
|
-
|------|---------|
|
|
25
|
-
| `frontend` | `client/` only. Cannot edit `server/` files. |
|
|
26
|
-
| `backend` | `server/` only. Cannot edit `client/` files. |
|
|
27
|
-
| `fullstack` | Everything. No restrictions. |
|
|
28
|
-
|
|
29
|
-
## Response format
|
|
30
|
-
|
|
31
|
-
After switching, confirm like this:
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
Role switched to **{role}**.
|
|
35
|
-
|
|
36
|
-
- frontend → can edit client/ only (can read everything)
|
|
37
|
-
- backend → can edit server/ only (can read everything)
|
|
38
|
-
- fullstack → can edit everything
|
|
39
|
-
```
|