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.
Files changed (56) hide show
  1. package/bin/create.js +26 -8
  2. package/package.json +1 -1
  3. package/template/.env.example +20 -3
  4. package/template/configs/database.json +173 -10
  5. package/template/configs/jwt.json +1 -0
  6. package/template/configs/oauth.json +37 -0
  7. package/template/configs/push.json +26 -0
  8. package/template/entities/Account/account_audit.json +4 -5
  9. package/template/entities/README.md +4 -4
  10. package/template/entities/{Auth → System/Auth}/account.json +0 -14
  11. package/template/entities/System/system_audit_log.json +14 -8
  12. package/template/samples/README.md +43 -21
  13. package/template/samples/browser/entity-server-client.js +453 -0
  14. package/template/samples/browser/example.html +498 -0
  15. package/template/samples/entities/01_basic_fields.json +39 -0
  16. package/template/samples/entities/02_types_and_defaults.json +67 -0
  17. package/template/samples/entities/03_hash_and_unique.json +33 -0
  18. package/template/samples/entities/04_fk_and_composite_unique.json +29 -0
  19. package/template/samples/entities/05_cache.json +55 -0
  20. package/template/samples/entities/06_history_and_hard_delete.json +60 -0
  21. package/template/samples/entities/07_license_scope.json +52 -0
  22. package/template/samples/entities/08_hook_sql.json +52 -0
  23. package/template/samples/entities/09_hook_entity.json +65 -0
  24. package/template/samples/entities/10_hook_submit_delete.json +78 -0
  25. package/template/samples/entities/11_hook_webhook.json +84 -0
  26. package/template/samples/entities/12_hook_push.json +73 -0
  27. package/template/samples/entities/13_read_only.json +54 -0
  28. package/template/samples/entities/14_optimistic_lock.json +29 -0
  29. package/template/samples/entities/15_reset_defaults.json +94 -0
  30. package/template/samples/entities/16_isolated_license.json +62 -0
  31. package/template/samples/entities/README.md +91 -0
  32. package/template/samples/flutter/lib/entity_server_client.dart +261 -48
  33. package/template/samples/java/EntityServerClient.java +325 -61
  34. package/template/samples/java/EntityServerExample.java +4 -3
  35. package/template/samples/kotlin/EntityServerClient.kt +261 -45
  36. package/template/samples/node/src/EntityServerClient.js +348 -59
  37. package/template/samples/node/src/example.js +9 -9
  38. package/template/samples/php/ci4/Config/EntityServer.php +14 -0
  39. package/template/samples/php/ci4/Controllers/EntityController.php +202 -0
  40. package/template/samples/php/ci4/Controllers/ProductController.php +16 -76
  41. package/template/samples/php/ci4/Libraries/EntityServer.php +352 -60
  42. package/template/samples/php/laravel/Services/EntityServerService.php +245 -40
  43. package/template/samples/python/entity_server.py +287 -68
  44. package/template/samples/python/example.py +7 -6
  45. package/template/samples/react/src/example.tsx +41 -25
  46. package/template/samples/swift/EntityServerClient.swift +248 -37
  47. package/template/scripts/normalize-entities.sh +10 -10
  48. package/template/scripts/run.ps1 +12 -3
  49. package/template/scripts/run.sh +120 -37
  50. package/template/scripts/update-server.ps1 +160 -4
  51. package/template/scripts/update-server.sh +132 -4
  52. package/template/samples/react/src/api/entityServerClient.ts +0 -290
  53. package/template/samples/react/src/hooks/useEntity.ts +0 -105
  54. /package/template/entities/{Auth → System/Auth}/api_keys.json +0 -0
  55. /package/template/entities/{Auth → System/Auth}/license.json +0 -0
  56. /package/template/entities/{Auth → System/Auth}/rbac_roles.json +0 -0
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "config_item",
3
+ "description": "캐시 예제 — 자주 읽히고 드물게 변경되는 설정값에 엔티티 레벨 캐시 활성화",
4
+ "index": {
5
+ "key": {
6
+ "comment": "설정 키 (고유)",
7
+ "required": true,
8
+ "unique": true
9
+ },
10
+ "category": {
11
+ "comment": "설정 카테고리",
12
+ "type": ["system", "ui", "feature_flag", "notification"],
13
+ "default": "system"
14
+ },
15
+ "is_public": {
16
+ "comment": "공개 여부 (is_* → TINYINT(1) 자동 추론)"
17
+ }
18
+ },
19
+ "cache": {
20
+ "enabled": true,
21
+ "ttl_seconds": 600
22
+ },
23
+ "fields": {
24
+ "value": {
25
+ "comment": "설정 값 (문자열 저장, 타입은 애플리케이션에서 해석)",
26
+ "required": true
27
+ },
28
+ "description": {
29
+ "comment": "설정 항목 설명"
30
+ }
31
+ },
32
+ "reset_defaults": [
33
+ {
34
+ "key": "site.name",
35
+ "category": "system",
36
+ "value": "My Service",
37
+ "is_public": true,
38
+ "description": "서비스명"
39
+ },
40
+ {
41
+ "key": "feature.dark_mode",
42
+ "category": "feature_flag",
43
+ "value": "true",
44
+ "is_public": true,
45
+ "description": "다크모드 기능 활성화"
46
+ },
47
+ {
48
+ "key": "notification.email_enabled",
49
+ "category": "notification",
50
+ "value": "true",
51
+ "is_public": false,
52
+ "description": "이메일 알림 활성화"
53
+ }
54
+ ]
55
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "article",
3
+ "description": "이력 보존(history_ttl) & 완전삭제(hard_delete) 예제 — 게시글 수정 이력 3년 보관",
4
+ "index": {
5
+ "author_seq": {
6
+ "comment": "작성자 user seq",
7
+ "required": true
8
+ },
9
+ "title": {
10
+ "comment": "제목",
11
+ "required": true
12
+ },
13
+ "category": {
14
+ "comment": "카테고리",
15
+ "type": ["notice", "blog", "faq", "news"],
16
+ "default": "blog"
17
+ },
18
+ "status": {
19
+ "comment": "게시 상태",
20
+ "type": ["draft", "published", "archived"],
21
+ "default": "draft"
22
+ },
23
+ "published_at": {
24
+ "comment": "게시일시 (*_at → DATETIME 자동 추론)"
25
+ },
26
+ "view_count": {
27
+ "comment": "조회 수 (*_count → INT 자동 추론)"
28
+ }
29
+ },
30
+ "fields": {
31
+ "content": {
32
+ "type": "longtext",
33
+ "comment": "게시글 본문",
34
+ "required": true
35
+ },
36
+ "summary": {
37
+ "type": "text",
38
+ "comment": "요약 (없으면 본문 앞 200자 자동 생성)"
39
+ },
40
+ "meta": {
41
+ "comment": "SEO / 부가 메타데이터 그룹 (중첩 fields 예제)",
42
+ "fields": {
43
+ "og_title": {
44
+ "type": "varchar(200)",
45
+ "comment": "OG 제목 (미입력 시 title 사용)"
46
+ },
47
+ "og_image_url": {
48
+ "type": "varchar(500)",
49
+ "comment": "OG 이미지 URL"
50
+ },
51
+ "tags_json": {
52
+ "type": "json",
53
+ "comment": "태그 목록 (JSON 배열)"
54
+ }
55
+ }
56
+ }
57
+ },
58
+ "history_ttl": 1095,
59
+ "hard_delete": false
60
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "exchange_rate",
3
+ "description": "license_scope: false 예제 — 전 라이선스 공용 기준 데이터 (환율). license_seq 컬럼 없이 모든 테넌트가 동일 데이터 공유",
4
+ "license_scope": false,
5
+ "index": {
6
+ "base_currency": {
7
+ "comment": "기준 통화 (ISO 4217)",
8
+ "type": "varchar(3)",
9
+ "required": true
10
+ },
11
+ "quote_currency": {
12
+ "comment": "환산 통화 (ISO 4217)",
13
+ "type": "varchar(3)",
14
+ "required": true
15
+ },
16
+ "rate": {
17
+ "comment": "환산 비율 (*_rate → 자동추론 없음, decimal 명시)",
18
+ "type": "decimal",
19
+ "required": true
20
+ },
21
+ "effective_date": {
22
+ "comment": "적용일 (*_date → DATE 자동 추론)",
23
+ "required": true
24
+ },
25
+ "source": {
26
+ "comment": "환율 출처",
27
+ "type": ["ecb", "openexchangerates", "manual"],
28
+ "default": "manual"
29
+ }
30
+ },
31
+ "cache": {
32
+ "enabled": true,
33
+ "ttl_seconds": 3600
34
+ },
35
+ "unique": [["base_currency", "quote_currency", "effective_date"]],
36
+ "reset_defaults": [
37
+ {
38
+ "base_currency": "USD",
39
+ "quote_currency": "KRW",
40
+ "rate": 1350.0,
41
+ "effective_date": "2025-01-01",
42
+ "source": "manual"
43
+ },
44
+ {
45
+ "base_currency": "EUR",
46
+ "quote_currency": "KRW",
47
+ "rate": 1480.0,
48
+ "effective_date": "2025-01-01",
49
+ "source": "manual"
50
+ }
51
+ ]
52
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "user_point",
3
+ "description": "SQL 훅 예제 — INSERT/UPDATE 시 자동 감사 로그 기록 (실행형) + SELECT 결과 주입 (조회형)",
4
+ "index": {
5
+ "user_seq": {
6
+ "comment": "사용자 seq",
7
+ "required": true,
8
+ "unique": true
9
+ },
10
+ "point": {
11
+ "comment": "보유 포인트",
12
+ "type": "uint"
13
+ },
14
+ "grade": {
15
+ "comment": "등급",
16
+ "type": ["bronze", "silver", "gold", "platinum"],
17
+ "default": "bronze"
18
+ },
19
+ "updated_at": {
20
+ "comment": "마지막 포인트 변경일시 (*_at → DATETIME 자동 추론)"
21
+ }
22
+ },
23
+ "hooks": {
24
+ "after_insert": [
25
+ {
26
+ "comment": "포인트 생성 이력 기록 (실행형 SQL 훅)",
27
+ "type": "sql",
28
+ "query": "INSERT INTO point_history (user_seq, delta, reason, created_time) VALUES (?, ?, ?, NOW())",
29
+ "params": ["${new.user_seq}", "${new.point}", "initial_grant"],
30
+ "async": false
31
+ }
32
+ ],
33
+ "after_update": [
34
+ {
35
+ "comment": "포인트 변경 이력 기록 (실행형 SQL 훅)",
36
+ "type": "sql",
37
+ "query": "INSERT INTO point_history (user_seq, delta, reason, created_time) VALUES (?, ?, ?, NOW())",
38
+ "params": ["${new.user_seq}", "${new.point}", "update"],
39
+ "async": true
40
+ }
41
+ ],
42
+ "after_get": [
43
+ {
44
+ "comment": "최근 포인트 변경 이력 5건 주입 (조회형 SQL 훅)",
45
+ "type": "sql",
46
+ "query": "SELECT delta, reason, created_time FROM point_history WHERE user_seq = ? ORDER BY created_time DESC LIMIT 5",
47
+ "params": ["${new.user_seq}"],
48
+ "assign_to": "recent_history"
49
+ }
50
+ ]
51
+ }
52
+ }
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "post",
3
+ "description": "entity 훅 예제 — after_get/after_list 시 관련 엔티티 데이터를 자동 주입",
4
+ "index": {
5
+ "author_seq": {
6
+ "comment": "작성자 user seq",
7
+ "required": true
8
+ },
9
+ "title": {
10
+ "comment": "제목",
11
+ "required": true
12
+ },
13
+ "status": {
14
+ "comment": "상태",
15
+ "type": ["draft", "published", "hidden"],
16
+ "default": "draft"
17
+ },
18
+ "comment_count": {
19
+ "comment": "댓글 수 (*_count → INT 자동 추론)"
20
+ },
21
+ "like_count": {
22
+ "comment": "좋아요 수 (*_count → INT 자동 추론)"
23
+ },
24
+ "published_at": {
25
+ "comment": "게시일시 (*_at → DATETIME 자동 추론)"
26
+ }
27
+ },
28
+ "hooks": {
29
+ "after_get": [
30
+ {
31
+ "comment": "게시글 조회 시 작성자 프로필 자동 주입",
32
+ "type": "entity",
33
+ "entity": "user",
34
+ "action": "get",
35
+ "conditions": {
36
+ "seq": "${new.author_seq}"
37
+ },
38
+ "assign_to": "author"
39
+ },
40
+ {
41
+ "comment": "게시글 조회 시 최근 댓글 10개 자동 주입",
42
+ "type": "entity",
43
+ "entity": "comment",
44
+ "action": "list",
45
+ "conditions": {
46
+ "post_seq": "${new.seq}",
47
+ "status": "visible"
48
+ },
49
+ "assign_to": "comments"
50
+ }
51
+ ],
52
+ "after_list": [
53
+ {
54
+ "comment": "목록 조회 시 각 게시글에 작성자 이름 주입",
55
+ "type": "entity",
56
+ "entity": "user",
57
+ "action": "get",
58
+ "conditions": {
59
+ "seq": "${new.author_seq}"
60
+ },
61
+ "assign_to": "author"
62
+ }
63
+ ]
64
+ }
65
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "project",
3
+ "description": "submit & delete 훅 예제 — 프로젝트 생성 시 기본 태스크 자동 생성, 삭제 시 연관 데이터 정리",
4
+ "index": {
5
+ "owner_seq": {
6
+ "comment": "프로젝트 소유자 user seq",
7
+ "required": true
8
+ },
9
+ "name": {
10
+ "comment": "프로젝트명",
11
+ "required": true
12
+ },
13
+ "status": {
14
+ "comment": "진행 상태",
15
+ "type": ["planning", "active", "on_hold", "completed", "cancelled"],
16
+ "default": "planning"
17
+ },
18
+ "due_date": {
19
+ "comment": "마감일 (*_date → DATE 자동 추론)"
20
+ },
21
+ "task_count": {
22
+ "comment": "태스크 수 (*_count → INT 자동 추론)"
23
+ }
24
+ },
25
+ "fields": {
26
+ "description": {
27
+ "type": "text",
28
+ "comment": "프로젝트 설명"
29
+ }
30
+ },
31
+ "hooks": {
32
+ "after_insert": [
33
+ {
34
+ "comment": "프로젝트 생성 시 기본 태스크 3개 자동 생성 (submit 훅)",
35
+ "type": "submit",
36
+ "entity": "task",
37
+ "data": {
38
+ "project_seq": "${new.seq}",
39
+ "title": "요구사항 분석",
40
+ "status": "todo",
41
+ "priority": "high"
42
+ }
43
+ },
44
+ {
45
+ "comment": "프로젝트 멤버에 소유자 자동 추가 (submit 훅 Upsert)",
46
+ "type": "submit",
47
+ "entity": "project_member",
48
+ "match": {
49
+ "project_seq": "${new.seq}",
50
+ "user_seq": "${new.owner_seq}"
51
+ },
52
+ "data": {
53
+ "project_seq": "${new.seq}",
54
+ "user_seq": "${new.owner_seq}",
55
+ "role": "owner"
56
+ }
57
+ }
58
+ ],
59
+ "after_delete": [
60
+ {
61
+ "comment": "프로젝트 삭제 시 모든 태스크 삭제 (delete 훅)",
62
+ "type": "delete",
63
+ "entity": "task",
64
+ "match": {
65
+ "project_seq": "${old.seq}"
66
+ }
67
+ },
68
+ {
69
+ "comment": "프로젝트 삭제 시 멤버 목록 삭제 (delete 훅)",
70
+ "type": "delete",
71
+ "entity": "project_member",
72
+ "match": {
73
+ "project_seq": "${old.seq}"
74
+ }
75
+ }
76
+ ]
77
+ }
78
+ }
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "payment",
3
+ "description": "webhook 훅 예제 — 결제 완료/환불 시 외부 정산 서비스 실시간 통보",
4
+ "index": {
5
+ "order_seq": {
6
+ "comment": "주문 seq",
7
+ "required": true
8
+ },
9
+ "user_seq": {
10
+ "comment": "결제자 user seq",
11
+ "required": true
12
+ },
13
+ "amount": {
14
+ "comment": "결제 금액 (*_amount → DECIMAL(15,2) 자동 추론)",
15
+ "required": true
16
+ },
17
+ "method": {
18
+ "comment": "결제 수단",
19
+ "type": ["card", "bank_transfer", "virtual_account", "point"],
20
+ "required": true
21
+ },
22
+ "status": {
23
+ "comment": "결제 상태",
24
+ "type": ["pending", "paid", "cancelled", "refunded"],
25
+ "default": "pending"
26
+ },
27
+ "paid_at": {
28
+ "comment": "결제 완료일시 (*_at → DATETIME 자동 추론)"
29
+ }
30
+ },
31
+ "fields": {
32
+ "pg_transaction_id": {
33
+ "type": "varchar(100)",
34
+ "comment": "PG사 거래 ID"
35
+ },
36
+ "failure_reason": {
37
+ "type": "text",
38
+ "comment": "결제 실패 사유"
39
+ }
40
+ },
41
+ "hooks": {
42
+ "after_insert": [
43
+ {
44
+ "comment": "결제 생성 시 정산 서버로 비동기 통보 (webhook 훅)",
45
+ "type": "webhook",
46
+ "url": "https://settlement.internal/hooks/payment-created",
47
+ "method": "POST",
48
+ "headers": {
49
+ "Authorization": "Bearer ${ctx.webhook_secret}",
50
+ "Content-Type": "application/json"
51
+ },
52
+ "body": {
53
+ "payment_seq": "${new.seq}",
54
+ "order_seq": "${new.order_seq}",
55
+ "user_seq": "${new.user_seq}",
56
+ "amount": "${new.amount}",
57
+ "method": "${new.method}",
58
+ "status": "${new.status}"
59
+ },
60
+ "async": true,
61
+ "timeout": 5000
62
+ }
63
+ ],
64
+ "after_update": [
65
+ {
66
+ "comment": "결제 상태 변경(환불 등) 시 정산 서버에 동기 통보 (webhook 훅)",
67
+ "type": "webhook",
68
+ "url": "https://settlement.internal/hooks/payment-updated",
69
+ "method": "POST",
70
+ "headers": {
71
+ "Authorization": "Bearer ${ctx.webhook_secret}"
72
+ },
73
+ "body": {
74
+ "payment_seq": "${new.seq}",
75
+ "old_status": "${old.status}",
76
+ "new_status": "${new.status}",
77
+ "amount": "${new.amount}"
78
+ },
79
+ "async": false,
80
+ "timeout": 8000
81
+ }
82
+ ]
83
+ }
84
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "delivery",
3
+ "description": "push 훅 예제 — 배송 상태 변경 시 고객에게 푸시 알림 전송 (FCM / APNs 공통, 다단계 상태 알림)",
4
+ "index": {
5
+ "order_seq": {
6
+ "comment": "주문 seq",
7
+ "required": true
8
+ },
9
+ "customer_seq": {
10
+ "comment": "고객 user seq",
11
+ "required": true
12
+ },
13
+ "tracking_number": {
14
+ "comment": "운송장 번호",
15
+ "unique": true
16
+ },
17
+ "carrier": {
18
+ "comment": "택배사",
19
+ "type": ["cj", "lotte", "hanjin", "post", "direct"],
20
+ "default": "cj"
21
+ },
22
+ "status": {
23
+ "comment": "배송 상태",
24
+ "type": [
25
+ "ready",
26
+ "picked_up",
27
+ "in_transit",
28
+ "out_for_delivery",
29
+ "delivered",
30
+ "failed"
31
+ ],
32
+ "default": "ready"
33
+ },
34
+ "estimated_date": {
35
+ "comment": "예상 도착일 (*_date → DATE 자동 추론)"
36
+ },
37
+ "delivered_at": {
38
+ "comment": "배송 완료일시 (*_at → DATETIME 자동 추론)"
39
+ }
40
+ },
41
+ "hooks": {
42
+ "after_insert": [
43
+ {
44
+ "comment": "배송 시작 푸시 알림",
45
+ "type": "push",
46
+ "target_account_seq": "customer_seq",
47
+ "title": "배송 시작",
48
+ "push_body": "주문 #${new.order_seq}의 배송이 시작되었습니다. 운송장: ${new.tracking_number}",
49
+ "push_data": {
50
+ "action": "delivery_started",
51
+ "delivery_seq": "${new.seq}",
52
+ "tracking_number": "${new.tracking_number}"
53
+ }
54
+ }
55
+ ],
56
+ "after_update": [
57
+ {
58
+ "comment": "배송 상태 변경 시 푸시 알림",
59
+ "type": "push",
60
+ "target_account_seq": "customer_seq",
61
+ "title": "배송 상태 업데이트",
62
+ "push_body": "배송 상태가 '${new.status}'(으)로 변경되었습니다. 운송장: ${new.tracking_number}",
63
+ "push_data": {
64
+ "action": "delivery_status_changed",
65
+ "delivery_seq": "${new.seq}",
66
+ "old_status": "${old.status}",
67
+ "new_status": "${new.status}",
68
+ "tracking_number": "${new.tracking_number}"
69
+ }
70
+ }
71
+ ]
72
+ }
73
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "activity_log",
3
+ "description": "read_only 예제 — API를 통한 수정·삭제가 불가하고 서버 내부(훅/SP) 로만 기록되는 감사 로그",
4
+ "read_only": true,
5
+ "hard_delete": true,
6
+ "history_ttl": 0,
7
+ "index": {
8
+ "actor_seq": {
9
+ "comment": "행위자 user seq (null = 시스템)"
10
+ },
11
+ "entity_name": {
12
+ "comment": "대상 엔티티명",
13
+ "required": true
14
+ },
15
+ "entity_seq": {
16
+ "comment": "대상 레코드 seq"
17
+ },
18
+ "action": {
19
+ "comment": "수행된 작업",
20
+ "type": [
21
+ "INSERT",
22
+ "UPDATE",
23
+ "DELETE",
24
+ "LOGIN",
25
+ "LOGOUT",
26
+ "EXPORT",
27
+ "IMPORT"
28
+ ],
29
+ "required": true
30
+ },
31
+ "ip_address": {
32
+ "comment": "요청 IP",
33
+ "type": "varchar(45)"
34
+ },
35
+ "result_code": {
36
+ "comment": "결과 코드 (HTTP 상태)",
37
+ "type": "int"
38
+ }
39
+ },
40
+ "fields": {
41
+ "before_snapshot": {
42
+ "type": "json",
43
+ "comment": "변경 전 데이터 스냅샷"
44
+ },
45
+ "after_snapshot": {
46
+ "type": "json",
47
+ "comment": "변경 후 데이터 스냅샷"
48
+ },
49
+ "detail": {
50
+ "type": "text",
51
+ "comment": "작업 상세 설명"
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "inventory",
3
+ "description": "optimistic_lock 예제 — 동시 수정 충돌 방지가 필요한 재고 엔티티",
4
+ "optimistic_lock": true,
5
+ "index": {
6
+ "product_seq": {
7
+ "comment": "상품 seq",
8
+ "required": true,
9
+ "unique": true
10
+ },
11
+ "warehouse_code": {
12
+ "comment": "창고 코드",
13
+ "required": true
14
+ },
15
+ "qty_available": {
16
+ "comment": "가용 재고 수량 (*_qty → INT 자동 추론)"
17
+ },
18
+ "qty_reserved": {
19
+ "comment": "예약된 재고 수량 (*_qty → INT 자동 추론)"
20
+ },
21
+ "location": {
22
+ "comment": "창고 내 위치 (예: A-01-03)"
23
+ },
24
+ "last_stocked_at": {
25
+ "comment": "마지막 입고일시 (*_at → DATETIME 자동 추론)"
26
+ }
27
+ },
28
+ "unique": [["product_seq", "warehouse_code"]]
29
+ }