create-entity-server 0.0.15 → 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 (42) hide show
  1. package/bin/create.js +15 -7
  2. package/package.json +1 -1
  3. package/template/.env.example +8 -7
  4. package/template/configs/database.json +173 -10
  5. package/template/entities/Account/account_audit.json +4 -5
  6. package/template/entities/System/system_audit_log.json +14 -8
  7. package/template/samples/README.md +28 -22
  8. package/template/samples/browser/entity-server-client.js +453 -0
  9. package/template/samples/browser/example.html +498 -0
  10. package/template/samples/entities/02_types_and_defaults.json +15 -16
  11. package/template/samples/entities/04_fk_and_composite_unique.json +0 -2
  12. package/template/samples/entities/05_cache.json +9 -8
  13. package/template/samples/entities/06_history_and_hard_delete.json +27 -9
  14. package/template/samples/entities/07_license_scope.json +40 -31
  15. package/template/samples/entities/09_hook_entity.json +0 -6
  16. package/template/samples/entities/10_hook_submit_delete.json +5 -2
  17. package/template/samples/entities/11_hook_webhook.json +9 -7
  18. package/template/samples/entities/12_hook_push.json +3 -3
  19. package/template/samples/entities/13_read_only.json +13 -10
  20. package/template/samples/entities/15_reset_defaults.json +0 -1
  21. package/template/samples/entities/16_isolated_license.json +62 -0
  22. package/template/samples/entities/README.md +36 -39
  23. package/template/samples/flutter/lib/entity_server_client.dart +170 -48
  24. package/template/samples/java/EntityServerClient.java +208 -61
  25. package/template/samples/java/EntityServerExample.java +4 -3
  26. package/template/samples/kotlin/EntityServerClient.kt +175 -45
  27. package/template/samples/node/src/EntityServerClient.js +232 -59
  28. package/template/samples/node/src/example.js +9 -9
  29. package/template/samples/php/ci4/Config/EntityServer.php +0 -1
  30. package/template/samples/php/ci4/Libraries/EntityServer.php +206 -53
  31. package/template/samples/php/laravel/Services/EntityServerService.php +190 -41
  32. package/template/samples/python/entity_server.py +181 -68
  33. package/template/samples/python/example.py +7 -6
  34. package/template/samples/react/src/example.tsx +41 -25
  35. package/template/samples/swift/EntityServerClient.swift +143 -37
  36. package/template/scripts/run.ps1 +12 -3
  37. package/template/scripts/run.sh +12 -8
  38. package/template/scripts/update-server.ps1 +68 -2
  39. package/template/scripts/update-server.sh +59 -2
  40. package/template/samples/entities/order_notification.json +0 -51
  41. package/template/samples/react/src/api/entityServerClient.ts +0 -413
  42. package/template/samples/react/src/hooks/useEntity.ts +0 -173
@@ -1,43 +1,52 @@
1
1
  {
2
- "name": "workspace",
3
- "description": "license_scope 예제 — 멀티테넌트에서 라이선스별 데이터 완전 분리",
4
- "license_scope": true,
2
+ "name": "exchange_rate",
3
+ "description": "license_scope: false 예제 — 라이선스 공용 기준 데이터 (환율). license_seq 컬럼 없이 모든 테넌트가 동일 데이터 공유",
4
+ "license_scope": false,
5
5
  "index": {
6
- "name": {
7
- "comment": "워크스페이스명",
6
+ "base_currency": {
7
+ "comment": "기준 통화 (ISO 4217)",
8
+ "type": "varchar(3)",
8
9
  "required": true
9
10
  },
10
- "plan": {
11
- "comment": "플랜",
12
- "type": ["free", "starter", "pro", "enterprise"],
13
- "default": "free"
14
- },
15
- "max_members": {
16
- "comment": "최대 팀원 수 (자동추론: 직접 uint 선언)"
11
+ "quote_currency": {
12
+ "comment": "환산 통화 (ISO 4217)",
13
+ "type": "varchar(3)",
14
+ "required": true
17
15
  },
18
- "owner_seq": {
19
- "comment": "소유자 user seq"
16
+ "rate": {
17
+ "comment": "환산 비율 (*_rate → 자동추론 없음, decimal 명시)",
18
+ "type": "decimal",
19
+ "required": true
20
20
  },
21
- "is_active": {
22
- "comment": "활성 여부 (is_* → TINYINT(1) 자동 추론)",
23
- "default": true
21
+ "effective_date": {
22
+ "comment": "적용일 (*_dateDATE 자동 추론)",
23
+ "required": true
24
24
  },
25
- "expires_at": {
26
- "comment": "만료일시 (*_at → DATETIME 자동 추론)"
25
+ "source": {
26
+ "comment": "환율 출처",
27
+ "type": ["ecb", "openexchangerates", "manual"],
28
+ "default": "manual"
27
29
  }
28
30
  },
29
- "types": {
30
- "max_members": "uint",
31
- "settings_json": "text"
32
- },
33
- "comments": {
34
- "settings_json": "워크스페이스 설정 JSON"
35
- },
36
- "defaults": {
37
- "max_members": 5
38
- },
39
31
  "cache": {
40
32
  "enabled": true,
41
- "ttl_seconds": 120
42
- }
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
+ ]
43
52
  }
@@ -25,12 +25,6 @@
25
25
  "comment": "게시일시 (*_at → DATETIME 자동 추론)"
26
26
  }
27
27
  },
28
- "types": {
29
- "content": "text"
30
- },
31
- "comments": {
32
- "content": "본문"
33
- },
34
28
  "hooks": {
35
29
  "after_get": [
36
30
  {
@@ -22,8 +22,11 @@
22
22
  "comment": "태스크 수 (*_count → INT 자동 추론)"
23
23
  }
24
24
  },
25
- "types": {
26
- "description": "text"
25
+ "fields": {
26
+ "description": {
27
+ "type": "text",
28
+ "comment": "프로젝트 설명"
29
+ }
27
30
  },
28
31
  "hooks": {
29
32
  "after_insert": [
@@ -28,13 +28,15 @@
28
28
  "comment": "결제 완료일시 (*_at → DATETIME 자동 추론)"
29
29
  }
30
30
  },
31
- "types": {
32
- "pg_transaction_id": "varchar(100)",
33
- "failure_reason": "text"
34
- },
35
- "comments": {
36
- "pg_transaction_id": "PG사 거래 ID",
37
- "failure_reason": "실패 사유"
31
+ "fields": {
32
+ "pg_transaction_id": {
33
+ "type": "varchar(100)",
34
+ "comment": "PG사 거래 ID"
35
+ },
36
+ "failure_reason": {
37
+ "type": "text",
38
+ "comment": "결제 실패 사유"
39
+ }
38
40
  },
39
41
  "hooks": {
40
42
  "after_insert": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delivery",
3
- "description": "push 훅 예제 — 배송 상태 변경 시 고객에게 푸시 알림 전송 (다단계 상태 알림)",
3
+ "description": "push 훅 예제 — 배송 상태 변경 시 고객에게 푸시 알림 전송 (FCM / APNs 공통, 다단계 상태 알림)",
4
4
  "index": {
5
5
  "order_seq": {
6
6
  "comment": "주문 seq",
@@ -43,7 +43,7 @@
43
43
  {
44
44
  "comment": "배송 시작 푸시 알림",
45
45
  "type": "push",
46
- "target_user_field": "customer_seq",
46
+ "target_account_seq": "customer_seq",
47
47
  "title": "배송 시작",
48
48
  "push_body": "주문 #${new.order_seq}의 배송이 시작되었습니다. 운송장: ${new.tracking_number}",
49
49
  "push_data": {
@@ -57,7 +57,7 @@
57
57
  {
58
58
  "comment": "배송 상태 변경 시 푸시 알림",
59
59
  "type": "push",
60
- "target_user_field": "customer_seq",
60
+ "target_account_seq": "customer_seq",
61
61
  "title": "배송 상태 업데이트",
62
62
  "push_body": "배송 상태가 '${new.status}'(으)로 변경되었습니다. 운송장: ${new.tracking_number}",
63
63
  "push_data": {
@@ -2,7 +2,6 @@
2
2
  "name": "activity_log",
3
3
  "description": "read_only 예제 — API를 통한 수정·삭제가 불가하고 서버 내부(훅/SP) 로만 기록되는 감사 로그",
4
4
  "read_only": true,
5
- "license_scope": false,
6
5
  "hard_delete": true,
7
6
  "history_ttl": 0,
8
7
  "index": {
@@ -38,14 +37,18 @@
38
37
  "type": "int"
39
38
  }
40
39
  },
41
- "types": {
42
- "before_snapshot": "text",
43
- "after_snapshot": "text",
44
- "detail": "text"
45
- },
46
- "comments": {
47
- "before_snapshot": "변경 전 데이터 스냅샷 (JSON)",
48
- "after_snapshot": "변경 후 데이터 스냅샷 (JSON)",
49
- "detail": "추가 상세 정보"
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
+ }
50
53
  }
51
54
  }
@@ -32,7 +32,6 @@
32
32
  "default": true
33
33
  }
34
34
  },
35
- "license_scope": false,
36
35
  "hard_delete": true,
37
36
  "reset_defaults": [
38
37
  {
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "organization",
3
+ "description": "isolated: license 예제 — 이 엔티티가 테넌트(라이선스) 경계를 정의하는 루트 엔티티. license_seq FK 없이 license_seq 컬럼을 직접 소유",
4
+ "isolated": "license",
5
+ "index": {
6
+ "name": {
7
+ "comment": "조직명",
8
+ "required": true,
9
+ "unique": true
10
+ },
11
+ "plan": {
12
+ "comment": "구독 플랜",
13
+ "type": ["free", "starter", "pro", "enterprise"],
14
+ "default": "free"
15
+ },
16
+ "max_members": {
17
+ "comment": "최대 팀원 수",
18
+ "type": "uint",
19
+ "default": 5
20
+ },
21
+ "owner_seq": {
22
+ "comment": "소유자 account seq"
23
+ },
24
+ "is_active": {
25
+ "comment": "활성 여부 (is_* → TINYINT(1) 자동 추론)",
26
+ "default": true
27
+ },
28
+ "expires_at": {
29
+ "comment": "구독 만료일시 (*_at → DATETIME 자동 추론)"
30
+ }
31
+ },
32
+ "fields": {
33
+ "billing_email": {
34
+ "type": "varchar(255)",
35
+ "comment": "청구용 이메일"
36
+ },
37
+ "settings": {
38
+ "comment": "조직 설정 (중첩 fields 그룹)",
39
+ "fields": {
40
+ "theme": {
41
+ "type": ["light", "dark", "system"],
42
+ "comment": "기본 UI 테마",
43
+ "default": "system"
44
+ },
45
+ "language": {
46
+ "type": "varchar(10)",
47
+ "comment": "기본 인터페이스 언어",
48
+ "default": "ko"
49
+ },
50
+ "timezone": {
51
+ "type": "varchar(50)",
52
+ "comment": "기본 타임존",
53
+ "default": "Asia/Seoul"
54
+ }
55
+ }
56
+ }
57
+ },
58
+ "cache": {
59
+ "enabled": true,
60
+ "ttl_seconds": 300
61
+ }
62
+ }
@@ -15,7 +15,7 @@
15
15
  | 파일 | 엔티티 | 설명 |
16
16
  | -------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------- |
17
17
  | [01_basic_fields.json](01_basic_fields.json) | `contact` | 필드명 패턴으로 타입이 **자동 추론**되는 모든 케이스 (`_seq`, `_date`, `_at`, `is_*`, `_count`, `_amount`, `email`, `phone`) |
18
- | [02_types_and_defaults.json](02_types_and_defaults.json) | `product` | 자동 추론이 안 될 때 **`types` 명시 선언** + `defaults` + `reset_defaults` 시딩 |
18
+ | [02_types_and_defaults.json](02_types_and_defaults.json) | `product` | 자동 추론이 안 될 때 **`fields` 명시 선언** (`type`, `comment`, `default` 인라인) + `reset_defaults` 시딩 |
19
19
 
20
20
  ### 제약 & 참조
21
21
 
@@ -32,11 +32,12 @@
32
32
  | [06_history_and_hard_delete.json](06_history_and_hard_delete.json) | `article` | **`history_ttl`** 수정 이력 3년 보관 + **`hard_delete: false`** 논리 삭제 |
33
33
  | [14_optimistic_lock.json](14_optimistic_lock.json) | `inventory` | **`optimistic_lock`** 동시 수정 충돌 방지 (재고 등 경쟁 조건 있는 데이터) |
34
34
 
35
- ### 멀티테넌트
35
+ ### 데이터 격리 & 공유
36
36
 
37
- | 파일 | 엔티티 | 설명 |
38
- | ---------------------------------------------- | ----------- | ------------------------------------------------------------ |
39
- | [07_license_scope.json](07_license_scope.json) | `workspace` | **`license_scope: true`** 라이선스별 데이터 완전 분리 + 캐시 |
37
+ | 파일 | 엔티티 | 설명 |
38
+ | ---------------------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------- |
39
+ | [07_license_scope.json](07_license_scope.json) | `exchange_rate` | **`license_scope: false`** 전 라이선스 **공용** 기준 데이터 (환율). `license_seq` 컬럼 없이 모든 테넌트가 공유 |
40
+ | [16_isolated_license.json](16_isolated_license.json) | `organization` | **`isolated: "license"`** — 테넌트 경계를 정의하는 루트 엔티티. `license_seq` FK 없이 직접 소유 (멀티테넌트 루트) |
40
41
 
41
42
  ### 특수 모드
42
43
 
@@ -47,48 +48,44 @@
47
48
 
48
49
  ### 훅 (Hooks)
49
50
 
50
- | 파일 | 엔티티 | 훅 타입 | 설명 |
51
- | -------------------------------------------------------- | ------------ | ------------------- | ------------------------------------------------------------------------- |
52
- | [08_hook_sql.json](08_hook_sql.json) | `user_point` | `sql` | 실행형 SQL (포인트 이력 INSERT) + 조회형 SQL (`assign_to`로 결과 주입) |
53
- | [09_hook_entity.json](09_hook_entity.json) | `post` | `entity` | `after_get`/`after_list`에서 관련 엔티티 자동 주입 (작성자 프로필, 댓글) |
54
- | [10_hook_submit_delete.json](10_hook_submit_delete.json) | `project` | `submit` / `delete` | 생성 시 연관 엔티티 자동 생성(Upsert 포함), 삭제 시 연관 데이터 자동 정리 |
55
- | [11_hook_webhook.json](11_hook_webhook.json) | `payment` | `webhook` | 외부 HTTP 통보 — 비동기(결제 생성) + 동기(상태 변경) |
56
- | [12_hook_push.json](12_hook_push.json) | `delivery` | `push` | 배송 상태 변경 시 고객 FCM 푸시 알림 |
57
-
58
- ### 이전 예제
59
-
60
- | 파일 | 엔티티 | 설명 |
61
- | -------------------------------------------------- | -------------------- | --------------------------------------------- |
62
- | [order_notification.json](order_notification.json) | `order_notification` | 주문 생성/수정 시 push 훅 (push 훅 기본 예제) |
51
+ | 파일 | 엔티티 | 훅 타입 | 설명 |
52
+ | -------------------------------------------------------- | ------------ | ------------------- | ------------------------------------------------------------------------------- |
53
+ | [08_hook_sql.json](08_hook_sql.json) | `user_point` | `sql` | 실행형 SQL (포인트 이력 INSERT) + 조회형 SQL (`assign_to`로 결과 주입) |
54
+ | [09_hook_entity.json](09_hook_entity.json) | `post` | `entity` | `after_get`/`after_list`에서 관련 엔티티 자동 주입 (작성자 프로필, 댓글) |
55
+ | [10_hook_submit_delete.json](10_hook_submit_delete.json) | `project` | `submit` / `delete` | 생성 시 연관 엔티티 자동 생성(Upsert 포함), 삭제 시 연관 데이터 자동 정리 |
56
+ | [11_hook_webhook.json](11_hook_webhook.json) | `payment` | `webhook` | 외부 HTTP 통보 — 비동기(결제 생성) + 동기(상태 변경) |
57
+ | [12_hook_push.json](12_hook_push.json) | `delivery` | `push` | 배송 상태 변경 시 고객 푸시 알림 (FCM / APNs 공통, 등록된 모든 디바이스로 전송) |
63
58
 
64
59
  ---
65
60
 
66
61
  ## 훅 타입 한눈에 보기
67
62
 
68
- | 훅 타입 | 핵심 필드 | 주요 용도 | 샘플 |
69
- | --------- | ----------------------------------------- | ----------------------- | ---------------------------------------------- |
70
- | `sql` | `query`, `params` | SQL 실행 또는 결과 주입 | `08_hook_sql.json` |
71
- | `entity` | `entity`, `action`, `assign_to` | 관련 데이터 자동 로드 | `09_hook_entity.json` |
72
- | `submit` | `entity`, `data`, `match` | 다른 엔티티 생성/수정 | `10_hook_submit_delete.json` |
73
- | `delete` | `entity`, `match` | 다른 엔티티 삭제 | `10_hook_submit_delete.json` |
74
- | `webhook` | `url`, `body`, `async` | 외부 HTTP 호출 | `11_hook_webhook.json` |
75
- | `push` | `target_user_field`, `title`, `push_body` | FCM 푸시 알림 전송 | `12_hook_push.json`, `order_notification.json` |
63
+ | 훅 타입 | 핵심 필드 | 주요 용도 | 샘플 |
64
+ | --------- | ------------------------------------------ | ------------------------- | ---------------------------- |
65
+ | `sql` | `query`, `params` | SQL 실행 또는 결과 주입 | `08_hook_sql.json` |
66
+ | `entity` | `entity`, `action`, `assign_to` | 관련 데이터 자동 로드 | `09_hook_entity.json` |
67
+ | `submit` | `entity`, `data`, `match` | 다른 엔티티 생성/수정 | `10_hook_submit_delete.json` |
68
+ | `delete` | `entity`, `match` | 다른 엔티티 삭제 | `10_hook_submit_delete.json` |
69
+ | `webhook` | `url`, `body`, `async` | 외부 HTTP 호출 | `11_hook_webhook.json` |
70
+ | `push` | `target_account_seq`, `title`, `push_body` | FCM / APNs 푸시 알림 전송 | `12_hook_push.json` |
76
71
 
77
72
  ## 훅 실행 시점
78
73
 
79
- | 시점 | 설명 | 주로 사용 |
80
- | -------------- | -------------- | -------------------------- |
81
- | `after_insert` | 데이터 삽입 | 감사 로그, 알림, 연관 생성 |
82
- | `after_update` | 데이터 수정 | 변경 이력, 상태 알림 |
83
- | `after_delete` | 데이터 삭제 | 연관 데이터 정리 |
84
- | `after_get` | 단건 조회 | 관련 데이터 병합 |
85
- | `after_list` | 목록 조회 | 항목 추가 정보 주입 |
74
+ | 시점 | 설명 | 주로 사용 |
75
+ | --------------- | --------------------- | -------------------------- |
76
+ | `before_insert` | 데이터 삽입 | 유효성 검사, 주입 |
77
+ | `after_insert` | 데이터 삽입 | 감사 로그, 알림, 연관 생성 |
78
+ | `before_update` | 데이터 수정 | 변경 검증, 권한 확인 |
79
+ | `after_update` | 데이터 수정 | 변경 이력, 상태 알림 |
80
+ | `before_delete` | 데이터 삭제 | 참조 무결성 확인 |
81
+ | `after_delete` | 데이터 삭제 후 | 연관 데이터 정리 |
82
+ | `after_get` | seq로 단건 조회 후 | 관련 데이터 병합 |
83
+ | `after_find` | 조건으로 단건 조회 후 | 관련 데이터 병합 |
84
+ | `after_list` | 목록 조회 후 | 각 항목 추가 정보 주입 |
86
85
 
87
86
  ## 훅 템플릿 변수
88
87
 
89
- | 변수 | 설명 |
90
- | -------------------- | ----------------------------- |
91
- | `${new.field}` | 삽입/수정 후 데이터의 필드 값 |
92
- | `${old.field}` | 수정/삭제 전 데이터의 필드 값 |
93
- | `${ctx.license_seq}` | 현재 요청의 라이선스 seq |
94
- | `${ctx.user_seq}` | 현재 로그인 사용자 seq |
88
+ | 변수 | 설명 |
89
+ | -------------- | ----------------------------- |
90
+ | `${new.field}` | 삽입/수정 후 데이터의 필드 값 |
91
+ | `${old.field}` | 수정/삭제 전 데이터의 필드 값 |