create-entity-server 0.0.9 → 0.0.23
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/bin/create.js +26 -8
- package/package.json +1 -1
- package/template/.env.example +20 -3
- package/template/configs/database.json +173 -10
- package/template/configs/jwt.json +1 -0
- package/template/configs/oauth.json +37 -0
- package/template/configs/push.json +26 -0
- package/template/entities/Account/account_audit.json +4 -5
- package/template/entities/README.md +4 -4
- package/template/entities/{Auth → System/Auth}/account.json +0 -14
- package/template/entities/System/system_audit_log.json +14 -8
- package/template/samples/README.md +43 -21
- package/template/samples/browser/entity-server-client.js +453 -0
- package/template/samples/browser/example.html +498 -0
- package/template/samples/entities/01_basic_fields.json +39 -0
- package/template/samples/entities/02_types_and_defaults.json +67 -0
- package/template/samples/entities/03_hash_and_unique.json +33 -0
- package/template/samples/entities/04_fk_and_composite_unique.json +29 -0
- package/template/samples/entities/05_cache.json +55 -0
- package/template/samples/entities/06_history_and_hard_delete.json +60 -0
- package/template/samples/entities/07_license_scope.json +52 -0
- package/template/samples/entities/08_hook_sql.json +52 -0
- package/template/samples/entities/09_hook_entity.json +65 -0
- package/template/samples/entities/10_hook_submit_delete.json +78 -0
- package/template/samples/entities/11_hook_webhook.json +84 -0
- package/template/samples/entities/12_hook_push.json +73 -0
- package/template/samples/entities/13_read_only.json +54 -0
- package/template/samples/entities/14_optimistic_lock.json +29 -0
- package/template/samples/entities/15_reset_defaults.json +94 -0
- package/template/samples/entities/16_isolated_license.json +62 -0
- package/template/samples/entities/README.md +91 -0
- package/template/samples/flutter/lib/entity_server_client.dart +261 -48
- package/template/samples/java/EntityServerClient.java +325 -61
- package/template/samples/java/EntityServerExample.java +4 -3
- package/template/samples/kotlin/EntityServerClient.kt +261 -45
- package/template/samples/node/src/EntityServerClient.js +348 -59
- package/template/samples/node/src/example.js +9 -9
- package/template/samples/php/ci4/Config/EntityServer.php +14 -0
- package/template/samples/php/ci4/Controllers/EntityController.php +202 -0
- package/template/samples/php/ci4/Controllers/ProductController.php +16 -76
- package/template/samples/php/ci4/Libraries/EntityServer.php +352 -60
- package/template/samples/php/laravel/Services/EntityServerService.php +245 -40
- package/template/samples/python/entity_server.py +287 -68
- package/template/samples/python/example.py +7 -6
- package/template/samples/react/src/example.tsx +41 -25
- package/template/samples/swift/EntityServerClient.swift +248 -37
- package/template/scripts/normalize-entities.sh +10 -10
- package/template/scripts/run.ps1 +12 -3
- package/template/scripts/run.sh +120 -37
- package/template/scripts/update-server.ps1 +160 -4
- package/template/scripts/update-server.sh +132 -4
- package/template/samples/react/src/api/entityServerClient.ts +0 -290
- package/template/samples/react/src/hooks/useEntity.ts +0 -105
- /package/template/entities/{Auth → System/Auth}/api_keys.json +0 -0
- /package/template/entities/{Auth → System/Auth}/license.json +0 -0
- /package/template/entities/{Auth → System/Auth}/rbac_roles.json +0 -0
package/bin/create.js
CHANGED
|
@@ -193,6 +193,15 @@ async function run() {
|
|
|
193
193
|
path.join(targetDir, "entities"),
|
|
194
194
|
);
|
|
195
195
|
|
|
196
|
+
// samples/ (없어도 빈 디렉터리 생성)
|
|
197
|
+
const samplesSrc = path.join(templateDir, "samples");
|
|
198
|
+
const samplesDest = path.join(targetDir, "samples");
|
|
199
|
+
if (fs.existsSync(samplesSrc)) {
|
|
200
|
+
copyDir(samplesSrc, samplesDest);
|
|
201
|
+
} else {
|
|
202
|
+
fs.mkdirSync(samplesDest, { recursive: true });
|
|
203
|
+
}
|
|
204
|
+
|
|
196
205
|
// .env.example 복사 + .env 생성 (없을 때만)
|
|
197
206
|
copyFile(
|
|
198
207
|
path.join(templateDir, ".env.example"),
|
|
@@ -219,11 +228,14 @@ async function run() {
|
|
|
219
228
|
);
|
|
220
229
|
console.warn(` https://github.com/${REPO}/releases\n`);
|
|
221
230
|
} else {
|
|
231
|
+
const binDir = path.join(targetDir, "bin");
|
|
232
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
233
|
+
|
|
222
234
|
for (const bin of ["entity-server", "entity-cli"]) {
|
|
223
235
|
const ext = platform === "windows" ? ".exe" : "";
|
|
224
236
|
const fileName = `${bin}-${platform}-${arch}${ext}`;
|
|
225
237
|
const url = `https://github.com/${REPO}/releases/download/v${VERSION}/${fileName}`;
|
|
226
|
-
const dest = path.join(
|
|
238
|
+
const dest = path.join(binDir, bin + ext);
|
|
227
239
|
|
|
228
240
|
process.stdout.write(` ↓ ${fileName} 다운로드 중...`);
|
|
229
241
|
try {
|
|
@@ -244,8 +256,9 @@ async function run() {
|
|
|
244
256
|
// ── 3. 완료 안내 ─────────────────────────────────────────────────────────
|
|
245
257
|
|
|
246
258
|
const relDir = path.relative(process.cwd(), targetDir) || dirName;
|
|
247
|
-
const serverExe = isWin
|
|
248
|
-
|
|
259
|
+
const serverExe = isWin
|
|
260
|
+
? ".\\bin\\entity-server.exe"
|
|
261
|
+
: "./bin/entity-server";
|
|
249
262
|
const runScript = isWin
|
|
250
263
|
? ".\\scripts\\run.ps1 start"
|
|
251
264
|
: "./scripts/run.sh start";
|
|
@@ -255,17 +268,22 @@ async function run() {
|
|
|
255
268
|
✓ 완료!
|
|
256
269
|
|
|
257
270
|
${relDir}/
|
|
258
|
-
|
|
259
|
-
|
|
271
|
+
├── bin/
|
|
272
|
+
│ ├── entity-server${binExt} ← 서버 바이너리
|
|
273
|
+
│ └── entity-cli${binExt} ← CLI 도구
|
|
260
274
|
├── .env ← 환경 변수 설정 (여기를 먼저 수정하세요)
|
|
261
275
|
├── configs/ ← CORS, JWT 등 서버 설정
|
|
262
276
|
├── entities/ ← 엔티티 스키마 JSON (샘플 포함)
|
|
263
|
-
|
|
277
|
+
├── scripts/ ← 운영 스크립트
|
|
278
|
+
└── samples/ ← 샘플 파일
|
|
264
279
|
|
|
265
280
|
다음 단계:
|
|
266
281
|
${cdCmd ? ` ${cdCmd}` : ""}
|
|
267
|
-
|
|
268
|
-
|
|
282
|
+
./scripts/generate-env-keys${isWin ? ".ps1 -Apply" : ".sh --apply"} # ENCRYPTION_KEY/JWT_SECRET 생성 후 .env 반영
|
|
283
|
+
vim .env # PORT, DB_PATH 등 기본 환경값 확인/수정
|
|
284
|
+
vim configs/*.json # database/server/jwt 등 운영 설정
|
|
285
|
+
vim entities/*.json # 사용할 엔티티 스키마 정의
|
|
286
|
+
${serverExe} # 마지막: 서버 실행
|
|
269
287
|
|
|
270
288
|
서버마다 .env 의 PORT 와 DB_PATH 를 다르게 설정하면
|
|
271
289
|
같은 머신에서 여러 프로젝트를 동시에 운영할 수 있습니다.
|
package/package.json
CHANGED
package/template/.env.example
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
# Entity Server 환경변수 설정 예시
|
|
2
2
|
# 실제 사용시 이 파일을 .env로 복사하고 값을 변경하세요: cp .env.example .env
|
|
3
|
+
# ./scripts/generate-env-keys.sh 를 사용하여 랜덤 시크릿을 생성할 수 있습니다.
|
|
3
4
|
|
|
4
|
-
# 기본 암복호화
|
|
5
|
-
#
|
|
5
|
+
# 기본 암복호화 마스터 시크릿 (license 엔티티 등 공통 fallback 키)
|
|
6
|
+
# HKDF-SHA256 으로 32바이트(256-bit) XChaCha20-Poly1305 키를 유도하는 마스터 시크릿입니다.
|
|
7
|
+
# 충분한 엔트로피를 가진 임의 문자열이면 됩니다 (예: openssl rand -hex 32 으로 생성).
|
|
6
8
|
ENCRYPTION_KEY=your-32-char-hex-encryption-key-here
|
|
7
9
|
|
|
8
10
|
# JWT 서명 키 (HS256)
|
|
9
11
|
# 운영에서는 충분히 긴 랜덤 시크릿을 사용하세요.
|
|
10
12
|
JWT_SECRET=your-jwt-secret-here
|
|
11
13
|
|
|
12
|
-
#
|
|
14
|
+
# 서버 포트 (configs/server.json의 port를 오버라이드)
|
|
15
|
+
SERVER_PORT=47200
|
|
16
|
+
|
|
17
|
+
# Database values (database.json의 ${ENV_VAR}와 매핑)
|
|
18
|
+
DB_HOST_DEVELOPMENT=127.0.0.1
|
|
19
|
+
DB_PORT_DEVELOPMENT=3306
|
|
20
|
+
DB_NAME_DEVELOPMENT=your-development-db-name
|
|
21
|
+
DB_USER_DEVELOPMENT=your-development-db-user
|
|
13
22
|
DB_PASSWORD_DEVELOPMENT=your-development-db-password
|
|
23
|
+
|
|
24
|
+
DB_HOST_PRODUCTION=127.0.0.1
|
|
25
|
+
DB_PORT_PRODUCTION=3306
|
|
26
|
+
DB_NAME_PRODUCTION=your-production-db-name
|
|
27
|
+
DB_USER_PRODUCTION=your-production-db-user
|
|
14
28
|
DB_PASSWORD_PRODUCTION=your-production-db-password
|
|
29
|
+
|
|
30
|
+
# 푸시 알림 (push.json에서 ${FCM_PROJECT_ID} 등으로 참조 가능)
|
|
31
|
+
# FCM_PROJECT_ID=your-firebase-project-id
|
|
@@ -1,23 +1,186 @@
|
|
|
1
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// database.json 설정 예시 (주석 포함 예시 전용 파일 — 실제 파일은 순수 JSON)
|
|
3
|
+
//
|
|
4
|
+
// "default" : 사용할 그룹 이름 (groups 키 중 하나)
|
|
5
|
+
// "groups" : 드라이버별 연결 설정 모음
|
|
6
|
+
// driver : "mysql" | "postgres" | "mongodb" | "dynamodb" |
|
|
7
|
+
// "firestore" | "scylladb" | "couchdb"
|
|
8
|
+
//
|
|
9
|
+
// 환경 변수 참조: "${ENV_VAR_NAME}" 형식으로 값을 주입합니다.
|
|
10
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
1
11
|
{
|
|
2
12
|
"default": "development",
|
|
3
13
|
"groups": {
|
|
14
|
+
// ── MySQL (SQL) ─────────────────────────────────────────────────
|
|
4
15
|
"development": {
|
|
5
16
|
"driver": "mysql",
|
|
6
|
-
"host": "
|
|
7
|
-
"port":
|
|
8
|
-
"database": "
|
|
9
|
-
"user": "
|
|
17
|
+
"host": "${DB_HOST_DEVELOPMENT}",
|
|
18
|
+
"port": "${DB_PORT_DEVELOPMENT}",
|
|
19
|
+
"database": "${DB_NAME_DEVELOPMENT}",
|
|
20
|
+
"user": "${DB_USER_DEVELOPMENT}",
|
|
10
21
|
"password": "${DB_PASSWORD_DEVELOPMENT}",
|
|
11
|
-
"maxOpenConns": 20
|
|
22
|
+
"maxOpenConns": 20,
|
|
23
|
+
"maxIdleConns": 10,
|
|
24
|
+
"connMaxLifetimeSec": 3600
|
|
12
25
|
},
|
|
13
26
|
"production": {
|
|
14
27
|
"driver": "mysql",
|
|
15
|
-
"host": "
|
|
16
|
-
"port":
|
|
17
|
-
"database": "
|
|
18
|
-
"user": "
|
|
28
|
+
"host": "${DB_HOST_PRODUCTION}",
|
|
29
|
+
"port": "${DB_PORT_PRODUCTION}",
|
|
30
|
+
"database": "${DB_NAME_PRODUCTION}",
|
|
31
|
+
"user": "${DB_USER_PRODUCTION}",
|
|
19
32
|
"password": "${DB_PASSWORD_PRODUCTION}",
|
|
20
|
-
"maxOpenConns": 50
|
|
33
|
+
"maxOpenConns": 50,
|
|
34
|
+
"maxIdleConns": 25,
|
|
35
|
+
"connMaxLifetimeSec": 3600
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// ── MongoDB ─────────────────────────────────────────────────────
|
|
39
|
+
// URI 우선 사용. uri 가 없으면 host/port/database/username/password 조합.
|
|
40
|
+
"mongodb_dev": {
|
|
41
|
+
"driver": "mongodb",
|
|
42
|
+
"uri": "mongodb://localhost:27017/entity_dev",
|
|
43
|
+
"database": "entity_dev"
|
|
44
|
+
},
|
|
45
|
+
"mongodb_prod": {
|
|
46
|
+
"driver": "mongodb",
|
|
47
|
+
"host": "${MONGO_HOST}",
|
|
48
|
+
"port": 27017,
|
|
49
|
+
"database": "${MONGO_DB}",
|
|
50
|
+
"username": "${MONGO_USER}",
|
|
51
|
+
"password": "${MONGO_PASSWORD}",
|
|
52
|
+
"uri": ""
|
|
53
|
+
},
|
|
54
|
+
"mongodb_replica": {
|
|
55
|
+
"driver": "mongodb",
|
|
56
|
+
"uri": "mongodb://${MONGO_USER}:${MONGO_PASS}@mongo1:27017,mongo2:27017,mongo3:27017/${MONGO_DB}?replicaSet=rs0&authSource=admin",
|
|
57
|
+
"database": "${MONGO_DB}"
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// ── Amazon DynamoDB ─────────────────────────────────────────────
|
|
61
|
+
// region 과 IAM 인증이 필요합니다.
|
|
62
|
+
// 로컬 테스트: endpoint 에 "http://localhost:8000" 지정.
|
|
63
|
+
"dynamodb_dev": {
|
|
64
|
+
"driver": "dynamodb",
|
|
65
|
+
"region": "ap-northeast-2",
|
|
66
|
+
"endpoint": "http://localhost:8000",
|
|
67
|
+
"access_key_id": "${AWS_ACCESS_KEY_ID}",
|
|
68
|
+
"secret_access_key": "${AWS_SECRET_ACCESS_KEY}"
|
|
69
|
+
},
|
|
70
|
+
"dynamodb_prod": {
|
|
71
|
+
"driver": "dynamodb",
|
|
72
|
+
"region": "${AWS_REGION}",
|
|
73
|
+
"access_key_id": "${AWS_ACCESS_KEY_ID}",
|
|
74
|
+
"secret_access_key": "${AWS_SECRET_ACCESS_KEY}"
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// ── Google Cloud Firestore ──────────────────────────────────────
|
|
78
|
+
// GCP 프로젝트 ID 와 서비스 계정 JSON 파일(또는 ADC)이 필요합니다.
|
|
79
|
+
// credentials_file : 서비스 계정 키 파일 경로 (선택)
|
|
80
|
+
// ADC 자동 사용 시 credentials_file 생략 가능.
|
|
81
|
+
"firestore_dev": {
|
|
82
|
+
"driver": "firestore",
|
|
83
|
+
"project_id": "${GCP_PROJECT_ID}",
|
|
84
|
+
"credentials_file": "${GOOGLE_APPLICATION_CREDENTIALS}"
|
|
85
|
+
},
|
|
86
|
+
"firestore_prod": {
|
|
87
|
+
"driver": "firestore",
|
|
88
|
+
"project_id": "${GCP_PROJECT_ID}"
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// ── ScyllaDB / Apache Cassandra ────────────────────────────────
|
|
92
|
+
// hosts 는 쉼표 구분 문자열 또는 배열 모두 허용합니다.
|
|
93
|
+
// keyspace 가 없으면 자동 생성됩니다.
|
|
94
|
+
"scylladb_dev": {
|
|
95
|
+
"driver": "scylladb",
|
|
96
|
+
"hosts": "localhost:9042",
|
|
97
|
+
"keyspace": "entity_dev",
|
|
98
|
+
"username": "",
|
|
99
|
+
"password": ""
|
|
100
|
+
},
|
|
101
|
+
"scylladb_prod": {
|
|
102
|
+
"driver": "scylladb",
|
|
103
|
+
"hosts": "${SCYLLA_HOSTS}",
|
|
104
|
+
"keyspace": "${SCYLLA_KEYSPACE}",
|
|
105
|
+
"username": "${SCYLLA_USER}",
|
|
106
|
+
"password": "${SCYLLA_PASSWORD}",
|
|
107
|
+
"consistency": "quorum",
|
|
108
|
+
"timeout": "10s"
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// ── Apache CouchDB ──────────────────────────────────────────────
|
|
112
|
+
// URI 우선 사용. uri 가 없으면 host/port/username/password 조합.
|
|
113
|
+
// 포트 기본값: 5984
|
|
114
|
+
"couchdb_dev": {
|
|
115
|
+
"driver": "couchdb",
|
|
116
|
+
"host": "localhost",
|
|
117
|
+
"port": 5984,
|
|
118
|
+
"username": "admin",
|
|
119
|
+
"password": "password"
|
|
120
|
+
},
|
|
121
|
+
"couchdb_uri": {
|
|
122
|
+
"driver": "couchdb",
|
|
123
|
+
"uri": "http://admin:password@localhost:5984/"
|
|
124
|
+
},
|
|
125
|
+
"couchdb_prod": {
|
|
126
|
+
"driver": "couchdb",
|
|
127
|
+
"uri": "http://${COUCH_USER}:${COUCH_PASS}@${COUCH_HOST}:${COUCH_PORT}/"
|
|
21
128
|
}
|
|
22
129
|
}
|
|
23
130
|
}
|
|
131
|
+
|
|
132
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
133
|
+
// DataStore 단독 사용 — flat 형식 (groups 없이 최상위에 driver 직접 지정)
|
|
134
|
+
//
|
|
135
|
+
// SQL 없이 NoSQL DataStore만 단독으로 사용할 때는 groups 없이
|
|
136
|
+
// database.json 최상위에 driver 를 직접 지정합니다.
|
|
137
|
+
// store_loader.go 가 groups 키가 없고 NoSQL driver 인 경우에만 DataStore로 인식합니다.
|
|
138
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
// MongoDB flat 형식
|
|
141
|
+
{
|
|
142
|
+
"driver": "mongodb",
|
|
143
|
+
"uri": "mongodb://localhost:27017",
|
|
144
|
+
"database": "entity_server"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// MongoDB — 환경변수 방식
|
|
148
|
+
{
|
|
149
|
+
"driver": "mongodb",
|
|
150
|
+
"host": "${MONGO_HOST}",
|
|
151
|
+
"port": "${MONGO_PORT}",
|
|
152
|
+
"username": "${MONGO_USER}",
|
|
153
|
+
"password": "${MONGO_PASSWORD}",
|
|
154
|
+
"database": "${MONGO_DATABASE}"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// DynamoDB flat 형식
|
|
158
|
+
{
|
|
159
|
+
"driver": "dynamodb",
|
|
160
|
+
"region": "${AWS_REGION}",
|
|
161
|
+
"access_key_id": "${AWS_ACCESS_KEY_ID}",
|
|
162
|
+
"secret_access_key": "${AWS_SECRET_ACCESS_KEY}"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Firestore flat 형식
|
|
166
|
+
{
|
|
167
|
+
"driver": "firestore",
|
|
168
|
+
"project_id": "${GCP_PROJECT_ID}",
|
|
169
|
+
"credentials_file": "${GOOGLE_APPLICATION_CREDENTIALS}"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ScyllaDB flat 형식
|
|
173
|
+
{
|
|
174
|
+
"driver": "scylladb",
|
|
175
|
+
"host": "${SCYLLA_HOST}",
|
|
176
|
+
"port": 9042,
|
|
177
|
+
"database": "${SCYLLA_KEYSPACE}",
|
|
178
|
+
"username": "${SCYLLA_USER}",
|
|
179
|
+
"password": "${SCYLLA_PASSWORD}"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// CouchDB flat 형식
|
|
183
|
+
{
|
|
184
|
+
"driver": "couchdb",
|
|
185
|
+
"uri": "http://${COUCH_USER}:${COUCH_PASS}@${COUCH_HOST}:5984/"
|
|
186
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "OAuth 2.0 설정 예시. 사용할 프로바이더만 남기고 실제 값으로 교체하세요.",
|
|
3
|
+
|
|
4
|
+
"state_secret": "${OAUTH_STATE_SECRET}",
|
|
5
|
+
"state_ttl_sec": 600,
|
|
6
|
+
|
|
7
|
+
"providers": {
|
|
8
|
+
"google": {
|
|
9
|
+
"client_id": "${GOOGLE_CLIENT_ID}",
|
|
10
|
+
"client_secret": "${GOOGLE_CLIENT_SECRET}",
|
|
11
|
+
"redirect_url": "https://your-domain.com/v1/oauth/google/callback",
|
|
12
|
+
"scopes": ["openid", "email", "profile"]
|
|
13
|
+
},
|
|
14
|
+
"github": {
|
|
15
|
+
"client_id": "${GITHUB_CLIENT_ID}",
|
|
16
|
+
"client_secret": "${GITHUB_CLIENT_SECRET}",
|
|
17
|
+
"redirect_url": "https://your-domain.com/v1/oauth/github/callback"
|
|
18
|
+
},
|
|
19
|
+
"naver": {
|
|
20
|
+
"client_id": "${NAVER_CLIENT_ID}",
|
|
21
|
+
"client_secret": "${NAVER_CLIENT_SECRET}",
|
|
22
|
+
"redirect_url": "https://your-domain.com/v1/oauth/naver/callback"
|
|
23
|
+
},
|
|
24
|
+
"kakao": {
|
|
25
|
+
"_comment": "Kakao — 커스텀 엔드포인트 예시",
|
|
26
|
+
"client_id": "${KAKAO_CLIENT_ID}",
|
|
27
|
+
"client_secret": "${KAKAO_CLIENT_SECRET}",
|
|
28
|
+
"redirect_url": "https://your-domain.com/v1/oauth/kakao/callback",
|
|
29
|
+
"auth_url": "https://kauth.kakao.com/oauth/authorize",
|
|
30
|
+
"token_url": "https://kauth.kakao.com/oauth/token",
|
|
31
|
+
"user_info_url": "https://kapi.kakao.com/v2/user/me",
|
|
32
|
+
"scopes": ["profile_nickname", "account_email"],
|
|
33
|
+
"email_field": "kakao_account.email",
|
|
34
|
+
"name_field": "properties.nickname"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"workers": 2,
|
|
3
|
+
"queue_size": 500,
|
|
4
|
+
"fcm": {
|
|
5
|
+
"enabled": true,
|
|
6
|
+
"type": "service_account",
|
|
7
|
+
"project_id": "your-firebase-project-id",
|
|
8
|
+
"private_key_id": "abc123def456...",
|
|
9
|
+
"private_key_file": "./configs/keys/firebase.pem",
|
|
10
|
+
"client_email": "firebase-adminsdk-xxxxx@your-firebase-project-id.iam.gserviceaccount.com",
|
|
11
|
+
"client_id": "123456789012345678901",
|
|
12
|
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
13
|
+
"token_uri": "https://oauth2.googleapis.com/token",
|
|
14
|
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
15
|
+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-xxxxx%40your-firebase-project-id.iam.gserviceaccount.com"
|
|
16
|
+
},
|
|
17
|
+
"apns": {
|
|
18
|
+
"enabled": false,
|
|
19
|
+
"_comment": "APNs 직접 전송 (미구현 - 예약)",
|
|
20
|
+
"key_file": "./configs/keys/apns.p8",
|
|
21
|
+
"key_id": "ABCDE12345",
|
|
22
|
+
"team_id": "FGHIJ67890",
|
|
23
|
+
"bundle_id": "com.example.myapp",
|
|
24
|
+
"production": false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "account_audit",
|
|
3
|
-
"description": "
|
|
3
|
+
"description": "account 감사 로그. JWT 인증 시에만 account_seq는 required",
|
|
4
4
|
"index": {
|
|
5
5
|
"account_seq": {
|
|
6
|
-
"comment": "계정 seq",
|
|
6
|
+
"comment": "계정 seq (JWT 인증 시 account.seq 참조. HMAC은 nullable)",
|
|
7
7
|
"type": "bigint",
|
|
8
|
-
"
|
|
8
|
+
"nullable": true
|
|
9
9
|
},
|
|
10
10
|
"action": {
|
|
11
11
|
"comment": "작업 유형",
|
|
12
12
|
"type": ["INSERT", "UPDATE", "DELETE", "LOGIN", "LOGOUT"],
|
|
13
13
|
"required": true
|
|
14
14
|
}
|
|
15
|
-
}
|
|
16
|
-
"license_scope": false
|
|
15
|
+
}
|
|
17
16
|
}
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
|
|
51
51
|
### 3. examples/Auth/account.json
|
|
52
52
|
|
|
53
|
-
사용자 관리 엔티티 (`entities/examples/Auth/` 에 위치 → 배포 시 `dist/entities/Auth/account.json`)
|
|
53
|
+
사용자 관리 엔티티 (`entities/examples/Auth/` 에 위치 → 배포 시 `dist/entities/System/Auth/account.json`)
|
|
54
54
|
|
|
55
55
|
> **⚠️ JWT 인증을 사용하려면 필수입니다.**
|
|
56
56
|
> HMAC 인증만 사용하는 경우에는 필요하지 않습니다.
|
|
57
|
-
> `jwt.json`에 `secret`이 설정된 경우 서버 기동 시 `entities/Auth/account.json`이 존재하지 않으면 오류가 발생합니다.
|
|
57
|
+
> `jwt.json`에 `secret`이 설정된 경우 서버 기동 시 `entities/System/Auth/account.json`이 존재하지 않으면 오류가 발생합니다.
|
|
58
58
|
> `email`, `rbac_role` 필드가 index에 포함되어 있어야 합니다.
|
|
59
59
|
> `./scripts/reset-all.sh --apply` 실행 시 파일이 없으면 자동으로 생성됩니다.
|
|
60
60
|
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
|
|
69
69
|
### 4. examples/Auth/rbac_roles.json
|
|
70
70
|
|
|
71
|
-
RBAC 역할 정의 엔티티 (`entities/examples/Auth/` 에 위치 → 배포 시 `dist/entities/Auth/rbac_roles.json`)
|
|
71
|
+
RBAC 역할 정의 엔티티 (`entities/examples/Auth/` 에 위치 → 배포 시 `dist/entities/System/Auth/rbac_roles.json`)
|
|
72
72
|
|
|
73
73
|
> **⚠️ RBAC 인증을 사용하려면 필수입니다.**
|
|
74
74
|
> `reset-all` 실행 시 `reset_defaults`의 5개 역할이 자동 시딩됩니다.
|
|
@@ -84,7 +84,7 @@ RBAC 역할 정의 엔티티 (`entities/examples/Auth/` 에 위치 → 배포
|
|
|
84
84
|
|
|
85
85
|
### 5. examples/Auth/api_keys.json
|
|
86
86
|
|
|
87
|
-
API 키 관리 엔티티 (`entities/examples/Auth/` 에 위치 → 배포 시 `dist/entities/Auth/api_keys.json`)
|
|
87
|
+
API 키 관리 엔티티 (`entities/examples/Auth/` 에 위치 → 배포 시 `dist/entities/System/Auth/api_keys.json`)
|
|
88
88
|
|
|
89
89
|
> **⚠️ HMAC 인증을 사용하려면 필수입니다.**
|
|
90
90
|
> `reset-all` 실행 시 admin 역할의 API 키 1개가 자동 생성되며 `key_value`와 `hmac_secret`이 출력됩니다.
|
|
@@ -34,20 +34,6 @@
|
|
|
34
34
|
}
|
|
35
35
|
],
|
|
36
36
|
"hooks": {
|
|
37
|
-
"before_insert": [
|
|
38
|
-
{
|
|
39
|
-
"type": "submit",
|
|
40
|
-
"entity": "user",
|
|
41
|
-
"match": {
|
|
42
|
-
"email": "${new.email}"
|
|
43
|
-
},
|
|
44
|
-
"data": {
|
|
45
|
-
"email": "${new.email}",
|
|
46
|
-
"status": "active"
|
|
47
|
-
},
|
|
48
|
-
"assign_seq_to": "user_seq"
|
|
49
|
-
}
|
|
50
|
-
],
|
|
51
37
|
"after_insert": [
|
|
52
38
|
{
|
|
53
39
|
"type": "sql",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "system_audit_log",
|
|
3
|
-
"description": "시스템 감사 로그. 서버 레벨에서 자동 기록되며 API를 통한 직접 수정은 허용되지 않습니다.",
|
|
3
|
+
"description": "시스템 감사 로그. 서버 레벨에서 자동 기록되며 API를 통한 직접 수정은 허용되지 않습니다. JWT 인증 시에만 account_seq 기록",
|
|
4
4
|
"db_group": "system",
|
|
5
5
|
"hard_delete": true,
|
|
6
6
|
"history_ttl": 0,
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"entity_seq": {
|
|
20
20
|
"comment": "대상 엔티티 레코드 seq. 로그인/로그아웃 등 레코드 없는 경우 NULL",
|
|
21
|
-
"type": "bigint"
|
|
21
|
+
"type": "bigint",
|
|
22
|
+
"nullable": true
|
|
22
23
|
},
|
|
23
24
|
"action": {
|
|
24
25
|
"comment": "수행된 작업 유형",
|
|
@@ -34,16 +35,19 @@
|
|
|
34
35
|
"required": true
|
|
35
36
|
},
|
|
36
37
|
"account_seq": {
|
|
37
|
-
"comment": "작업을 수행한 계정 seq.
|
|
38
|
-
"type": "bigint"
|
|
38
|
+
"comment": "작업을 수행한 계정 seq. JWT 인증 요청은 account.seq, 비인증/HMAC 요청은 NULL",
|
|
39
|
+
"type": "bigint",
|
|
40
|
+
"nullable": true
|
|
39
41
|
},
|
|
40
42
|
"ip_address": {
|
|
41
43
|
"comment": "요청 IP 주소 (IPv4/IPv6)",
|
|
42
|
-
"type": "varchar(45)"
|
|
44
|
+
"type": "varchar(45)",
|
|
45
|
+
"nullable": true
|
|
43
46
|
},
|
|
44
47
|
"endpoint": {
|
|
45
48
|
"comment": "요청 API 엔드포인트",
|
|
46
|
-
"type": "varchar(200)"
|
|
49
|
+
"type": "varchar(200)",
|
|
50
|
+
"nullable": true
|
|
47
51
|
},
|
|
48
52
|
"request_method": {
|
|
49
53
|
"comment": "HTTP 메서드 (GET/POST/PUT/DELETE 등)",
|
|
@@ -51,7 +55,8 @@
|
|
|
51
55
|
},
|
|
52
56
|
"request_payload": {
|
|
53
57
|
"comment": "요청 본문 JSON. 민감 필드(password, token 등) 자동 마스킹 후 저장. ServerConfig.AuditLogPayload = true 일 때만 기록",
|
|
54
|
-
"type": "text"
|
|
58
|
+
"type": "text",
|
|
59
|
+
"nullable": true
|
|
55
60
|
},
|
|
56
61
|
"result_code": {
|
|
57
62
|
"comment": "HTTP 응답 코드 (200, 400, 401, 403, 500 등)",
|
|
@@ -59,7 +64,8 @@
|
|
|
59
64
|
},
|
|
60
65
|
"error_message": {
|
|
61
66
|
"comment": "실패 시 오류 메시지 요약",
|
|
62
|
-
"type": "varchar(500)"
|
|
67
|
+
"type": "varchar(500)",
|
|
68
|
+
"nullable": true
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
}
|
|
@@ -15,11 +15,12 @@ Client / Browser
|
|
|
15
15
|
Backend Server ←── 이 샘플이 구현하는 부분
|
|
16
16
|
│
|
|
17
17
|
▼ HMAC 서명 (서버 간 통신)
|
|
18
|
-
Entity Server
|
|
18
|
+
Entity Server
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
>
|
|
22
|
-
>
|
|
21
|
+
> **브라우저(React·Vanilla)** 환경에서도 HMAC / JWT 모두 사용 가능합니다.
|
|
22
|
+
> 단, 브라우저에서 HMAC을 사용하면 `hmacSecret`이 클라이언트에 노출되므로,
|
|
23
|
+
> **프론트엔드 프로덕션 환경에서는 JWT 사용을 권장합니다.**
|
|
23
24
|
|
|
24
25
|
## HMAC 서명 공식
|
|
25
26
|
|
|
@@ -39,27 +40,48 @@ signature = HMAC-SHA256(hmacSecret, payload) → hex
|
|
|
39
40
|
|
|
40
41
|
## API 엔드포인트
|
|
41
42
|
|
|
42
|
-
| 동작
|
|
43
|
-
|
|
|
44
|
-
| 단건 조회
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
43
|
+
| 동작 | 메서드 | 경로 |
|
|
44
|
+
| -------------- | ------ | ---------------------------------- |
|
|
45
|
+
| 단건 조회 | GET | `/v1/entity/{name}/{seq}` |
|
|
46
|
+
| 조건 단건 조회 | POST | `/v1/entity/{name}/find` |
|
|
47
|
+
| 목록 조회 | POST | `/v1/entity/{name}/list` |
|
|
48
|
+
| 필터 검색 | POST | `/v1/entity/{name}/query` |
|
|
49
|
+
| 건수 조회 | POST | `/v1/entity/{name}/count` |
|
|
50
|
+
| 생성/수정 | POST | `/v1/entity/{name}/submit` |
|
|
51
|
+
| 삭제 | POST | `/v1/entity/{name}/delete/{seq}` |
|
|
52
|
+
| 이력 조회 | GET | `/v1/entity/{name}/history/{seq}` |
|
|
53
|
+
| 롤백 | POST | `/v1/entity/{name}/rollback/{seq}` |
|
|
52
54
|
|
|
53
55
|
- `list` 쿼리 파라미터: `?page=1&limit=20&order_by=<field>`
|
|
54
56
|
- `submit` — body에 `seq` 포함 시 수정, 없으면 생성
|
|
55
57
|
|
|
56
58
|
## 샘플 목록
|
|
57
59
|
|
|
58
|
-
| 디렉토리 | 프레임워크
|
|
59
|
-
| -------------- |
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
60
|
+
| 디렉토리 | 프레임워크 | 인증 방식 |
|
|
61
|
+
| -------------- | ---------------------------------------------- | ---------- |
|
|
62
|
+
| `entities/` | 엔티티 설정 예제 | — |
|
|
63
|
+
| `browser/` | 브라우저 (Vanilla ES Module, 빌드 도구 불필요) | HMAC / JWT |
|
|
64
|
+
| `php/ci4/` | CodeIgniter 4 | HMAC / JWT |
|
|
65
|
+
| `php/laravel/` | Laravel | HMAC / JWT |
|
|
66
|
+
| `java/` | Java (표준 라이브러리) | HMAC / JWT |
|
|
67
|
+
| `kotlin/` | Kotlin (표준 라이브러리) | HMAC / JWT |
|
|
68
|
+
| `swift/` | Swift (URLSession) | HMAC / JWT |
|
|
69
|
+
| `flutter/` | Flutter (Dart) | HMAC / JWT |
|
|
70
|
+
| `node/` | Node.js (fetch) | HMAC / JWT |
|
|
71
|
+
| `python/` | Python (requests) | HMAC / JWT |
|
|
72
|
+
| `react/` | React + TypeScript | HMAC / JWT |
|
|
73
|
+
|
|
74
|
+
### CI4 설정 방식
|
|
75
|
+
|
|
76
|
+
- `php/ci4` 샘플은 `app/Config/EntityServer.php` 중심으로 설정을 관리합니다.
|
|
77
|
+
- 다른 언어 샘플은 각 라이브러리/클라이언트 파일 내부 설정값을 직접 수정해 사용합니다.
|
|
78
|
+
|
|
79
|
+
### Push 헬퍼
|
|
80
|
+
|
|
81
|
+
- 모든 샘플 클라이언트는 푸시 트리거용 헬퍼를 제공합니다. - `push(pushEntity, payload[, transactionId])` : 내부적으로 `submit(pushEntity, payload)` 호출 - `pushLogList(...)` : 내부적으로 `list('push_log', ...)` 호출
|
|
82
|
+
- Python 샘플은 PEP8 네이밍으로 `push_log_list(...)` 메서드를 제공합니다.
|
|
83
|
+
|
|
84
|
+
### 요청 본문(암호 패킷) 읽기 헬퍼
|
|
85
|
+
|
|
86
|
+
- 모든 샘플에 요청 본문 파싱 헬퍼를 제공합니다. - `application/octet-stream`이면 XChaCha20-Poly1305 패킷을 복호화해 JSON 반환 - 그 외 Content-Type이면 평문 JSON 파싱 - 옵션으로 `requireEncrypted=true` 정책을 줄 수 있습니다.
|
|
87
|
+
- 언어별 메서드명 - Node/React/Kotlin/Java/Swift/Laravel: `readRequestBody(...)` - Python: `read_request_body(...)` - Flutter: `readRequestBody(...)`
|