create-entity-server 0.0.9 → 0.0.15

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 (46) hide show
  1. package/bin/create.js +11 -1
  2. package/package.json +1 -1
  3. package/template/.env.example +17 -1
  4. package/template/configs/jwt.json +1 -0
  5. package/template/configs/oauth.json +37 -0
  6. package/template/configs/push.json +26 -0
  7. package/template/entities/README.md +4 -4
  8. package/template/entities/{Auth → System/Auth}/account.json +0 -14
  9. package/template/samples/README.md +16 -0
  10. package/template/samples/entities/01_basic_fields.json +39 -0
  11. package/template/samples/entities/02_types_and_defaults.json +68 -0
  12. package/template/samples/entities/03_hash_and_unique.json +33 -0
  13. package/template/samples/entities/04_fk_and_composite_unique.json +31 -0
  14. package/template/samples/entities/05_cache.json +54 -0
  15. package/template/samples/entities/06_history_and_hard_delete.json +42 -0
  16. package/template/samples/entities/07_license_scope.json +43 -0
  17. package/template/samples/entities/08_hook_sql.json +52 -0
  18. package/template/samples/entities/09_hook_entity.json +71 -0
  19. package/template/samples/entities/10_hook_submit_delete.json +75 -0
  20. package/template/samples/entities/11_hook_webhook.json +82 -0
  21. package/template/samples/entities/12_hook_push.json +73 -0
  22. package/template/samples/entities/13_read_only.json +51 -0
  23. package/template/samples/entities/14_optimistic_lock.json +29 -0
  24. package/template/samples/entities/15_reset_defaults.json +95 -0
  25. package/template/samples/entities/README.md +94 -0
  26. package/template/samples/entities/order_notification.json +51 -0
  27. package/template/samples/flutter/lib/entity_server_client.dart +91 -0
  28. package/template/samples/java/EntityServerClient.java +117 -0
  29. package/template/samples/kotlin/EntityServerClient.kt +86 -0
  30. package/template/samples/node/src/EntityServerClient.js +116 -0
  31. package/template/samples/php/ci4/Config/EntityServer.php +15 -0
  32. package/template/samples/php/ci4/Controllers/EntityController.php +202 -0
  33. package/template/samples/php/ci4/Controllers/ProductController.php +16 -76
  34. package/template/samples/php/ci4/Libraries/EntityServer.php +150 -11
  35. package/template/samples/php/laravel/Services/EntityServerService.php +56 -0
  36. package/template/samples/python/entity_server.py +106 -0
  37. package/template/samples/react/src/api/entityServerClient.ts +123 -0
  38. package/template/samples/react/src/hooks/useEntity.ts +68 -0
  39. package/template/samples/swift/EntityServerClient.swift +105 -0
  40. package/template/scripts/normalize-entities.sh +10 -10
  41. package/template/scripts/run.sh +108 -29
  42. package/template/scripts/update-server.ps1 +92 -2
  43. package/template/scripts/update-server.sh +73 -2
  44. /package/template/entities/{Auth → System/Auth}/api_keys.json +0 -0
  45. /package/template/entities/{Auth → System/Auth}/license.json +0 -0
  46. /package/template/entities/{Auth → System/Auth}/rbac_roles.json +0 -0
@@ -126,6 +126,111 @@ public final class EntityServerClient {
126
126
  try await request(method: "POST", path: "/v1/entity/\(entity)/rollback/\(historySeq)")
127
127
  }
128
128
 
129
+ /// 푸시 발송 트리거 엔티티에 submit합니다.
130
+ public func push(pushEntity: String, payload: [String: Any], transactionId: String? = nil) async throws -> [String: Any] {
131
+ try await submit(entity: pushEntity, data: payload, transactionId: transactionId)
132
+ }
133
+
134
+ /// push_log 목록 조회 헬퍼
135
+ public func pushLogList(page: Int = 1, limit: Int = 20) async throws -> [String: Any] {
136
+ try await list(entity: "push_log", page: page, limit: limit)
137
+ }
138
+
139
+ /// account_device 디바이스 등록/갱신 헬퍼 (push_token 단일 필드)
140
+ public func registerPushDevice(
141
+ accountSeq: Int64,
142
+ deviceId: String,
143
+ pushToken: String,
144
+ platform: String? = nil,
145
+ deviceType: String? = nil,
146
+ browser: String? = nil,
147
+ browserVersion: String? = nil,
148
+ pushEnabled: Bool = true,
149
+ transactionId: String? = nil
150
+ ) async throws -> [String: Any] {
151
+ var payload: [String: Any] = [
152
+ "id": deviceId,
153
+ "account_seq": accountSeq,
154
+ "push_token": pushToken,
155
+ "push_enabled": pushEnabled,
156
+ ]
157
+ if let platform { payload["platform"] = platform }
158
+ if let deviceType { payload["device_type"] = deviceType }
159
+ if let browser { payload["browser"] = browser }
160
+ if let browserVersion { payload["browser_version"] = browserVersion }
161
+ return try await submit(entity: "account_device", data: payload, transactionId: transactionId)
162
+ }
163
+
164
+ /// account_device.seq 기준 push_token 갱신 헬퍼
165
+ public func updatePushDeviceToken(
166
+ deviceSeq: Int64,
167
+ pushToken: String,
168
+ pushEnabled: Bool = true,
169
+ transactionId: String? = nil
170
+ ) async throws -> [String: Any] {
171
+ try await submit(
172
+ entity: "account_device",
173
+ data: [
174
+ "seq": deviceSeq,
175
+ "push_token": pushToken,
176
+ "push_enabled": pushEnabled,
177
+ ],
178
+ transactionId: transactionId
179
+ )
180
+ }
181
+
182
+ /// account_device.seq 기준 푸시 수신 비활성화 헬퍼
183
+ public func disablePushDevice(
184
+ deviceSeq: Int64,
185
+ transactionId: String? = nil
186
+ ) async throws -> [String: Any] {
187
+ try await submit(
188
+ entity: "account_device",
189
+ data: [
190
+ "seq": deviceSeq,
191
+ "push_enabled": false,
192
+ ],
193
+ transactionId: transactionId
194
+ )
195
+ }
196
+
197
+ /// 요청 본문을 읽어 JSON으로 반환합니다.
198
+ /// - application/octet-stream: 암호 패킷 복호화
199
+ /// - 그 외: 평문 JSON 파싱
200
+ public func readRequestBody(
201
+ _ rawBody: Data,
202
+ contentType: String = "application/json",
203
+ requireEncrypted: Bool = false
204
+ ) throws -> [String: Any] {
205
+ let lowered = contentType.lowercased()
206
+ let isEncrypted = lowered.contains("application/octet-stream")
207
+
208
+ if requireEncrypted && !isEncrypted {
209
+ throw NSError(
210
+ domain: "EntityServerClient",
211
+ code: -1,
212
+ userInfo: [NSLocalizedDescriptionKey: "Encrypted request required: Content-Type must be application/octet-stream"]
213
+ )
214
+ }
215
+
216
+ if isEncrypted {
217
+ if rawBody.isEmpty {
218
+ throw NSError(
219
+ domain: "EntityServerClient",
220
+ code: -1,
221
+ userInfo: [NSLocalizedDescriptionKey: "Encrypted request body is empty"]
222
+ )
223
+ }
224
+ return try decryptPacket(rawBody)
225
+ }
226
+
227
+ if rawBody.isEmpty { return [:] }
228
+ guard let json = try JSONSerialization.jsonObject(with: rawBody) as? [String: Any] else {
229
+ throw EntityServerError.invalidResponse
230
+ }
231
+ return json
232
+ }
233
+
129
234
  // ─── 내부 ─────────────────────────────────────────────────────────
130
235
 
131
236
  private func request(method: String, path: String, body: Data? = nil, extraHeaders: [String: String] = [:]) async throws -> [String: Any] {
@@ -20,7 +20,7 @@ if [ $# -eq 0 ]; then
20
20
  echo "====================="
21
21
  echo ""
22
22
  echo "Remove redundant default values and reorder keys in entity JSON files."
23
- echo "Also auto-creates missing required entities (api_keys, rbac_roles, and account/user when JWT is enabled)."
23
+ echo "Also auto-creates missing required entities (api_keys, rbac_roles, and account/account_login_log when JWT is enabled)."
24
24
  echo ""
25
25
  echo "Usage: $0 [options]"
26
26
  echo ""
@@ -41,10 +41,10 @@ if [ $# -eq 0 ]; then
41
41
  echo " - Reorder top-level keys to canonical order"
42
42
  echo ""
43
43
  echo "Required entities (auto-created if missing, full mode only):"
44
- echo " - api_keys → entities/Auth/api_keys.json"
45
- echo " - rbac_roles → entities/Auth/rbac_roles.json"
46
- echo " - account → entities/Auth/account.json (JWT enabled only)"
47
- echo " - user → entities/User/user.json (JWT enabled only)"
44
+ echo " - api_keys → entities/System/Auth/api_keys.json"
45
+ echo " - rbac_roles → entities/System/Auth/rbac_roles.json"
46
+ echo " - account → entities/System/Auth/account.json (JWT enabled only)"
47
+ echo " - account_login_log → entities/System/Auth/account_login_log.json (JWT enabled only)"
48
48
  echo ""
49
49
  echo "Examples:"
50
50
  echo " $0 # Dry-run all entities"
@@ -56,7 +56,7 @@ if [ $# -eq 0 ]; then
56
56
  echo "=================="
57
57
  echo ""
58
58
  echo "엔티티 JSON 파일에서 불필요한 기본값을 제거하고 키 순서를 정렬합니다."
59
- echo "전체 모드에서는 필수 엔티티(api_keys, rbac_roles, JWT 사용 시 account/user)가 없으면 자동 생성합니다."
59
+ echo "전체 모드에서는 필수 엔티티(api_keys, rbac_roles, JWT 사용 시 account/account_login_log)가 없으면 자동 생성합니다."
60
60
  echo ""
61
61
  echo "사용법: $0 [옵션]"
62
62
  echo ""
@@ -77,10 +77,10 @@ if [ $# -eq 0 ]; then
77
77
  echo " - 최상위 키 순서 정규화"
78
78
  echo ""
79
79
  echo "필수 엔티티 자동 생성 (전체 모드, 없을 경우):"
80
- echo " - api_keys → entities/Auth/api_keys.json"
81
- echo " - rbac_roles → entities/Auth/rbac_roles.json"
82
- echo " - account → entities/Auth/account.json (JWT 활성 시)"
83
- echo " - user → entities/User/user.json (JWT 활성 시)"
80
+ echo " - api_keys → entities/System/Auth/api_keys.json"
81
+ echo " - rbac_roles → entities/System/Auth/rbac_roles.json"
82
+ echo " - account → entities/System/Auth/account.json (JWT 활성 시)"
83
+ echo " - account_login_log → entities/System/Auth/account_login_log.json (JWT 활성 시)"
84
84
  echo ""
85
85
  echo "예제:"
86
86
  echo " $0 # 전체 엔티티 dry-run 미리보기"
@@ -25,6 +25,22 @@ get_server_value() {
25
25
  local fallback="$2"
26
26
  local value
27
27
 
28
+ if [ "$key" = "port" ]; then
29
+ local env_port
30
+ env_port="${SERVER_PORT:-${PORT:-}}"
31
+ if [ -z "$env_port" ] && [ -f .env ]; then
32
+ env_port=$(grep '^SERVER_PORT=' .env | tail -n 1 | cut -d '=' -f2-)
33
+ if [ -z "$env_port" ]; then
34
+ env_port=$(grep '^PORT=' .env | tail -n 1 | cut -d '=' -f2-)
35
+ fi
36
+ fi
37
+ env_port=$(echo "$env_port" | tr -d '[:space:]')
38
+ if [[ "$env_port" =~ ^[0-9]+$ ]] && [ "$env_port" -gt 0 ]; then
39
+ echo "$env_port"
40
+ return
41
+ fi
42
+ fi
43
+
28
44
  value=$(grep -E "\"$key\"[[:space:]]*:" "$SERVER_CONFIG" | head -n 1 | sed -E 's/.*:[[:space:]]*"?([^",}]+)"?.*/\1/')
29
45
  value=$(echo "$value" | tr -d '[:space:]')
30
46
  if [ -z "$value" ]; then
@@ -35,60 +51,61 @@ get_server_value() {
35
51
  }
36
52
 
37
53
  is_running() {
54
+ local port_pid
55
+ port_pid=$(find_pid_by_port)
56
+
38
57
  if [ ! -f "$PID_FILE" ]; then
58
+ if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
59
+ return 0
60
+ fi
39
61
  return 1
40
62
  fi
41
63
  local pid
42
64
  pid=$(cat "$PID_FILE" 2>/dev/null)
43
65
  if [ -z "$pid" ]; then
66
+ if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
67
+ return 0
68
+ fi
44
69
  return 1
45
70
  fi
46
- kill -0 "$pid" 2>/dev/null
47
- }
48
-
49
- stop_server() {
50
- if [ ! -f "$PID_FILE" ]; then
51
- if [ "$LANGUAGE" = "en" ]; then
52
- echo "ℹ️ Server is not running (pid file not found)."
53
- else
54
- echo "ℹ️ 서버가 실행 중이 아닙니다 (pid 파일 없음)."
55
- fi
71
+ if kill -0 "$pid" 2>/dev/null; then
56
72
  return 0
57
73
  fi
58
74
 
59
- local pid
60
- pid=$(cat "$PID_FILE" 2>/dev/null)
61
- if [ -z "$pid" ]; then
62
- rm -f "$PID_FILE"
63
- if [ "$LANGUAGE" = "en" ]; then
64
- echo "ℹ️ Empty pid file removed."
65
- else
66
- echo "ℹ️ 비어있는 pid 파일을 정리했습니다."
67
- fi
75
+ if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
68
76
  return 0
69
77
  fi
70
78
 
79
+ return 1
80
+ }
81
+
82
+ find_pid_by_port() {
83
+ local port
84
+ port=$(get_server_value "port" "3400")
85
+ ss -ltnp 2>/dev/null | grep ":$port " | sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | head -n 1
86
+ }
87
+
88
+ stop_pid_with_confirm() {
89
+ local pid="$1"
90
+ local reason="$2"
91
+
92
+ if [ -z "$pid" ]; then
93
+ return 1
94
+ fi
71
95
  if ! kill -0 "$pid" 2>/dev/null; then
72
- rm -f "$PID_FILE"
73
- if [ "$LANGUAGE" = "en" ]; then
74
- echo "ℹ️ Stale pid file removed (process not found)."
75
- else
76
- echo "ℹ️ 실행 중인 프로세스가 없어 stale pid 파일을 정리했습니다."
77
- fi
78
- return 0
96
+ return 1
79
97
  fi
80
98
 
81
- # 실행 중인 프로세스 정보 표시 후 확인
82
99
  local proc_info
83
100
  proc_info=$(ps -p "$pid" -o pid,user,etime,args --no-headers 2>/dev/null | head -1)
84
101
  if [ "$LANGUAGE" = "en" ]; then
85
- echo "Running process:"
102
+ echo "Running process ($reason):"
86
103
  echo " PID USER ELAPSED COMMAND"
87
104
  echo " $proc_info"
88
105
  echo ""
89
106
  read -r -p "Stop this process? [y/N]: " input
90
107
  else
91
- echo "실행 중인 프로세스:"
108
+ echo "실행 중인 프로세스($reason):"
92
109
  echo " PID USER 실행시간 COMMAND"
93
110
  echo " $proc_info"
94
111
  echo ""
@@ -128,12 +145,74 @@ stop_server() {
128
145
  return 0
129
146
  }
130
147
 
148
+ stop_server() {
149
+ if [ ! -f "$PID_FILE" ]; then
150
+ local port_pid
151
+ port_pid=$(find_pid_by_port)
152
+ if [ -n "$port_pid" ]; then
153
+ stop_pid_with_confirm "$port_pid" "port fallback"
154
+ return $?
155
+ fi
156
+ if [ "$LANGUAGE" = "en" ]; then
157
+ echo "ℹ️ Server is not running (pid file not found)."
158
+ else
159
+ echo "ℹ️ 서버가 실행 중이 아닙니다 (pid 파일 없음)."
160
+ fi
161
+ return 0
162
+ fi
163
+
164
+ local pid
165
+ pid=$(cat "$PID_FILE" 2>/dev/null)
166
+ if [ -z "$pid" ]; then
167
+ rm -f "$PID_FILE"
168
+ local port_pid
169
+ port_pid=$(find_pid_by_port)
170
+ if [ -n "$port_pid" ]; then
171
+ stop_pid_with_confirm "$port_pid" "port fallback"
172
+ return $?
173
+ fi
174
+ if [ "$LANGUAGE" = "en" ]; then
175
+ echo "ℹ️ Empty pid file removed."
176
+ else
177
+ echo "ℹ️ 비어있는 pid 파일을 정리했습니다."
178
+ fi
179
+ return 0
180
+ fi
181
+
182
+ if ! kill -0 "$pid" 2>/dev/null; then
183
+ rm -f "$PID_FILE"
184
+ local port_pid
185
+ port_pid=$(find_pid_by_port)
186
+ if [ -n "$port_pid" ]; then
187
+ stop_pid_with_confirm "$port_pid" "port fallback"
188
+ return $?
189
+ fi
190
+ if [ "$LANGUAGE" = "en" ]; then
191
+ echo "ℹ️ Stale pid file removed (process not found)."
192
+ else
193
+ echo "ℹ️ 실행 중인 프로세스가 없어 stale pid 파일을 정리했습니다."
194
+ fi
195
+ return 0
196
+ fi
197
+
198
+ stop_pid_with_confirm "$pid" "pid file"
199
+ return $?
200
+ }
201
+
131
202
  show_status() {
132
203
  if is_running; then
204
+ local status_pid
205
+ status_pid=$(find_pid_by_port)
133
206
  ./bin/entity-server banner-status RUNNING
134
207
  if [ "$LANGUAGE" = "en" ]; then
208
+ if [ -n "$status_pid" ]; then
209
+ echo "PID: $status_pid (detected by port)"
210
+ fi
135
211
  echo "Stop: ./run.sh stop"
136
212
  else
213
+ if [ -n "$status_pid" ]; then
214
+ echo "PID: $status_pid (포트 기준 감지)"
215
+ fi
137
216
  echo "중지: ./run.sh stop"
138
217
  fi
139
218
  else
@@ -1,10 +1,14 @@
1
- # update-server.ps1 — entity-server / entity-cli 바이너리 업데이트
1
+ # update-server.ps1 — entity-server / entity-cli 바이너리 및 파일 업데이트
2
2
  #
3
3
  # 사용법:
4
4
  # .\scripts\update-server.ps1 # 도움말
5
5
  # .\scripts\update-server.ps1 version # 현재 버전 + 최신 버전 확인
6
6
  # .\scripts\update-server.ps1 latest # 최신 버전으로 업데이트
7
7
  # .\scripts\update-server.ps1 1.5.0 # 특정 버전으로 업데이트
8
+ #
9
+ # 업데이트 대상:
10
+ # - 바이너리: entity-server, entity-cli
11
+ # - 파일: scripts/ samples/ (configs/ entities/ docs/ 제외)
8
12
 
9
13
  param([string]$Action = "")
10
14
 
@@ -12,6 +16,7 @@ $ErrorActionPreference = "Stop"
12
16
 
13
17
  $REPO = "ehfuse/entity-server"
14
18
  $BINARIES = @("entity-server", "entity-cli")
19
+ $DIST_DIRS = @("scripts", "samples")
15
20
  $PLATFORM = "windows"
16
21
  $ARCH_TAG = "x64"
17
22
  $ProjectRoot = Split-Path -Parent $PSScriptRoot
@@ -71,22 +76,107 @@ function Install-Version([string]$TargetVer) {
71
76
  }
72
77
  }
73
78
 
79
+ Install-Dist $TargetVer
80
+
74
81
  Write-Host ""
75
82
  Write-Host "✅ 업데이트 완료: v$CurrentVer → v$TargetVer"
76
83
  Write-Host " 서버를 재시작하면 새 버전이 적용됩니다."
77
84
  }
78
85
 
86
+ # ── dist 파일 업데이트 (scripts / samples) ──────────────────────────────────
87
+
88
+ function Install-Dist([string]$TargetVer) {
89
+ $TargetVer = $TargetVer -replace '^v', ''
90
+ $FileName = "dist.tar.gz"
91
+ $Url = "https://github.com/$REPO/releases/download/v$TargetVer/$FileName"
92
+ $TmpTar = Join-Path $env:TEMP ("entity-server-dist-$TargetVer.tar.gz")
93
+ $TmpDir = Join-Path $env:TEMP ("entity-server-dist-$TargetVer")
94
+
95
+ Write-Host (" ↓ {0,-35}" -f $FileName) -NoNewline
96
+ try {
97
+ Invoke-WebRequest -Uri $Url -OutFile $TmpTar -UseBasicParsing
98
+ Write-Host "✓"
99
+ } catch {
100
+ Write-Host "✗ 실패 (업데이트 스킵)"
101
+ Write-Host " URL: $Url"
102
+ Write-Host " ⚠️ dist.tar.gz 가 릴리스에 없습니다. 바이너리만 업데이트됩니다."
103
+ return
104
+ }
105
+
106
+ if (Test-Path $TmpDir) {
107
+ Remove-Item -Recurse -Force $TmpDir
108
+ }
109
+ New-Item -ItemType Directory -Path $TmpDir | Out-Null
110
+
111
+ # tar.exe 사용 (Windows 10+/PowerShell 5+ 기본 포함 환경 가정)
112
+ $TarCmd = Get-Command tar -ErrorAction SilentlyContinue
113
+ if (-not $TarCmd) {
114
+ Write-Host " ⚠️ tar 명령을 찾지 못해 dist 동기화를 건너뜁니다."
115
+ if (Test-Path $TmpTar) { Remove-Item -Force $TmpTar }
116
+ return
117
+ }
118
+
119
+ try {
120
+ & tar -xzf $TmpTar -C $TmpDir
121
+ } catch {
122
+ Write-Host " ⚠️ dist 압축 해제 실패: $_"
123
+ if (Test-Path $TmpTar) { Remove-Item -Force $TmpTar }
124
+ if (Test-Path $TmpDir) { Remove-Item -Recurse -Force $TmpDir }
125
+ return
126
+ }
127
+
128
+ $SrcRoot = $TmpDir
129
+ $HasTopLevel = $false
130
+ foreach ($Dir in $DIST_DIRS) {
131
+ if (Test-Path (Join-Path $TmpDir $Dir)) {
132
+ $HasTopLevel = $true
133
+ break
134
+ }
135
+ }
136
+
137
+ if (-not $HasTopLevel) {
138
+ $FirstSubdir = Get-ChildItem -Path $TmpDir -Directory | Select-Object -First 1
139
+ if ($FirstSubdir) {
140
+ $SrcRoot = $FirstSubdir.FullName
141
+ }
142
+ }
143
+
144
+ Write-Host ""
145
+ Write-Host " 파일 동기화 (configs/ entities/ docs/ 제외):"
146
+ foreach ($Dir in $DIST_DIRS) {
147
+ $Src = Join-Path $SrcRoot $Dir
148
+ $Dest = Join-Path $ProjectRoot $Dir
149
+ if (Test-Path $Src) {
150
+ if (Test-Path $Dest) {
151
+ Remove-Item -Recurse -Force $Dest
152
+ }
153
+ Copy-Item -Recurse -Force $Src $Dest
154
+ Write-Host (" ✔ {0,-20}" -f ("$Dir/"))
155
+ } else {
156
+ Write-Host (" – {0,-20} (릴리스에 없음, 스킵)" -f ("$Dir/"))
157
+ }
158
+ }
159
+
160
+ if (Test-Path $TmpTar) { Remove-Item -Force $TmpTar }
161
+ if (Test-Path $TmpDir) { Remove-Item -Recurse -Force $TmpDir }
162
+ }
163
+
79
164
  # ── 서브커맨드 분기 ───────────────────────────────────────────────────────────
80
165
 
81
166
  switch ($Action) {
82
167
  "" {
83
- Write-Host "update-server.ps1 — entity-server / entity-cli 바이너리 업데이트"
168
+ Write-Host "update-server.ps1 — entity-server / entity-cli 바이너리 및 파일 업데이트"
84
169
  Write-Host ""
85
170
  Write-Host "사용법:"
86
171
  Write-Host " .\scripts\update-server.ps1 version 현재 버전 + 최신 버전 확인"
87
172
  Write-Host " .\scripts\update-server.ps1 latest 최신 버전으로 업데이트"
88
173
  Write-Host " .\scripts\update-server.ps1 <버전> 특정 버전으로 업데이트"
89
174
  Write-Host ""
175
+ Write-Host "업데이트 대상:"
176
+ Write-Host " 바이너리 entity-server entity-cli"
177
+ Write-Host " 파일 scripts/ samples/"
178
+ Write-Host " 제외 configs/ entities/ docs/ (local 설정 보존)"
179
+ Write-Host ""
90
180
  Write-Host "예시:"
91
181
  Write-Host " .\scripts\update-server.ps1 version"
92
182
  Write-Host " .\scripts\update-server.ps1 latest"
@@ -1,11 +1,15 @@
1
1
  #!/bin/bash
2
- # update-server.sh — entity-server / entity-cli 바이너리 업데이트
2
+ # update-server.sh — entity-server / entity-cli 바이너리 및 파일 업데이트
3
3
  #
4
4
  # 사용법:
5
5
  # ./scripts/update-server.sh # 도움말
6
6
  # ./scripts/update-server.sh version # 현재 버전 + 최신 버전 확인
7
7
  # ./scripts/update-server.sh latest # 최신 버전으로 업데이트
8
8
  # ./scripts/update-server.sh 1.5.0 # 특정 버전으로 업데이트
9
+ #
10
+ # 업데이트 대상:
11
+ # - 바이너리: entity-server, entity-cli
12
+ # - 파일: scripts/ samples/ (configs/ entities/ docs/ 제외)
9
13
 
10
14
  set -e
11
15
 
@@ -13,6 +17,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
17
  PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
14
18
  REPO="ehfuse/entity-server"
15
19
  BINARIES=("entity-server" "entity-cli")
20
+ DIST_DIRS=("scripts" "samples")
16
21
 
17
22
  # ── 플랫폼 감지 ───────────────────────────────────────────────────────────────
18
23
 
@@ -99,6 +104,7 @@ _install() {
99
104
  echo "📦 entity-server v${target_ver} 다운로드 중... (${PLATFORM}-${ARCH_TAG})"
100
105
  echo ""
101
106
 
107
+ # 바이너리 다운로드
102
108
  for BIN in "${BINARIES[@]}"; do
103
109
  local file="${BIN}-${PLATFORM}-${ARCH_TAG}"
104
110
  local url="https://github.com/${REPO}/releases/download/v${target_ver}/${file}"
@@ -114,24 +120,89 @@ _install() {
114
120
  fi
115
121
  done
116
122
 
123
+ # scripts / samples 업데이트
124
+ _install_dist "$target_ver"
125
+
117
126
  echo ""
118
127
  echo "✅ 업데이트 완료: v${current_ver} → v${target_ver}"
119
128
  echo " 서버를 재시작하면 새 버전이 적용됩니다."
120
129
  }
121
130
 
131
+ # ── dist 파일 업데이트 (scripts / samples) ──────────────────────────────────────
132
+
133
+ _install_dist() {
134
+ local target_ver="$1"
135
+ local file="dist.tar.gz"
136
+ local url="https://github.com/${REPO}/releases/download/v${target_ver}/${file}"
137
+ local tmp_tar="/tmp/entity-server-dist-${target_ver}.tar.gz"
138
+ local tmp_dir="/tmp/entity-server-dist-${target_ver}"
139
+
140
+ printf " ↓ %-32s" "$file"
141
+ if ! _download "$url" "$tmp_tar" 2>/dev/null; then
142
+ echo "✗ 실패 (업데이트 스킵)"
143
+ echo " URL: $url"
144
+ echo " ⚠️ dist.tar.gz 가 릴리스에 없익니다. 바이너리만 업데이트됨."
145
+ return 0
146
+ fi
147
+ echo "✓"
148
+
149
+ rm -rf "$tmp_dir"
150
+ mkdir -p "$tmp_dir"
151
+ tar -xzf "$tmp_tar" -C "$tmp_dir" 2>/dev/null
152
+
153
+ # 아카이브 내부 디렉토리 구조 자동 탐지 (top-level 또는 서브디렉토리)
154
+ local src_root="$tmp_dir"
155
+ local found_dir="false"
156
+ for dir in "${DIST_DIRS[@]}"; do
157
+ if [ -d "${tmp_dir}/${dir}" ]; then
158
+ found_dir="true"
159
+ break
160
+ fi
161
+ done
162
+ if [ "$found_dir" = "false" ]; then
163
+ # tar가 서브디렉토리 하나로 풌주는 경우 (예: entity-server-1.5.0/)
164
+ local subdir
165
+ subdir="$(ls -1 "$tmp_dir" | head -1)"
166
+ if [ -n "$subdir" ] && [ -d "${tmp_dir}/${subdir}" ]; then
167
+ src_root="${tmp_dir}/${subdir}"
168
+ fi
169
+ fi
170
+
171
+ echo ""
172
+ echo " 파일 동기화 (configs/ entities/ 제외):"
173
+ for DIR in "${DIST_DIRS[@]}"; do
174
+ local src="${src_root}/${DIR}"
175
+ local dest="${PROJECT_ROOT}/${DIR}"
176
+ if [ -d "$src" ]; then
177
+ rm -rf "$dest"
178
+ cp -r "$src" "$dest"
179
+ printf " ✔ %-20s\n" "${DIR}/"
180
+ else
181
+ printf " – %-20s (릴리스에 없음, 스킵)\n" "${DIR}/"
182
+ fi
183
+ done
184
+
185
+ rm -rf "$tmp_dir" "$tmp_tar"
186
+ }
187
+
122
188
  # ── 서브커맨드 분기 ───────────────────────────────────────────────────────────
123
189
 
124
190
  ARG="${1:-}"
125
191
 
126
192
  case "$ARG" in
127
193
  "")
128
- echo "update-server.sh — entity-server / entity-cli 바이너리 업데이트"
194
+ echo "update-server.sh — entity-server / entity-cli 바이너리 및 파일 업데이트"
129
195
  echo ""
130
196
  echo "사용법:"
131
197
  echo " ./scripts/update-server.sh version 현재 버전 + 최신 버전 확인"
132
198
  echo " ./scripts/update-server.sh latest 최신 버전으로 업데이트"
133
199
  echo " ./scripts/update-server.sh <버전> 특정 버전으로 업데이트"
134
200
  echo ""
201
+ echo "업데이트 대상:"
202
+ echo " 바이너리 entity-server entity-cli"
203
+ echo " 파일 scripts/ samples/"
204
+ echo " 제외 configs/ entities/ docs/ (local 설정 보존)"
205
+ echo ""
135
206
  echo "예시:"
136
207
  echo " ./scripts/update-server.sh version"
137
208
  echo " ./scripts/update-server.sh latest"