mirror of
https://github.com/rnvm9wjdtj-bot/myaps_api.git
synced 2026-06-02 05:54:40 +00:00
升级为win服务
This commit is contained in:
@@ -1,305 +0,0 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
FastAPI Server Startup Script (PowerShell Version)
|
||||
.DESCRIPTION
|
||||
Start FastAPI server with environment variable configuration, port checking and auto cleanup
|
||||
.PARAMETER Port
|
||||
Server port number (default: 8000, can be overridden from .env file or command line parameter)
|
||||
.PARAMETER Host
|
||||
Server host address (default: 0.0.0.0)
|
||||
.EXAMPLE
|
||||
.\run.ps1
|
||||
.\run.ps1 -Port 8001
|
||||
.\run.ps1 -Port 8001 -Host 127.0.0.1
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Position=0)]
|
||||
[int]$Port = 8000,
|
||||
|
||||
[Parameter()]
|
||||
[string]$HostAddress = "0.0.0.0"
|
||||
)
|
||||
|
||||
# Set output encoding to UTF-8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# Configuration variables
|
||||
$App = "main:app"
|
||||
$LogFile = "logs\fastapi_server.log"
|
||||
$EnvFile = ".env"
|
||||
|
||||
# Function: Write log
|
||||
function Write-Log {
|
||||
param([string]$Message)
|
||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$logEntry = "$timestamp - $Message"
|
||||
Add-Content -Path $LogFile -Value $logEntry -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# Function: Load .env file
|
||||
function Import-DotEnv {
|
||||
param([string]$Path)
|
||||
|
||||
if (-not (Test-Path $Path)) {
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Loading environment variables from .env file..."
|
||||
|
||||
Get-Content $Path | ForEach-Object {
|
||||
$line = $_.Trim()
|
||||
|
||||
# Skip empty lines and comments
|
||||
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) {
|
||||
return
|
||||
}
|
||||
|
||||
# Parse KEY=VALUE
|
||||
if ($line -match "^([^=]+)=(.*)$") {
|
||||
$key = $matches[1].Trim()
|
||||
$value = $matches[2].Trim()
|
||||
|
||||
# Remove possible quotes
|
||||
$value = $value -replace "^[`"']|[`"']$"
|
||||
|
||||
# Set environment variable
|
||||
[Environment]::SetEnvironmentVariable($key, $value, "Process")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Check if port is in use
|
||||
function Test-PortInUse {
|
||||
param([int]$PortNumber)
|
||||
|
||||
$connections = Get-NetTCPConnection -LocalPort $PortNumber -ErrorAction SilentlyContinue
|
||||
return $connections.Count -gt 0
|
||||
}
|
||||
|
||||
# Function: Get process using port
|
||||
function Get-PortProcess {
|
||||
param([int]$PortNumber)
|
||||
|
||||
$connection = Get-NetTCPConnection -LocalPort $PortNumber -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($connection) {
|
||||
return Get-Process -Id $connection.OwningProcess -ErrorAction SilentlyContinue
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# Function: Clear port usage
|
||||
function Clear-Port {
|
||||
param([int]$PortNumber)
|
||||
|
||||
Write-Host "Checking if port $PortNumber is available..."
|
||||
|
||||
$process = Get-PortProcess -PortNumber $PortNumber
|
||||
if ($process) {
|
||||
# Skip Idle process (PID 0) as it's a system process and cannot be killed
|
||||
if ($process.Id -eq 0) {
|
||||
Write-Host "[INFO] Found Idle process (PID: 0), skipping termination as it's a system process" -ForegroundColor Yellow
|
||||
Write-Log "Found Idle process (PID: 0), skipping termination"
|
||||
} else {
|
||||
Write-Host "[WARNING] Found process $($process.ProcessName) (PID: $($process.Id)) using port $PortNumber" -ForegroundColor Yellow
|
||||
Write-Log "Found process $($process.Id) using port $PortNumber"
|
||||
|
||||
Write-Host "Killing process $($process.Id)..."
|
||||
try {
|
||||
Stop-Process -Id $process.Id -Force -ErrorAction Stop
|
||||
Write-Log "Killed process $($process.Id)"
|
||||
Write-Host "[INFO] Process killed successfully" -ForegroundColor Green
|
||||
}
|
||||
catch {
|
||||
Write-Host "[ERROR] Failed to kill process: $_" -ForegroundColor Red
|
||||
Write-Log "Failed to kill process $($process.Id): $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Wait for port release
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
# Verify port is available
|
||||
if (Test-PortInUse -PortNumber $PortNumber) {
|
||||
# Check if it's still the Idle process
|
||||
$process = Get-PortProcess -PortNumber $PortNumber
|
||||
if ($process -and $process.Id -eq 0) {
|
||||
# Idle process doesn't actually use the port, so consider it available
|
||||
Write-Host "[INFO] Port $PortNumber is available for use (Idle process detected)" -ForegroundColor Green
|
||||
Write-Log "Port $PortNumber is available (Idle process detected)"
|
||||
return $true
|
||||
} else {
|
||||
Write-Host "[ERROR] Port $PortNumber is still in use!" -ForegroundColor Red
|
||||
Write-Log "Port $PortNumber is still in use!"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Host "[INFO] Port $PortNumber is available for use" -ForegroundColor Green
|
||||
Write-Log "Port $PortNumber is available"
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Find Python interpreter
|
||||
function Find-Python {
|
||||
$venvPython = "venv\Scripts\python.exe"
|
||||
|
||||
if (Test-Path $venvPython) {
|
||||
Write-Host "Using virtual Python"
|
||||
return (Resolve-Path $venvPython).Path
|
||||
}
|
||||
else {
|
||||
Write-Host "Using system Python"
|
||||
$pythonCmd = Get-Command python -ErrorAction SilentlyContinue
|
||||
if ($pythonCmd) {
|
||||
return $pythonCmd.Source
|
||||
}
|
||||
throw "Python not found!"
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== Main Program ====================
|
||||
|
||||
try {
|
||||
# Create log directory
|
||||
if (-not (Test-Path "logs")) {
|
||||
New-Item -ItemType Directory -Path "logs" -Force | Out-Null
|
||||
}
|
||||
Write-Host "[INFO] Logs directory ensured"
|
||||
|
||||
# Load configuration from .env file (command line parameter has highest priority)
|
||||
Import-DotEnv -Path $EnvFile
|
||||
|
||||
# Read default values from environment variables (if not specified in command line)
|
||||
if ($PSBoundParameters.ContainsKey("Port") -eq $false -and $env:PORT) {
|
||||
$Port = [int]$env:PORT
|
||||
}
|
||||
if ($PSBoundParameters.ContainsKey("Host") -eq $false -and $env:HOST) {
|
||||
$HostAddress = $env:HOST
|
||||
}
|
||||
|
||||
# Set environment variables
|
||||
[Environment]::SetEnvironmentVariable("PORT", $Port, "Process")
|
||||
[Environment]::SetEnvironmentVariable("HOST", $HostAddress, "Process")
|
||||
[Environment]::SetEnvironmentVariable("START_MODE", "script", "Process")
|
||||
[Environment]::SetEnvironmentVariable("PYTHONPATH", (Get-Location).Path, "Process")
|
||||
# Set unbuffered output for better log display
|
||||
[Environment]::SetEnvironmentVariable("PYTHONUNBUFFERED", "1", "Process")
|
||||
|
||||
# Display configuration information
|
||||
Write-Host ""
|
||||
Write-Host "FastAPI Server Starting..." -ForegroundColor Cyan
|
||||
Write-Host "Port: $Port, Host: $HostAddress"
|
||||
Write-Host "Press Ctrl+C to stop gracefully"
|
||||
Write-Host ""
|
||||
|
||||
# Record startup log
|
||||
Write-Log "Server starting on port $Port"
|
||||
|
||||
# Clear port usage
|
||||
if (-not (Clear-Port -PortNumber $Port)) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Find Python
|
||||
$Python = Find-Python
|
||||
Write-Host "Python: $Python"
|
||||
|
||||
# Start server
|
||||
Write-Host ""
|
||||
Write-Host "Starting uvicorn server with host $HostAddress and port $Port..." -ForegroundColor Cyan
|
||||
Write-Log "Starting uvicorn server with host $HostAddress and port $Port"
|
||||
|
||||
# Build uvicorn arguments
|
||||
$uvicornArgs = @(
|
||||
"-m", "uvicorn",
|
||||
$App,
|
||||
"--host", $HostAddress,
|
||||
"--port", "$Port",
|
||||
"--log-level", "info",
|
||||
"--access-log"
|
||||
)
|
||||
|
||||
# Start process with graceful shutdown support
|
||||
$serverProcess = Start-Process -FilePath $Python -ArgumentList $uvicornArgs -NoNewWindow -PassThru
|
||||
|
||||
# Setup graceful shutdown handler
|
||||
$shutdownRequested = $false
|
||||
|
||||
$null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
|
||||
$script:shutdownRequested = $true
|
||||
}
|
||||
|
||||
try {
|
||||
# Wait for process to exit
|
||||
Wait-Process -Id $serverProcess.Id -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
if ($shutdownRequested -or $Error[0].Exception.Message -match "was not found") {
|
||||
Write-Host ""
|
||||
Write-Host "Shutting down server gracefully..." -ForegroundColor Yellow
|
||||
Write-Log "Shutting down server gracefully"
|
||||
|
||||
# Try graceful shutdown first
|
||||
$serverProcess.CloseMainWindow() | Out-Null
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
# Force kill if still running
|
||||
if (!$serverProcess.HasExited) {
|
||||
Write-Host "Force killing server process..." -ForegroundColor Yellow
|
||||
$serverProcess | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Host ""
|
||||
Write-Host "[ERROR] Server process error: $_" -ForegroundColor Red
|
||||
Write-Log "Server process error: $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Get actual exit code
|
||||
$exitCode = $serverProcess.ExitCode
|
||||
|
||||
# Check exit code
|
||||
if ($exitCode -ne 0) {
|
||||
Write-Host ""
|
||||
Write-Host "[ERROR] Application failed to start with error code $exitCode" -ForegroundColor Red
|
||||
Write-Log "Application failed to start with error code $exitCode"
|
||||
exit $exitCode
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host ""
|
||||
Write-Host "[ERROR] An error occurred: $_" -ForegroundColor Red
|
||||
Write-Log "Error: $_"
|
||||
exit 1
|
||||
}
|
||||
finally {
|
||||
# Cleanup work
|
||||
Write-Host ""
|
||||
Write-Host "Server shutting down..." -ForegroundColor Yellow
|
||||
Write-Log "Server shutting down"
|
||||
|
||||
# Clear port usage
|
||||
Write-Host "Cleaning up any remaining processes on port $Port..."
|
||||
$process = Get-PortProcess -PortNumber $Port
|
||||
if ($process) {
|
||||
try {
|
||||
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
|
||||
Write-Host "Killed process $($process.Id)"
|
||||
Write-Log "Killed process $($process.Id) during cleanup"
|
||||
}
|
||||
catch {
|
||||
# Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Shutdown complete." -ForegroundColor Green
|
||||
Write-Log "Server shutdown complete"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
@echo off
|
||||
title MyAPS API SERVER
|
||||
:: 配置变量
|
||||
set "PS_SCRIPT=%~dp0run.ps1"
|
||||
set "ENV_FILE=%~dp0.env"
|
||||
set "VENV_PYTHON=%~dp0..\venv\Scripts\python.exe"
|
||||
set "PROJECT_ROOT=%~dp0.."
|
||||
set "ENV_FILE=%PROJECT_ROOT%\.env"
|
||||
set "RESTART_DELAY=5"
|
||||
set "MAX_RESTARTS=5"
|
||||
set "RESTART_COUNT=0"
|
||||
set "HOST=0.0.0.0"
|
||||
set "PORT=8001"
|
||||
|
||||
:: 从 .env 文件读取 HOST 和 PORT 配置
|
||||
if exist "%ENV_FILE%" (
|
||||
@@ -16,33 +19,32 @@ if exist "%ENV_FILE%" (
|
||||
:: 显示启动信息
|
||||
echo =========================================
|
||||
echo FastAPI Server Monitor
|
||||
echo Monitoring: %PS_SCRIPT%
|
||||
echo Project Root: %PROJECT_ROOT%
|
||||
echo Python: %VENV_PYTHON%
|
||||
echo Environment: %ENV_FILE%
|
||||
echo Host: %HOST%
|
||||
echo Port: %PORT%
|
||||
echo Press Ctrl+C twice to stop monitoring
|
||||
echo Press Ctrl+C to stop
|
||||
echo =========================================
|
||||
echo.
|
||||
|
||||
:: 进入项目根目录
|
||||
cd /d "%PROJECT_ROOT%"
|
||||
|
||||
:: 无限循环监测
|
||||
:LOOP
|
||||
echo [%date% %time%] Starting run.ps1...
|
||||
echo [%date% %time%] Starting FastAPI server...
|
||||
|
||||
:: 构建命令参数
|
||||
set "PS_ARGS="
|
||||
if defined HOST set "PS_ARGS=%PS_ARGS% -HostAddress %HOST%"
|
||||
if defined PORT set "PS_ARGS=%PS_ARGS% -Port %PORT%"
|
||||
|
||||
:: 执行 PowerShell 脚本
|
||||
powershell -ExecutionPolicy Bypass -NoProfile -Command "& '%PS_SCRIPT%' %PS_ARGS% %*"
|
||||
:: 执行 Python 命令
|
||||
%VENV_PYTHON% -m uvicorn main:app --host %HOST% --port %PORT% --log-level info --access-log
|
||||
|
||||
:: 检查退出码(0=正常退出,非0=异常退出)
|
||||
if %errorlevel% equ 0 (
|
||||
echo [%date% %time%] run.ps1 exited normally.
|
||||
echo [%date% %time%] Server exited normally.
|
||||
goto END
|
||||
) else (
|
||||
set /a "RESTART_COUNT=%RESTART_COUNT%+1"
|
||||
echo [%date% %time%] run.ps1 exited with error code %errorlevel%.
|
||||
echo [%date% %time%] Server exited with error code %errorlevel%.
|
||||
echo [%date% %time%] Restart count: %RESTART_COUNT%/%MAX_RESTARTS%
|
||||
|
||||
:: 检查是否达到最大重启次数
|
||||
Binary file not shown.
+492
@@ -0,0 +1,492 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
FastAPI Server Startup Script
|
||||
.DESCRIPTION
|
||||
Start FastAPI server with support for both service mode and development mode
|
||||
.PARAMETER Port
|
||||
Server port number (default: 8000, can be overridden from .env file or command line parameter)
|
||||
.PARAMETER Host
|
||||
Server host address (default: 0.0.0.0)
|
||||
.PARAMETER Mode
|
||||
Run mode: "service" for Windows service, "dev" for development (default: "dev")
|
||||
.EXAMPLE
|
||||
.\run.ps1
|
||||
.\run.ps1 -Port 8001
|
||||
.\run.ps1 -Mode service
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Position=0)]
|
||||
[int]$Port = 8000,
|
||||
|
||||
[Parameter()]
|
||||
[string]$HostAddress = "0.0.0.0",
|
||||
|
||||
[Parameter()]
|
||||
[ValidateSet("service", "dev")]
|
||||
[string]$Mode = "dev"
|
||||
)
|
||||
|
||||
# Set output encoding to UTF-8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# Get project root directory
|
||||
$ProjectRoot = Split-Path -Parent $PSScriptRoot
|
||||
|
||||
# Set working directory to project root
|
||||
Set-Location -Path $ProjectRoot
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Working directory set to: $ProjectRoot"
|
||||
}
|
||||
|
||||
# Configuration variables
|
||||
$App = "main:app"
|
||||
$LogFile = "$ProjectRoot\logs\fastapi_server.log"
|
||||
$EnvFile = "$ProjectRoot\.env"
|
||||
|
||||
# Function: Write log
|
||||
function Write-Log {
|
||||
param([string]$Message)
|
||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$logEntry = "$timestamp - $Message"
|
||||
try {
|
||||
if (-not (Test-Path "$ProjectRoot\logs")) {
|
||||
New-Item -ItemType Directory -Path "$ProjectRoot\logs" -Force | Out-Null
|
||||
}
|
||||
Add-Content -Path $LogFile -Value $logEntry
|
||||
} catch {
|
||||
# Ignore logging errors
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Load .env file
|
||||
function Import-DotEnv {
|
||||
param([string]$Path)
|
||||
|
||||
if (-not (Test-Path $Path)) {
|
||||
Write-Log "WARNING: .env file not found at $Path"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "WARNING: .env file not found at $Path"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Write-Log "Loading environment variables from .env file..."
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Loading environment variables from .env file..."
|
||||
}
|
||||
|
||||
Get-Content $Path | ForEach-Object {
|
||||
$line = $_.Trim()
|
||||
|
||||
# Skip empty lines and comments
|
||||
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) {
|
||||
return
|
||||
}
|
||||
|
||||
# Parse KEY=VALUE
|
||||
if ($line -match "^([^=]+)=(.*)$") {
|
||||
$key = $matches[1].Trim()
|
||||
$value = $matches[2].Trim()
|
||||
|
||||
# Remove possible quotes
|
||||
$value = $value -replace "^[`"']|[`"']$"
|
||||
|
||||
# Set environment variable
|
||||
[Environment]::SetEnvironmentVariable($key, $value, "Process")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Load project JSON configuration
|
||||
function Import-ProjectConfig {
|
||||
param([string]$ProjectDir, [string]$ProjectJson)
|
||||
|
||||
$configPath = "$ProjectRoot\project_files\$ProjectDir\$ProjectJson.json"
|
||||
|
||||
if (-not (Test-Path $configPath)) {
|
||||
Write-Log "WARNING: Project config file not found at $configPath"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "WARNING: Project config file not found at $configPath"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Write-Log "Loading project configuration from $configPath"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Loading project configuration from $configPath"
|
||||
}
|
||||
|
||||
try {
|
||||
$config = Get-Content $configPath -Raw | ConvertFrom-Json
|
||||
|
||||
# Load environment variables from config.env section
|
||||
if ($config.env) {
|
||||
foreach ($key in $config.env.PSObject.Properties.Name) {
|
||||
$value = $config.env.$key
|
||||
if ($value -ne $null -and $value -ne "") {
|
||||
[Environment]::SetEnvironmentVariable($key, $value, "Process")
|
||||
Write-Log "Set environment variable: $key"
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-Log "ERROR: Failed to load project config: $_"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "ERROR: Failed to load project config: $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Check if port is in use (dev mode only)
|
||||
function Test-PortInUse {
|
||||
param([int]$PortNumber)
|
||||
|
||||
$connections = Get-NetTCPConnection -LocalPort $PortNumber -ErrorAction SilentlyContinue
|
||||
if ($connections -eq $null) {
|
||||
return $false
|
||||
}
|
||||
return $connections.Count -gt 0
|
||||
}
|
||||
|
||||
# Function: Get process using port (dev mode only)
|
||||
function Get-PortProcess {
|
||||
param([int]$PortNumber)
|
||||
|
||||
$connection = Get-NetTCPConnection -LocalPort $PortNumber -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($connection -ne $null) {
|
||||
try {
|
||||
return Get-Process -Id $connection.OwningProcess -ErrorAction SilentlyContinue
|
||||
} catch {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# Function: Clear port usage (dev mode only)
|
||||
function Clear-Port {
|
||||
param([int]$PortNumber)
|
||||
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Checking if port $PortNumber is available..."
|
||||
}
|
||||
|
||||
$process = Get-PortProcess -PortNumber $PortNumber
|
||||
if ($process) {
|
||||
# Skip Idle process (PID 0) as it's a system process and cannot be killed
|
||||
if ($process.Id -eq 0) {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[INFO] Found Idle process (PID: 0), skipping termination as it's a system process" -ForegroundColor Yellow
|
||||
}
|
||||
Write-Log "Found Idle process (PID: 0), skipping termination"
|
||||
} else {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[WARNING] Found process $($process.ProcessName) (PID: $($process.Id)) using port $PortNumber" -ForegroundColor Yellow
|
||||
}
|
||||
Write-Log "Found process $($process.Id) using port $PortNumber"
|
||||
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Killing process $($process.Id)..."
|
||||
}
|
||||
try {
|
||||
Stop-Process -Id $process.Id -Force -ErrorAction Stop
|
||||
Write-Log "Killed process $($process.Id)"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[INFO] Process killed successfully" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
catch {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[ERROR] Failed to kill process: $_" -ForegroundColor Red
|
||||
}
|
||||
Write-Log "Failed to kill process $($process.Id): $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Wait for port release
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
# Verify port is available
|
||||
if (Test-PortInUse -PortNumber $PortNumber) {
|
||||
# Check if it's still the Idle process
|
||||
$process = Get-PortProcess -PortNumber $PortNumber
|
||||
if ($process -and $process.Id -eq 0) {
|
||||
# Idle process doesn't actually use the port, so consider it available
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[INFO] Port $PortNumber is available for use (Idle process detected)" -ForegroundColor Green
|
||||
}
|
||||
Write-Log "Port $PortNumber is available (Idle process detected)"
|
||||
return $true
|
||||
} else {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[ERROR] Port $PortNumber is still in use!" -ForegroundColor Red
|
||||
}
|
||||
Write-Log "Port $PortNumber is still in use!"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[INFO] Port $PortNumber is available for use" -ForegroundColor Green
|
||||
}
|
||||
Write-Log "Port $PortNumber is available"
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Find Python interpreter
|
||||
function Find-Python {
|
||||
$venvPython = "$ProjectRoot\venv\Scripts\python.exe"
|
||||
|
||||
if (Test-Path $venvPython) {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[DEBUG] Virtual Python found: $venvPython"
|
||||
}
|
||||
return $venvPython
|
||||
}
|
||||
else {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[DEBUG] Virtual Python not found, using system Python"
|
||||
}
|
||||
$pythonCmd = Get-Command python -ErrorAction SilentlyContinue
|
||||
if ($pythonCmd) {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[DEBUG] System Python found: $($pythonCmd.Source)"
|
||||
}
|
||||
return $pythonCmd.Source
|
||||
}
|
||||
throw "Python not found!"
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== Main Program ====================
|
||||
|
||||
try {
|
||||
# Create log directory
|
||||
if (-not (Test-Path "$ProjectRoot\logs")) {
|
||||
New-Item -ItemType Directory -Path "$ProjectRoot\logs" -Force | Out-Null
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[INFO] Logs directory created"
|
||||
}
|
||||
}
|
||||
|
||||
# Load configuration from .env file (command line parameter has highest priority)
|
||||
Import-DotEnv -Path $EnvFile
|
||||
|
||||
# Load project configuration based on PROJECT_DIR and PROJECT_JSON
|
||||
$projectDir = $env:PROJECT_DIR
|
||||
$projectJson = $env:PROJECT_JSON
|
||||
|
||||
if ($projectDir -and $projectJson) {
|
||||
Write-Log "Loading project config: $projectDir/$projectJson"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Loading project config: $projectDir/$projectJson"
|
||||
}
|
||||
Import-ProjectConfig -ProjectDir $projectDir -ProjectJson $projectJson
|
||||
} else {
|
||||
Write-Log "WARNING: PROJECT_DIR or PROJECT_JSON not set in .env file"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "WARNING: PROJECT_DIR or PROJECT_JSON not set in .env file"
|
||||
}
|
||||
}
|
||||
|
||||
# Read default values from environment variables (if not specified in command line)
|
||||
if ($PSBoundParameters.ContainsKey("Port") -eq $false -and $env:PORT) {
|
||||
$Port = [int]$env:PORT
|
||||
}
|
||||
if ($PSBoundParameters.ContainsKey("Host") -eq $false -and $env:HOST) {
|
||||
$HostAddress = $env:HOST
|
||||
}
|
||||
|
||||
# Set environment variables
|
||||
[Environment]::SetEnvironmentVariable("PORT", $Port, "Process")
|
||||
[Environment]::SetEnvironmentVariable("HOST", $HostAddress, "Process")
|
||||
[Environment]::SetEnvironmentVariable("START_MODE", $Mode, "Process")
|
||||
[Environment]::SetEnvironmentVariable("PYTHONPATH", $ProjectRoot, "Process")
|
||||
[Environment]::SetEnvironmentVariable("PYTHONUNBUFFERED", "1", "Process")
|
||||
|
||||
# Check if static directory exists
|
||||
$staticDir = "$ProjectRoot\static"
|
||||
if (Test-Path $staticDir) {
|
||||
Write-Log "Static directory found: $staticDir"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Static directory found: $staticDir"
|
||||
}
|
||||
} else {
|
||||
Write-Log "WARNING: Static directory not found at $staticDir"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "WARNING: Static directory not found at $staticDir"
|
||||
}
|
||||
}
|
||||
|
||||
# Display configuration information (dev mode only)
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host ""
|
||||
Write-Host "FastAPI Server Starting..." -ForegroundColor Cyan
|
||||
Write-Host "Port: $Port, Host: $HostAddress"
|
||||
Write-Host "Project Root: $ProjectRoot" -ForegroundColor Yellow
|
||||
Write-Host "Mode: $Mode"
|
||||
Write-Host "Press Ctrl+C to stop gracefully"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# Record startup log
|
||||
Write-Log "Server starting on port $Port, host $HostAddress, mode $Mode"
|
||||
|
||||
# Check port availability (dev mode only)
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[INFO] Checking port availability..."
|
||||
Clear-Port -PortNumber $Port
|
||||
}
|
||||
|
||||
# Find Python
|
||||
try {
|
||||
$Python = Find-Python
|
||||
Write-Log "Found Python: $Python"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Python: $Python"
|
||||
}
|
||||
} catch {
|
||||
Write-Log "Failed to find Python: $_"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[ERROR] Failed to find Python: $_" -ForegroundColor Red
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Build uvicorn arguments
|
||||
$uvicornArgs = @(
|
||||
"-m", "uvicorn",
|
||||
$App,
|
||||
"--host", $HostAddress,
|
||||
"--port", "$Port",
|
||||
"--log-level", "info",
|
||||
"--access-log"
|
||||
)
|
||||
|
||||
Write-Log "Starting uvicorn server: $Python $($uvicornArgs -join ' '); Working directory: $ProjectRoot"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host ""
|
||||
Write-Host "Starting uvicorn server..." -ForegroundColor Cyan
|
||||
Write-Host "Uvicorn command: $Python $($uvicornArgs -join ' ')" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Start server
|
||||
if ($Mode -eq "service") {
|
||||
# Service mode: run directly
|
||||
try {
|
||||
& $Python $uvicornArgs
|
||||
} catch {
|
||||
Write-Log "Failed to start server: $_"
|
||||
exit 1
|
||||
}
|
||||
} else {
|
||||
# Dev mode: run with graceful shutdown support
|
||||
try {
|
||||
$serverProcess = Start-Process -FilePath $Python -ArgumentList $uvicornArgs -NoNewWindow -PassThru
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Server process started with PID: $($serverProcess.Id)" -ForegroundColor Green
|
||||
}
|
||||
Write-Log "Server process started with PID: $($serverProcess.Id)"
|
||||
} catch {
|
||||
Write-Log "Failed to start server process: $_"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[ERROR] Failed to start server process: $_" -ForegroundColor Red
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Setup graceful shutdown handler
|
||||
$shutdownRequested = $false
|
||||
|
||||
$null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
|
||||
$script:shutdownRequested = $true
|
||||
}
|
||||
|
||||
try {
|
||||
# Wait for process to exit
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "[DEBUG] Waiting for server process to exit..."
|
||||
}
|
||||
Wait-Process -Id $serverProcess.Id -ErrorAction Stop
|
||||
} catch {
|
||||
if ($shutdownRequested -or $Error[0].Exception.Message -match "was not found") {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host ""
|
||||
Write-Host "Shutting down server gracefully..." -ForegroundColor Yellow
|
||||
}
|
||||
Write-Log "Shutting down server gracefully"
|
||||
|
||||
# Try graceful shutdown first
|
||||
$serverProcess.CloseMainWindow() | Out-Null
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
# Force kill if still running
|
||||
if (!$serverProcess.HasExited) {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Force killing server process..." -ForegroundColor Yellow
|
||||
}
|
||||
$serverProcess | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host ""
|
||||
Write-Host "[ERROR] Server process error: $_" -ForegroundColor Red
|
||||
}
|
||||
Write-Log "Server process error: $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Get actual exit code
|
||||
$exitCode = $serverProcess.ExitCode
|
||||
|
||||
# Check exit code
|
||||
if ($exitCode -ne 0) {
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host ""
|
||||
Write-Host "[ERROR] Application failed to start with error code $exitCode" -ForegroundColor Red
|
||||
}
|
||||
Write-Log "Application failed to start with error code $exitCode"
|
||||
exit $exitCode
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-Log "Error: $_"
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host ""
|
||||
Write-Host "[ERROR] An error occurred: $_" -ForegroundColor Red
|
||||
}
|
||||
exit 1
|
||||
} finally {
|
||||
# Cleanup work (dev mode only)
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host ""
|
||||
Write-Host "Server shutting down..." -ForegroundColor Yellow
|
||||
}
|
||||
Write-Log "Server shutting down"
|
||||
|
||||
# Clear port usage (dev mode only)
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Cleaning up any remaining processes on port $Port..."
|
||||
$process = Get-PortProcess -PortNumber $Port
|
||||
if ($process) {
|
||||
try {
|
||||
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
|
||||
if ($Mode -eq "dev") {
|
||||
Write-Host "Killed process $($process.Id)"
|
||||
}
|
||||
Write-Log "Killed process $($process.Id) during cleanup"
|
||||
} catch {
|
||||
# Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Shutdown complete." -ForegroundColor Green
|
||||
}
|
||||
Write-Log "Server shutdown complete"
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
@echo off
|
||||
|
||||
rem Simple service management script
|
||||
setlocal
|
||||
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
set "NSSM_EXE=%SCRIPT_DIR%nssm.exe"
|
||||
set "SERVICE_NAME=MyAPS_API"
|
||||
set "RUN_PS1=%SCRIPT_DIR%run.ps1"
|
||||
|
||||
rem Calculate project root (parent directory of scripts)
|
||||
for %%i in ("%SCRIPT_DIR%..") do set "PROJECT_ROOT=%%~fi"
|
||||
set "LOG_DIR=%PROJECT_ROOT%\logs"
|
||||
|
||||
rem Check if NSSM exists
|
||||
if not exist "%NSSM_EXE%" (
|
||||
echo ERROR: NSSM not found at %NSSM_EXE%
|
||||
echo Please download NSSM and place it in the scripts directory
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
rem Check if run.ps1 exists
|
||||
if not exist "%RUN_PS1%" (
|
||||
echo ERROR: run.ps1 not found at %RUN_PS1%
|
||||
echo Please create run.ps1 script in the scripts directory
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
rem Check admin rights
|
||||
net session >nul 2>&1
|
||||
if %errorLevel% neq 0 (
|
||||
echo ERROR: Please run as administrator
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:MENU
|
||||
cls
|
||||
echo FastAPI Service Management
|
||||
echo 1. Install service
|
||||
echo 2. Start service
|
||||
echo 3. Stop service
|
||||
echo 4. Restart service
|
||||
echo 5. Uninstall service
|
||||
echo 6. Check status
|
||||
echo 7. Test run.ps1 (dev mode)
|
||||
echo 8. Test run.ps1 (service mode)
|
||||
echo 0. Exit
|
||||
|
||||
echo.
|
||||
set /p choice=Enter choice:
|
||||
|
||||
if "%choice%"=="1" goto :INSTALL
|
||||
if "%choice%"=="2" goto :START
|
||||
if "%choice%"=="3" goto :STOP
|
||||
if "%choice%"=="4" goto :RESTART
|
||||
if "%choice%"=="5" goto :UNINSTALL
|
||||
if "%choice%"=="6" goto :STATUS
|
||||
if "%choice%"=="7" goto :TEST_RUNPS1_DEV
|
||||
if "%choice%"=="8" goto :TEST_RUNPS1_SERVICE
|
||||
if "%choice%"=="0" goto :EXIT
|
||||
|
||||
echo Invalid choice
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:INSTALL
|
||||
echo Installing service...
|
||||
echo Using run.ps1: %RUN_PS1%
|
||||
echo Project root: %PROJECT_ROOT%
|
||||
|
||||
rem Create logs directory
|
||||
if not exist "%LOG_DIR%" (
|
||||
echo Creating logs directory...
|
||||
mkdir "%LOG_DIR%"
|
||||
)
|
||||
|
||||
echo Installing service with AppDirectory: %PROJECT_ROOT%
|
||||
"%NSSM_EXE%" install "%SERVICE_NAME%" powershell.exe
|
||||
"%NSSM_EXE%" set "%SERVICE_NAME%" AppParameters "-ExecutionPolicy Bypass -NoProfile -File %RUN_PS1% -Mode service"
|
||||
"%NSSM_EXE%" set "%SERVICE_NAME%" AppDirectory "%PROJECT_ROOT%"
|
||||
"%NSSM_EXE%" set "%SERVICE_NAME%" Start SERVICE_AUTO_START
|
||||
"%NSSM_EXE%" set "%SERVICE_NAME%" AppStdout "%LOG_DIR%\nssm_stdout.log"
|
||||
"%NSSM_EXE%" set "%SERVICE_NAME%" AppStderr "%LOG_DIR%\nssm_stderr.log"
|
||||
|
||||
echo Service installed
|
||||
echo Logs will be saved to: %LOG_DIR%
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:START
|
||||
echo Starting service...
|
||||
"%NSSM_EXE%" start "%SERVICE_NAME%"
|
||||
if %errorLevel% neq 0 (
|
||||
echo ERROR: Failed to start service
|
||||
echo Possible reasons:
|
||||
echo 1. run.ps1 script has errors
|
||||
echo 2. PowerShell execution policy issues
|
||||
echo 3. Missing dependencies
|
||||
echo 4. Working directory issues
|
||||
echo.
|
||||
echo Check logs at: %LOG_DIR%
|
||||
)
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:STOP
|
||||
echo Stopping service...
|
||||
"%NSSM_EXE%" stop "%SERVICE_NAME%"
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:RESTART
|
||||
echo Restarting service...
|
||||
"%NSSM_EXE%" restart "%SERVICE_NAME%"
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:UNINSTALL
|
||||
echo Uninstalling service...
|
||||
"%NSSM_EXE%" stop "%SERVICE_NAME%" >nul 2>&1
|
||||
"%NSSM_EXE%" remove "%SERVICE_NAME%" confirm
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:STATUS
|
||||
echo Checking status...
|
||||
"%NSSM_EXE%" status "%SERVICE_NAME%"
|
||||
echo.
|
||||
echo Service configuration:
|
||||
"%NSSM_EXE%" get "%SERVICE_NAME%" AppParameters
|
||||
"%NSSM_EXE%" get "%SERVICE_NAME%" AppDirectory
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:TEST_RUNPS1_DEV
|
||||
echo Testing run.ps1 script in dev mode...
|
||||
echo Running: %RUN_PS1% -Mode dev
|
||||
echo Working directory: %PROJECT_ROOT%
|
||||
echo.
|
||||
echo Output:
|
||||
echo ----------------------------------------
|
||||
powershell.exe -ExecutionPolicy Bypass -NoProfile -File %RUN_PS1% -Mode dev
|
||||
echo ----------------------------------------
|
||||
echo.
|
||||
echo Test completed.
|
||||
echo If you see errors above, fix them before starting the service.
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:TEST_RUNPS1_SERVICE
|
||||
echo Testing run.ps1 script in service mode...
|
||||
echo Running: %RUN_PS1% -Mode service
|
||||
echo Working directory: %PROJECT_ROOT%
|
||||
echo.
|
||||
echo Output:
|
||||
echo ----------------------------------------
|
||||
powershell.exe -ExecutionPolicy Bypass -NoProfile -File %RUN_PS1% -Mode service
|
||||
echo ----------------------------------------
|
||||
echo.
|
||||
echo Test completed.
|
||||
echo If you see errors above, fix them before starting the service.
|
||||
pause
|
||||
goto :MENU
|
||||
|
||||
:EXIT
|
||||
echo Exiting...
|
||||
pause
|
||||
@@ -1003,6 +1003,44 @@ function formatRelativeTime(timestamp) {
|
||||
return Math.floor(diff / 86400) + '天前';
|
||||
}
|
||||
|
||||
// 格式化相对日期时间(今天,昨天,前天,早于前天才使用具体日期时间)
|
||||
function formatRelativeDate(timestamp) {
|
||||
const now = new Date();
|
||||
const date = new Date(timestamp * 1000);
|
||||
|
||||
// 今天
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
// 昨天
|
||||
const yesterday = new Date(today);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
// 前天
|
||||
const dayBeforeYesterday = new Date(today);
|
||||
dayBeforeYesterday.setDate(dayBeforeYesterday.getDate() - 2);
|
||||
|
||||
const logDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
|
||||
// 判断是否是今天、昨天、前天
|
||||
let dayLabel = '';
|
||||
if (logDate.getTime() === today.getTime()) {
|
||||
dayLabel = '今天';
|
||||
} else if (logDate.getTime() === yesterday.getTime()) {
|
||||
dayLabel = '昨天';
|
||||
} else if (logDate.getTime() === dayBeforeYesterday.getTime()) {
|
||||
dayLabel = '前天';
|
||||
} else {
|
||||
// 显示具体日期
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
dayLabel = `${month}-${day}`;
|
||||
}
|
||||
|
||||
// 显示具体时间
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||
return `${dayLabel} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
// 刷新事件统计
|
||||
function refreshEventStats() {
|
||||
fetchEventStats();
|
||||
@@ -1191,8 +1229,7 @@ function updateAPIRequestsDisplay(data) {
|
||||
}
|
||||
|
||||
tbodyEl.innerHTML = filteredRequests.map((req, index) => {
|
||||
const date = new Date(req.timestamp * 1000);
|
||||
const timeStr = `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2,'0')}-${date.getDate().toString().padStart(2,'0')} ${date.getHours().toString().padStart(2,'0')}:${date.getMinutes().toString().padStart(2,'0')}:${date.getSeconds().toString().padStart(2,'0')}`;
|
||||
const timeStr = formatRelativeDate(req.timestamp);
|
||||
|
||||
const methodClass = req.method.toLowerCase();
|
||||
const isSuccess = req.status_code < 400;
|
||||
@@ -1587,8 +1624,7 @@ function updateLogsPageDisplay(logs) {
|
||||
}
|
||||
|
||||
tbodyEl.innerHTML = filteredLogs.map((log, index) => {
|
||||
const date = new Date(log.timestamp * 1000);
|
||||
const timeStr = `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2,'0')}-${date.getDate().toString().padStart(2,'0')} ${date.getHours().toString().padStart(2,'0')}:${date.getMinutes().toString().padStart(2,'0')}:${date.getSeconds().toString().padStart(2,'0')}`;
|
||||
const timeStr = formatRelativeDate(log.timestamp);
|
||||
|
||||
// 确保 title 属性使用完整的消息内容
|
||||
const fullMessage = log.message || '';
|
||||
@@ -1609,12 +1645,15 @@ function updateLogsPageDisplay(logs) {
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
|
||||
// 移除模块名称中的"smart_"前缀
|
||||
const moduleName = log.module.replace(/^smart_/, '');
|
||||
|
||||
return `
|
||||
<tr data-log-id="${logId}" data-log-index="${index}" class="${readStatusClass}" onclick="showLogDetail(${index})">
|
||||
<td class="font-mono">${index + 1}</td>
|
||||
<td>${timeStr}</td>
|
||||
<td><span class="log-level-badge ${log.level}">${log.level.toUpperCase()}</span></td>
|
||||
<td>${log.module}</td>
|
||||
<td>${moduleName}</td>
|
||||
<td class="log-message-cell" title="${escapedFullMessage}" data-full-message="${escapedFullMessage}">${escapeHtml(log.message || '')}</td>
|
||||
<td class="read-status-cell">
|
||||
<span class="read-checkbox ${readStatusClass}" onclick="toggleLogReadStatus('${logId}'); event.stopPropagation();">${readIcon}</span>
|
||||
@@ -2282,8 +2321,7 @@ function getStatusDescription(statusCode) {
|
||||
|
||||
// 格式化发送请求行
|
||||
function formatOutboundRequestRow(request, index) {
|
||||
const date = new Date(request.timestamp * 1000);
|
||||
const timestamp = `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2,'0')}-${date.getDate().toString().padStart(2,'0')} ${date.getHours().toString().padStart(2,'0')}:${date.getMinutes().toString().padStart(2,'0')}:${date.getSeconds().toString().padStart(2,'0')}`;
|
||||
const timestamp = formatRelativeDate(request.timestamp);
|
||||
const duration = (request.duration * 1000).toFixed(0);
|
||||
const statusClass = request.status_code >= 400 ? 'error' : 'success';
|
||||
const durationClass = request.duration > 1 ? 'slow' : '';
|
||||
|
||||
Reference in New Issue
Block a user