create-entity-server 0.5.5 → 0.6.0

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.5",
3
+ "version": "0.6.0",
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",
@@ -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,173 @@ 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
+ # 포트 점유 프로세스 상세 정보를 출력합니다.
79
+ print_port_process_details() {
80
+ local pid=""
81
+ local process_name=""
82
+ local cmdline=""
83
+
84
+ while read -r pid; do
85
+ pid=$(echo "$pid" | tr -d '[:space:]')
86
+ [ -z "$pid" ] && continue
87
+
88
+ process_name=$(get_pid_name "$pid")
89
+ cmdline=$(get_pid_cmdline "$pid")
90
+ [ -z "$process_name" ] && process_name="unknown"
91
+ [ -z "$cmdline" ] && cmdline="(command line unavailable)"
92
+
93
+ if [ "$LANGUAGE" = "en" ]; then
94
+ echo " PID: $pid | NAME: $process_name"
95
+ echo " CMD: $cmdline"
96
+ else
97
+ echo " PID: $pid | NAME: $process_name"
98
+ echo " CMD: $cmdline"
99
+ fi
100
+ done < <(find_server_pids)
101
+ }
102
+
103
+ # PID가 실제 실행 중인지 Windows 폴백까지 포함해 확인합니다.
104
+ is_pid_running() {
105
+ local pid="$1"
106
+
107
+ if [ -z "$pid" ]; then
108
+ return 1
109
+ fi
110
+
111
+ if kill -0 "$pid" 2>/dev/null; then
112
+ return 0
113
+ fi
114
+
115
+ if has_command powershell.exe; then
116
+ powershell.exe -NoProfile -Command "if (Get-Process -Id $pid -ErrorAction SilentlyContinue) { exit 0 } else { exit 1 }" \
117
+ >/dev/null 2>&1
118
+ return $?
119
+ fi
120
+
121
+ return 1
122
+ }
123
+
124
+ # PID를 종료하고 남아 있으면 강제 종료까지 진행합니다.
125
+ force_stop_pid() {
126
+ local pid="$1"
127
+
128
+ if [ -z "$pid" ]; then
129
+ return 1
130
+ fi
131
+
132
+ kill "$pid" 2>/dev/null || true
133
+ if is_pid_running "$pid" && has_command powershell.exe; then
134
+ powershell.exe -NoProfile -Command "Stop-Process -Id $pid -ErrorAction SilentlyContinue" >/dev/null 2>&1 || true
135
+ fi
136
+
137
+ for _ in $(seq 1 30); do
138
+ if ! is_pid_running "$pid"; then
139
+ return 0
140
+ fi
141
+ sleep 0.1
142
+ done
143
+
144
+ kill -9 "$pid" 2>/dev/null || true
145
+ if is_pid_running "$pid" && has_command powershell.exe; then
146
+ powershell.exe -NoProfile -Command "Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue" >/dev/null 2>&1 || true
147
+ fi
148
+
149
+ for _ in $(seq 1 20); do
150
+ if ! is_pid_running "$pid"; then
151
+ return 0
152
+ fi
153
+ sleep 0.1
154
+ done
155
+
156
+ return 1
157
+ }
158
+
159
+ # Windows PowerShell로 listen 중인 포트의 PID를 조회합니다.
160
+ find_pid_by_port_powershell() {
161
+ local port="$1"
162
+ if ! has_command powershell.exe; then
163
+ return 0
164
+ fi
165
+
166
+ powershell.exe -NoProfile -Command "Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess" \
167
+ 2>/dev/null | tr -d '\r' | awk '/^[0-9]+$/ { print }' | sort -u
168
+ }
169
+
170
+ # netstat 출력에서 포트 점유 PID를 조회합니다.
171
+ find_pid_by_port_netstat() {
172
+ local port="$1"
173
+ if ! has_command netstat; then
174
+ return 0
175
+ fi
176
+
177
+ netstat -ano 2>/dev/null | awk -v target=":$port" '
178
+ $1 ~ /^TCP/ && index($2, target) && $NF ~ /^[0-9]+$/ { print $NF }
179
+ ' | sort -u
180
+ }
181
+
182
+ # PID가 현재 프로젝트의 Entity Server 프로세스인지 확인합니다.
183
+ is_managed_server_pid() {
184
+ local pid="$1"
185
+ local process_name=""
186
+
187
+ if ! is_pid_running "$pid"; then
188
+ return 1
189
+ fi
190
+
191
+ process_name=$(get_pid_name "$pid")
192
+ [[ "$process_name" == "entity-server" || "$process_name" == "entity-server.exe" ]]
193
+ }
194
+
27
195
  get_server_value() {
28
196
  local key="$1"
29
197
  local fallback="$2"
@@ -118,55 +286,77 @@ sync_database_default_for_environment() {
118
286
  return 0
119
287
  }
120
288
 
121
- is_running() {
122
- local port_pid
123
- port_pid=$(find_pid_by_port)
289
+ find_server_pids() {
290
+ local port
291
+ port=$(get_server_value "port" "3400")
124
292
 
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
293
+ if has_command ss; then
294
+ ss -ltnp 2>/dev/null | sed -n "s/.*:$port .*pid=\([0-9]\+\).*/\1/p" | sort -u
295
+ return
144
296
  fi
145
- if kill -0 "$pid" 2>/dev/null; then
146
- return 0
297
+
298
+ local detected_pid
299
+ detected_pid="$(find_pid_by_port_powershell "$port")"
300
+ if [ -n "$detected_pid" ]; then
301
+ echo "$detected_pid" | awk '/^[0-9]+$/ { print }' | sort -u
302
+ return
147
303
  fi
148
304
 
149
- if [ -n "$port_pid" ] && kill -0 "$port_pid" 2>/dev/null; then
150
- return 0
305
+ detected_pid="$(find_pid_by_port_netstat "$port")"
306
+ if [ -n "$detected_pid" ]; then
307
+ echo "$detected_pid" | awk '/^[0-9]+$/ { print }' | sort -u
151
308
  fi
309
+ }
152
310
 
153
- if is_port_in_use; then
154
- return 0
311
+ # pid 파일과 포트 기준으로 현재 서버 PID를 찾습니다.
312
+ find_active_server_pid() {
313
+ local pid=""
314
+ local port_pid=""
315
+
316
+ if [ -f "$PID_FILE" ]; then
317
+ pid=$(tr -d '[:space:]' < "$PID_FILE" 2>/dev/null)
318
+ if [ -n "$pid" ] && is_managed_server_pid "$pid"; then
319
+ echo "$pid"
320
+ return
321
+ fi
155
322
  fi
156
323
 
157
- return 1
324
+ while read -r port_pid; do
325
+ port_pid=$(echo "$port_pid" | tr -d '[:space:]')
326
+ [ -z "$port_pid" ] && continue
327
+ if is_managed_server_pid "$port_pid"; then
328
+ echo "$port_pid"
329
+ return
330
+ fi
331
+ done < <(find_server_pids)
158
332
  }
159
333
 
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
334
+ is_running() {
335
+ [ -n "$(find_active_server_pid || true)" ]
164
336
  }
165
337
 
166
338
  is_port_in_use() {
167
339
  local port
168
340
  port=$(get_server_value "port" "3400")
169
- ss -ltn 2>/dev/null | grep -q ":$port "
341
+
342
+ if [ -n "$(find_server_pids | head -n 1)" ]; then
343
+ return 0
344
+ fi
345
+
346
+ if has_command ss; then
347
+ ss -ltn 2>/dev/null | grep -q ":$port "
348
+ return
349
+ fi
350
+
351
+ if has_command netstat; then
352
+ netstat -ano 2>/dev/null | awk -v target=":$port" '
353
+ $1 ~ /^TCP/ && index($2, target) { found = 1; exit }
354
+ END { exit(found ? 0 : 1) }
355
+ '
356
+ return
357
+ fi
358
+
359
+ return 1
170
360
  }
171
361
 
172
362
  show_port_in_use_message() {
@@ -175,14 +365,30 @@ show_port_in_use_message() {
175
365
  if [ "$LANGUAGE" = "en" ]; then
176
366
  echo "❌ Port $port is already in use, but the owning PID could not be identified."
177
367
  echo "Check with: ss -ltnp | grep :$port"
368
+ echo "Or: powershell Get-NetTCPConnection -LocalPort $port -State Listen"
178
369
  echo "Or: lsof -iTCP:$port -sTCP:LISTEN -n -P"
179
370
  else
180
371
  echo "❌ 포트 $port 가 이미 사용 중이지만, 점유 PID를 식별할 수 없습니다."
181
372
  echo "확인: ss -ltnp | grep :$port"
373
+ echo "또는: powershell Get-NetTCPConnection -LocalPort $port -State Listen"
182
374
  echo "또는: lsof -iTCP:$port -sTCP:LISTEN -n -P"
183
375
  fi
184
376
  }
185
377
 
378
+ # start/dev 용 포트 충돌 안내를 출력합니다.
379
+ show_start_port_in_use_message() {
380
+ local port
381
+ port=$(get_server_value "port" "3400")
382
+
383
+ if [ "$LANGUAGE" = "en" ]; then
384
+ echo "❌ Port $port is already in use. Stop the existing process first: ./run.sh stop"
385
+ else
386
+ echo "❌ 포트 $port 가 이미 사용 중입니다. 먼저 중지하세요: ./run.sh stop"
387
+ fi
388
+
389
+ print_port_process_details
390
+ }
391
+
186
392
  show_unmanaged_server_message() {
187
393
  local port
188
394
  port=$(get_server_value "port" "3400")
@@ -194,6 +400,8 @@ show_unmanaged_server_message() {
194
400
  echo "ℹ️ 포트 $port 를 사용하는 프로세스가 있지만, 현재 프로젝트의 pid 파일로 시작한 서버가 아닙니다."
195
401
  echo " 안전을 위해 run.sh stop 은 포트 일치만으로 프로세스를 종료하지 않습니다."
196
402
  fi
403
+
404
+ print_port_process_details
197
405
  }
198
406
 
199
407
  stop_pid_with_confirm() {
@@ -203,7 +411,7 @@ stop_pid_with_confirm() {
203
411
  if [ -z "$pid" ]; then
204
412
  return 1
205
413
  fi
206
- if ! kill -0 "$pid" 2>/dev/null; then
414
+ if ! is_pid_running "$pid"; then
207
415
  return 1
208
416
  fi
209
417
 
@@ -219,118 +427,62 @@ stop_pid_with_confirm() {
219
427
  echo " $proc_info"
220
428
  fi
221
429
 
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
430
+ if force_stop_pid "$pid"; then
431
+ rm -f "$PID_FILE"
432
+ if [ "$LANGUAGE" = "en" ]; then
433
+ echo "✅ ${SERVER_NAME} stopped (pid: $pid)"
434
+ else
435
+ echo "✅ ${SERVER_NAME} 종료 완료 (PID: $pid)"
232
436
  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)"
437
+ return 0
242
438
  fi
243
- return 0
439
+
440
+ return 1
244
441
  }
245
442
 
246
443
  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
444
+ local pid=""
445
+
446
+ pid=$(find_active_server_pid)
447
+ rm -f "$PID_FILE"
448
+
449
+ if [ -n "$pid" ]; then
450
+ stop_pid_with_confirm "$pid" "active process"
451
+ return $?
264
452
  fi
265
453
 
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
454
+ if is_port_in_use; then
455
+ show_unmanaged_server_message
456
+ return 1
286
457
  fi
287
458
 
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
459
+ if [ "$LANGUAGE" = "en" ]; then
460
+ echo "ℹ️ ${SERVER_NAME} is not running."
461
+ else
462
+ echo "ℹ️ ${SERVER_NAME}가 실행 중이 아닙니다."
310
463
  fi
311
464
 
312
- stop_pid_with_confirm "$pid" "pid file"
313
- return $?
465
+ return 0
314
466
  }
315
467
 
316
468
  show_status() {
317
- if is_running; then
318
- local status_pid
319
- status_pid=$(find_pid_by_port)
469
+ local status_pid=""
470
+
471
+ status_pid=$(find_active_server_pid || true)
472
+ if [ -n "$status_pid" ]; then
320
473
  "$SERVER_BIN" banner-status RUNNING
321
474
  if [ "$LANGUAGE" = "en" ]; then
322
- if [ -n "$status_pid" ]; then
323
- echo "PID: $status_pid (detected by port)"
324
- fi
475
+ echo "PID: $status_pid (managed process)"
325
476
  echo "Stop: ./run.sh stop"
326
477
  else
327
- if [ -n "$status_pid" ]; then
328
- echo "PID: $status_pid (포트 기준 감지)"
329
- fi
478
+ echo "PID: $status_pid (관리 대상 프로세스)"
330
479
  echo "중지: ./run.sh stop"
331
480
  fi
332
481
  else
333
482
  "$SERVER_BIN" banner-status STOPPED
483
+ if is_port_in_use; then
484
+ show_unmanaged_server_message
485
+ fi
334
486
  if [ "$LANGUAGE" = "en" ]; then
335
487
  echo "Start: ./run.sh start"
336
488
  else
@@ -432,40 +584,42 @@ fi
432
584
 
433
585
  case "$MODE" in
434
586
  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
587
+ running_pid=$(find_active_server_pid || true)
588
+ if [ -n "$running_pid" ]; then
589
+ if [ "$LANGUAGE" = "en" ]; then
590
+ echo "❌ Server already running (pid: $running_pid). Stop first: ./run.sh stop"
443
591
  else
444
- show_port_in_use_message
592
+ echo "❌ 이미 서버가 실행 중입니다 (pid: $running_pid). 먼저 중지하세요: ./run.sh stop"
445
593
  fi
446
594
  exit 1
447
595
  fi
448
596
 
597
+ if is_port_in_use; then
598
+ show_start_port_in_use_message
599
+ exit 1
600
+ fi
601
+
449
602
  sed -E -i 's/("environment"[[:space:]]*:[[:space:]]*")[^"]+(")/\1development\2/' "$SERVER_CONFIG"
450
603
  sync_database_default_for_environment "$LINENO" || exit 1
451
604
  "$SERVER_BIN"
452
605
  ;;
453
606
 
454
607
  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
608
+ running_pid=$(find_active_server_pid || true)
609
+ if [ -n "$running_pid" ]; then
610
+ if [ "$LANGUAGE" = "en" ]; then
611
+ echo "❌ Server already running (pid: $running_pid). Stop first: ./run.sh stop"
463
612
  else
464
- show_port_in_use_message
613
+ echo "❌ 이미 서버가 실행 중입니다 (pid: $running_pid). 먼저 중지하세요: ./run.sh stop"
465
614
  fi
466
615
  exit 1
467
616
  fi
468
617
 
618
+ if is_port_in_use; then
619
+ show_start_port_in_use_message
620
+ exit 1
621
+ fi
622
+
469
623
  "$SERVER_BIN" banner
470
624
  nohup "$SERVER_BIN" >> "$STDOUT_LOG" 2>&1 &
471
625
  SERVER_PID=$!