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.
- package/bin/create.js +11 -1
- package/package.json +1 -1
- package/template/.env.example +17 -1
- package/template/configs/jwt.json +1 -0
- package/template/configs/oauth.json +37 -0
- package/template/configs/push.json +26 -0
- package/template/entities/README.md +4 -4
- package/template/entities/{Auth → System/Auth}/account.json +0 -14
- package/template/samples/README.md +16 -0
- package/template/samples/entities/01_basic_fields.json +39 -0
- package/template/samples/entities/02_types_and_defaults.json +68 -0
- package/template/samples/entities/03_hash_and_unique.json +33 -0
- package/template/samples/entities/04_fk_and_composite_unique.json +31 -0
- package/template/samples/entities/05_cache.json +54 -0
- package/template/samples/entities/06_history_and_hard_delete.json +42 -0
- package/template/samples/entities/07_license_scope.json +43 -0
- package/template/samples/entities/08_hook_sql.json +52 -0
- package/template/samples/entities/09_hook_entity.json +71 -0
- package/template/samples/entities/10_hook_submit_delete.json +75 -0
- package/template/samples/entities/11_hook_webhook.json +82 -0
- package/template/samples/entities/12_hook_push.json +73 -0
- package/template/samples/entities/13_read_only.json +51 -0
- package/template/samples/entities/14_optimistic_lock.json +29 -0
- package/template/samples/entities/15_reset_defaults.json +95 -0
- package/template/samples/entities/README.md +94 -0
- package/template/samples/entities/order_notification.json +51 -0
- package/template/samples/flutter/lib/entity_server_client.dart +91 -0
- package/template/samples/java/EntityServerClient.java +117 -0
- package/template/samples/kotlin/EntityServerClient.kt +86 -0
- package/template/samples/node/src/EntityServerClient.js +116 -0
- package/template/samples/php/ci4/Config/EntityServer.php +15 -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 +150 -11
- package/template/samples/php/laravel/Services/EntityServerService.php +56 -0
- package/template/samples/python/entity_server.py +106 -0
- package/template/samples/react/src/api/entityServerClient.ts +123 -0
- package/template/samples/react/src/hooks/useEntity.ts +68 -0
- package/template/samples/swift/EntityServerClient.swift +105 -0
- package/template/scripts/normalize-entities.sh +10 -10
- package/template/scripts/run.sh +108 -29
- package/template/scripts/update-server.ps1 +92 -2
- package/template/scripts/update-server.sh +73 -2
- /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
|
@@ -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/
|
|
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 " -
|
|
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/
|
|
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 " -
|
|
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 미리보기"
|
package/template/scripts/run.sh
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|