Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 155 additions & 114 deletions install/autorun.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -80,116 +80,118 @@ if (-not $SkipConfirm) {
}

# ------------------------------------------------------------------
# 4. 安装 nvm-windows
# 4. 统一预检 & 安装(仅调整顺序,不改动原实现)
# ------------------------------------------------------------------
$needRestart = $false

# ---------- nvm ----------
if (-not (Test-Cmd "nvm")) {
Write-Host "正在安装 nvm-windows..." -ForegroundColor Green
$nvmSetup = "$env:TEMP\nvm-setup.exe"
Invoke-WebRequestWithProxy "https://github.com/coreybutler/nvm-windows/releases/latest/download/nvm-setup.exe" $nvmSetup
Start-Process -Wait -FilePath $nvmSetup -ArgumentList "/silent"
Remove-Item $nvmSetup
Write-Host ""
Write-Host "--------------------------------------------------" -ForegroundColor Yellow
Write-Host "nvm 已安装完毕,但当前 PowerShell 会话尚未识别到它。" -ForegroundColor Yellow
Write-Host "请执行以下任意一步后再继续:" -ForegroundColor Cyan
Write-Host " 1) 关闭本窗口,重新打开一个『管理员』PowerShell后,再次执行脚本;" -ForegroundColor Cyan
Write-Host " 2) 或者再次右键选PS运行本脚本。" -ForegroundColor Cyan
Write-Host "--------------------------------------------------" -ForegroundColor Yellow
Read-Host "按 Enter 键退出本窗口"
exit
$needRestart = $true
} else {
Write-Host "已检测到 nvm,跳过安装" -ForegroundColor Green
}

# -------------------------------------------------
# 5. 安装/切换 Node
# -------------------------------------------------
$needNode = $false
if (Test-Cmd "node") {
$nodeVer = (node -v) -replace '^v','' -split '\.' | ForEach-Object { [int]$_ }
$current = $nodeVer[0]*10000 + $nodeVer[1]*100 + $nodeVer[2]
$require = 20*10000 + 18*100 + 3 # 20.18.3
if ($current -ge $require) {
Write-Host "已检测到 Node v$($nodeVer -join '.') ≥20.18.3,跳过安装" -ForegroundColor Green
} else {
Write-Host "Node 版本低于 20.18.3,将使用 nvm 安装/切换到 20.18.3" -ForegroundColor Yellow
$needNode = $true
}
} else {
Write-Host "未检测到 Node,准备安装" -ForegroundColor Yellow
$needNode = $true
}
if ($needNode) {
nvm install 20.18.3
nvm use 20.18.3
}

# -------------------------------------------------
# 6. 安装 Python 3.11(优先 winget)
# -------------------------------------------------
$pyNeed = $false
$pythonOk = $false
try {
$ver = (python -V 2>$null) -replace 'Python ',''
if ($ver -match '^3\.11') {
Write-Host "已检测到 Python 3.11 ($ver),跳过安装" -ForegroundColor Green
} else {
Write-Host "检测到非 3.11 版本,准备覆盖安装 3.11" -ForegroundColor Yellow
$pyNeed = $true
if ([version]$ver -ge [version]"3.10") {
Write-Host "已检测到 Python $ver ≥ 3.10,跳过安装" -ForegroundColor Green
$pythonOk = $true
}
} catch {
Write-Host "未检测到 Python,准备安装 3.11" -ForegroundColor Yellow
$pyNeed = $true
}
if ($pyNeed) {
} catch {}
if (-not $pythonOk) {
Install-Winget
Write-Host "正在通过 winget 安装 Python 3.11..." -ForegroundColor Green
winget install --id Python.Python.3.11 -e --accept-source-agreements --accept-package-agreements
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
$latestId = (winget search --id Python.Python.* --exact --source winget |
Select-String '^Python\.Python\.\d+' |
ForEach-Object { $_.Matches.Value } |
Sort-Object { [version]($_ -replace 'Python\.Python\.','') } |
Select-Object -Last 1)
if (-not $latestId) { $latestId = "Python.Python.3.12" }
Write-Host "准备通过 winget 安装 $latestId ..." -ForegroundColor Green
winget install --id $latestId -e --accept-source-agreements --accept-package-agreements
$needRestart = $true
}

# -------------------------------------------------
# 7. 安装 Git:winget 优先,失败自动离线
# -------------------------------------------------
if (-not (Test-Cmd "git")) {
# 1) winget 交互式安装
$gitOk = $false
if (Test-Cmd "git") {
Write-Host "已检测到 Git,跳过安装" -ForegroundColor Green
$gitOk = $true
}
if (-not $gitOk) {
# 1) winget 交互
Install-Winget
if (Test-Cmd winget) {
Write-Host "正在通过 winget 安装 Git(交互模式)..." -ForegroundColor Green
try {
winget install --id Git.Git -e --source winget

# 重新加载 PATH(关键)
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") +
";" +
[System.Environment]::GetEnvironmentVariable("Path","User")

# 再检测一次
if (Test-Cmd git) {
Write-Host "Git 安装成功(winget)" -ForegroundColor Green
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
continue
$gitOk = $true # 这里才会真正阻止后面的离线安装
}
} catch {
Write-Host "winget 安装失败,将使用离线包..." -ForegroundColor Yellow
}
}

# 2) winget 失败 → 离线安装
Write-Host "正在解析 Git 最新版本..." -ForegroundColor Green
try {
$latestUri = (Invoke-WebRequest -Uri "https://github.com/git-for-windows/git/releases/latest" -MaximumRedirection 0 -ErrorAction SilentlyContinue).Headers.Location
$ver = if ($latestUri) { $latestUri -replace '.*/tag/v([0-9.]+).*$','$1' } else { "2.51.0" }
} catch {
$ver = "2.51.0"
# 2) winget 失败 → 离线包
if (-not $gitOk) {
Write-Host "正在解析 Git 最新版本..." -ForegroundColor Green
try {
$latestUri = (Invoke-WebRequest -Uri "https://github.com/git-for-windows/git/releases/latest" -MaximumRedirection 0 -ErrorAction SilentlyContinue).Headers.Location
$ver = if ($latestUri) { $latestUri -replace '.*/tag/v([0-9.]+).*$','$1' } else { "2.51.0" }
} catch {
$ver = "2.51.0"
}

Write-Host "正在下载 Git $ver ..." -ForegroundColor Green
$gitSetup = "$env:TEMP\Git-$ver-64-bit.exe"
$gitUrl = "https://github.com/git-for-windows/git/releases/download/v$ver.windows.1/Git-$ver-64-bit.exe"
Invoke-WebRequestWithProxy $gitUrl $gitSetup
Start-Process -Wait -FilePath $gitSetup -ArgumentList "/VERYSILENT /NORESTART /NOCANCEL /SP- /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS"
Remove-Item $gitSetup -Force
$needRestart = $true
}
}

Write-Host "正在下载 Git $ver ..." -ForegroundColor Green
$gitSetup = "$env:TEMP\Git-$ver-64-bit.exe"
$gitUrl = "https://github.com/git-for-windows/git/releases/download/v$ver.windows.1/Git-$ver-64-bit.exe"
Invoke-WebRequestWithProxy $gitUrl $gitSetup
Start-Process -Wait -FilePath $gitSetup -ArgumentList "/VERYSILENT /NORESTART /NOCANCEL /SP- /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS"
Remove-Item $gitSetup -Force
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
} else {
Write-Host "已检测到 Git,跳过安装" -ForegroundColor Green
# ---------- 统一重启提示 ----------
if ($needRestart) {
Write-Host "--------------------------------------------------" -ForegroundColor Yellow
Write-Host "依赖已安装/更新完成,但需重新加载环境变量。" -ForegroundColor Yellow
Write-Host "请关闭本窗口,重新打开『管理员』PowerShell 后再运行本脚本继续后续步骤。" -ForegroundColor Cyan
Write-Host "--------------------------------------------------" -ForegroundColor Yellow
Read-Host "按 Enter 键退出"
exit
}

# -------------------------------------------------
# 8. 安装全局 npm 工具
# -------------------------------------------------
# ------------------------------------------------------------------
# 5. 阶段二:Node / yarn / pm2 / 克隆 / 配置
# ------------------------------------------------------------------
# 此时 nvm 已生效
if (-not (Test-Cmd "node") -or -not (node -v).StartsWith("v20.")) {
Write-Host "正在安装/切换到 Node 20.18.3 ..." -ForegroundColor Green
nvm install 20.18.3
nvm use 20.18.3
}

# 安装 yarn / pm2
$tools = @{
yarn = { npm install -g yarn }
pm2 = { npm install -g pm2 }
Expand Down Expand Up @@ -241,84 +243,123 @@ Use-ProxyIfNeeded -Script {
[System.IO.File]::WriteAllLines($configJson, $jsonText, $utf8NoBom)
}

# 生成 .env(UTF-8 无 BOM,不乱码
$envFile = Join-Path $projectPath ".env"
if (-not (Test-Path $envFile)) {
# 生成 .env(UTF-8 无 BOM)
$envFile = Join-Path $projectPath ".env"
if (-not (Test-Path $envFile)) {
# 如果仓库没带模板,就写一份最小模板(同样无 BOM)
$template = Join-Path $projectPath ".env.development"
if (-not (Test-Path $template)) {
@"
$template = Join-Path $projectPath ".env.development"
if (-not (Test-Path $template)) {
@"
NODE_ENV=development
COOKIE_AUTH_CODE=drpys
API_AUTH_NAME=admin
API_AUTH_CODE=drpys
API_PWD=dzyyds
"@ | Out-File $template -Encoding UTF8
}

}
# 复制模板
Copy-Item $template $envFile
Copy-Item $template $envFile

# 依次输入
$cookieAuth = (Read-Host "网盘入库密码(默认 drpys)").Trim()
$apiUser = (Read-Host "登录用户名(默认 admin)").Trim()
$apiPass = (Read-Host "登录密码(默认 drpys)").Trim()
$apiPwd = (Read-Host "订阅PWD值(默认 dzyyds)").Trim()
$cookieAuth = (Read-Host "网盘入库密码(默认 drpys)").Trim()
$apiUser = (Read-Host "登录用户名(默认 admin)").Trim()
$apiPass = (Read-Host "登录密码(默认 drpys)").Trim()
$apiPwd = (Read-Host "订阅PWD值(默认 dzyyds)").Trim()

# 空值兜底
if ([string]::IsNullOrWhiteSpace($cookieAuth)) { $cookieAuth = 'drpys' }
if ([string]::IsNullOrWhiteSpace($apiUser)) { $apiUser = 'admin' }
if ([string]::IsNullOrWhiteSpace($apiPass)) { $apiPass = 'drpys' }
if ([string]::IsNullOrWhiteSpace($apiPwd)) { $apiPwd = 'dzyyds' }
if ([string]::IsNullOrWhiteSpace($cookieAuth)) { $cookieAuth = 'drpys' }
if ([string]::IsNullOrWhiteSpace($apiUser)) { $apiUser = 'admin' }
if ([string]::IsNullOrWhiteSpace($apiPass)) { $apiPass = 'drpys' }
if ([string]::IsNullOrWhiteSpace($apiPwd)) { $apiPwd = 'dzyyds' }

# 逐行替换,最后统一 UTF-8 无 BOM 写回
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
$lines = [System.IO.File]::ReadAllLines($template, $utf8NoBom)

for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match '^\s*COOKIE_AUTH_CODE\s*=') {
$lines[$i] = "COOKIE_AUTH_CODE = $cookieAuth"
}
elseif ($lines[$i] -match '^\s*API_AUTH_NAME\s*=') {
$lines[$i] = "API_AUTH_NAME = $apiUser"
}
elseif ($lines[$i] -match '^\s*API_AUTH_CODE\s*=') {
$lines[$i] = "API_AUTH_CODE = $apiPass"
}
elseif ($lines[$i] -match '^\s*API_PWD\s*=') {
$lines[$i] = "API_PWD = $apiPwd"
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
$lines = [System.IO.File]::ReadAllLines($template, $utf8NoBom)
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match '^\s*COOKIE_AUTH_CODE\s*=') {
$lines[$i] = "COOKIE_AUTH_CODE = $cookieAuth"
} elseif ($lines[$i] -match '^\s*API_AUTH_NAME\s*=') {
$lines[$i] = "API_AUTH_NAME = $apiUser"
} elseif ($lines[$i] -match '^\s*API_AUTH_CODE\s*=') {
$lines[$i] = "API_AUTH_CODE = $apiPass"
} elseif ($lines[$i] -match '^\s*API_PWD\s*=') {
$lines[$i] = "API_PWD = $apiPwd"
}
}
[System.IO.File]::WriteAllLines($envFile, $lines, $utf8NoBom)
}

[System.IO.File]::WriteAllLines($envFile, $lines, $utf8NoBom)
}
# ---------- Node 依赖 ----------
function Invoke-YarnWithRetry {
param([int]$MaxRetry = 3)
$mirrors = @(
'https://registry.npmmirror.com/',
'https://registry.yarnpkg.com',
'https://registry.npmjs.org'
)
$attempt = 0
while ($attempt -lt $MaxRetry) {
$attempt++
$mirror = $mirrors[$attempt-1]
Write-Host "尝试使用镜像 $mirror 安装 Node 依赖(第 $attempt/$MaxRetry 次)..." -ForegroundColor Cyan
yarn config set registry $mirror | Out-Null
try {
yarn --frozen-lockfile
if ($LASTEXITCODE -eq 0) { return }
} catch {}
}
Write-Host "[ERROR] 所有镜像均失败,请手动执行 yarn" -ForegroundColor Red
}

# Node 依赖
if (-not (Test-Path "node_modules")) {
Write-Host "首次安装 Node 依赖..." -ForegroundColor Yellow
yarn config set registry https://registry.npmmirror.com/
yarn
} elseif ((git diff HEAD^ HEAD --name-only 2>$null) -match [regex]::Escape("yarn.lock")) {
Invoke-YarnWithRetry
} elseif ((git diff HEAD~1 HEAD --name-only 2>$null) -match [regex]::Escape("yarn.lock")) {
Write-Host "检测到 yarn.lock 变动,更新 Node 依赖..." -ForegroundColor Yellow
yarn install --registry https://registry.npmmirror.com/
Invoke-YarnWithRetry
}

# Python 虚拟环境 & 依赖
# ---------- Python 虚拟环境 & 依赖 ----------
$venvActivate = Join-Path $projectPath ".venv\Scripts\Activate.ps1"
if (-not (Test-Path ".venv\pyvenv.cfg")) {
Write-Host "首次创建 Python 虚拟环境..." -ForegroundColor Yellow
python -m venv .venv
}
& $venvActivate
python -m pip install --upgrade pip -q
pip install -r spider\py\base\requirements.txt -i https://mirrors.cloud.tencent.com/pypi/simple -q
Write-Host "虚拟环境创建完成" -ForegroundColor Green
function Invoke-PipWithRetry {
param(
[string]$ReqFile,
[int]$MaxRetry = 3
)
$mirrors = @(
'https://mirrors.cloud.tencent.com/pypi/simple',
'https://pypi.tuna.tsinghua.edu.cn/simple',
'https://pypi.org/simple'
)
$attempt = 0
while ($attempt -lt $MaxRetry) {
$attempt++
$mirror = $mirrors[$attempt-1]
Write-Host "尝试使用镜像 $mirror 安装 Python 依赖(第 $attempt/$MaxRetry 次)..." -ForegroundColor Cyan
try {
pip install -r $ReqFile -i $mirror --no-warn-script-location -q
Write-Host "pip install 完成" -ForegroundColor Green
if ($LASTEXITCODE -eq 0) { return }
} catch {}
}
Write-Host "[ERROR] 所有镜像均失败,请手动执行 pip install" -ForegroundColor Red
}

Invoke-PipWithRetry "spider\py\base\requirements.txt"

if ((git diff HEAD^ HEAD --name-only 2>$null) -match [regex]::Escape("spider\py\base\requirements.txt")) {
if ((git diff HEAD~1 HEAD --name-only 2>$null) -match [regex]::Escape("spider\py\base\requirements.txt")) {
Write-Host "检测到 requirements.txt 变动,更新 Python 依赖..." -ForegroundColor Yellow
pip install -r spider\py\base\requirements.txt -i https://mirrors.cloud.tencent.com/pypi/simple -q
Invoke-PipWithRetry "spider\py\base\requirements.txt"
}

# PM2
# ---------- PM2 ----------
if (-not (pm2 list | Select-String "drpyS.*online")) {
Write-Host "首次启动 PM2 进程..." -ForegroundColor Yellow
pm2 start index.js --name drpyS --update-env
Expand Down