# ============================================================ # 将「智慧缫丝系统」(f9-web-ai-test) 发布到本机 IIS # - 自动安装 ASP.NET Core Hosting Bundle(IIS 托管 .NET 8 必需) # - 创建应用池 / 站点 / 目录权限,并做一次自检 # 必须以【管理员身份】运行 # ============================================================ $ErrorActionPreference = 'Stop' # 全过程写入日志文件,便于提权窗口关闭后回看 $logDir = Join-Path $PSScriptRoot '_runlog' New-Item -ItemType Directory -Force -Path $logDir | Out-Null try { Start-Transcript -Path (Join-Path $logDir 'deploy-iis.log') -Force -ErrorAction Stop | Out-Null } catch { } # 发布物放在项目外的独立目录,与代码分离(便于测试与备份) $publishDir = 'C:\F9WebAiTest-IIS' $siteName = 'F9WebAiTest' $appPoolName = 'F9WebAiTest' $preferredPort = 80 $ancmPath = 'C:\Program Files\IIS\Asp.Net Core Module\V2\aspnetcorev2.dll' $hostingBundle = 'https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/8.0.21/dotnet-hosting-8.0.21-win.exe' $installer = Join-Path $env:TEMP 'dotnet-hosting-8.0.21-win.exe' function Info($m) { Write-Host "[部署] $m" -ForegroundColor Cyan } function Warn($m) { Write-Host "[警告] $m" -ForegroundColor Yellow } # ---------- 0. 前置校验 ---------- if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw '请以管理员身份运行本脚本' } if (-not (Test-Path $publishDir)) { throw "发布目录不存在:$publishDir" } if (-not (Test-Path (Join-Path $publishDir 'wwwroot\index.html'))) { throw "前端产物缺失:$publishDir\wwwroot\index.html" } if (-not (Test-Path (Join-Path $publishDir 'F9WebAiTest.Api.dll'))) { throw "后端产物缺失:$publishDir\F9WebAiTest.Api.dll" } if (-not (Test-Path 'C:\Windows\System32\inetsrv\appcmd.exe')) { throw '本机未安装 IIS,请先启用 IIS 功能' } Import-Module WebAdministration # ---------- 1. ASP.NET Core Hosting Bundle ---------- if (-not (Test-Path $ancmPath)) { Info '未检测到 AspNetCoreModuleV2,开始下载 Hosting Bundle 8.0.21 ...' Invoke-WebRequest -Uri $hostingBundle -OutFile $installer -UseBasicParsing Info '下载完成,静默安装中(约 1-2 分钟)...' Start-Process -FilePath $installer -ArgumentList '/install', '/quiet', '/norestart' -Wait if (-not (Test-Path $ancmPath)) { throw 'Hosting Bundle 安装失败,请手动安装后重试' } Info '安装完成,重启 IIS 使模块生效 ...' & iisreset /noforce | Out-Null } else { Info 'AspNetCoreModuleV2 已安装,跳过' } # ---------- 2. 选择端口 ---------- $port = $preferredPort $occupied = netstat -ano | Select-String 'LISTENING' | Select-String ":$port\s" if ($occupied) { $port = 8080 Warn "端口 $preferredPort 已被占用,改用 $port" } # ---------- 3. 应用池(无托管代码,与 .NET Core 应用匹配)---------- if (Test-Path "IIS:\AppPools\$appPoolName") { Info "应用池 $appPoolName 已存在,更新配置" } else { New-WebAppPool -Name $appPoolName | Out-Null Info "已创建应用池 $appPoolName" } Set-ItemProperty "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value '' Set-ItemProperty "IIS:\AppPools\$appPoolName" -Name startMode -Value 'AlwaysRunning' # ---------- 4. 站点 ---------- if (Test-Path "IIS:\Sites\$siteName") { Info "站点 $siteName 已存在,更新物理路径与应用池" Set-ItemProperty "IIS:\Sites\$siteName" -Name physicalPath -Value $publishDir Set-ItemProperty "IIS:\Sites\$siteName" -Name applicationPool -Value $appPoolName if (-not (Get-WebBinding -Name $siteName -Protocol http -Port $port -ErrorAction SilentlyContinue)) { New-WebBinding -Name $siteName -Protocol http -Port $port -IPAddress '*' Info "已补充绑定 http://*:$port" } } else { New-Website -Name $siteName -PhysicalPath $publishDir -Port $port -ApplicationPool $appPoolName -Force | Out-Null Info "已创建站点 $siteName" } # ---------- 5. 目录权限(应用池身份需读写:SQLite 库文件、日志)---------- $aclUser = "IIS AppPool\$appPoolName" & icacls $publishDir /grant "${aclUser}:(OI)(CI)M" /T /Q | Out-Null Info "已授予 $aclUser 读写权限" # ---------- 6. 启动 ---------- if ((Get-WebAppPoolState -Name $appPoolName).Value -ne 'Started') { Start-WebAppPool -Name $appPoolName } if ((Get-Website -Name $siteName).State -ne 'Started') { Start-Website -Name $siteName } Start-Sleep -Seconds 4 # ---------- 7. 自检 ---------- $url = "http://localhost:$port/" Info '开始自检 ...' try { $r = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 40 Info "首页:HTTP $($r.StatusCode)(前端已托管)" } catch { Warn "首页访问失败:$($_.Exception.Message)" Warn "如为 500,请查看 事件查看器 → Windows 日志 → 应用程序,或打开 $publishDir\web.config 把 stdoutLogEnabled 改为 true 后重试" } try { $login = Invoke-RestMethod -Uri "${url}api/auth/login" -Method Post -ContentType 'application/json' ` -Body '{"userName":"admin","password":"123456"}' -TimeoutSec 40 Info "登录接口:code=$($login.code) $($login.message)" } catch { Warn "登录接口调用失败:$($_.Exception.Message)" } Write-Host '' Info "部署完成,访问地址:$url" Info "API 文档:${url}swagger 默认账号:admin / 123456"