2026-05-14 18:09:15 +08:00
|
|
|
|
@echo off
|
|
|
|
|
|
setlocal EnableExtensions EnableDelayedExpansion
|
|
|
|
|
|
|
|
|
|
|
|
set "ROOT_DIR=%~dp0"
|
|
|
|
|
|
pushd "%ROOT_DIR%" >nul
|
|
|
|
|
|
|
|
|
|
|
|
set "VENV_DIR=.venv"
|
|
|
|
|
|
set "VENV_PYTHON=%VENV_DIR%\Scripts\python.exe"
|
|
|
|
|
|
set "LOG_DIR=logs"
|
|
|
|
|
|
set "API_PID_FILE=%LOG_DIR%\api.pid"
|
|
|
|
|
|
set "FRONTEND_PID_FILE=%LOG_DIR%\frontend.pid"
|
|
|
|
|
|
set "API_LOG_FILE=%CD%\%LOG_DIR%\api.log"
|
|
|
|
|
|
set "FRONTEND_LOG_FILE=%CD%\%LOG_DIR%\frontend.log"
|
|
|
|
|
|
|
|
|
|
|
|
call :load_env
|
|
|
|
|
|
if not defined API_HOST set "API_HOST=0.0.0.0"
|
|
|
|
|
|
if not defined API_PORT set "API_PORT=8000"
|
|
|
|
|
|
if not defined FRONTEND_PORT set "FRONTEND_PORT=5173"
|
|
|
|
|
|
if not defined FRONTEND_MODE set "FRONTEND_MODE=dev"
|
|
|
|
|
|
|
|
|
|
|
|
if "%~1"=="" goto help
|
|
|
|
|
|
if /I "%~1"=="help" goto help
|
|
|
|
|
|
if /I "%~1"=="-h" goto help
|
|
|
|
|
|
if /I "%~1"=="--help" goto help
|
|
|
|
|
|
if /I "%~1"=="setup" goto setup
|
|
|
|
|
|
if /I "%~1"=="start" goto start
|
|
|
|
|
|
if /I "%~1"=="stop" goto stop
|
|
|
|
|
|
if /I "%~1"=="restart" goto restart
|
|
|
|
|
|
if /I "%~1"=="status" goto status
|
|
|
|
|
|
if /I "%~1"=="logs" goto logs
|
|
|
|
|
|
|
|
|
|
|
|
echo Unknown command: %~1
|
|
|
|
|
|
echo Use dev.bat help to see available commands.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
:help
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo ========================================
|
|
|
|
|
|
echo AI+合规智能中枢统一脚本(Windows)
|
|
|
|
|
|
echo ========================================
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo Usage:
|
|
|
|
|
|
echo dev.bat help
|
|
|
|
|
|
echo dev.bat setup
|
|
|
|
|
|
echo dev.bat start [all^|api^|frontend] [--foreground] [--mode dev^|static]
|
|
|
|
|
|
echo dev.bat stop [all^|api^|frontend]
|
|
|
|
|
|
echo dev.bat restart [all^|api^|frontend] [--mode dev^|static]
|
|
|
|
|
|
echo dev.bat status
|
|
|
|
|
|
echo dev.bat logs ^<api^|frontend^> [--follow]
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo Command details:
|
|
|
|
|
|
echo help
|
|
|
|
|
|
echo Show the full command list, default ports, log paths and examples.
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo setup
|
|
|
|
|
|
echo Create .venv if missing, install backend dependencies from backend\requirements.txt,
|
|
|
|
|
|
echo run npm install in frontend, then report Docker base service status.
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo start
|
|
|
|
|
|
echo Start services. Default target is all.
|
|
|
|
|
|
echo start api --foreground Run uvicorn in the current window with --reload for debugging.
|
|
|
|
|
|
echo start frontend --mode static Build the frontend and serve frontend\dist as static files.
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo stop
|
|
|
|
|
|
echo Stop services. It first uses logs\*.pid, then falls back to listeners on the configured ports.
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo restart
|
|
|
|
|
|
echo Stop and then start the selected target.
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo status
|
|
|
|
|
|
echo Show API, frontend and Docker service status. API status also checks /health.
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo logs
|
|
|
|
|
|
echo Show the last 50 lines by default. Add --follow to stream updates.
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo Defaults:
|
|
|
|
|
|
echo API_HOST = %API_HOST%
|
|
|
|
|
|
echo API_PORT = %API_PORT%
|
|
|
|
|
|
echo FRONTEND_PORT = %FRONTEND_PORT%
|
|
|
|
|
|
echo FRONTEND_MODE = %FRONTEND_MODE%
|
|
|
|
|
|
echo Log files = logs\api.log, logs\frontend.log
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo Examples:
|
|
|
|
|
|
echo dev.bat setup
|
|
|
|
|
|
echo dev.bat start
|
|
|
|
|
|
echo dev.bat start api --foreground
|
|
|
|
|
|
echo dev.bat start frontend --mode static
|
|
|
|
|
|
echo dev.bat restart frontend --mode dev
|
|
|
|
|
|
echo dev.bat status
|
|
|
|
|
|
echo dev.bat logs api --follow
|
|
|
|
|
|
echo.
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:setup
|
2026-05-18 13:29:57 +08:00
|
|
|
|
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
|
2026-05-14 18:09:15 +08:00
|
|
|
|
echo.
|
|
|
|
|
|
echo ========================================
|
|
|
|
|
|
echo AI+合规智能中枢 - 环境初始化
|
|
|
|
|
|
echo ========================================
|
|
|
|
|
|
echo.
|
|
|
|
|
|
call :resolve_python_bootstrap || exit /b 1
|
|
|
|
|
|
call %PYTHON_BOOTSTRAP% -c "import sys; raise SystemExit(0 if sys.version_info ^>= (3, 10) else 1)" || (
|
|
|
|
|
|
echo Python 3.10+ is required.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if not exist "%VENV_PYTHON%" (
|
|
|
|
|
|
call %PYTHON_BOOTSTRAP% -m venv "%VENV_DIR%" || exit /b 1
|
|
|
|
|
|
echo Created virtual environment: %VENV_DIR%
|
|
|
|
|
|
) else (
|
|
|
|
|
|
echo Virtual environment already exists: %VENV_DIR%
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
"%VENV_PYTHON%" -m pip install --upgrade pip || exit /b 1
|
|
|
|
|
|
"%VENV_PYTHON%" -m pip install -r backend\requirements.txt || exit /b 1
|
|
|
|
|
|
echo Backend dependencies installed.
|
|
|
|
|
|
|
|
|
|
|
|
if not exist "frontend\package.json" (
|
|
|
|
|
|
echo Frontend package.json not found.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
where npm >nul 2>nul || (
|
|
|
|
|
|
echo npm was not found. Install Node.js 20+ first.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
pushd frontend >nul
|
|
|
|
|
|
call npm install || (
|
|
|
|
|
|
popd >nul
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
popd >nul
|
|
|
|
|
|
echo Frontend dependencies installed.
|
|
|
|
|
|
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo Docker base services:
|
|
|
|
|
|
for %%C in (milvus minio redis postgres) do call :print_docker_status %%C
|
|
|
|
|
|
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo Setup complete.
|
|
|
|
|
|
echo dev.bat start
|
|
|
|
|
|
echo dev.bat status
|
|
|
|
|
|
echo dev.bat logs api --follow
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:start
|
|
|
|
|
|
set "TARGET=all"
|
|
|
|
|
|
set "MODE=%FRONTEND_MODE%"
|
|
|
|
|
|
set "FOREGROUND=0"
|
|
|
|
|
|
if /I "%~2"=="all" set "TARGET=all"
|
|
|
|
|
|
if /I "%~2"=="api" set "TARGET=api"
|
|
|
|
|
|
if /I "%~2"=="frontend" set "TARGET=frontend"
|
|
|
|
|
|
call :parse_start_options %* || exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
if /I "%TARGET%"=="all" (
|
|
|
|
|
|
if "%FOREGROUND%"=="1" (
|
|
|
|
|
|
echo start all does not support --foreground.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
call :start_api_background || exit /b 1
|
|
|
|
|
|
call :start_frontend "%MODE%" || exit /b 1
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if /I "%TARGET%"=="api" (
|
|
|
|
|
|
if "%FOREGROUND%"=="1" (
|
|
|
|
|
|
call :start_api_foreground
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
)
|
|
|
|
|
|
call :start_api_background
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if /I "%TARGET%"=="frontend" (
|
|
|
|
|
|
call :start_frontend "%MODE%"
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
echo Invalid start target.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
:parse_start_options
|
|
|
|
|
|
set "MODE=%FRONTEND_MODE%"
|
|
|
|
|
|
set "FOREGROUND=0"
|
|
|
|
|
|
:parse_start_options_loop
|
|
|
|
|
|
if "%~1"=="" exit /b 0
|
|
|
|
|
|
if /I "%~1"=="start" (
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_start_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="all" (
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_start_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="api" (
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_start_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="frontend" (
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_start_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="--foreground" (
|
|
|
|
|
|
set "FOREGROUND=1"
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_start_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="--mode" (
|
|
|
|
|
|
if "%~2"=="" (
|
|
|
|
|
|
echo --mode requires dev or static.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
set "MODE=%~2"
|
|
|
|
|
|
call :validate_mode "%MODE%" || exit /b 1
|
|
|
|
|
|
shift
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_start_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
echo Unknown argument: %~1
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
:stop
|
|
|
|
|
|
set "TARGET=all"
|
|
|
|
|
|
if /I "%~2"=="api" set "TARGET=api"
|
|
|
|
|
|
if /I "%~2"=="frontend" set "TARGET=frontend"
|
|
|
|
|
|
|
|
|
|
|
|
if /I "%TARGET%"=="all" (
|
|
|
|
|
|
call :stop_frontend
|
|
|
|
|
|
call :stop_api
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%TARGET%"=="api" (
|
|
|
|
|
|
call :stop_api
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%TARGET%"=="frontend" (
|
|
|
|
|
|
call :stop_frontend
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
echo Invalid stop target.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
:restart
|
|
|
|
|
|
set "TARGET=all"
|
|
|
|
|
|
set "MODE=%FRONTEND_MODE%"
|
|
|
|
|
|
if /I "%~2"=="api" set "TARGET=api"
|
|
|
|
|
|
if /I "%~2"=="frontend" set "TARGET=frontend"
|
|
|
|
|
|
call :parse_restart_options %* || exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
if /I "%TARGET%"=="all" (
|
|
|
|
|
|
call :stop_frontend
|
|
|
|
|
|
call :stop_api
|
|
|
|
|
|
call :start_api_background || exit /b 1
|
|
|
|
|
|
call :start_frontend "%MODE%" || exit /b 1
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%TARGET%"=="api" (
|
|
|
|
|
|
call :stop_api
|
|
|
|
|
|
call :start_api_background
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%TARGET%"=="frontend" (
|
|
|
|
|
|
call :stop_frontend
|
|
|
|
|
|
call :start_frontend "%MODE%"
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
)
|
|
|
|
|
|
echo Invalid restart target.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
:parse_restart_options
|
|
|
|
|
|
set "MODE=%FRONTEND_MODE%"
|
|
|
|
|
|
:parse_restart_options_loop
|
|
|
|
|
|
if "%~1"=="" exit /b 0
|
|
|
|
|
|
if /I "%~1"=="restart" (
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_restart_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="all" (
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_restart_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="api" (
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_restart_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="frontend" (
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_restart_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~1"=="--mode" (
|
|
|
|
|
|
if "%~2"=="" (
|
|
|
|
|
|
echo --mode requires dev or static.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
set "MODE=%~2"
|
|
|
|
|
|
call :validate_mode "%MODE%" || exit /b 1
|
|
|
|
|
|
shift
|
|
|
|
|
|
shift
|
|
|
|
|
|
goto parse_restart_options_loop
|
|
|
|
|
|
)
|
|
|
|
|
|
echo Unknown argument: %~1
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
:status
|
2026-05-18 13:29:57 +08:00
|
|
|
|
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
|
2026-05-14 18:09:15 +08:00
|
|
|
|
echo.
|
|
|
|
|
|
echo ========================================
|
|
|
|
|
|
echo AI+合规智能中枢 - 服务状态
|
|
|
|
|
|
echo ========================================
|
|
|
|
|
|
echo.
|
|
|
|
|
|
|
|
|
|
|
|
echo API service:
|
|
|
|
|
|
set "API_PID="
|
|
|
|
|
|
set "API_RUNNING=0"
|
2026-05-18 13:29:57 +08:00
|
|
|
|
set "API_LISTENER="
|
|
|
|
|
|
set "API_DISPLAY_PID="
|
2026-05-14 18:09:15 +08:00
|
|
|
|
if exist "%API_PID_FILE%" set /p API_PID=<"%API_PID_FILE%"
|
|
|
|
|
|
if defined API_PID (
|
2026-05-18 13:29:57 +08:00
|
|
|
|
call :pid_exists !API_PID!
|
|
|
|
|
|
if errorlevel 1 (
|
2026-05-14 18:09:15 +08:00
|
|
|
|
del /q "%API_PID_FILE%" >nul 2>nul
|
|
|
|
|
|
set "API_PID="
|
2026-05-18 13:29:57 +08:00
|
|
|
|
) else (
|
|
|
|
|
|
set "API_RUNNING=1"
|
|
|
|
|
|
set "API_DISPLAY_PID=!API_PID!"
|
2026-05-14 18:09:15 +08:00
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-18 13:29:57 +08:00
|
|
|
|
if "%API_RUNNING%"=="1" goto api_running
|
2026-05-14 18:09:15 +08:00
|
|
|
|
call :get_listener_pid %API_PORT% API_LISTENER
|
2026-05-18 13:29:57 +08:00
|
|
|
|
if defined API_LISTENER goto api_listener
|
|
|
|
|
|
echo Status: stopped
|
|
|
|
|
|
goto api_done
|
|
|
|
|
|
|
|
|
|
|
|
:api_running
|
|
|
|
|
|
echo Status: running
|
|
|
|
|
|
if defined API_DISPLAY_PID echo PID: !API_DISPLAY_PID!
|
|
|
|
|
|
call :check_api_health
|
|
|
|
|
|
if errorlevel 1 (
|
|
|
|
|
|
echo Health: failed
|
2026-05-14 18:09:15 +08:00
|
|
|
|
) else (
|
2026-05-18 13:29:57 +08:00
|
|
|
|
echo Health: ok
|
2026-05-14 18:09:15 +08:00
|
|
|
|
)
|
2026-05-18 13:29:57 +08:00
|
|
|
|
goto api_done
|
2026-05-14 18:09:15 +08:00
|
|
|
|
|
2026-05-18 13:29:57 +08:00
|
|
|
|
:api_listener
|
|
|
|
|
|
echo Status: running (no PID file)
|
|
|
|
|
|
echo PID: !API_LISTENER!
|
|
|
|
|
|
call :check_api_health
|
|
|
|
|
|
if errorlevel 1 (
|
|
|
|
|
|
echo Health: failed
|
|
|
|
|
|
) else (
|
|
|
|
|
|
echo Health: ok
|
2026-05-14 18:09:15 +08:00
|
|
|
|
)
|
2026-05-18 13:29:57 +08:00
|
|
|
|
|
2026-05-14 18:09:15 +08:00
|
|
|
|
:api_done
|
|
|
|
|
|
echo URL: http://localhost:%API_PORT%
|
|
|
|
|
|
echo Docs: http://localhost:%API_PORT%/docs
|
|
|
|
|
|
echo.
|
|
|
|
|
|
|
|
|
|
|
|
echo Frontend service:
|
|
|
|
|
|
set "FRONTEND_PID="
|
2026-05-18 13:29:57 +08:00
|
|
|
|
set "FRONTEND_RUNNING=0"
|
|
|
|
|
|
set "FRONTEND_LISTENER="
|
|
|
|
|
|
set "FRONTEND_DISPLAY_PID="
|
2026-05-14 18:09:15 +08:00
|
|
|
|
if exist "%FRONTEND_PID_FILE%" set /p FRONTEND_PID=<"%FRONTEND_PID_FILE%"
|
|
|
|
|
|
if defined FRONTEND_PID (
|
2026-05-18 13:29:57 +08:00
|
|
|
|
call :pid_exists !FRONTEND_PID!
|
|
|
|
|
|
if errorlevel 1 (
|
2026-05-14 18:09:15 +08:00
|
|
|
|
del /q "%FRONTEND_PID_FILE%" >nul 2>nul
|
|
|
|
|
|
set "FRONTEND_PID="
|
2026-05-18 13:29:57 +08:00
|
|
|
|
) else (
|
|
|
|
|
|
set "FRONTEND_RUNNING=1"
|
|
|
|
|
|
set "FRONTEND_DISPLAY_PID=!FRONTEND_PID!"
|
2026-05-14 18:09:15 +08:00
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-18 13:29:57 +08:00
|
|
|
|
if "%FRONTEND_RUNNING%"=="1" goto frontend_running
|
2026-05-14 18:09:15 +08:00
|
|
|
|
call :get_listener_pid %FRONTEND_PORT% FRONTEND_LISTENER
|
2026-05-18 13:29:57 +08:00
|
|
|
|
if defined FRONTEND_LISTENER goto frontend_listener
|
|
|
|
|
|
echo Status: stopped
|
|
|
|
|
|
goto frontend_done
|
|
|
|
|
|
|
|
|
|
|
|
:frontend_running
|
|
|
|
|
|
echo Status: running
|
|
|
|
|
|
if defined FRONTEND_DISPLAY_PID echo PID: !FRONTEND_DISPLAY_PID!
|
|
|
|
|
|
goto frontend_done
|
|
|
|
|
|
|
|
|
|
|
|
:frontend_listener
|
|
|
|
|
|
echo Status: running (no PID file)
|
|
|
|
|
|
echo PID: !FRONTEND_LISTENER!
|
2026-05-14 18:09:15 +08:00
|
|
|
|
|
|
|
|
|
|
:frontend_done
|
|
|
|
|
|
echo Mode: %FRONTEND_MODE%
|
|
|
|
|
|
echo URL: http://localhost:%FRONTEND_PORT%
|
|
|
|
|
|
echo.
|
|
|
|
|
|
|
|
|
|
|
|
echo Docker services:
|
|
|
|
|
|
where docker >nul 2>nul || (
|
|
|
|
|
|
echo Docker not installed.
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
for %%C in (milvus minio redis postgres) do call :print_docker_status %%C
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:logs
|
|
|
|
|
|
if "%~2"=="" (
|
|
|
|
|
|
echo Specify api or frontend.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if /I "%~2"=="api" (
|
|
|
|
|
|
set "LOG_FILE=%API_LOG_FILE%"
|
|
|
|
|
|
)
|
|
|
|
|
|
if /I "%~2"=="frontend" (
|
|
|
|
|
|
set "LOG_FILE=%FRONTEND_LOG_FILE%"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if not defined LOG_FILE (
|
|
|
|
|
|
echo Unknown log target: %~2
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
if not exist "%LOG_FILE%" (
|
|
|
|
|
|
echo Log file not found: %LOG_FILE%
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if /I "%~3"=="--follow" (
|
|
|
|
|
|
pwsh -NoProfile -Command "Get-Content -LiteralPath '%LOG_FILE%' -Wait"
|
|
|
|
|
|
) else (
|
|
|
|
|
|
pwsh -NoProfile -Command "Get-Content -LiteralPath '%LOG_FILE%' -Tail 50"
|
|
|
|
|
|
)
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
|
|
|
|
|
|
:start_api_background
|
2026-05-18 13:29:57 +08:00
|
|
|
|
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
|
2026-05-14 18:09:15 +08:00
|
|
|
|
if not exist "%VENV_PYTHON%" (
|
|
|
|
|
|
echo Virtual environment not found. Run dev.bat setup first.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
set "EXISTING_PID="
|
|
|
|
|
|
if exist "%API_PID_FILE%" set /p EXISTING_PID=<"%API_PID_FILE%"
|
|
|
|
|
|
if defined EXISTING_PID (
|
|
|
|
|
|
call :pid_exists %EXISTING_PID%
|
|
|
|
|
|
if not errorlevel 1 (
|
|
|
|
|
|
echo API is already running. PID: %EXISTING_PID%
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
) else del /q "%API_PID_FILE%" >nul 2>nul
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
for /f %%P in ('pwsh -NoProfile -Command "$p = Start-Process -FilePath 'pwsh' -ArgumentList '-NoProfile','-Command',('Set-Location ''%CD%''; $env:PYTHONPATH=''backend''; & ''.\%VENV_PYTHON%'' -m uvicorn app.main:app --host ''%API_HOST%'' --port ''%API_PORT%'' *^> ''%API_LOG_FILE%''') -WindowStyle Hidden -PassThru; $p.Id"') do set "API_PID=%%P"
|
|
|
|
|
|
|
|
|
|
|
|
if not defined API_PID (
|
|
|
|
|
|
echo Failed to start API.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
> "%API_PID_FILE%" echo %API_PID%
|
|
|
|
|
|
timeout /t 3 /nobreak >nul
|
|
|
|
|
|
call :pid_exists %API_PID%
|
|
|
|
|
|
if errorlevel 1 (
|
|
|
|
|
|
del /q "%API_PID_FILE%" >nul 2>nul
|
|
|
|
|
|
echo API failed to start. Check logs\api.log
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
echo API started. PID: %API_PID%
|
|
|
|
|
|
echo URL: http://localhost:%API_PORT%
|
|
|
|
|
|
echo Docs: http://localhost:%API_PORT%/docs
|
|
|
|
|
|
echo Log: logs\api.log
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:start_api_foreground
|
|
|
|
|
|
if not exist "%VENV_PYTHON%" (
|
|
|
|
|
|
echo Virtual environment not found. Run dev.bat setup first.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
set "PYTHONPATH=backend;%PYTHONPATH%"
|
|
|
|
|
|
echo API running in foreground with reload enabled.
|
|
|
|
|
|
echo URL: http://localhost:%API_PORT%
|
|
|
|
|
|
echo Docs: http://localhost:%API_PORT%/docs
|
|
|
|
|
|
"%VENV_PYTHON%" -m uvicorn app.main:app --host "%API_HOST%" --port "%API_PORT%" --reload
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
|
|
|
|
|
|
:start_frontend
|
|
|
|
|
|
set "MODE=%~1"
|
|
|
|
|
|
if "%MODE%"=="" set "MODE=%FRONTEND_MODE%"
|
2026-05-18 13:29:57 +08:00
|
|
|
|
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
|
2026-05-14 18:09:15 +08:00
|
|
|
|
|
|
|
|
|
|
where npm >nul 2>nul || (
|
|
|
|
|
|
echo npm was not found. Install Node.js 20+ first.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if not exist "frontend\node_modules\vite" (
|
|
|
|
|
|
echo Frontend dependencies are missing. Running npm install...
|
|
|
|
|
|
pushd frontend >nul
|
|
|
|
|
|
call npm install || (
|
|
|
|
|
|
popd >nul
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
popd >nul
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
set "EXISTING_PID="
|
|
|
|
|
|
if exist "%FRONTEND_PID_FILE%" set /p EXISTING_PID=<"%FRONTEND_PID_FILE%"
|
|
|
|
|
|
if defined EXISTING_PID (
|
|
|
|
|
|
call :pid_exists %EXISTING_PID%
|
|
|
|
|
|
if not errorlevel 1 (
|
|
|
|
|
|
echo Frontend is already running. PID: %EXISTING_PID%
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
) else del /q "%FRONTEND_PID_FILE%" >nul 2>nul
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if /I "%MODE%"=="static" (
|
|
|
|
|
|
pushd frontend >nul
|
|
|
|
|
|
call npm run build || (
|
|
|
|
|
|
popd >nul
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
popd >nul
|
|
|
|
|
|
call :resolve_python_bootstrap || exit /b 1
|
|
|
|
|
|
for /f %%P in ('pwsh -NoProfile -Command "$p = Start-Process -FilePath 'pwsh' -ArgumentList '-NoProfile','-Command',('Set-Location ''%CD%''; & %PYTHON_BOOTSTRAP% -m http.server %FRONTEND_PORT% --bind 0.0.0.0 --directory frontend/dist *^> ''%FRONTEND_LOG_FILE%''') -WindowStyle Hidden -PassThru; $p.Id"') do set "FRONTEND_PID=%%P"
|
|
|
|
|
|
) else (
|
|
|
|
|
|
for /f %%P in ('pwsh -NoProfile -Command "$p = Start-Process -FilePath 'pwsh' -ArgumentList '-NoProfile','-Command',('Set-Location ''%CD%''; & npm --prefix frontend run dev -- --host 0.0.0.0 --port %FRONTEND_PORT% *^> ''%FRONTEND_LOG_FILE%''') -WindowStyle Hidden -PassThru; $p.Id"') do set "FRONTEND_PID=%%P"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if not defined FRONTEND_PID (
|
|
|
|
|
|
echo Failed to start frontend.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
> "%FRONTEND_PID_FILE%" echo %FRONTEND_PID%
|
|
|
|
|
|
timeout /t 4 /nobreak >nul
|
|
|
|
|
|
call :pid_exists %FRONTEND_PID%
|
|
|
|
|
|
if errorlevel 1 (
|
|
|
|
|
|
del /q "%FRONTEND_PID_FILE%" >nul 2>nul
|
|
|
|
|
|
echo Frontend failed to start. Check logs\frontend.log
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
echo Frontend started. PID: %FRONTEND_PID%
|
|
|
|
|
|
echo URL: http://localhost:%FRONTEND_PORT%
|
|
|
|
|
|
echo Mode: %MODE%
|
|
|
|
|
|
echo Log: logs\frontend.log
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:stop_api
|
|
|
|
|
|
set "API_PID="
|
|
|
|
|
|
if exist "%API_PID_FILE%" set /p API_PID=<"%API_PID_FILE%"
|
|
|
|
|
|
if defined API_PID (
|
|
|
|
|
|
call :stop_pid %API_PID%
|
|
|
|
|
|
del /q "%API_PID_FILE%" >nul 2>nul
|
|
|
|
|
|
echo API stopped.
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
call :get_listener_pid %API_PORT% API_PORT_PID
|
|
|
|
|
|
if defined API_PORT_PID (
|
|
|
|
|
|
call :stop_pid %API_PORT_PID%
|
|
|
|
|
|
echo Stopped process listening on API port %API_PORT%.
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
echo API is not running.
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:stop_frontend
|
|
|
|
|
|
set "FRONTEND_PID="
|
|
|
|
|
|
if exist "%FRONTEND_PID_FILE%" set /p FRONTEND_PID=<"%FRONTEND_PID_FILE%"
|
|
|
|
|
|
if defined FRONTEND_PID (
|
|
|
|
|
|
call :stop_pid %FRONTEND_PID%
|
|
|
|
|
|
del /q "%FRONTEND_PID_FILE%" >nul 2>nul
|
|
|
|
|
|
echo Frontend stopped.
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
call :get_listener_pid %FRONTEND_PORT% FRONTEND_PORT_PID
|
|
|
|
|
|
if defined FRONTEND_PORT_PID (
|
|
|
|
|
|
call :stop_pid %FRONTEND_PORT_PID%
|
|
|
|
|
|
echo Stopped process listening on frontend port %FRONTEND_PORT%.
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
echo Frontend is not running.
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:validate_mode
|
|
|
|
|
|
if /I "%~1"=="dev" exit /b 0
|
|
|
|
|
|
if /I "%~1"=="static" exit /b 0
|
|
|
|
|
|
echo Invalid frontend mode: %~1
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
:resolve_python_bootstrap
|
|
|
|
|
|
where python >nul 2>nul && (
|
|
|
|
|
|
set "PYTHON_BOOTSTRAP=python"
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
where py >nul 2>nul && (
|
|
|
|
|
|
set "PYTHON_BOOTSTRAP=py -3"
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
echo Python was not found.
|
|
|
|
|
|
exit /b 1
|
|
|
|
|
|
|
|
|
|
|
|
:load_env
|
|
|
|
|
|
for %%K in (API_HOST API_PORT FRONTEND_PORT FRONTEND_MODE) do call :read_env %%K
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:read_env
|
|
|
|
|
|
if not exist ".env" exit /b 0
|
|
|
|
|
|
for /f "usebackq tokens=1,* delims==" %%A in (`findstr /r /b /c:"%~1=" ".env" 2^>nul`) do set "%~1=%%B"
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:pid_exists
|
|
|
|
|
|
pwsh -NoProfile -Command "exit([int](-not [bool](Get-Process -Id %~1 -ErrorAction SilentlyContinue)))"
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
|
|
|
|
|
|
:stop_pid
|
|
|
|
|
|
pwsh -NoProfile -Command "$p = Get-Process -Id %~1 -ErrorAction SilentlyContinue; if ($p) { Stop-Process -Id %~1 -Force }"
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:get_listener_pid
|
|
|
|
|
|
set "%~2="
|
|
|
|
|
|
for /f %%P in ('pwsh -NoProfile -Command "(Get-NetTCPConnection -LocalPort %~1 -State Listen -ErrorAction SilentlyContinue ^| Select-Object -ExpandProperty OwningProcess -Unique ^| Select-Object -First 1)"') do set "%~2=%%P"
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
|
|
|
|
|
|
:check_api_health
|
|
|
|
|
|
pwsh -NoProfile -Command "try { $r = Invoke-WebRequest -Uri 'http://localhost:%API_PORT%/health' -TimeoutSec 3 -UseBasicParsing; exit([int](-not ($r.Content -match 'healthy'))) } catch { exit 1 }"
|
|
|
|
|
|
exit /b %errorlevel%
|
|
|
|
|
|
|
|
|
|
|
|
:print_docker_status
|
|
|
|
|
|
docker ps --format "{{.Names}}" | findstr /x /c:"%~1" >nul && (
|
|
|
|
|
|
echo %~1: running
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
docker ps -a --format "{{.Names}}" | findstr /x /c:"%~1" >nul && (
|
|
|
|
|
|
echo %~1: stopped
|
|
|
|
|
|
exit /b 0
|
|
|
|
|
|
)
|
|
|
|
|
|
echo %~1: not created
|
|
|
|
|
|
exit /b 0
|