create-entity-server 0.5.4 → 0.5.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-entity-server",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "Create a new entity-server project in one command — like create-react-app or create-vite.",
5
5
  "keywords": [
6
6
  "entity-server",
@@ -18,6 +18,37 @@ if [ -f .env ]; then
18
18
  fi
19
19
  LANGUAGE=${LANGUAGE:-ko}
20
20
 
21
+ # entity-cli api-key 서브커맨드를 공통 경로로 실행합니다.
22
+ run_api_key_cli() {
23
+ ENTITY_CLI_NAME="api-key" "$BIN_PATH" api-key "$@"
24
+ }
25
+
26
+ # list 출력 테이블에서 seq 목록만 추출합니다.
27
+ extract_api_key_seqs() {
28
+ run_api_key_cli list "$@" | awk '
29
+ /^[[:space:]]*[0-9]+[[:space:]]/ {
30
+ print $1
31
+ }
32
+ '
33
+ }
34
+
35
+ # list 결과를 show 상세 블록으로 다시 출력합니다.
36
+ print_revealed_api_key_list() {
37
+ local -a seqs
38
+ mapfile -t seqs < <(extract_api_key_seqs "$@")
39
+
40
+ if [ ${#seqs[@]} -eq 0 ]; then
41
+ run_api_key_cli list "$@"
42
+ return
43
+ fi
44
+
45
+ local seq
46
+ for seq in "${seqs[@]}"; do
47
+ run_api_key_cli show --seq="$seq" --reveal-secret || return $?
48
+ echo ""
49
+ done
50
+ }
51
+
21
52
  show_help() {
22
53
  if [ "$LANGUAGE" = "en" ]; then
23
54
  echo "API Key Management (CLI mode)"
@@ -36,6 +67,7 @@ show_help() {
36
67
  echo ""
37
68
  echo "list options:"
38
69
  echo " --limit=<n> Max rows to show (default: 20)"
70
+ echo " Each row is expanded with full key_value/hmac_secret"
39
71
  echo ""
40
72
  echo "add options:"
41
73
  echo " --role=<name> Role name (default: admin)"
@@ -72,6 +104,7 @@ show_help() {
72
104
  echo ""
73
105
  echo "list 옵션:"
74
106
  echo " --limit=<n> 최대 출력 행 수 (기본: 20)"
107
+ echo " 각 항목을 key_value, hmac_secret 전체 값으로 펼쳐서 출력"
75
108
  echo ""
76
109
  echo "add 옵션:"
77
110
  echo " --role=<이름> 역할명 (기본: admin)"
@@ -114,8 +147,11 @@ SUBCOMMAND="$1"
114
147
  shift
115
148
 
116
149
  case "$SUBCOMMAND" in
117
- list|show|add|delete)
118
- ENTITY_CLI_NAME="api-key" "$BIN_PATH" api-key "$SUBCOMMAND" "$@"
150
+ list)
151
+ print_revealed_api_key_list "$@"
152
+ ;;
153
+ show|add|delete)
154
+ run_api_key_cli "$SUBCOMMAND" "$@"
119
155
  ;;
120
156
  help|-h|--help)
121
157
  show_help
@@ -17,6 +17,7 @@ if [ ! -f "$SERVER_BIN" ] && [ -f "$PROJECT_ROOT/entity-server" ]; then
17
17
  fi
18
18
 
19
19
  mkdir -p "$RUN_DIR" "$PROJECT_ROOT/logs"
20
+ SERVER_NAME="Entity Server"
20
21
 
21
22
  # Load language from .env
22
23
  if [ -f .env ]; then
@@ -24,6 +25,148 @@ if [ -f .env ]; then
24
25
  fi
25
26
  LANGUAGE=${LANGUAGE:-ko}
26
27
 
28
+ has_command() {
29
+ command -v "$1" >/dev/null 2>&1
30
+ }
31
+
32
+ # PID 프로세스명을 읽습니다.
33
+ get_pid_name() {
34
+ local pid="$1"
35
+ local result=""
36
+
37
+ result=$(ps -p "$pid" -o comm= 2>/dev/null | awk '{print $1}' || true)
38
+ if [ -n "$result" ]; then
39
+ echo "$result" | tr '[:upper:]' '[:lower:]'
40
+ return
41
+ fi
42
+
43
+ if has_command powershell.exe; then
44
+ powershell.exe -NoProfile -Command "(Get-Process -Id $pid -ErrorAction SilentlyContinue).ProcessName" \
45
+ 2>/dev/null | tr -d '\r' | tr '[:upper:]' '[:lower:]' || true
46
+ fi
47
+ }
48
+
49
+ # PID 명령행을 읽습니다.
50
+ get_pid_cmdline() {
51
+ local pid="$1"
52
+
53
+ if [ -r "/proc/$pid/cmdline" ]; then
54
+ tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null || true
55
+ return
56
+ fi
57
+
58
+ local result
59
+ result=$(ps -p "$pid" -o args= 2>/dev/null || true)
60
+ if [ -n "$result" ]; then
61
+ echo "$result"
62
+ return
63
+ fi
64
+
65
+ if has_command powershell.exe; then
66
+ powershell.exe -NoProfile -Command "(Get-WmiObject Win32_Process -Filter 'ProcessId = $pid').CommandLine" \
67
+ 2>/dev/null | tr -d '\r' | tr '\\' '/' || true
68
+ fi
69
+ }
70
+
71
+ # PID 작업 디렉터리를 읽습니다.
72
+ get_pid_cwd() {
73
+ local pid="$1"
74
+
75
+ readlink -f "/proc/$pid/cwd" 2>/dev/null || true
76
+ }
77
+
78
+ # PID가 실제 실행 중인지 Windows 폴백까지 포함해 확인합니다.
79
+ is_pid_running() {
80
+ local pid="$1"
81
+
82
+ if [ -z "$pid" ]; then
83
+ return 1
84
+ fi
85
+
86
+ if kill -0 "$pid" 2>/dev/null; then
87
+ return 0
88
+ fi
89
+
90
+ if has_command powershell.exe; then
91
+ powershell.exe -NoProfile -Command "if (Get-Process -Id $pid -ErrorAction SilentlyContinue) { exit 0 } else { exit 1 }" \
92
+ >/dev/null 2>&1
93
+ return $?
94
+ fi
95
+
96
+ return 1
97
+ }
98
+
99
+ # PID를 종료하고 남아 있으면 강제 종료까지 진행합니다.
100
+ force_stop_pid() {
101
+ local pid="$1"
102
+
103
+ if [ -z "$pid" ]; then
104
+ return 1
105
+ fi
106
+
107
+ kill "$pid" 2>/dev/null || true
108
+ if is_pid_running "$pid" && has_command powershell.exe; then
109
+ powershell.exe -NoProfile -Command "Stop-Process -Id $pid -ErrorAction SilentlyContinue" >/dev/null 2>&1 || true
110
+ fi
111
+
112
+ for _ in $(seq 1 30); do
113
+ if ! is_pid_running "$pid"; then
114
+ return 0
115
+ fi
116
+ sleep 0.1
117
+ done
118
+
119
+ kill -9 "$pid" 2>/dev/null || true
120
+ if is_pid_running "$pid" && has_command powershell.exe; then
121
+ powershell.exe -NoProfile -Command "Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue" >/dev/null 2>&1 || true
122
+ fi
123
+
124
+ for _ in $(seq 1 20); do
125
+ if ! is_pid_running "$pid"; then
126
+ return 0
127
+ fi
128
+ sleep 0.1
129
+ done
130
+
131
+ return 1
132
+ }
133
+
134
+ # Windows PowerShell로 listen 중인 포트의 PID를 조회합니다.
135
+ find_pid_by_port_powershell() {
136
+ local port="$1"
137
+ if ! has_command powershell.exe; then
138
+ return 0
139
+ fi
140
+
141
+ powershell.exe -NoProfile -Command "Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess" \
142
+ 2>/dev/null | tr -d '\r' | awk '/^[0-9]+$/ { print }' | sort -u
143
+ }
144
+
145
+ # netstat 출력에서 포트 점유 PID를 조회합니다.
146
+ find_pid_by_port_netstat() {
147
+ local port="$1"
148
+ if ! has_command netstat; then
149
+ return 0
150
+ fi
151
+
152
+ netstat -ano 2>/dev/null | awk -v target=":$port" '
153
+ $1 ~ /^TCP/ && index($2, target) && $NF ~ /^[0-9]+$/ { print $NF }
154
+ ' | sort -u
155
+ }
156
+
157
+ # PID가 현재 프로젝트의 Entity Server 프로세스인지 확인합니다.
158
+ is_managed_server_pid() {
159
+ local pid="$1"
160
+ local process_name=""
161
+
162
+ if ! is_pid_running "$pid"; then
163
+ return 1
164
+ fi
165
+
166
+ process_name=$(get_pid_name "$pid")
167
+ [[ "$process_name" == "entity-server" || "$process_name" == "entity-server.exe" ]]
168
+ }
169
+
27
170
  get_server_value() {
28
171
  local key="$1"
29
172
  local fallback="$2"
@@ -118,55 +261,77 @@ sync_database_default_for_environment() {
118
261
  return 0
119
262
  }
120
263
 
121
- is_running() {
122
- local port_pid
123
- port_pid=$(find_pid_by_port)
264
+ find_server_pids() {
265
+ local port
266
+ port=$(get_server_value "port" "3400")
124
267
 
125
- if [ ! -f "$PID_FILE" ]; then
126
- if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
127
- return 0
128
- fi
129
- if is_port_in_use; then
130
- return 0
131
- fi
132
- return 1
133
- fi
134
- local pid
135
- pid=$(cat "$PID_FILE" 2>/dev/null)
136
- if [ -z "$pid" ]; then
137
- if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
138
- return 0
139
- fi
140
- if is_port_in_use; then
141
- return 0
142
- fi
143
- return 1
268
+ if has_command ss; then
269
+ ss -ltnp 2>/dev/null | sed -n "s/.*:$port .*pid=\([0-9]\+\).*/\1/p" | sort -u
270
+ return
144
271
  fi
145
- if kill -0 "$pid" 2>/dev/null; then
146
- return 0
272
+
273
+ local detected_pid
274
+ detected_pid="$(find_pid_by_port_powershell "$port")"
275
+ if [ -n "$detected_pid" ]; then
276
+ echo "$detected_pid" | awk '/^[0-9]+$/ { print }' | sort -u
277
+ return
147
278
  fi
148
279
 
149
- if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
150
- return 0
280
+ detected_pid="$(find_pid_by_port_netstat "$port")"
281
+ if [ -n "$detected_pid" ]; then
282
+ echo "$detected_pid" | awk '/^[0-9]+$/ { print }' | sort -u
151
283
  fi
284
+ }
152
285
 
153
- if is_port_in_use; then
154
- return 0
286
+ # pid 파일과 포트 기준으로 현재 서버 PID를 찾습니다.
287
+ find_active_server_pid() {
288
+ local pid=""
289
+ local port_pid=""
290
+
291
+ if [ -f "$PID_FILE" ]; then
292
+ pid=$(tr -d '[:space:]' < "$PID_FILE" 2>/dev/null)
293
+ if [ -n "$pid" ] && is_managed_server_pid "$pid"; then
294
+ echo "$pid"
295
+ return
296
+ fi
155
297
  fi
156
298
 
157
- return 1
299
+ while read -r port_pid; do
300
+ port_pid=$(echo "$port_pid" | tr -d '[:space:]')
301
+ [ -z "$port_pid" ] && continue
302
+ if is_managed_server_pid "$port_pid"; then
303
+ echo "$port_pid"
304
+ return
305
+ fi
306
+ done < <(find_server_pids)
158
307
  }
159
308
 
160
- find_pid_by_port() {
161
- local port
162
- port=$(get_server_value "port" "3400")
163
- ss -ltnp 2>/dev/null | grep ":$port " | sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | head -n 1
309
+ is_running() {
310
+ [ -n "$(find_active_server_pid || true)" ]
164
311
  }
165
312
 
166
313
  is_port_in_use() {
167
314
  local port
168
315
  port=$(get_server_value "port" "3400")
169
- ss -ltn 2>/dev/null | grep -q ":$port "
316
+
317
+ if [ -n "$(find_server_pids | head -n 1)" ]; then
318
+ return 0
319
+ fi
320
+
321
+ if has_command ss; then
322
+ ss -ltn 2>/dev/null | grep -q ":$port "
323
+ return
324
+ fi
325
+
326
+ if has_command netstat; then
327
+ netstat -ano 2>/dev/null | awk -v target=":$port" '
328
+ $1 ~ /^TCP/ && index($2, target) { found = 1; exit }
329
+ END { exit(found ? 0 : 1) }
330
+ '
331
+ return
332
+ fi
333
+
334
+ return 1
170
335
  }
171
336
 
172
337
  show_port_in_use_message() {
@@ -175,14 +340,28 @@ show_port_in_use_message() {
175
340
  if [ "$LANGUAGE" = "en" ]; then
176
341
  echo "❌ Port $port is already in use, but the owning PID could not be identified."
177
342
  echo "Check with: ss -ltnp | grep :$port"
343
+ echo "Or: powershell Get-NetTCPConnection -LocalPort $port -State Listen"
178
344
  echo "Or: lsof -iTCP:$port -sTCP:LISTEN -n -P"
179
345
  else
180
346
  echo "❌ 포트 $port 가 이미 사용 중이지만, 점유 PID를 식별할 수 없습니다."
181
347
  echo "확인: ss -ltnp | grep :$port"
348
+ echo "또는: powershell Get-NetTCPConnection -LocalPort $port -State Listen"
182
349
  echo "또는: lsof -iTCP:$port -sTCP:LISTEN -n -P"
183
350
  fi
184
351
  }
185
352
 
353
+ # start/dev 용 포트 충돌 안내를 출력합니다.
354
+ show_start_port_in_use_message() {
355
+ local port
356
+ port=$(get_server_value "port" "3400")
357
+
358
+ if [ "$LANGUAGE" = "en" ]; then
359
+ echo "❌ Port $port is already in use. Stop the existing process first: ./run.sh stop"
360
+ else
361
+ echo "❌ 포트 $port 가 이미 사용 중입니다. 먼저 중지하세요: ./run.sh stop"
362
+ fi
363
+ }
364
+
186
365
  show_unmanaged_server_message() {
187
366
  local port
188
367
  port=$(get_server_value "port" "3400")
@@ -203,7 +382,7 @@ stop_pid_with_confirm() {
203
382
  if [ -z "$pid" ]; then
204
383
  return 1
205
384
  fi
206
- if ! kill -0 "$pid" 2>/dev/null; then
385
+ if ! is_pid_running "$pid"; then
207
386
  return 1
208
387
  fi
209
388
 
@@ -219,118 +398,62 @@ stop_pid_with_confirm() {
219
398
  echo " $proc_info"
220
399
  fi
221
400
 
222
- kill "$pid" 2>/dev/null
223
- for _ in $(seq 1 30); do
224
- if ! kill -0 "$pid" 2>/dev/null; then
225
- rm -f "$PID_FILE"
226
- if [ "$LANGUAGE" = "en" ]; then
227
- echo "✅ Server stopped (pid: $pid)"
228
- else
229
- echo "✅ 서버가 중지되었습니다 (pid: $pid)"
230
- fi
231
- return 0
401
+ if force_stop_pid "$pid"; then
402
+ rm -f "$PID_FILE"
403
+ if [ "$LANGUAGE" = "en" ]; then
404
+ echo "✅ ${SERVER_NAME} stopped (pid: $pid)"
405
+ else
406
+ echo "✅ ${SERVER_NAME} 종료 완료 (PID: $pid)"
232
407
  fi
233
- sleep 0.1
234
- done
235
-
236
- kill -9 "$pid" 2>/dev/null
237
- rm -f "$PID_FILE"
238
- if [ "$LANGUAGE" = "en" ]; then
239
- echo "⚠️ Server force-stopped (pid: $pid)"
240
- else
241
- echo "⚠️ 서버를 강제 종료했습니다 (pid: $pid)"
408
+ return 0
242
409
  fi
243
- return 0
410
+
411
+ return 1
244
412
  }
245
413
 
246
414
  stop_server() {
247
- if [ ! -f "$PID_FILE" ]; then
248
- local port_pid
249
- port_pid=$(find_pid_by_port)
250
- if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
251
- stop_pid_with_confirm "$port_pid" "port"
252
- return $?
253
- fi
254
- if is_port_in_use; then
255
- show_port_in_use_message
256
- return 0
257
- fi
258
- if [ "$LANGUAGE" = "en" ]; then
259
- echo "ℹ️ Server is not running (pid file not found)."
260
- else
261
- echo "ℹ️ 서버가 실행 중이 아닙니다 (pid 파일 없음)."
262
- fi
263
- return 0
415
+ local pid=""
416
+
417
+ pid=$(find_active_server_pid)
418
+ rm -f "$PID_FILE"
419
+
420
+ if [ -n "$pid" ]; then
421
+ stop_pid_with_confirm "$pid" "active process"
422
+ return $?
264
423
  fi
265
424
 
266
- local pid
267
- pid=$(cat "$PID_FILE" 2>/dev/null)
268
- if [ -z "$pid" ]; then
269
- rm -f "$PID_FILE"
270
- local port_pid
271
- port_pid=$(find_pid_by_port)
272
- if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
273
- stop_pid_with_confirm "$port_pid" "port"
274
- return $?
275
- fi
276
- if is_port_in_use; then
277
- show_port_in_use_message
278
- return 0
279
- fi
280
- if [ "$LANGUAGE" = "en" ]; then
281
- echo "ℹ️ Empty pid file removed."
282
- else
283
- echo "ℹ️ 비어있는 pid 파일을 정리했습니다."
284
- fi
285
- return 0
425
+ if is_port_in_use; then
426
+ show_unmanaged_server_message
427
+ return 1
286
428
  fi
287
429
 
288
- if ! kill -0 "$pid" 2>/dev/null; then
289
- rm -f "$PID_FILE"
290
- local port_pid
291
- port_pid=$(find_pid_by_port)
292
- if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
293
- stop_pid_with_confirm "$port_pid" "port"
294
- return $?
295
- fi
296
- if is_port_in_use; then
297
- show_port_in_use_message
298
- return 0
299
- fi
300
- if [ "$LANGUAGE" = "en" ]; then
301
- echo "ℹ️ Server process already exited (stale pid file removed)."
302
- echo " Last log ($STDOUT_LOG):"
303
- tail -5 "$STDOUT_LOG" 2>/dev/null | sed 's/^/ /'
304
- else
305
- echo "ℹ️ 서버 프로세스가 이미 종료되어 있습니다 (stale pid 파일 정리됨)."
306
- echo " 최근 로그 ($STDOUT_LOG):"
307
- tail -5 "$STDOUT_LOG" 2>/dev/null | sed 's/^/ /'
308
- fi
309
- return 0
430
+ if [ "$LANGUAGE" = "en" ]; then
431
+ echo "ℹ️ ${SERVER_NAME} is not running."
432
+ else
433
+ echo "ℹ️ ${SERVER_NAME}가 실행 중이 아닙니다."
310
434
  fi
311
435
 
312
- stop_pid_with_confirm "$pid" "pid file"
313
- return $?
436
+ return 0
314
437
  }
315
438
 
316
439
  show_status() {
317
- if is_running; then
318
- local status_pid
319
- status_pid=$(find_pid_by_port)
440
+ local status_pid=""
441
+
442
+ status_pid=$(find_active_server_pid || true)
443
+ if [ -n "$status_pid" ]; then
320
444
  "$SERVER_BIN" banner-status RUNNING
321
445
  if [ "$LANGUAGE" = "en" ]; then
322
- if [ -n "$status_pid" ]; then
323
- echo "PID: $status_pid (detected by port)"
324
- fi
446
+ echo "PID: $status_pid (managed process)"
325
447
  echo "Stop: ./run.sh stop"
326
448
  else
327
- if [ -n "$status_pid" ]; then
328
- echo "PID: $status_pid (포트 기준 감지)"
329
- fi
449
+ echo "PID: $status_pid (관리 대상 프로세스)"
330
450
  echo "중지: ./run.sh stop"
331
451
  fi
332
452
  else
333
453
  "$SERVER_BIN" banner-status STOPPED
454
+ if is_port_in_use; then
455
+ show_unmanaged_server_message
456
+ fi
334
457
  if [ "$LANGUAGE" = "en" ]; then
335
458
  echo "Start: ./run.sh start"
336
459
  else
@@ -432,40 +555,42 @@ fi
432
555
 
433
556
  case "$MODE" in
434
557
  dev|development)
435
- if is_running; then
436
- running_pid=$(find_pid_by_port)
437
- if [ -n "$running_pid" ]; then
438
- if [ "$LANGUAGE" = "en" ]; then
439
- echo "❌ Server already running (pid: $running_pid). Stop first: ./run.sh stop"
440
- else
441
- echo "❌ 이미 서버가 실행 중입니다 (pid: $running_pid). 먼저 중지하세요: ./run.sh stop"
442
- fi
558
+ running_pid=$(find_active_server_pid || true)
559
+ if [ -n "$running_pid" ]; then
560
+ if [ "$LANGUAGE" = "en" ]; then
561
+ echo "❌ Server already running (pid: $running_pid). Stop first: ./run.sh stop"
443
562
  else
444
- show_port_in_use_message
563
+ echo "❌ 이미 서버가 실행 중입니다 (pid: $running_pid). 먼저 중지하세요: ./run.sh stop"
445
564
  fi
446
565
  exit 1
447
566
  fi
448
567
 
568
+ if is_port_in_use; then
569
+ show_start_port_in_use_message
570
+ exit 1
571
+ fi
572
+
449
573
  sed -E -i 's/("environment"[[:space:]]*:[[:space:]]*")[^"]+(")/\1development\2/' "$SERVER_CONFIG"
450
574
  sync_database_default_for_environment "$LINENO" || exit 1
451
575
  "$SERVER_BIN"
452
576
  ;;
453
577
 
454
578
  start)
455
- if is_running; then
456
- running_pid=$(find_pid_by_port)
457
- if [ -n "$running_pid" ]; then
458
- if [ "$LANGUAGE" = "en" ]; then
459
- echo "❌ Server already running (pid: $running_pid). Stop first: ./run.sh stop"
460
- else
461
- echo "❌ 이미 서버가 실행 중입니다 (pid: $running_pid). 먼저 중지하세요: ./run.sh stop"
462
- fi
579
+ running_pid=$(find_active_server_pid || true)
580
+ if [ -n "$running_pid" ]; then
581
+ if [ "$LANGUAGE" = "en" ]; then
582
+ echo "❌ Server already running (pid: $running_pid). Stop first: ./run.sh stop"
463
583
  else
464
- show_port_in_use_message
584
+ echo "❌ 이미 서버가 실행 중입니다 (pid: $running_pid). 먼저 중지하세요: ./run.sh stop"
465
585
  fi
466
586
  exit 1
467
587
  fi
468
588
 
589
+ if is_port_in_use; then
590
+ show_start_port_in_use_message
591
+ exit 1
592
+ fi
593
+
469
594
  "$SERVER_BIN" banner
470
595
  nohup "$SERVER_BIN" >> "$STDOUT_LOG" 2>&1 &
471
596
  SERVER_PID=$!