sonamu 0.8.24 → 0.8.26
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/api/__tests__/config.test.js +189 -0
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +7 -2
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +14 -10
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +2 -1
- package/dist/auth/knex-adapter.d.ts +23 -0
- package/dist/auth/knex-adapter.d.ts.map +1 -0
- package/dist/auth/knex-adapter.js +163 -0
- package/dist/auth/plugins/wrappers/admin.d.ts +2 -2
- package/dist/bin/__tests__/ts-loader-register.test.js +45 -0
- package/dist/bin/cli.js +47 -9
- package/dist/bin/ts-loader-register.js +3 -29
- package/dist/bin/ts-loader-registration.d.ts +2 -0
- package/dist/bin/ts-loader-registration.d.ts.map +1 -0
- package/dist/bin/ts-loader-registration.js +42 -0
- package/dist/cone/cone-generator.js +3 -3
- package/dist/database/puri-subset.test-d.js +9 -1
- package/dist/database/puri-subset.types.d.ts +1 -1
- package/dist/database/puri-subset.types.d.ts.map +1 -1
- package/dist/database/puri-subset.types.js +1 -1
- package/dist/testing/fixture-generator.js +5 -5
- package/dist/ui/ai-client.js +2 -2
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +14 -14
- package/dist/ui/cdd-service.d.ts +15 -18
- package/dist/ui/cdd-service.d.ts.map +1 -1
- package/dist/ui/cdd-service.js +246 -222
- package/dist/ui/cdd-types.d.ts +41 -68
- package/dist/ui/cdd-types.d.ts.map +1 -1
- package/dist/ui/cdd-types.js +2 -2
- package/dist/ui-web/assets/index-CKo0Z2Iu.css +1 -0
- package/dist/ui-web/assets/{index-CxiydzeC.js → index-DK-2aacv.js} +83 -83
- package/dist/ui-web/index.html +2 -2
- package/package.json +6 -2
- package/src/api/__tests__/config.test.ts +225 -0
- package/src/api/config.ts +10 -4
- package/src/api/sonamu.ts +16 -13
- package/src/auth/index.ts +1 -0
- package/src/auth/knex-adapter.ts +208 -0
- package/src/bin/__tests__/ts-loader-register.test.ts +62 -0
- package/src/bin/cli.ts +52 -9
- package/src/bin/ts-loader-register.ts +2 -32
- package/src/bin/ts-loader-registration.ts +55 -0
- package/src/cone/cone-generator.ts +2 -2
- package/src/database/puri-subset.test-d.ts +102 -0
- package/src/database/puri-subset.types.ts +1 -1
- package/src/skills/commands/sonamu-skills.md +20 -0
- package/src/skills/sonamu/SKILL.md +179 -137
- package/src/skills/sonamu/ai-agents.md +69 -69
- package/src/skills/sonamu/api.md +147 -147
- package/src/skills/sonamu/auth-migration.md +220 -220
- package/src/skills/sonamu/auth-plugins.md +83 -83
- package/src/skills/sonamu/auth.md +106 -106
- package/src/skills/sonamu/cdd.md +65 -200
- package/src/skills/sonamu/cone.md +138 -138
- package/src/skills/sonamu/config.md +191 -191
- package/src/skills/sonamu/create-sonamu.md +66 -66
- package/src/skills/sonamu/database.md +158 -158
- package/src/skills/sonamu/entity-basic.md +292 -293
- package/src/skills/sonamu/entity-relations.md +246 -246
- package/src/skills/sonamu/entity-validation-checklist.md +124 -124
- package/src/skills/sonamu/fixture-cli.md +231 -231
- package/src/skills/sonamu/framework-change.md +37 -37
- package/src/skills/sonamu/frontend.md +223 -223
- package/src/skills/sonamu/i18n.md +82 -82
- package/src/skills/sonamu/migration.md +77 -77
- package/src/skills/sonamu/model.md +222 -222
- package/src/skills/sonamu/naite.md +86 -86
- package/src/skills/sonamu/project-init.md +228 -228
- package/src/skills/sonamu/puri.md +122 -122
- package/src/skills/sonamu/scaffolding.md +154 -154
- package/src/skills/sonamu/skill-contribution.md +124 -124
- package/src/skills/sonamu/subset.md +46 -46
- package/src/skills/sonamu/tasks.md +82 -82
- package/src/skills/sonamu/testing-devrunner.md +147 -147
- package/src/skills/sonamu/testing.md +673 -673
- package/src/skills/sonamu/upsert.md +79 -79
- package/src/skills/sonamu/vector.md +67 -67
- package/src/testing/fixture-generator.ts +4 -4
- package/src/ui/ai-client.ts +1 -1
- package/src/ui/api.ts +18 -17
- package/src/ui/cdd-service.ts +264 -254
- package/src/ui/cdd-types.ts +40 -75
- package/dist/ui-web/assets/index-BrQKU3j9.css +0 -1
- package/src/skills/sonamu/workflow.md +0 -317
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sonamu-config
|
|
3
|
-
description: Sonamu
|
|
3
|
+
description: Sonamu project configuration. .env environment variables, sonamu.config.ts settings. Use when configuring a new Sonamu project.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Sonamu
|
|
6
|
+
# Sonamu Project Configuration
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
A guide for setting up `.env` and `sonamu.config.ts` after project creation.
|
|
9
9
|
|
|
10
|
-
## .env
|
|
10
|
+
## .env File
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Location: `packages/api/.env`
|
|
13
13
|
|
|
14
|
-
###
|
|
14
|
+
### Default Environment Variables (generated by create-sonamu)
|
|
15
15
|
|
|
16
16
|
```env
|
|
17
17
|
# Database Configuration
|
|
@@ -24,49 +24,49 @@ DATABASE_NAME=myproject
|
|
|
24
24
|
PROJECT_NAME=myproject
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
###
|
|
27
|
+
### Additional Environment Variables (as needed)
|
|
28
28
|
|
|
29
29
|
```env
|
|
30
|
-
# Session (
|
|
30
|
+
# Session (must be changed in production)
|
|
31
31
|
SESSION_SECRET=your-secret-key-change-in-production
|
|
32
32
|
SESSION_SALT=random-16-char-salt
|
|
33
33
|
|
|
34
|
-
# AWS S3 (
|
|
34
|
+
# AWS S3 (for file uploads)
|
|
35
35
|
AWS_ACCESS_KEY_ID=your-access-key
|
|
36
36
|
AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
37
37
|
S3_REGION=ap-northeast-2
|
|
38
38
|
S3_BUCKET=your-bucket-name
|
|
39
39
|
|
|
40
|
-
# Slack
|
|
40
|
+
# Slack notifications (for migration confirmation)
|
|
41
41
|
SLACK_BOT_TOKEN=xoxb-your-token
|
|
42
42
|
SLACK_CHANNEL_ID=C0123456789
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
###
|
|
45
|
+
### Environment Variable Reference
|
|
46
46
|
|
|
47
|
-
|
|
|
47
|
+
| Variable | Required | Description |
|
|
48
48
|
|-----|-----|------|
|
|
49
|
-
| `DB_HOST` | ✓ | DB
|
|
50
|
-
| `DB_PORT` | ✓ | DB
|
|
51
|
-
| `DB_USER` | ✓ | DB
|
|
52
|
-
| `DB_PASSWORD` | ✓ | DB
|
|
53
|
-
| `DATABASE_NAME` | ✓ |
|
|
54
|
-
| `PROJECT_NAME` | ✓ |
|
|
55
|
-
| `CONTAINER_NAME` | | Docker
|
|
56
|
-
| `SESSION_SECRET` | |
|
|
57
|
-
| `SESSION_SALT` | |
|
|
58
|
-
| `AWS_ACCESS_KEY_ID` | |
|
|
59
|
-
| `AWS_SECRET_ACCESS_KEY` | |
|
|
60
|
-
| `S3_REGION` | | S3
|
|
61
|
-
| `S3_BUCKET` | | S3
|
|
49
|
+
| `DB_HOST` | ✓ | DB host (Docker: `0.0.0.0`, external DB: the relevant IP) |
|
|
50
|
+
| `DB_PORT` | ✓ | DB port (default: `5432`) |
|
|
51
|
+
| `DB_USER` | ✓ | DB username (default: `postgres`) |
|
|
52
|
+
| `DB_PASSWORD` | ✓ | DB password |
|
|
53
|
+
| `DATABASE_NAME` | ✓ | Database name |
|
|
54
|
+
| `PROJECT_NAME` | ✓ | Project name (used in Docker, config) |
|
|
55
|
+
| `CONTAINER_NAME` | | Docker container name |
|
|
56
|
+
| `SESSION_SECRET` | | Session encryption key (required in production) |
|
|
57
|
+
| `SESSION_SALT` | | Session salt (16 characters) |
|
|
58
|
+
| `AWS_ACCESS_KEY_ID` | | Required when using S3 |
|
|
59
|
+
| `AWS_SECRET_ACCESS_KEY` | | Required when using S3 |
|
|
60
|
+
| `S3_REGION` | | S3 region (default: `ap-northeast-2`) |
|
|
61
|
+
| `S3_BUCKET` | | S3 bucket name |
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
65
|
## sonamu.config.ts
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
Location: `packages/api/src/sonamu.config.ts`
|
|
68
68
|
|
|
69
|
-
###
|
|
69
|
+
### Full Structure
|
|
70
70
|
|
|
71
71
|
```typescript
|
|
72
72
|
import path from "node:path";
|
|
@@ -79,18 +79,18 @@ const port = 34900;
|
|
|
79
79
|
|
|
80
80
|
export default defineConfig({
|
|
81
81
|
projectName: process.env.PROJECT_NAME ?? "MyProject",
|
|
82
|
-
api: { /* API
|
|
83
|
-
i18n: { /*
|
|
84
|
-
sync: { /*
|
|
85
|
-
database: { /* DB
|
|
86
|
-
logging: { /*
|
|
87
|
-
test: { /*
|
|
88
|
-
server: { /*
|
|
89
|
-
slackConfirm: { /* Production
|
|
82
|
+
api: { /* API settings */ },
|
|
83
|
+
i18n: { /* i18n settings */ },
|
|
84
|
+
sync: { /* sync settings */ },
|
|
85
|
+
database: { /* DB settings */ },
|
|
86
|
+
logging: { /* logging settings (disable with false) */ },
|
|
87
|
+
test: { /* test settings */ },
|
|
88
|
+
server: { /* server settings */ },
|
|
89
|
+
slackConfirm: { /* Production migration Slack approval */ },
|
|
90
90
|
});
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
###
|
|
93
|
+
### Key Section Settings
|
|
94
94
|
|
|
95
95
|
#### projectName
|
|
96
96
|
|
|
@@ -102,10 +102,10 @@ projectName: process.env.PROJECT_NAME ?? "MyProject",
|
|
|
102
102
|
|
|
103
103
|
```typescript
|
|
104
104
|
api: {
|
|
105
|
-
dir: "api", // API
|
|
106
|
-
timezone: "Asia/Seoul", //
|
|
105
|
+
dir: "api", // API directory name
|
|
106
|
+
timezone: "Asia/Seoul", // timezone
|
|
107
107
|
route: {
|
|
108
|
-
prefix: "/api", // API
|
|
108
|
+
prefix: "/api", // API route prefix
|
|
109
109
|
},
|
|
110
110
|
},
|
|
111
111
|
```
|
|
@@ -119,13 +119,13 @@ i18n: {
|
|
|
119
119
|
},
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
See `i18n.md` for details.
|
|
123
123
|
|
|
124
124
|
#### sync
|
|
125
125
|
|
|
126
126
|
```typescript
|
|
127
127
|
sync: {
|
|
128
|
-
targets: ["web"], //
|
|
128
|
+
targets: ["web"], // target packages for type sync
|
|
129
129
|
},
|
|
130
130
|
```
|
|
131
131
|
|
|
@@ -146,65 +146,65 @@ database: {
|
|
|
146
146
|
},
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
See `database.md` for details.
|
|
150
150
|
|
|
151
151
|
#### test
|
|
152
152
|
|
|
153
153
|
```typescript
|
|
154
154
|
test: {
|
|
155
|
-
parallel: true, //
|
|
156
|
-
maxWorkers: 4, //
|
|
157
|
-
devRunner: { //
|
|
155
|
+
parallel: true, // enable parallel testing
|
|
156
|
+
maxWorkers: 4, // number of workers
|
|
157
|
+
devRunner: { // Vitest resident instance inside Dev server
|
|
158
158
|
enabled: true,
|
|
159
159
|
watch: true,
|
|
160
160
|
},
|
|
161
161
|
},
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
See `testing.md`, `testing-devrunner.md` for details.
|
|
165
165
|
|
|
166
166
|
#### server
|
|
167
167
|
|
|
168
168
|
```typescript
|
|
169
169
|
server: {
|
|
170
170
|
listen: { port: 34900, host: "localhost" },
|
|
171
|
-
plugins: { /*
|
|
171
|
+
plugins: { /* plugin settings */ },
|
|
172
172
|
auth: true,
|
|
173
|
-
apiConfig: { /* API
|
|
174
|
-
storage: { /*
|
|
175
|
-
cache: { /*
|
|
176
|
-
lifecycle: { /*
|
|
173
|
+
apiConfig: { /* API settings */ },
|
|
174
|
+
storage: { /* storage settings */ },
|
|
175
|
+
cache: { /* cache settings */ },
|
|
176
|
+
lifecycle: { /* lifecycle hooks */ },
|
|
177
177
|
},
|
|
178
178
|
```
|
|
179
179
|
|
|
180
180
|
---
|
|
181
181
|
|
|
182
|
-
## server.auth
|
|
182
|
+
## server.auth Details (better-auth Authentication)
|
|
183
183
|
|
|
184
|
-
Sonamu
|
|
184
|
+
Sonamu provides an authentication system using **better-auth**.
|
|
185
185
|
|
|
186
|
-
### 1.
|
|
186
|
+
### 1. Auto-generate Entities
|
|
187
187
|
|
|
188
188
|
```bash
|
|
189
189
|
pnpm sonamu auth generate
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
- **User** -
|
|
194
|
-
- **Session** -
|
|
195
|
-
- **Account** -
|
|
196
|
-
- **Verification** -
|
|
192
|
+
Generated entities:
|
|
193
|
+
- **User** - user (id, name, email, email_verified, image)
|
|
194
|
+
- **Session** - session (token, expires_at, user_id)
|
|
195
|
+
- **Account** - account (provider_id, access_token, etc.)
|
|
196
|
+
- **Verification** - email verification
|
|
197
197
|
|
|
198
|
-
### 2. server.auth
|
|
198
|
+
### 2. server.auth Configuration
|
|
199
199
|
|
|
200
200
|
```typescript
|
|
201
201
|
server: {
|
|
202
|
-
//
|
|
202
|
+
// Basic configuration (emailAndPassword enabled)
|
|
203
203
|
auth: {
|
|
204
204
|
emailAndPassword: { enabled: true },
|
|
205
205
|
},
|
|
206
206
|
|
|
207
|
-
//
|
|
207
|
+
// Add social login
|
|
208
208
|
// auth: {
|
|
209
209
|
// emailAndPassword: { enabled: true },
|
|
210
210
|
// socialProviders: {
|
|
@@ -217,18 +217,18 @@ server: {
|
|
|
217
217
|
}
|
|
218
218
|
```
|
|
219
219
|
|
|
220
|
-
### 3.
|
|
220
|
+
### 3. Authentication API Endpoints
|
|
221
221
|
|
|
222
|
-
`/api/auth/*`
|
|
222
|
+
Automatically registered under the `/api/auth/*` path:
|
|
223
223
|
|
|
224
|
-
|
|
|
224
|
+
| Endpoint | Method | Description |
|
|
225
225
|
|------------|--------|------|
|
|
226
|
-
| `/api/auth/sign-up/email` | POST |
|
|
227
|
-
| `/api/auth/sign-in/email` | POST |
|
|
228
|
-
| `/api/auth/sign-out` | POST |
|
|
229
|
-
| `/api/auth/get-session` | GET |
|
|
226
|
+
| `/api/auth/sign-up/email` | POST | Sign up |
|
|
227
|
+
| `/api/auth/sign-in/email` | POST | Sign in |
|
|
228
|
+
| `/api/auth/sign-out` | POST | Sign out |
|
|
229
|
+
| `/api/auth/get-session` | GET | Get session |
|
|
230
230
|
|
|
231
|
-
### 4.
|
|
231
|
+
### 4. Accessing user/session from Context
|
|
232
232
|
|
|
233
233
|
```typescript
|
|
234
234
|
@api({ httpMethod: "GET", guards: ["user"] })
|
|
@@ -239,9 +239,9 @@ async me(): Promise<UserSubsetA | null> {
|
|
|
239
239
|
}
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
### 5.
|
|
242
|
+
### 5. Field Mapping (camelCase → snake_case)
|
|
243
243
|
|
|
244
|
-
better-auth
|
|
244
|
+
better-auth uses camelCase, Sonamu uses snake_case. Automatic mapping is applied:
|
|
245
245
|
|
|
246
246
|
| better-auth | Sonamu |
|
|
247
247
|
|-------------|--------|
|
|
@@ -252,22 +252,22 @@ better-auth는 camelCase, Sonamu는 snake_case 사용. 자동 매핑 적용:
|
|
|
252
252
|
|
|
253
253
|
---
|
|
254
254
|
|
|
255
|
-
## Guards
|
|
255
|
+
## Guards System (Access Control)
|
|
256
256
|
|
|
257
|
-
Sonamu
|
|
257
|
+
The Sonamu permission system consists of 2 components:
|
|
258
258
|
|
|
259
|
-
1. **GuardKeys** -
|
|
260
|
-
2. **guardHandler** -
|
|
259
|
+
1. **GuardKeys** - permission key definitions
|
|
260
|
+
2. **guardHandler** - permission check logic
|
|
261
261
|
|
|
262
|
-
### 1. GuardKeys
|
|
262
|
+
### 1. Extending GuardKeys (Custom Permissions)
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
**Source code:** `modules/sonamu/src/api/decorators.ts` (GuardKeys interface)
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
Provided by default: `query`, `admin`, `user`
|
|
267
267
|
|
|
268
|
-
|
|
268
|
+
To add custom permissions, extend in `src/typings/sonamu.d.ts`:
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
**File location:** `src/typings/sonamu.d.ts`
|
|
271
271
|
|
|
272
272
|
```typescript
|
|
273
273
|
import {} from "sonamu";
|
|
@@ -277,13 +277,13 @@ declare module "sonamu" {
|
|
|
277
277
|
query: true;
|
|
278
278
|
admin: true;
|
|
279
279
|
user: true;
|
|
280
|
-
manager: true; //
|
|
281
|
-
superadmin: true; //
|
|
280
|
+
manager: true; // added
|
|
281
|
+
superadmin: true; // added
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
```
|
|
285
285
|
|
|
286
|
-
### 2. @api
|
|
286
|
+
### 2. Using guards in the @api Decorator
|
|
287
287
|
|
|
288
288
|
```typescript
|
|
289
289
|
// user.model.ts
|
|
@@ -292,22 +292,22 @@ import { api } from "sonamu";
|
|
|
292
292
|
class UserModelClass extends BaseModelClass {
|
|
293
293
|
@api({ httpMethod: "GET", guards: ["user"] })
|
|
294
294
|
async me(): Promise<UserSubsetA | null> {
|
|
295
|
-
//
|
|
295
|
+
// only logged-in users can access
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
@api({ httpMethod: "DELETE", guards: ["admin"] })
|
|
299
299
|
async del(ids: number[]): Promise<number> {
|
|
300
|
-
//
|
|
300
|
+
// only admins can access
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
@api({ httpMethod: "GET", guards: ["admin", "manager"] })
|
|
304
304
|
async adminList(): Promise<UserSubsetA[]> {
|
|
305
|
-
// admin
|
|
305
|
+
// admin or manager permission
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
```
|
|
309
309
|
|
|
310
|
-
### 3. guardHandler
|
|
310
|
+
### 3. Implementing guardHandler
|
|
311
311
|
|
|
312
312
|
```typescript
|
|
313
313
|
import { Sonamu } from "sonamu";
|
|
@@ -315,44 +315,44 @@ import { Sonamu } from "sonamu";
|
|
|
315
315
|
// sonamu.config.ts
|
|
316
316
|
apiConfig: {
|
|
317
317
|
guardHandler: (guard, request, api) => {
|
|
318
|
-
// better-auth Context
|
|
318
|
+
// Access user from better-auth Context
|
|
319
319
|
const { user } = Sonamu.getContext();
|
|
320
320
|
|
|
321
321
|
switch (guard) {
|
|
322
322
|
case "user":
|
|
323
|
-
//
|
|
323
|
+
// login required
|
|
324
324
|
if (!user) {
|
|
325
|
-
throw new Error("
|
|
325
|
+
throw new Error("Login is required");
|
|
326
326
|
}
|
|
327
327
|
break;
|
|
328
328
|
|
|
329
329
|
case "admin":
|
|
330
|
-
//
|
|
330
|
+
// admin permission (requires adding role field to User entity)
|
|
331
331
|
if (!user || (user as any).role !== "admin") {
|
|
332
|
-
throw new Error("
|
|
332
|
+
throw new Error("Only admins can access");
|
|
333
333
|
}
|
|
334
334
|
break;
|
|
335
335
|
|
|
336
336
|
case "manager":
|
|
337
|
-
//
|
|
338
|
-
if (!user || ![
|
|
339
|
-
throw new Error("
|
|
337
|
+
// manager permission (custom Guard example)
|
|
338
|
+
if (!user || !["admin", "manager"].includes((user as any).role)) {
|
|
339
|
+
throw new Error("Manager permission is required");
|
|
340
340
|
}
|
|
341
341
|
break;
|
|
342
342
|
|
|
343
343
|
case "query":
|
|
344
|
-
//
|
|
344
|
+
// allow all users (including unauthenticated)
|
|
345
345
|
break;
|
|
346
346
|
}
|
|
347
347
|
},
|
|
348
348
|
},
|
|
349
349
|
```
|
|
350
350
|
|
|
351
|
-
**NOTE:** better-auth
|
|
351
|
+
**NOTE:** better-auth's default User entity does not have a `role` field. If role-based authentication is needed, add a `role` field to the User entity or create a separate Role entity.
|
|
352
352
|
|
|
353
|
-
###
|
|
353
|
+
### Menu/Screen Access Control by Permission
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
UI access control by permission is handled **on the frontend**:
|
|
356
356
|
|
|
357
357
|
```typescript
|
|
358
358
|
// web/src/lib/auth.ts
|
|
@@ -371,34 +371,34 @@ export function canAccess(userRole: string, menu: keyof typeof menuPermissions)
|
|
|
371
371
|
```tsx
|
|
372
372
|
// web/src/components/Sidebar.tsx
|
|
373
373
|
{canAccess(user.role, "userManagement") && (
|
|
374
|
-
<MenuItem href="/admin/users"
|
|
374
|
+
<MenuItem href="/admin/users">User Management</MenuItem>
|
|
375
375
|
)}
|
|
376
376
|
```
|
|
377
377
|
|
|
378
378
|
---
|
|
379
379
|
|
|
380
|
-
## server.plugins
|
|
380
|
+
## server.plugins Details
|
|
381
381
|
|
|
382
|
-
### session (
|
|
382
|
+
### session (Session Management)
|
|
383
383
|
|
|
384
384
|
```typescript
|
|
385
385
|
session: {
|
|
386
386
|
secret: process.env.SESSION_SECRET || "change-this-in-production",
|
|
387
387
|
salt: process.env.SESSION_SALT || "mq9hDxBCDbsQDR6N",
|
|
388
388
|
cookie: {
|
|
389
|
-
domain: "localhost", //
|
|
389
|
+
domain: "localhost", // change to actual domain in production
|
|
390
390
|
path: "/",
|
|
391
|
-
maxAge: 60 * 60 * 24 * 365 * 10, // 10
|
|
391
|
+
maxAge: 60 * 60 * 24 * 365 * 10, // 10 years
|
|
392
392
|
},
|
|
393
393
|
},
|
|
394
394
|
```
|
|
395
395
|
|
|
396
|
-
|
|
397
|
-
- `SESSION_SECRET`:
|
|
398
|
-
- `SESSION_SALT`: 16
|
|
399
|
-
- `cookie.domain`:
|
|
396
|
+
**Production checklist:**
|
|
397
|
+
- `SESSION_SECRET`: must be changed to a strong random string
|
|
398
|
+
- `SESSION_SALT`: change to a 16-character random string
|
|
399
|
+
- `cookie.domain`: change to the actual domain
|
|
400
400
|
|
|
401
|
-
### static (
|
|
401
|
+
### static (Static Files)
|
|
402
402
|
|
|
403
403
|
```typescript
|
|
404
404
|
static: {
|
|
@@ -407,7 +407,7 @@ static: {
|
|
|
407
407
|
},
|
|
408
408
|
```
|
|
409
409
|
|
|
410
|
-
### multipart (
|
|
410
|
+
### multipart (File Upload)
|
|
411
411
|
|
|
412
412
|
```typescript
|
|
413
413
|
multipart: {
|
|
@@ -419,9 +419,9 @@ multipart: {
|
|
|
419
419
|
|
|
420
420
|
---
|
|
421
421
|
|
|
422
|
-
## server.storage
|
|
422
|
+
## server.storage Details
|
|
423
423
|
|
|
424
|
-
###
|
|
424
|
+
### Local File System
|
|
425
425
|
|
|
426
426
|
```typescript
|
|
427
427
|
storage: {
|
|
@@ -462,9 +462,9 @@ storage: {
|
|
|
462
462
|
|
|
463
463
|
---
|
|
464
464
|
|
|
465
|
-
## server.cache
|
|
465
|
+
## server.cache Details
|
|
466
466
|
|
|
467
|
-
Sonamu
|
|
467
|
+
Sonamu uses **BentoCache**.
|
|
468
468
|
|
|
469
469
|
```typescript
|
|
470
470
|
import { drivers as cacheDrivers, store } from "sonamu/cache";
|
|
@@ -479,21 +479,21 @@ cache: {
|
|
|
479
479
|
},
|
|
480
480
|
```
|
|
481
481
|
|
|
482
|
-
|
|
483
|
-
- `memory` -
|
|
484
|
-
- `file` -
|
|
485
|
-
- `redis` - Redis
|
|
486
|
-
- `knex` - DB
|
|
482
|
+
**Available drivers:**
|
|
483
|
+
- `memory` - in-memory cache (default)
|
|
484
|
+
- `file` - file-based cache
|
|
485
|
+
- `redis` - Redis cache
|
|
486
|
+
- `knex` - DB-based cache
|
|
487
487
|
|
|
488
|
-
|
|
488
|
+
For other drivers, refer to the [BentoCache documentation](https://bentocache.dev/).
|
|
489
489
|
|
|
490
490
|
---
|
|
491
491
|
|
|
492
|
-
## server.apiConfig
|
|
492
|
+
## server.apiConfig Details
|
|
493
493
|
|
|
494
494
|
### contextProvider
|
|
495
495
|
|
|
496
|
-
|
|
496
|
+
Inject additional information into Context per request:
|
|
497
497
|
|
|
498
498
|
```typescript
|
|
499
499
|
contextProvider: (defaultContext, request) => {
|
|
@@ -502,34 +502,34 @@ contextProvider: (defaultContext, request) => {
|
|
|
502
502
|
ip: request.ip,
|
|
503
503
|
session: request.session,
|
|
504
504
|
body: request.body,
|
|
505
|
-
//
|
|
505
|
+
// custom fields can be added
|
|
506
506
|
};
|
|
507
507
|
},
|
|
508
508
|
```
|
|
509
509
|
|
|
510
510
|
### guardHandler
|
|
511
511
|
|
|
512
|
-
API
|
|
512
|
+
Handle API guard processing:
|
|
513
513
|
|
|
514
514
|
```typescript
|
|
515
515
|
guardHandler: (guard, request, api) => {
|
|
516
|
-
//
|
|
516
|
+
// access control based on guard value
|
|
517
517
|
if (guard === "admin" && request.user?.role !== "admin") {
|
|
518
|
-
throw new Error("
|
|
518
|
+
throw new Error("Only admins can access");
|
|
519
519
|
}
|
|
520
520
|
},
|
|
521
521
|
```
|
|
522
522
|
|
|
523
523
|
### cacheControlHandler
|
|
524
524
|
|
|
525
|
-
HTTP
|
|
525
|
+
Set HTTP cache headers:
|
|
526
526
|
|
|
527
527
|
```typescript
|
|
528
528
|
cacheControlHandler: (req) => {
|
|
529
529
|
switch (req.type) {
|
|
530
530
|
case "assets":
|
|
531
531
|
if (req.path.match(/-[a-f0-9]+\./)) {
|
|
532
|
-
return CachePresets.immutable; //
|
|
532
|
+
return CachePresets.immutable; // files with hash
|
|
533
533
|
}
|
|
534
534
|
return CachePresets.longLived;
|
|
535
535
|
|
|
@@ -550,7 +550,7 @@ cacheControlHandler: (req) => {
|
|
|
550
550
|
|
|
551
551
|
---
|
|
552
552
|
|
|
553
|
-
## server.lifecycle
|
|
553
|
+
## server.lifecycle Details
|
|
554
554
|
|
|
555
555
|
```typescript
|
|
556
556
|
lifecycle: {
|
|
@@ -559,7 +559,7 @@ lifecycle: {
|
|
|
559
559
|
},
|
|
560
560
|
onShutdown: () => {
|
|
561
561
|
console.log("graceful shutdown");
|
|
562
|
-
// DB
|
|
562
|
+
// close DB connections, clean up resources, etc.
|
|
563
563
|
},
|
|
564
564
|
onError: (error, request, reply) => {
|
|
565
565
|
console.error(error);
|
|
@@ -573,78 +573,78 @@ lifecycle: {
|
|
|
573
573
|
|
|
574
574
|
---
|
|
575
575
|
|
|
576
|
-
## Sonamu
|
|
576
|
+
## Sonamu Local Development Environment Setup
|
|
577
577
|
|
|
578
|
-
|
|
579
|
-
- Sonamu
|
|
580
|
-
-
|
|
578
|
+
**When is this needed:**
|
|
579
|
+
- When modifying the Sonamu framework source code during development
|
|
580
|
+
- When linking a local Sonamu repository to a project for development
|
|
581
581
|
|
|
582
|
-
|
|
582
|
+
**Problem:**
|
|
583
583
|
|
|
584
|
-
pnpm link
|
|
584
|
+
When linking Sonamu with pnpm link, type errors occur at build time:
|
|
585
585
|
|
|
586
586
|
```
|
|
587
587
|
error TS2345: Argument of type 'ZodNumber' is not assignable to parameter...
|
|
588
588
|
Type '2' is not assignable to type '3'.
|
|
589
589
|
```
|
|
590
590
|
|
|
591
|
-
|
|
591
|
+
**Cause:**
|
|
592
592
|
|
|
593
|
-
-
|
|
594
|
-
-
|
|
595
|
-
- TypeScript
|
|
593
|
+
- The linked Sonamu and the project each maintain their own `node_modules`
|
|
594
|
+
- TypeScript type mismatches occur due to different versions of shared dependencies (e.g. zod)
|
|
595
|
+
- TypeScript simultaneously references two different type definitions, causing errors
|
|
596
596
|
|
|
597
|
-
|
|
597
|
+
**Solution:**
|
|
598
598
|
|
|
599
|
-
### 1. pnpm-workspace.yaml
|
|
599
|
+
### 1. Add override to pnpm-workspace.yaml
|
|
600
600
|
|
|
601
|
-
|
|
601
|
+
In the project root's `pnpm-workspace.yaml`:
|
|
602
602
|
|
|
603
603
|
```yaml
|
|
604
604
|
overrides:
|
|
605
605
|
sonamu: link:../../sonamu/modules/sonamu
|
|
606
606
|
```
|
|
607
607
|
|
|
608
|
-
### 2. packages/api/package.json
|
|
608
|
+
### 2. Specify published version in packages/api/package.json
|
|
609
609
|
|
|
610
610
|
```json
|
|
611
611
|
{
|
|
612
612
|
"dependencies": {
|
|
613
|
-
"sonamu": "^0.7.45" //
|
|
613
|
+
"sonamu": "^0.7.45" // specify the latest published version
|
|
614
614
|
}
|
|
615
615
|
}
|
|
616
616
|
```
|
|
617
617
|
|
|
618
|
-
### 3.
|
|
618
|
+
### 3. Run install
|
|
619
619
|
|
|
620
620
|
```bash
|
|
621
621
|
pnpm install
|
|
622
622
|
```
|
|
623
623
|
|
|
624
|
-
### 4.
|
|
624
|
+
### 4. Verify build
|
|
625
625
|
|
|
626
626
|
```bash
|
|
627
627
|
cd packages/api
|
|
628
628
|
pnpm build
|
|
629
629
|
```
|
|
630
630
|
|
|
631
|
-
|
|
631
|
+
**How it works:**
|
|
632
632
|
|
|
633
|
-
- **TypeScript
|
|
634
|
-
-
|
|
635
|
-
-
|
|
633
|
+
- **TypeScript type check**: references type definitions from the npm registry based on the published version in `package.json`
|
|
634
|
+
- **Actual runtime**: `pnpm overrides` local link takes priority and runs local source code
|
|
635
|
+
- Separates type checking and runtime to resolve version mismatch issues
|
|
636
636
|
|
|
637
|
-
|
|
637
|
+
**Notes:**
|
|
638
638
|
|
|
639
|
-
- Sonamu
|
|
640
|
-
-
|
|
641
|
-
-
|
|
639
|
+
- Changes to Sonamu source code are immediately reflected in the project
|
|
640
|
+
- Restarting the project is required after building Sonamu
|
|
641
|
+
- For general project development, using the npm version is recommended
|
|
642
642
|
|
|
643
643
|
---
|
|
644
644
|
|
|
645
|
-
##
|
|
645
|
+
## Environment-Specific Configuration
|
|
646
646
|
|
|
647
|
-
###
|
|
647
|
+
### Development Environment
|
|
648
648
|
|
|
649
649
|
```env
|
|
650
650
|
# packages/api/.env
|
|
@@ -656,7 +656,7 @@ DATABASE_NAME=myproject
|
|
|
656
656
|
PROJECT_NAME=myproject
|
|
657
657
|
```
|
|
658
658
|
|
|
659
|
-
###
|
|
659
|
+
### Production Environment
|
|
660
660
|
|
|
661
661
|
```env
|
|
662
662
|
# packages/api/.env.production
|
|
@@ -678,54 +678,54 @@ S3_BUCKET=myproject-prod-bucket
|
|
|
678
678
|
|
|
679
679
|
---
|
|
680
680
|
|
|
681
|
-
## server
|
|
681
|
+
## server Additional Options
|
|
682
682
|
|
|
683
683
|
### baseUrl
|
|
684
684
|
|
|
685
685
|
```typescript
|
|
686
686
|
server: {
|
|
687
|
-
baseUrl: "https://api.example.com", //
|
|
687
|
+
baseUrl: "https://api.example.com", // external access URL (default: host:port)
|
|
688
688
|
}
|
|
689
689
|
```
|
|
690
690
|
|
|
691
691
|
### fastify
|
|
692
692
|
|
|
693
|
-
Fastify
|
|
693
|
+
Pass Fastify server options directly (excluding `logger`).
|
|
694
694
|
|
|
695
|
-
###
|
|
695
|
+
### Full Plugin List
|
|
696
696
|
|
|
697
|
-
|
|
|
697
|
+
| Plugin | Type | Description |
|
|
698
698
|
|----------|------|------|
|
|
699
|
-
| `compress` | `boolean \| FastifyCompressOptions` |
|
|
700
|
-
| `cors` | `boolean \| FastifyCorsOptions` | CORS
|
|
701
|
-
| `formbody` | `boolean \| FastifyFormbodyOptions` | x-www-form-urlencoded
|
|
702
|
-
| `multipart` | `boolean \| FastifyMultipartOptions` |
|
|
703
|
-
| `qs` | `boolean \| QsPluginOptions` |
|
|
704
|
-
| `session` |
|
|
699
|
+
| `compress` | `boolean \| FastifyCompressOptions` | Response compression (@fastify/compress) |
|
|
700
|
+
| `cors` | `boolean \| FastifyCorsOptions` | CORS configuration |
|
|
701
|
+
| `formbody` | `boolean \| FastifyFormbodyOptions` | x-www-form-urlencoded parsing |
|
|
702
|
+
| `multipart` | `boolean \| FastifyMultipartOptions` | File upload |
|
|
703
|
+
| `qs` | `boolean \| QsPluginOptions` | Query string parsing |
|
|
704
|
+
| `session` | session config | Session management |
|
|
705
705
|
| `sse` | `boolean \| SsePluginOptions` | Server-Sent Events |
|
|
706
|
-
| `static` | `boolean \| FastifyStaticOptions` |
|
|
707
|
-
| `custom` | `(server: FastifyInstance) => void` |
|
|
706
|
+
| `static` | `boolean \| FastifyStaticOptions` | Static file serving |
|
|
707
|
+
| `custom` | `(server: FastifyInstance) => void` | Custom plugin registration function |
|
|
708
708
|
|
|
709
709
|
## logging
|
|
710
710
|
|
|
711
|
-
|
|
711
|
+
Define logging configuration. Set to `false` to completely disable logging.
|
|
712
712
|
|
|
713
713
|
```typescript
|
|
714
|
-
logging: false, //
|
|
715
|
-
//
|
|
714
|
+
logging: false, // disable logging
|
|
715
|
+
// or
|
|
716
716
|
logging: {
|
|
717
|
-
sinks: { /*
|
|
718
|
-
filters: { /*
|
|
717
|
+
sinks: { /* define log output targets */ },
|
|
718
|
+
filters: { /* define filters */ },
|
|
719
719
|
},
|
|
720
720
|
```
|
|
721
721
|
|
|
722
722
|
## slackConfirm
|
|
723
723
|
|
|
724
|
-
|
|
724
|
+
Activates a Slack-based approval process for production DB migrations.
|
|
725
725
|
|
|
726
726
|
```typescript
|
|
727
727
|
slackConfirm: {
|
|
728
|
-
targets: ["production"], //
|
|
728
|
+
targets: ["production"], // list of DB keys requiring approval
|
|
729
729
|
botToken: process.env.SLACK_BOT_TOKEN ?? "", // Slack Bot Token (xoxb-...)
|
|
730
730
|
channelId: process.env.SLACK_CHANNEL_ID ?? "", // Slack Channel ID (C...)
|
|
731
731
|
},
|
|
@@ -733,17 +733,17 @@ slackConfirm: {
|
|
|
733
733
|
|
|
734
734
|
---
|
|
735
735
|
|
|
736
|
-
##
|
|
736
|
+
## Post-Configuration Checklist
|
|
737
737
|
|
|
738
|
-
1. `.env`
|
|
739
|
-
2. Docker
|
|
740
|
-
3.
|
|
741
|
-
4.
|
|
742
|
-
5. Sonamu UI
|
|
738
|
+
1. Confirm `.env` file is created
|
|
739
|
+
2. Start Docker: `pnpm docker:up`
|
|
740
|
+
3. Verify build: `pnpm build`
|
|
741
|
+
4. Start server: `pnpm dev`
|
|
742
|
+
5. Access Sonamu UI: http://localhost:34900/sonamu-ui
|
|
743
743
|
|
|
744
|
-
|
|
745
|
-
- [ ] `SESSION_SECRET`
|
|
746
|
-
- [ ] `SESSION_SALT`
|
|
747
|
-
- [ ] `cookie.domain`
|
|
748
|
-
- [ ] S3
|
|
749
|
-
- [ ]
|
|
744
|
+
Before production deployment:
|
|
745
|
+
- [ ] Change `SESSION_SECRET`
|
|
746
|
+
- [ ] Change `SESSION_SALT`
|
|
747
|
+
- [ ] Change `cookie.domain` to the actual domain
|
|
748
|
+
- [ ] Configure S3 (if needed)
|
|
749
|
+
- [ ] Add error handling logic
|