create-sonamu 0.0.1 → 0.0.3
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 +365 -2
- package/index.js +632 -0
- package/package.json +30 -20
- package/template/src/README.md +274 -0
- package/template/src/packages/api/.swcrc +18 -0
- package/template/src/packages/api/custom-sequencer.ts +23 -0
- package/template/src/packages/api/database/docker-compose.yml +19 -0
- package/template/src/packages/api/database/fixtures/init.sh +15 -0
- package/template/src/packages/api/database/scripts/dump.sh +62 -0
- package/template/src/packages/api/database/scripts/seed.sh +60 -0
- package/template/src/packages/api/package.json +55 -0
- package/template/src/packages/api/package.json.bak +55 -0
- package/template/src/packages/api/src/application/.gitkeep +1 -0
- package/template/src/packages/api/src/i18n/en.ts +59 -0
- package/template/src/packages/api/src/i18n/ko.ts +57 -0
- package/template/src/packages/api/src/index.ts +6 -0
- package/template/src/packages/api/src/migrations/.gitkeep +1 -0
- package/template/src/packages/api/src/sonamu.config.ts +162 -0
- package/template/src/packages/api/src/testing/fixture.ts +6 -0
- package/template/src/packages/api/src/testing/global.ts +6 -0
- package/template/src/packages/api/src/testing/setup-mocks.ts +44 -0
- package/template/src/packages/api/src/typings/fastify.d.ts +7 -0
- package/template/src/packages/api/src/typings/sonamu.d.ts +19 -0
- package/template/src/packages/api/src/utils/subset-loaders.ts +11 -0
- package/template/src/packages/api/tsconfig.json +60 -0
- package/template/src/packages/api/tsconfig.schemas.json +5 -0
- package/template/src/packages/api/tsconfig.types.json +5 -0
- package/template/src/packages/api/vitest.config.ts +36 -0
- package/template/src/packages/web/.sonamu.env +2 -0
- package/template/src/{web → packages/web}/index.html +3 -3
- package/template/src/packages/web/package.json +49 -0
- package/template/src/packages/web/package.json.bak +49 -0
- package/template/src/packages/web/src/App.tsx +17 -0
- package/template/src/packages/web/src/admin-common/ApiLogViewer.tsx +285 -0
- package/template/src/packages/web/src/admin-common/CommonModal.tsx +91 -0
- package/template/src/packages/web/src/contexts/sonamu-provider.tsx +41 -0
- package/template/src/packages/web/src/entry-client.tsx +72 -0
- package/template/src/packages/web/src/entry-server.generated.tsx +58 -0
- package/template/src/packages/web/src/i18n/en.ts +63 -0
- package/template/src/packages/web/src/i18n/ko.ts +61 -0
- package/template/src/packages/web/src/routeTree.gen.ts +27 -0
- package/template/src/packages/web/src/routes/__root.tsx +44 -0
- package/template/src/packages/web/src/routes/index.tsx +14 -0
- package/template/src/packages/web/src/styles/tailwind.css +5 -0
- package/template/src/packages/web/src/vite-env.d.ts +2 -0
- package/template/src/packages/web/tailwind.config.ts +8 -0
- package/template/src/{web → packages/web}/tsconfig.json +5 -3
- package/template/src/packages/web/vite.config.ts +51 -0
- package/template/src/api/README.md +0 -3
- package/template/src/api/database/docker-compose.yml +0 -17
- package/template/src/api/package.json +0 -39
- package/template/src/api/sonamu.config.json +0 -11
- package/template/src/api/src/configs/db.ts +0 -25
- package/template/src/api/src/index.ts +0 -36
- package/template/src/api/src/testing/bootstrap.ts +0 -20
- package/template/src/api/src/testing/fixture.ts +0 -18
- package/template/src/api/src/testing/global.ts +0 -7
- package/template/src/api/src/typings/sonamu.d.ts +0 -5
- package/template/src/api/tsconfig.json +0 -115
- package/template/src/api/vite.config.mts +0 -15
- package/template/src/web/package.json +0 -40
- package/template/src/web/public/vite.svg +0 -1
- package/template/src/web/src/App.css +0 -34
- package/template/src/web/src/App.tsx +0 -15
- package/template/src/web/src/assets/react.svg +0 -1
- package/template/src/web/src/index.css +0 -76
- package/template/src/web/src/main.tsx +0 -30
- package/template/src/web/src/pages/index.tsx +0 -11
- package/template/src/web/src/vite-env.d.ts +0 -1
- package/template/src/web/vite.config.ts +0 -20
- /package/template/src/{web/src/services → packages/api/database/dumps}/.gitkeep +0 -0
- /package/template/src/{api/database/scripts/init.sql → packages/web/src/services/.gitkeep} +0 -0
- /package/template/src/{web → packages/web}/tsconfig.node.json +0 -0
package/package.json
CHANGED
|
@@ -1,45 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-sonamu",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Create a new Sonamu project",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"sonamu",
|
|
7
|
+
"typescript",
|
|
8
|
+
"fullstack",
|
|
9
|
+
"framework",
|
|
10
|
+
"cli",
|
|
11
|
+
"scaffold"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
4
14
|
"type": "module",
|
|
5
|
-
"bin":
|
|
15
|
+
"bin": {
|
|
16
|
+
"create-sonamu": "index.js"
|
|
17
|
+
},
|
|
6
18
|
"files": [
|
|
7
19
|
"index.js",
|
|
8
20
|
"template"
|
|
9
21
|
],
|
|
10
22
|
"engines": {
|
|
11
|
-
"node": ">=
|
|
23
|
+
"node": ">=22.0.0"
|
|
12
24
|
},
|
|
13
25
|
"author": {
|
|
14
|
-
"name": "
|
|
15
|
-
"email": "
|
|
26
|
+
"name": "noasouth",
|
|
27
|
+
"email": "noa@cartanova.ai"
|
|
16
28
|
},
|
|
17
29
|
"repository": {
|
|
18
30
|
"type": "git",
|
|
19
|
-
"url": "https://github.com/
|
|
31
|
+
"url": "https://github.com/cartanova-ai/sonamu"
|
|
20
32
|
},
|
|
21
33
|
"publishConfig": {
|
|
22
34
|
"access": "public"
|
|
23
35
|
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "tsc",
|
|
26
|
-
"start": "node index.js",
|
|
27
|
-
"prepublishOnly": "yarn zx ./scripts/prepublish.mjs",
|
|
28
|
-
"clean": "rm index.js"
|
|
29
|
-
},
|
|
30
36
|
"devDependencies": {
|
|
31
|
-
"@
|
|
32
|
-
"@types/node": "
|
|
37
|
+
"@types/minimist": "^1.2.5",
|
|
38
|
+
"@types/node": "25.0.7",
|
|
33
39
|
"@types/prompts": "^2",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"zx": "^7.2.3"
|
|
40
|
+
"@biomejs/biome": "^2.3.10",
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"zx": "^8.8.5"
|
|
38
43
|
},
|
|
39
|
-
"packageManager": "yarn@4.1.1",
|
|
40
44
|
"dependencies": {
|
|
41
|
-
"chalk": "^
|
|
45
|
+
"chalk": "^4.1.2",
|
|
46
|
+
"minimist": "^1.2.8",
|
|
42
47
|
"ora": "^8.0.1",
|
|
43
48
|
"prompts": "^2.4.2"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc",
|
|
52
|
+
"start": "node index.js",
|
|
53
|
+
"clean": "rm index.js"
|
|
44
54
|
}
|
|
45
55
|
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# 🌲 Sonamu 프로젝트
|
|
2
|
+
|
|
3
|
+
> End-to-End 타입 안전성을 갖춘 Entity 중심 풀스택 TypeScript 프레임워크
|
|
4
|
+
|
|
5
|
+
## 📁 프로젝트 구조
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
├── packages/
|
|
9
|
+
│ ├── api/ # 백엔드 (Sonamu - Fastify 기반)
|
|
10
|
+
│ │ ├── src/
|
|
11
|
+
│ │ │ ├── application/ # 엔티티, 모델, 타입 (엔티티 생성 후 자동 생성)
|
|
12
|
+
│ │ │ ├── i18n/ # 다국어 지원 (ko, en)
|
|
13
|
+
│ │ │ ├── testing/ # 테스트 유틸리티
|
|
14
|
+
│ │ │ ├── index.ts # 서버 진입점
|
|
15
|
+
│ │ │ └── sonamu.config.ts # Sonamu 설정
|
|
16
|
+
│ │ ├── database/
|
|
17
|
+
│ │ │ ├── docker-compose.yml
|
|
18
|
+
│ │ │ ├── fixtures/ # DB 초기화 스크립트
|
|
19
|
+
│ │ │ └── scripts/ # dump, seed 스크립트
|
|
20
|
+
│ │ └── vitest.config.ts # 테스트 설정
|
|
21
|
+
│ │
|
|
22
|
+
│ └── web/ # 프론트엔드 (React + Vite + SSR)
|
|
23
|
+
│ └── src/
|
|
24
|
+
│ ├── routes/ # TanStack Router (파일 기반 라우팅)
|
|
25
|
+
│ ├── services/ # API 클라이언트 (엔티티 생성 후 자동 생성)
|
|
26
|
+
│ ├── i18n/ # 다국어 지원
|
|
27
|
+
│ ├── contexts/ # React Context (Sonamu Provider)
|
|
28
|
+
│ ├── admin-common/ # 공통 컴포넌트 (ApiLogViewer 등)
|
|
29
|
+
│ ├── entry-client.tsx # 클라이언트 진입점
|
|
30
|
+
│ └── entry-server.generated.tsx # SSR 진입점
|
|
31
|
+
└── pnpm-workspace.yaml # pnpm workspace 설정
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**참고**: `application/`과 `services/` 하위의 많은 파일들은 Sonamu UI에서 첫 번째 엔티티를 생성한 후 자동으로 생성됩니다.
|
|
35
|
+
|
|
36
|
+
## 🚀 빠른 시작
|
|
37
|
+
|
|
38
|
+
### 1. 의존성 설치 (프로젝트 루트에서)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pnpm install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. 데이터베이스 시작
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd packages/api
|
|
48
|
+
pnpm docker:up
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 3. API 서버 시작
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cd packages/api
|
|
55
|
+
pnpm dev
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
API 서버가 시작되면 다음 주소로 접속할 수 있습니다:
|
|
59
|
+
- **API 서버**: http://localhost:1028
|
|
60
|
+
- **Sonamu UI**: http://localhost:1028/sonamu-ui (엔티티 관리)
|
|
61
|
+
|
|
62
|
+
### 4. 첫 번째 엔티티 생성
|
|
63
|
+
|
|
64
|
+
1. Sonamu UI 열기: http://localhost:1028/sonamu-ui
|
|
65
|
+
2. **Entities** 탭 → **"+ Entity"** 클릭
|
|
66
|
+
3. 엔티티 정의 (예: `User`, `Post`)
|
|
67
|
+
4. `api/src/application/`과 `web/src/services/`에 파일이 자동으로 생성됩니다
|
|
68
|
+
|
|
69
|
+
### 5. Web 서버 시작 (새 터미널에서)
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cd packages/web
|
|
73
|
+
pnpm dev
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
http://localhost:3028 을 열어서 앱을 확인하세요!
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 📝 자동 생성되는 파일들
|
|
81
|
+
|
|
82
|
+
첫 번째 엔티티를 생성하면 다음 파일들이 자동으로 생성됩니다:
|
|
83
|
+
|
|
84
|
+
### API 측
|
|
85
|
+
```
|
|
86
|
+
api/src/application/
|
|
87
|
+
├── user/
|
|
88
|
+
│ ├── user.entity.json # 엔티티 정의 (단일 진실 공급원)
|
|
89
|
+
│ ├── user.types.ts # Zod 스키마 & TypeScript 타입
|
|
90
|
+
│ ├── user.model.ts # 비즈니스 로직
|
|
91
|
+
│ └── user.model.test.ts # 테스트 파일
|
|
92
|
+
├── sonamu.generated.ts # 모든 엔티티의 공통 타입
|
|
93
|
+
└── sonamu.generated.sso.ts # 서브셋 쿼리
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Web 측
|
|
97
|
+
```
|
|
98
|
+
web/src/services/
|
|
99
|
+
├── user/
|
|
100
|
+
│ └── user.service.ts # API 클라이언트 (자동 생성)
|
|
101
|
+
├── sonamu.generated.ts # 타입 (API에서 복사)
|
|
102
|
+
├── sonamu.shared.ts # 공유 유틸리티 (josa, dateReviver 등)
|
|
103
|
+
└── services.generated.ts # 통합 서비스 내보내기
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 🌐 포트 구성
|
|
107
|
+
|
|
108
|
+
| 서비스 | 포트 | URL |
|
|
109
|
+
| ----------- | ----------------------- | ------------------------------- |
|
|
110
|
+
| API 서버 | `BASE_PORT` (기본 1028) | http://localhost:1028 |
|
|
111
|
+
| Sonamu UI | - | http://localhost:1028/sonamu-ui |
|
|
112
|
+
| Web 클라이언트 | `BASE_PORT + 2000` | http://localhost:3028 |
|
|
113
|
+
| PostgreSQL | 5432 | - |
|
|
114
|
+
|
|
115
|
+
## 📜 주요 스크립트
|
|
116
|
+
|
|
117
|
+
### Root (workspace)
|
|
118
|
+
|
|
119
|
+
| 명령어 | 설명 |
|
|
120
|
+
| --------------- | --------------------------- |
|
|
121
|
+
| `pnpm install` | 모든 패키지 의존성 설치 |
|
|
122
|
+
| `pnpm -r build` | 모든 패키지 빌드 (api, web) |
|
|
123
|
+
| `pnpm -r test` | 모든 패키지 테스트 실행 |
|
|
124
|
+
|
|
125
|
+
### API (`packages/api/`)
|
|
126
|
+
|
|
127
|
+
| 명령어 | 설명 |
|
|
128
|
+
| ------------------- | -------------------------------------- |
|
|
129
|
+
| `pnpm dev` | 개발 서버 시작 (HMR, Sonamu UI 포함) |
|
|
130
|
+
| `pnpm build` | 프로덕션 빌드 |
|
|
131
|
+
| `pnpm start` | 프로덕션 서버 시작 |
|
|
132
|
+
| `pnpm test` | 테스트 실행 |
|
|
133
|
+
| `pnpm docker:up` | Docker DB 시작 |
|
|
134
|
+
| `pnpm docker:down` | Docker DB 중지 |
|
|
135
|
+
| `pnpm docker:reset` | Docker DB 초기화 (볼륨 삭제 후 재시작) |
|
|
136
|
+
| `pnpm dump` | 테스트 DB 덤프 생성 |
|
|
137
|
+
| `pnpm seed` | 덤프를 fixture DB에 적용 |
|
|
138
|
+
|
|
139
|
+
### Web (`packages/web/`)
|
|
140
|
+
|
|
141
|
+
| 명령어 | 설명 |
|
|
142
|
+
| -------------- | ------------------ |
|
|
143
|
+
| `pnpm dev` | 개발 서버 시작 |
|
|
144
|
+
| `pnpm build` | 프로덕션 빌드 |
|
|
145
|
+
| `pnpm preview` | 빌드 결과 미리보기 |
|
|
146
|
+
|
|
147
|
+
## 🛠️ 개발 워크플로우
|
|
148
|
+
|
|
149
|
+
### 1. 엔티티 생성
|
|
150
|
+
|
|
151
|
+
1. API 서버 시작 후 Sonamu UI 열기 (http://localhost:1028/sonamu-ui)
|
|
152
|
+
2. **Entities** 탭 → **"+ Entity"** 클릭
|
|
153
|
+
3. 엔티티 정보 입력 (이름, 필드 등)
|
|
154
|
+
4. **Create** 클릭 - 파일이 자동으로 생성됩니다!
|
|
155
|
+
|
|
156
|
+
### 2. 비즈니스 로직 작성
|
|
157
|
+
|
|
158
|
+
`api/src/application/{entity}/{entity}.model.ts` 파일 편집:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { api, BaseModelClass } from "sonamu";
|
|
162
|
+
|
|
163
|
+
class UserModelClass extends BaseModelClass {
|
|
164
|
+
@api({ httpMethod: "GET", clients: ["axios", "tanstack-query"] })
|
|
165
|
+
async findById(id: number): Promise<UserSubsetA | null> {
|
|
166
|
+
// 비즈니스 로직 작성
|
|
167
|
+
return this.findOne("A", { where: { id } });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export const UserModel = new UserModelClass();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
저장 → `web/src/services/user/`에 Web 서비스 코드가 자동으로 생성됩니다!
|
|
175
|
+
|
|
176
|
+
### 3. 프론트엔드에서 사용
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// web/src/routes/users/$id.tsx
|
|
180
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
181
|
+
import { useQuery } from "@tanstack/react-query";
|
|
182
|
+
import { UserService } from "@/services/user/user.service";
|
|
183
|
+
|
|
184
|
+
export const Route = createFileRoute("/users/$id")({
|
|
185
|
+
component: UserDetailPage,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
function UserDetailPage() {
|
|
189
|
+
const { id } = Route.useParams();
|
|
190
|
+
const { data: user } = useQuery({
|
|
191
|
+
queryKey: ["user", id],
|
|
192
|
+
queryFn: () => UserService.findById(Number(id)),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return <div>{user?.name}</div>;
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 4. 데이터베이스 마이그레이션
|
|
200
|
+
|
|
201
|
+
1. Sonamu UI 열기 → **"DB Migration"** 탭
|
|
202
|
+
2. 스키마 변경사항 확인
|
|
203
|
+
3. 마이그레이션 파일 생성
|
|
204
|
+
4. 마이그레이션 적용
|
|
205
|
+
|
|
206
|
+
### 5. 테스트 데이터 관리 (Fixtures)
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# 1. DB 클라이언트(TablePlus 등)로 테스트 데이터 수정
|
|
210
|
+
|
|
211
|
+
# 2. 덤프 생성
|
|
212
|
+
pnpm dump
|
|
213
|
+
|
|
214
|
+
# 3. Git에 커밋
|
|
215
|
+
git add database/dumps/
|
|
216
|
+
git commit -m "feat: 테스트 데이터 추가"
|
|
217
|
+
|
|
218
|
+
# --- 팀원이 pull 받은 후 ---
|
|
219
|
+
|
|
220
|
+
# 4. fixture DB에 덤프 적용
|
|
221
|
+
pnpm seed
|
|
222
|
+
|
|
223
|
+
# 5. test DB에 fixture 동기화
|
|
224
|
+
pnpm sonamu fixture sync
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## 데이터베이스
|
|
228
|
+
|
|
229
|
+
### Docker 이미지
|
|
230
|
+
|
|
231
|
+
기본적으로 `pgvector/pgvector:pg18` 이미지를 사용합니다. 이 이미지에는 다음 extension이 포함되어 있습니다:
|
|
232
|
+
|
|
233
|
+
- **pgvector** - 벡터 검색 (AI/임베딩용)
|
|
234
|
+
|
|
235
|
+
> **pgroonga (전문 검색)가 필요한 경우**
|
|
236
|
+
>
|
|
237
|
+
> pgroonga는 C 라이브러리 기반으로 별도 컴파일이 필요하여 기본 이미지에 포함되어 있지 않습니다.
|
|
238
|
+
> 전문 검색이 필요하다면 [pgroonga Docker 이미지](https://hub.docker.com/r/groonga/pgroonga)를 사용하거나,
|
|
239
|
+
> 직접 Dockerfile을 작성하여 pgroonga를 설치하세요.
|
|
240
|
+
|
|
241
|
+
### 데이터베이스 구성
|
|
242
|
+
|
|
243
|
+
| DB 이름 | 용도 |
|
|
244
|
+
| ------------------ | --------------- |
|
|
245
|
+
| `{name}` | 메인 개발 DB |
|
|
246
|
+
| `{name}_fixture` | fixture DB |
|
|
247
|
+
| `{name}_test` | 테스트 실행용 |
|
|
248
|
+
|
|
249
|
+
## 🔧 기술 스택
|
|
250
|
+
|
|
251
|
+
### 백엔드
|
|
252
|
+
- **Sonamu** - 엔티티 중심 프레임워크
|
|
253
|
+
- **Fastify** - 빠르고 오버헤드가 적은 웹 프레임워크
|
|
254
|
+
- **Knex.js** - SQL 쿼리 빌더
|
|
255
|
+
- **Zod** - TypeScript 우선 스키마 검증
|
|
256
|
+
- **Vitest** - 테스팅 프레임워크
|
|
257
|
+
|
|
258
|
+
### 프론트엔드
|
|
259
|
+
- **React 19** - UI 라이브러리
|
|
260
|
+
- **Vite** - 빌드 도구
|
|
261
|
+
- **TanStack Router** - 타입 안전 라우팅
|
|
262
|
+
- **TanStack Query** - 데이터 패칭 및 캐싱
|
|
263
|
+
- **Tailwind CSS** - 유틸리티 우선 CSS
|
|
264
|
+
- **SSR** - 서버 사이드 렌더링 지원
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 📚 더 알아보기
|
|
269
|
+
|
|
270
|
+
- [Sonamu 문서](https://rurruur.github.io/test-docs)
|
|
271
|
+
- [Sonamu GitHub](https://github.com/ping-alive/sonamu)
|
|
272
|
+
- [Fastify 문서](https://www.fastify.io/)
|
|
273
|
+
- [TanStack Router](https://tanstack.com/router)
|
|
274
|
+
- [TanStack Query](https://tanstack.com/query)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://swc.rs/schema.json",
|
|
3
|
+
"module": {
|
|
4
|
+
"type": "es6", // import/export 쓰는 esm으로 가겠습니다.
|
|
5
|
+
"resolveFully": true // esm이 요구하는 대로, 임포트 경로를 실제 파일 경로(확장자 포함)로 풀어줍니다.
|
|
6
|
+
},
|
|
7
|
+
"jsc": {
|
|
8
|
+
"parser": {
|
|
9
|
+
"syntax": "typescript",
|
|
10
|
+
"decorators": true
|
|
11
|
+
},
|
|
12
|
+
"baseUrl": ".", // 위 resolveFully 옵션이 작동하려면 이게 필요합니다. 이 파일이 있는 로컬에서 쉘에 직접 실행할 때에는 baseUrl에 이 파일이 위치한 경로 기준으로 절대경로가 resolve되어 들어갑니다. 그러나 만약 swc를 코드 상에서 import해서 쓰거나, 아니면 설정 파일(.swcrc) 대신 -C로 설정을 명령줄에 직접 넘기는 경우, baseUrl에 직접 올바른 절대경로를 넣어주어야 합니다.
|
|
13
|
+
"target": "esnext" // 타겟은 그냥 최신 문법으로.
|
|
14
|
+
},
|
|
15
|
+
"minify": false, // 어차피 용량 10%정도 차이밖에 안 남. minify를 끄면 혹시 혹시 정말 혹시나 나중에 소스맵 없이 코드를 봐야 하는 끔찍한 상황에 조금이나마 도움이 될 수 있지 않을까 해서 끔.
|
|
16
|
+
"sourceMaps": "inline", // 인라인 소스맵 생성 - yarn PnP 가상 경로 문제 해결
|
|
17
|
+
"inlineSourcesContent": true // 소스 코드를 소스맵에 포함
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// custom-sequencer.ts
|
|
2
|
+
import type { TestSequencer, TestSpecification } from "vitest/node";
|
|
3
|
+
|
|
4
|
+
export class PrioritySequencer implements TestSequencer {
|
|
5
|
+
async sort(files: TestSpecification[]) {
|
|
6
|
+
const priority = ["migrator", "syncer", "type-safety"];
|
|
7
|
+
|
|
8
|
+
return files.sort((a, b) => {
|
|
9
|
+
const aPriority = priority.findIndex((p) => a.moduleId.includes(p));
|
|
10
|
+
const bPriority = priority.findIndex((p) => b.moduleId.includes(p));
|
|
11
|
+
|
|
12
|
+
if (aPriority !== -1 && bPriority === -1) return -1;
|
|
13
|
+
if (aPriority === -1 && bPriority !== -1) return 1;
|
|
14
|
+
if (aPriority !== bPriority) return aPriority - bPriority;
|
|
15
|
+
|
|
16
|
+
return a.moduleId.localeCompare(b.moduleId);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async shard(files: TestSpecification[]) {
|
|
21
|
+
return files;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
version: "3.8"
|
|
2
|
+
name: ${CONTAINER_NAME}
|
|
3
|
+
services:
|
|
4
|
+
pg:
|
|
5
|
+
platform: linux/arm64
|
|
6
|
+
image: pgvector/pgvector:pg18
|
|
7
|
+
container_name: ${CONTAINER_NAME}
|
|
8
|
+
env_file:
|
|
9
|
+
- ../.env
|
|
10
|
+
volumes:
|
|
11
|
+
- ./fixtures/init.sh:/docker-entrypoint-initdb.d/init.sh
|
|
12
|
+
environment:
|
|
13
|
+
DATABASE_NAME: ${DATABASE_NAME}
|
|
14
|
+
POSTGRES_DB: template1
|
|
15
|
+
POSTGRES_USER: ${DB_USER}
|
|
16
|
+
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
17
|
+
TZ: Asia/Seoul
|
|
18
|
+
ports:
|
|
19
|
+
- "${DB_PORT}:5432"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# 컨테이너가 시작되고 데이터베이스가 준비된 직후에 실행될 스크립트입니다.
|
|
5
|
+
# 이 스크립트는 데이터베이스를 생성하는 것 까지만 담당합니다.
|
|
6
|
+
# 이후 DDL 추가는 Sonamu UI의 DB Migration 기능을 사용해주세요.
|
|
7
|
+
|
|
8
|
+
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
9
|
+
-- 데이터베이스에서 vector 확장 설치 (기본접속 template1)
|
|
10
|
+
CREATE EXTENSION IF NOT EXISTS vector;
|
|
11
|
+
|
|
12
|
+
CREATE DATABASE ${DATABASE_NAME};
|
|
13
|
+
CREATE DATABASE ${DATABASE_NAME}_fixture;
|
|
14
|
+
CREATE DATABASE ${DATABASE_NAME}_test;
|
|
15
|
+
EOSQL
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 테스트 DB 덤프 스크립트
|
|
3
|
+
# Usage: pnpm dump
|
|
4
|
+
|
|
5
|
+
source .env
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
DB_NAME="${DATABASE_NAME}_test"
|
|
10
|
+
DUMP_DIR="database/dumps"
|
|
11
|
+
DUMP_FILE="${DUMP_DIR}/${DATABASE_NAME}_test_latest.sql"
|
|
12
|
+
|
|
13
|
+
mkdir -p ${DUMP_DIR}
|
|
14
|
+
|
|
15
|
+
echo "📦 Dumping ${DB_NAME}..."
|
|
16
|
+
|
|
17
|
+
# Docker 컨테이너 이름 찾기 (여러 방법 시도)
|
|
18
|
+
if [ -n "$CONTAINER_NAME" ]; then
|
|
19
|
+
# .env에서 CONTAINER_NAME이 설정된 경우 사용
|
|
20
|
+
echo "🐳 Using container from .env: ${CONTAINER_NAME}"
|
|
21
|
+
else
|
|
22
|
+
# 자동 탐지
|
|
23
|
+
CONTAINER_NAME=$(docker ps --format "{{.Names}}" | grep -i postgres | head -n 1)
|
|
24
|
+
|
|
25
|
+
if [ -z "$CONTAINER_NAME" ]; then
|
|
26
|
+
CONTAINER_NAME=$(docker ps --filter "ancestor=postgres:18" --format "{{.Names}}" | head -n 1)
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if [ -z "$CONTAINER_NAME" ]; then
|
|
30
|
+
CONTAINER_NAME=$(docker ps --format "{{.Names}}\t{{.Ports}}" | grep 5432 | cut -f1 | head -n 1)
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [ -z "$CONTAINER_NAME" ]; then
|
|
35
|
+
echo "❌ PostgreSQL 컨테이너를 찾을 수 없습니다."
|
|
36
|
+
echo "💡 다음 명령어로 컨테이너를 확인하세요:"
|
|
37
|
+
echo " docker ps"
|
|
38
|
+
echo ""
|
|
39
|
+
echo ".env 파일에 CONTAINER_NAME을 설정하세요."
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo "🐳 Using container: ${CONTAINER_NAME}"
|
|
44
|
+
|
|
45
|
+
# Docker 컨테이너 내부의 pg_dump 사용
|
|
46
|
+
docker exec ${CONTAINER_NAME} pg_dump \
|
|
47
|
+
--username=${DB_USER:-postgres} \
|
|
48
|
+
--dbname=${DB_NAME} \
|
|
49
|
+
--no-owner \
|
|
50
|
+
--no-privileges \
|
|
51
|
+
--no-comments \
|
|
52
|
+
--inserts > ${DUMP_FILE}
|
|
53
|
+
|
|
54
|
+
if [ $? -eq 0 ]; then
|
|
55
|
+
# 파일 크기 확인
|
|
56
|
+
FILE_SIZE=$(du -h ${DUMP_FILE} | cut -f1)
|
|
57
|
+
echo "✅ Updated: ${DUMP_FILE} (${FILE_SIZE})"
|
|
58
|
+
echo "🎉 Dump completed!"
|
|
59
|
+
else
|
|
60
|
+
echo "❌ Dump failed!"
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# fixture DB에 덤프 적용하는 스크립트
|
|
3
|
+
# Usage: pnpm seed
|
|
4
|
+
|
|
5
|
+
source .env
|
|
6
|
+
|
|
7
|
+
set -e # 에러 발생 시 즉시 중단
|
|
8
|
+
|
|
9
|
+
SOURCE_DB="${DATABASE_NAME}_test"
|
|
10
|
+
FIXTURE_DB="${DATABASE_NAME}_fixture_remote"
|
|
11
|
+
DUMP_FILE="database/dumps/${DATABASE_NAME}_test_latest.sql"
|
|
12
|
+
|
|
13
|
+
# DB 설정 (환경변수 또는 기본값)
|
|
14
|
+
DB_HOST="${DB_HOST:-0.0.0.0}"
|
|
15
|
+
DB_PORT="${DB_PORT:-5432}"
|
|
16
|
+
DB_USER="${DB_USER:-postgres}"
|
|
17
|
+
|
|
18
|
+
# PostgreSQL 패스워드 환경변수 설정
|
|
19
|
+
export PGPASSWORD="${DB_PASSWORD}"
|
|
20
|
+
|
|
21
|
+
if [ ! -f "${DUMP_FILE}" ]; then
|
|
22
|
+
echo "❌ Dump file not found: ${DUMP_FILE}"
|
|
23
|
+
echo "💡 Run 'pnpm dump' first!"
|
|
24
|
+
unset PGPASSWORD
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
echo "📥 Seeding ${FIXTURE_DB} from ${DUMP_FILE}..."
|
|
29
|
+
echo "🔗 Target: ${DB_USER}@${DB_HOST}:${DB_PORT}"
|
|
30
|
+
|
|
31
|
+
# 1. fixture DB 초기화
|
|
32
|
+
echo "🗑️ Recreating ${FIXTURE_DB}..."
|
|
33
|
+
psql \
|
|
34
|
+
-h "${DB_HOST}" \
|
|
35
|
+
-p "${DB_PORT}" \
|
|
36
|
+
-U "${DB_USER}" \
|
|
37
|
+
-d postgres \
|
|
38
|
+
-c "SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
39
|
+
FROM pg_stat_activity
|
|
40
|
+
WHERE datname = '${FIXTURE_DB}'
|
|
41
|
+
AND pid <> pg_backend_pid();" \
|
|
42
|
+
-c "DROP DATABASE IF EXISTS \"${FIXTURE_DB}\";" \
|
|
43
|
+
-c "CREATE DATABASE \"${FIXTURE_DB}\";"
|
|
44
|
+
|
|
45
|
+
# 2. 덤프 적용
|
|
46
|
+
echo "📝 Applying dump file to ${FIXTURE_DB}..."
|
|
47
|
+
psql \
|
|
48
|
+
-h "${DB_HOST}" \
|
|
49
|
+
-p "${DB_PORT}" \
|
|
50
|
+
-U "${DB_USER}" \
|
|
51
|
+
-d "${FIXTURE_DB}" \
|
|
52
|
+
-f "${DUMP_FILE}"
|
|
53
|
+
|
|
54
|
+
# 환경변수 정리
|
|
55
|
+
unset PGPASSWORD
|
|
56
|
+
|
|
57
|
+
echo "✅ Dump applied to ${FIXTURE_DB}"
|
|
58
|
+
echo "🎉 Seed completed!"
|
|
59
|
+
echo ""
|
|
60
|
+
echo "💡 Next step: pnpm sonamu fixture sync"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-sonamu-api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"description": "Sonamu API server",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "sonamu dev",
|
|
10
|
+
"build": "sonamu build",
|
|
11
|
+
"start": "sonamu start",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest watch --standalone",
|
|
14
|
+
"test:coverage": "vitest run --coverage",
|
|
15
|
+
"sonamu": "sonamu",
|
|
16
|
+
"dump": "bash database/scripts/dump.sh",
|
|
17
|
+
"seedOnly": "bash database/scripts/seed.sh",
|
|
18
|
+
"seed": "pnpm seedOnly && sonamu fixture sync",
|
|
19
|
+
"sync:dump": "pnpm seed && pnpm sonamu migrate run && pnpm dump",
|
|
20
|
+
"docker:up": "docker compose --env-file .env -f database/docker-compose.yml up -d",
|
|
21
|
+
"docker:down": "docker compose --env-file .env -f database/docker-compose.yml down"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@ai-sdk/anthropic": "^3.0.0",
|
|
25
|
+
"@ai-sdk/openai": "^3.0.0",
|
|
26
|
+
"@ai-sdk/provider": "^3.0.0",
|
|
27
|
+
"@ai-sdk/provider-utils": "^4.0.0",
|
|
28
|
+
"@logtape/logtape": "2.0.0",
|
|
29
|
+
"@logtape/pretty": "2.0.0",
|
|
30
|
+
"@logtape/redaction": "2.0.0",
|
|
31
|
+
"@swc/core": "^1.13.5",
|
|
32
|
+
"ai": "^6.0.1",
|
|
33
|
+
"bcrypt": "^6.0.0",
|
|
34
|
+
"chalk": "^4.1.2",
|
|
35
|
+
"date-fns": "^4.1.0",
|
|
36
|
+
"dotenv": "^16",
|
|
37
|
+
"knex": "^3.1.0",
|
|
38
|
+
"pg": "^8.16.3",
|
|
39
|
+
"radashi": "^12.2.0",
|
|
40
|
+
"sonamu": "^0.7.45",
|
|
41
|
+
"zod": "^4.1.12"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@fastify/compress": "^7.0.3",
|
|
45
|
+
"@swc/cli": "^0.7.8",
|
|
46
|
+
"@types/bcrypt": "^6",
|
|
47
|
+
"@types/node": "25.0.7",
|
|
48
|
+
"@vitest/coverage-v8": "^4.0.12",
|
|
49
|
+
"fastify": "^4",
|
|
50
|
+
"ioredis": "^5.8.2",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"vite": "7.3.0",
|
|
53
|
+
"vitest": "^4.0.10"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-sonamu-api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"description": "Sonamu API server",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "sonamu dev",
|
|
10
|
+
"build": "sonamu build",
|
|
11
|
+
"start": "sonamu start",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest watch --standalone",
|
|
14
|
+
"test:coverage": "vitest run --coverage",
|
|
15
|
+
"sonamu": "sonamu",
|
|
16
|
+
"dump": "bash database/scripts/dump.sh",
|
|
17
|
+
"seedOnly": "bash database/scripts/seed.sh",
|
|
18
|
+
"seed": "pnpm seedOnly && sonamu fixture sync",
|
|
19
|
+
"sync:dump": "pnpm seed && pnpm sonamu migrate run && pnpm dump",
|
|
20
|
+
"docker:up": "docker compose --env-file .env -f database/docker-compose.yml up -d",
|
|
21
|
+
"docker:down": "docker compose --env-file .env -f database/docker-compose.yml down"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@ai-sdk/anthropic": "catalog:",
|
|
25
|
+
"@ai-sdk/openai": "catalog:",
|
|
26
|
+
"@ai-sdk/provider": "catalog:",
|
|
27
|
+
"@ai-sdk/provider-utils": "catalog:",
|
|
28
|
+
"@logtape/logtape": "catalog:",
|
|
29
|
+
"@logtape/pretty": "catalog:",
|
|
30
|
+
"@logtape/redaction": "catalog:",
|
|
31
|
+
"@swc/core": "catalog:",
|
|
32
|
+
"ai": "catalog:",
|
|
33
|
+
"bcrypt": "catalog:",
|
|
34
|
+
"chalk": "catalog:",
|
|
35
|
+
"date-fns": "catalog:",
|
|
36
|
+
"dotenv": "catalog:",
|
|
37
|
+
"knex": "catalog:",
|
|
38
|
+
"pg": "catalog:",
|
|
39
|
+
"radashi": "catalog:",
|
|
40
|
+
"sonamu": "workspace:^",
|
|
41
|
+
"zod": "catalog:"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@fastify/compress": "catalog:",
|
|
45
|
+
"@swc/cli": "catalog:",
|
|
46
|
+
"@types/bcrypt": "catalog:",
|
|
47
|
+
"@types/node": "catalog:",
|
|
48
|
+
"@vitest/coverage-v8": "catalog:",
|
|
49
|
+
"fastify": "catalog:",
|
|
50
|
+
"ioredis": "catalog:",
|
|
51
|
+
"typescript": "catalog:",
|
|
52
|
+
"vite": "catalog:",
|
|
53
|
+
"vitest": "catalog:"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# This directory will contain auto-generated entity files after you create your first entity in Sonamu UI.
|