@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 ^ [--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 if not exist "%LOG_DIR%" mkdir "%LOG_DIR%" 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 if not exist "%LOG_DIR%" mkdir "%LOG_DIR%" echo. echo ======================================== echo AI+合规智能中枢 - 服务状态 echo ======================================== echo. echo API service: set "API_PID=" set "API_RUNNING=0" set "API_LISTENER=" set "API_DISPLAY_PID=" if exist "%API_PID_FILE%" set /p API_PID=<"%API_PID_FILE%" if defined API_PID ( call :pid_exists !API_PID! if errorlevel 1 ( del /q "%API_PID_FILE%" >nul 2>nul set "API_PID=" ) else ( set "API_RUNNING=1" set "API_DISPLAY_PID=!API_PID!" ) ) if "%API_RUNNING%"=="1" goto api_running call :get_listener_pid %API_PORT% API_LISTENER 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 ) else ( echo Health: ok ) goto api_done :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 ) :api_done echo URL: http://localhost:%API_PORT% echo Docs: http://localhost:%API_PORT%/docs echo. echo Frontend service: set "FRONTEND_PID=" set "FRONTEND_RUNNING=0" set "FRONTEND_LISTENER=" set "FRONTEND_DISPLAY_PID=" if exist "%FRONTEND_PID_FILE%" set /p FRONTEND_PID=<"%FRONTEND_PID_FILE%" if defined FRONTEND_PID ( call :pid_exists !FRONTEND_PID! if errorlevel 1 ( del /q "%FRONTEND_PID_FILE%" >nul 2>nul set "FRONTEND_PID=" ) else ( set "FRONTEND_RUNNING=1" set "FRONTEND_DISPLAY_PID=!FRONTEND_PID!" ) ) if "%FRONTEND_RUNNING%"=="1" goto frontend_running call :get_listener_pid %FRONTEND_PORT% FRONTEND_LISTENER 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! :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 if not exist "%LOG_DIR%" mkdir "%LOG_DIR%" 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%" if not exist "%LOG_DIR%" mkdir "%LOG_DIR%" 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 .env for %%K in (API_HOST API_PORT FRONTEND_PORT FRONTEND_MODE) do call :read_env %%K .env.development exit /b 0 :read_env if "%~2"=="" exit /b 0 if not exist "%~2" exit /b 0 for /f "usebackq tokens=1,* delims==" %%A in (`findstr /r /b /c:"%~1=" "%~2" 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