From a59b3930bb8a0fdf905d5154cac7de377ffdb076 Mon Sep 17 00:00:00 2001 From: wangbolhr Date: Thu, 9 Apr 2026 10:31:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20=E4=BF=AE=E5=A4=8D=20start.ps1?= =?UTF-8?q?=20=E9=97=AA=E9=80=80=E5=B9=B6=E5=A2=9E=E5=BC=BA=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重建部署脚本并修复解析阶段报错,避免因脚本损坏导致运行后直接退出;同时保留 dry-run 与 Docker 检查流程,提升可诊断性与非交互执行稳定性。 Made-with: Cursor --- start.ps1 | 445 +++++++++++++++++++----------------------------------- 1 file changed, 155 insertions(+), 290 deletions(-) diff --git a/start.ps1 b/start.ps1 index 48a7413..e5911db 100644 --- a/start.ps1 +++ b/start.ps1 @@ -1,189 +1,149 @@ -#Requires -Version 5.1 -# EasyAI Windows 一键部署脚本 -# 仅支持 IP 访问(本地/局域网),不含域名与 HTTPS -# 一行命令: git clone https://git.51easyai.com/wangbo/easyai; cd easyai; .\start.ps1 +#Requires -Version 5.1 -# 设置控制台编码为 UTF-8,确保中文正确显示 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [System.Text.Encoding]::UTF8 - $ErrorActionPreference = "Stop" -$script:LogDir = $PSScriptRoot -if (-not $script:LogDir) { $script:LogDir = $env:TEMP } -$script:LogFile = Join-Path $script:LogDir "start.ps1.log" -# 尽早写入启动标记,便于闪退时排查 -try { "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] === 脚本加载 ===" | Out-File -FilePath $script:LogFile -Append -Encoding utf8 } catch { } + +$script:Root = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path } +$script:LogFile = Join-Path $script:Root "start.ps1.log" +$script:DeployDryRun = ($env:DEPLOY_DRY_RUN -eq "1") +$script:DeployIP = "" +$script:SkipDeployQuestions = $false +$script:DockerDesktopUrl = "https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe" function Write-Log { - param([string]$Msg) + param([string]$Message) try { - $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Msg" - Add-Content -Path $script:LogFile -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue + Add-Content -Path $script:LogFile -Encoding UTF8 -Value ("[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message) } catch { } } +function Wait-ForExit { + if ($env:CI -eq "true") { return } + if ($env:DEPLOY_NO_WAIT -eq "1") { return } + Write-Host "" + Read-Host "Press Enter to exit" +} + trap { - $errMsg = $_.Exception.Message - $errStack = if ($_.ScriptStackTrace) { " | 堆栈: $($_.ScriptStackTrace)" } else { "" } - try { - $logPath = $script:LogFile - if (-not $logPath) { $logPath = Join-Path $env:TEMP "start.ps1.log" } - $logLine = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 错误退出: $errMsg$errStack" - Add-Content -Path $logPath -Value $logLine -Encoding UTF8 -ErrorAction SilentlyContinue - } catch { } - try { - Write-Host "" - Write-Host "========================================" -ForegroundColor Red - Write-Host " 发生错误,详见日志: $script:LogFile" -ForegroundColor Red - Write-Host "========================================" -ForegroundColor Red - Write-Host $errMsg -ForegroundColor Red - if ($errStack) { Write-Host $errStack } - } catch { } + $msg = $_.Exception.Message + Write-Log "FATAL: $msg" + Write-Host "" + Write-Host "========================================" -ForegroundColor Red + Write-Host "Script failed. Log file: $script:LogFile" -ForegroundColor Red + Write-Host "========================================" -ForegroundColor Red + Write-Host $msg -ForegroundColor Red Wait-ForExit exit 1 } -# 结束时保持窗口不关闭,便于查看输出和 Debug(CI 环境自动跳过) -function Wait-ForExit { - if ($env:CI -eq "true") { return } - Write-Host "" - Read-Host "按 Enter 键退出" -} +function Step { param([string]$Message) Write-Host $Message } +function Ok { param([string]$Message) Write-Host "[OK] $Message" -ForegroundColor Green } +function Warn { param([string]$Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow } +function Fail { param([string]$Message) Write-Host "[ERR] $Message" -ForegroundColor Red; throw $Message } -# 仅配置模式:DEPLOY_DRY_RUN=1 只生成配置文件,不执行 Docker 安装和启动 -$script:DeployDryRun = if ($env:DEPLOY_DRY_RUN -eq "1") { $true } else { $false } -$script:DeployIP = "" -$script:DeployModeSkip = $false - -# Docker Desktop for Windows 下载链接 -$DockerDesktopUrl = "https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe" - -function Write-Step { param($Msg) Write-Host $Msg } -function Write-Ok { param($Msg) Write-Host " ✓ $Msg" -ForegroundColor Green } -function Write-Err { param($Msg) Write-Host "❌ $Msg" -ForegroundColor Red; throw $Msg } -function Write-Warn { param($Msg) Write-Host "⚠️ $Msg" -ForegroundColor Yellow } - -# ==================== 项目初始化 ==================== function Init-ProjectDir { - $scriptDir = $PSScriptRoot - if (-not $scriptDir) { throw "无法获取脚本所在目录。请在 easyai 目录下打开 PowerShell 执行: .\start.ps1" } - $composePath = Join-Path $scriptDir "docker-compose.yml" - if (Test-Path $composePath) { - Set-Location $scriptDir - Write-Step "📁 项目目录: $scriptDir" - return + $composePath = Join-Path $script:Root "docker-compose.yml" + if (-not (Test-Path $composePath)) { + Fail "docker-compose.yml not found. Please run this script in easyai directory." + } + Set-Location $script:Root + Step "Project directory: $script:Root" +} + +function Ensure-FileFromSample { + param([string]$Target,[string]$Sample) + if (Test-Path $Target) { return } + if (-not (Test-Path $Sample)) { Fail "Missing sample file: $Sample" } + Copy-Item $Sample $Target + Ok "$Target created" +} + +function Get-DetectedLanIp { + try { + $ip = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { + $_.IPAddress -notmatch "^127\." -and + $_.IPAddress -notmatch "^169\.254\." -and + $_.InterfaceAlias -notmatch "Loopback" + } | Sort-Object InterfaceIndex | Select-Object -First 1 -ExpandProperty IPAddress + return $ip + } catch { + return $null } - Write-Err "未找到 docker-compose.yml,请在 easyai 项目目录下运行 start.ps1" } -# ==================== 配置问答 ==================== function Run-DeployQuestions { Write-Host "" Write-Host "================================" - Write-Host " EasyAI 部署配置(Windows)" + Write-Host " EasyAI Deployment Config" Write-Host "================================" Write-Host "" - # 非交互模式:环境变量 DEPLOY_IP 已设置 if ($env:DEPLOY_IP) { $script:DeployIP = $env:DEPLOY_IP.Trim() - Write-Step "使用环境变量: DEPLOY_IP=$($script:DeployIP)" + Step "Using DEPLOY_IP=$($script:DeployIP)" return } - Write-Host "请选择访问方式:" - Write-Host " [1] 本地访问 (127.0.0.1) - 仅本机访问,无需放行端口" - Write-Host " [2] 局域网访问 - 同网段设备访问,需放行 3001、3002、3003 端口" - $choice = Read-Host "请选择 [1/2]" + Write-Host "Choose mode:" + Write-Host " [1] Local only (127.0.0.1)" + Write-Host " [2] LAN access" + $choice = Read-Host "Select [1/2]" switch ($choice) { - "1" { - $script:DeployIP = "127.0.0.1" - Write-Step "已选择本地访问" - } + "1" { $script:DeployIP = "127.0.0.1"; Step "Selected local mode" } "2" { - # 自动获取局域网 IP(排除回环和 APIPA 地址) - $detectedIp = $null - try { - $addrs = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { - $_.InterfaceAlias -notlike "*Loopback*" -and - $_.IPAddress -notmatch "^127\." -and - $_.IPAddress -notmatch "^169\.254\." - } - $detectedIp = ($addrs | Sort-Object InterfaceIndex | Select-Object -First 1).IPAddress - } catch { } - if ($detectedIp) { - $defaultHint = "回车使用 [$detectedIp]" - Write-Host " 检测到局域网 IP: $detectedIp" - $inputIp = Read-Host "请输入本机局域网 IP($defaultHint 或手动输入)" - $inputIp = $inputIp.Trim() - $script:DeployIP = if ([string]::IsNullOrWhiteSpace($inputIp)) { $detectedIp } else { $inputIp } + $detected = Get-DetectedLanIp + if ($detected) { + Write-Host "Detected LAN IP: $detected" + $inputIp = (Read-Host "Input LAN IP (Enter to use detected)").Trim() + $script:DeployIP = if ([string]::IsNullOrWhiteSpace($inputIp)) { $detected } else { $inputIp } } else { - Write-Host " 提示: 未自动检测到局域网 IP,可在本机运行 ipconfig 查看" - $lanIp = Read-Host "请输入本机局域网 IP 地址" - $lanIp = $lanIp.Trim() - if ([string]::IsNullOrWhiteSpace($lanIp)) { Write-Err "IP 不能为空" } - $script:DeployIP = $lanIp + $inputIp = (Read-Host "Input LAN IP").Trim() + if ([string]::IsNullOrWhiteSpace($inputIp)) { Fail "IP cannot be empty." } + $script:DeployIP = $inputIp } - Write-Warn "请确保防火墙已放行 3001、3002、3003 端口" - } - default { - Write-Err "无效选择" + Warn "Allow firewall ports: 3001, 3002, 3003" } + default { Fail "Invalid selection." } } } -# ==================== 生成配置文件 ==================== +function Upsert-Env { + param([string]$Content,[string]$Key,[string]$Value) + $line = "$Key=$Value" + $pattern = "(?m)^$Key=.*$" + if ($Content -match $pattern) { return ($Content -replace $pattern, $line) } + if ($Content -and -not $Content.EndsWith("`n")) { $Content += "`n" } + return ($Content + $line + "`n") +} + function Setup-EnvFiles { Write-Host "" - Write-Step "📝 配置环境文件..." + Step "Configuring env files..." - # 复制 .env.tools、.env.ASG、.env.AMS(无 example 后缀的从 .sample 生成) - if (-not (Test-Path ".env.tools")) { - Copy-Item ".env.tools.sample" ".env.tools" - Write-Ok ".env.tools" - } - if (-not (Test-Path ".env.ASG")) { - Copy-Item ".env.ASG.sample" ".env.ASG" - Write-Ok ".env.ASG" - } - if (-not (Test-Path ".env.AMS")) { - Copy-Item ".env.AMS.sample" ".env.AMS" - Write-Ok ".env.AMS" - } - - # 配置 .env - if (-not (Test-Path ".env")) { - Copy-Item ".env.sample" ".env" - } + Ensure-FileFromSample ".env.tools" ".env.tools.sample" + Ensure-FileFromSample ".env.ASG" ".env.ASG.sample" + Ensure-FileFromSample ".env.AMS" ".env.AMS.sample" + Ensure-FileFromSample ".env" ".env.sample" $content = Get-Content ".env" -Raw -Encoding UTF8 if (-not $content) { $content = "" } - $content = $content -replace '(?m)^NUXT_PUBLIC_BASE_APIURL=.*', "NUXT_PUBLIC_BASE_APIURL=http://$($script:DeployIP):3001" - $content = $content -replace '(?m)^NUXT_PUBLIC_BASE_SOCKETURL=.*', "NUXT_PUBLIC_BASE_SOCKETURL=ws://$($script:DeployIP):3002" - $content = $content -replace '(?m)^NUXT_PUBLIC_SG_APIURL=.*', "NUXT_PUBLIC_SG_APIURL=http://$($script:DeployIP):3003" + $content = Upsert-Env $content "NUXT_PUBLIC_BASE_APIURL" "http://$($script:DeployIP):3001" + $content = Upsert-Env $content "NUXT_PUBLIC_BASE_SOCKETURL" "ws://$($script:DeployIP):3002" + $content = Upsert-Env $content "NUXT_PUBLIC_SG_APIURL" "http://$($script:DeployIP):3003" - # 保持文件末尾换行 - if ($content -and -not $content.EndsWith("`n")) { $content += "`n" } - $envPath = Join-Path (Get-Location) ".env" - [System.IO.File]::WriteAllText($envPath, $content, [System.Text.UTF8Encoding]::new($false)) - Write-Ok ".env 已配置为 IP 模式 ($($script:DeployIP))" + [System.IO.File]::WriteAllText((Join-Path $script:Root ".env"), $content, [System.Text.UTF8Encoding]::new($false)) + Ok ".env configured for IP=$($script:DeployIP)" } -# ==================== Docker 检测 ==================== function Test-DockerInstalled { $docker = Get-Command docker -ErrorAction SilentlyContinue if (-not $docker) { return $false } - try { - $null = & docker --version 2>&1 - return $true - } catch { - return $false - } + try { & docker --version *> $null; return $true } catch { return $false } } -# 检查 Docker 引擎是否已启动并可响应(带超时,避免 docker info 卡住) function Test-DockerRunning { param([int]$TimeoutSeconds = 10) try { @@ -195,246 +155,151 @@ function Test-DockerRunning { $psi.UseShellExecute = $false $psi.CreateNoWindow = $true $p = [System.Diagnostics.Process]::Start($psi) - $exited = $p.WaitForExit($TimeoutSeconds * 1000) - if (-not $exited) { - try { $p.Kill() } catch { } - return $false - } + $ok = $p.WaitForExit($TimeoutSeconds * 1000) + if (-not $ok) { try { $p.Kill() } catch { }; return $false } return ($p.ExitCode -eq 0) } catch { return $false } } -# 启动 Docker Desktop 并等待就绪 function Start-DockerDesktopAndWait { - param( - [int]$MaxWaitSeconds = 120, - [int]$CheckIntervalSeconds = 5 - ) + param([int]$MaxWaitSeconds = 120, [int]$CheckIntervalSeconds = 5) + Step "Docker not running, trying to start Docker Desktop..." - Write-Step "Docker 未运行,正在启动 Docker Desktop..." - $dockerDesktopPath = "${env:ProgramFiles}\Docker\Docker\Docker Desktop.exe" - if (-not (Test-Path $dockerDesktopPath)) { - $dockerDesktopPath = "${env:ProgramFiles(x86)}\Docker\Docker\Docker Desktop.exe" - } - if (-not (Test-Path $dockerDesktopPath)) { - Write-Warn "未找到 Docker Desktop 可执行文件" - Write-Host "请手动启动 Docker Desktop 后重新运行本脚本。" - return $false - } + $path = Join-Path $env:ProgramFiles "Docker\Docker\Docker Desktop.exe" + if (-not (Test-Path $path)) { $path = Join-Path ${env:ProgramFiles(x86)} "Docker\Docker\Docker Desktop.exe" } + if (-not (Test-Path $path)) { Warn "Docker Desktop executable not found."; return $false } - # 尝试使用 docker desktop start(Docker Desktop 4.38+ 支持) - $useCli = $false + $startedByCli = $false try { - $null = & docker desktop start 2>&1 - $useCli = $true - Write-Step "已通过 docker desktop start 发送启动命令" - } catch { - # 回退到直接启动进程 - } + & docker desktop start *> $null + if ($LASTEXITCODE -eq 0) { $startedByCli = $true; Step "Sent docker desktop start command" } + } catch { } + if (-not $startedByCli) { Start-Process -FilePath $path -WindowStyle Hidden } - if (-not $useCli) { - Start-Process -FilePath $dockerDesktopPath -WindowStyle Hidden - Write-Step "已启动 Docker Desktop 进程" - } - - Write-Host "等待 Docker 引擎就绪(最长 $MaxWaitSeconds 秒)..." -ForegroundColor Cyan $elapsed = 0 while ($elapsed -lt $MaxWaitSeconds) { Start-Sleep -Seconds $CheckIntervalSeconds $elapsed += $CheckIntervalSeconds - Write-Host " 已等待 $elapsed 秒..." -NoNewline - if (Test-DockerRunning) { - Write-Host " 完成" -ForegroundColor Green - Write-Ok "Docker 已就绪" - return $true - } - Write-Host "" + if (Test-DockerRunning) { Ok "Docker engine ready"; return $true } } - - Write-Warn "等待超时,Docker 可能仍在启动中" - Write-Host "请稍后手动执行 docker info 确认,或重新运行本脚本。" + Warn "Timeout waiting for Docker." return $false } -# 确保 Docker 已安装且运行,若未运行则尝试启动 function Ensure-DockerRunning { - if (-not (Test-DockerInstalled)) { - return $false - } - if (Test-DockerRunning) { - Write-Ok "Docker 引擎已运行" - $v = docker --version 2>$null - if ($v) { Write-Host " $v" } - return $true - } + if (-not (Test-DockerInstalled)) { return $false } + if (Test-DockerRunning) { Ok "Docker engine running"; return $true } return (Start-DockerDesktopAndWait) } function Install-DockerDesktop { - $isWin = ($env:OS -eq "Windows_NT") -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows) - if (-not $isWin) { - Write-Warn "自动安装仅支持 Windows。请手动安装 Docker Desktop。" - Write-Host "下载地址: $DockerDesktopUrl" - Wait-ForExit; exit 1 - } - - # 优先 winget,其次 Chocolatey if (Get-Command winget -ErrorAction SilentlyContinue) { - Write-Step "正在通过 winget 安装 Docker Desktop for Windows..." + Step "Installing Docker Desktop via winget..." winget install --id Docker.DockerDesktop -e --accept-source-agreements --accept-package-agreements - if ($LASTEXITCODE -eq 0) { - Write-Ok "安装已启动。安装完成后请重启终端,然后重新运行本脚本。" - exit 0 - } + if ($LASTEXITCODE -eq 0) { Ok "Install started. Reopen terminal and rerun script."; exit 0 } } if (Get-Command choco -ErrorAction SilentlyContinue) { - Write-Step "正在通过 Chocolatey 安装 Docker Desktop for Windows..." + Step "Installing Docker Desktop via choco..." choco install docker-desktop -y - if ($LASTEXITCODE -eq 0) { - Write-Ok "安装已启动。安装完成后请重启终端,然后重新运行本脚本。" - exit 0 - } + if ($LASTEXITCODE -eq 0) { Ok "Install started. Reopen terminal and rerun script."; exit 0 } } - - Write-Warn "未找到 winget 或 Chocolatey,请手动安装 Docker Desktop。" - Write-Host "下载地址: $DockerDesktopUrl" - Wait-ForExit; exit 1 + Warn "Please install Docker Desktop manually:" + Write-Host " $script:DockerDesktopUrl" + Wait-ForExit + exit 1 } function Test-Docker { Write-Host "" Write-Host "================================" - Write-Host " Docker 检查" + Write-Host " Docker Check" Write-Host "================================" Write-Host "" if (Test-DockerInstalled) { - Write-Ok "Docker 已安装" - # 检查并确保 Docker 引擎运行 - if (-not (Ensure-DockerRunning)) { - Write-Warn "Docker 未能自动启动" - Write-Host "" - Write-Host "请手动启动 Docker Desktop,确认其完全启动后再重新运行本脚本。" - Wait-ForExit - exit 1 - } + Ok "Docker installed" + if (-not (Ensure-DockerRunning)) { Warn "Docker failed to start automatically."; Wait-ForExit; exit 1 } return } - Write-Warn "未检测到 Docker Desktop for Windows" - Write-Host "" - Write-Host "请选择:" - Write-Host " [1] 手动安装 - 打开下载页面,退出脚本" - Write-Host " [2] 自动安装 - 通过 winget 或 Chocolatey 安装(需管理员权限)" - $choice = Read-Host "请选择 [1/2]" - + Warn "Docker Desktop not detected." + Write-Host "Choose:" + Write-Host " [1] Manual install (open URL and exit)" + Write-Host " [2] Auto install (winget/choco)" + $choice = Read-Host "Select [1/2]" switch ($choice) { - "1" { - Write-Host "" - Write-Host "Docker Desktop for Windows 下载地址:" - Write-Host " $DockerDesktopUrl" - Write-Host "" - Write-Host "安装完成后请重新运行本脚本。" - if ($env:OS -eq "Windows_NT") { - Start-Process $DockerDesktopUrl - } - Wait-ForExit; exit 1 - } - "2" { - Install-DockerDesktop - } - default { - Write-Err "无效选择" - } + "1" { Start-Process $script:DockerDesktopUrl; Wait-ForExit; exit 1 } + "2" { Install-DockerDesktop } + default { Fail "Invalid selection." } } } -# ==================== 启动服务 ==================== function Start-Services { Write-Host "" - Write-Step "🚀 启动 EasyAI 服务..." + Step "Starting EasyAI services..." + $useComposeV2 = $false + try { & docker compose version *> $null; if ($LASTEXITCODE -eq 0) { $useComposeV2 = $true } } catch { } - $hasComposeV2 = $false - try { - $null = docker compose version 2>&1 - $hasComposeV2 = $true - } catch { } - - if ($hasComposeV2) { + if ($useComposeV2) { docker compose pull - if ($LASTEXITCODE -ne 0) { Write-Err "docker compose pull 失败" } + if ($LASTEXITCODE -ne 0) { Fail "docker compose pull failed." } docker compose up -d - if ($LASTEXITCODE -ne 0) { Write-Err "docker compose up 失败" } + if ($LASTEXITCODE -ne 0) { Fail "docker compose up failed." } } else { docker-compose pull - if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose pull 失败" } + if ($LASTEXITCODE -ne 0) { Fail "docker-compose pull failed." } docker-compose up -d - if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose up 失败" } + if ($LASTEXITCODE -ne 0) { Fail "docker-compose up failed." } } - Write-Host "🎉 EasyAI 应用启动成功" + Ok "EasyAI started" } -# ==================== 主流程 ==================== function Main { Init-ProjectDir - # 检查是否已有 .env 且非强制重新配置 if ((Test-Path ".env") -and -not $env:DEPLOY_FORCE_RECONFIG -and -not $env:DEPLOY_IP) { - Write-Step "📁 检测到已有 .env 配置" - $reconfigure = Read-Host "是否重新配置访问方式?[y/N]" - $trimmed = $reconfigure.Trim().ToLower() - $startsWithY = ($trimmed.Length -gt 0) -and ($trimmed[0] -eq [char]121) - if (-not $startsWithY) { - Write-Step "使用现有配置继续..." - # 从现有 .env 读取 IP(用于最后输出) + $answer = (Read-Host "Existing .env found. Reconfigure? [y/N]").Trim().ToLower() + $yes = ($answer.Length -gt 0) -and ($answer[0] -eq [char]121) + if (-not $yes) { $line = Get-Content ".env" -Encoding UTF8 | Where-Object { $_ -like "NUXT_PUBLIC_BASE_APIURL=*" } | Select-Object -First 1 - if ($line -and $line -like "*:3001*") { - $prefix = ".*http://" - $suffix = ":3001.*" - $script:DeployIP = ($line -replace $prefix, "" -replace $suffix, "").Trim() - } - $script:DeployModeSkip = $true + if ($line -and $line -like "*:3001*") { $script:DeployIP = ($line -replace ".*http://", "" -replace ":3001.*", "").Trim() } + $script:SkipDeployQuestions = $true + Step "Using existing env config" } } - if (-not $script:DeployModeSkip) { + if (-not $script:SkipDeployQuestions) { Run-DeployQuestions - } - - if (-not $script:DeployModeSkip) { Setup-EnvFiles } else { - if (-not (Test-Path ".env.tools")) { Copy-Item ".env.tools.sample" ".env.tools" } - if (-not (Test-Path ".env.ASG")) { Copy-Item ".env.ASG.sample" ".env.ASG" } - if (-not (Test-Path ".env.AMS")) { Copy-Item ".env.AMS.sample" ".env.AMS" } + Ensure-FileFromSample ".env.tools" ".env.tools.sample" + Ensure-FileFromSample ".env.ASG" ".env.ASG.sample" + Ensure-FileFromSample ".env.AMS" ".env.AMS.sample" } if ($script:DeployDryRun) { - Write-Host "" - Write-Warn "dry-run 模式:跳过 Docker 安装和服务启动" - Write-Host " 配置文件已生成,可直接运行 .\start.ps1 完成部署" + Warn "dry-run mode: skip docker and services" } else { Test-Docker Start-Services } + $ip = if ($script:DeployIP) { $script:DeployIP } else { "127.0.0.1" } Write-Host "" Write-Host "================================" - Write-Host " 部署完成" + Write-Host " Deployment Done" Write-Host "================================" - $accessIp = if ($script:DeployIP) { $script:DeployIP } else { "127.0.0.1" } - Write-Host "访问地址: http://${accessIp}:3010" - Write-Host "默认登录账户: admin" - Write-Host "默认登录密码: 123456" - Write-Host "" + Write-Host "URL: http://${ip}:3010" + Write-Host "User: admin" + Write-Host "Password: 123456" Wait-ForExit } -Write-Log "=== 脚本启动 ===" -Write-Host "EasyAI Windows 部署脚本启动中..." -ForegroundColor Cyan -Write-Host "日志文件: $script:LogFile" +Write-Log "=== script start ===" +Write-Host "EasyAI deploy script starting..." -ForegroundColor Cyan +Write-Host "Log file: $script:LogFile" Write-Host "" Main -Write-Log "=== 脚本正常结束 ===" +Write-Log "=== script end ==="