You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
6.4 KiB

# Pack production upload bundle (上传到服务器)
# Usage: powershell -ExecutionPolicy Bypass -File deploy/pack-server-upload.ps1
$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
$Out = Join-Path $Root ([char]0x4e0a + [char]0x4f20 + [char]0x5230 + [char]0x670d + [char]0x52a1 + [char]0x5668)
$Code = Join-Path $Out ([char]0x4ee3 + [char]0x7801)
$Db = Join-Path $Out ([char]0x6570 + [char]0x636e + [char]0x5e93)
Write-Host "pack: clean $Out"
if (Test-Path $Out) {
Remove-Item -Recurse -Force $Out
}
New-Item -ItemType Directory -Path $Code -Force | Out-Null
New-Item -ItemType Directory -Path $Db -Force | Out-Null
function Copy-TreeLiteral {
param(
[Parameter(Mandatory = $true)][string]$SourceDir,
[Parameter(Mandatory = $true)][string]$DestDir
)
$stack = New-Object System.Collections.Stack
$stack.Push(@($SourceDir, $DestDir))
while ($stack.Count -gt 0) {
$pair = $stack.Pop()
$currentSrc = $pair[0]
$currentDst = $pair[1]
if (-not (Test-Path -LiteralPath $currentDst)) {
New-Item -ItemType Directory -Path $currentDst -Force | Out-Null
}
Get-ChildItem -LiteralPath $currentSrc -Force | ForEach-Object {
if ($_.PSIsContainer) {
$stack.Push(@($_.FullName, (Join-Path $currentDst $_.Name)))
} elseif ($_.Extension -ne ".md") {
Copy-Item -LiteralPath $_.FullName -Destination (Join-Path $currentDst $_.Name) -Force
}
}
}
}
$RequiredRoutes = @(
"src\app\api\mails\[id]\route.ts",
"src\app\api\mails\[id]\import\route.ts",
"src\app\api\mails\[id]\do\confirm\route.ts",
"src\app\api\mails\[id]\transfer\confirm\route.ts",
"src\app\api\mails\[id]\work-order\confirm\route.ts",
"src\app\api\mails\[id]\attachments\[attId]\download\route.ts",
"src\app\api\compensations\[id]\retry\route.ts",
"src\app\api\imports\[importId]\retry\route.ts",
"src\app\api\settings\mailboxes\[id]\route.ts",
"src\app\api\settings\imap\filters\[id]\route.ts",
"src\app\api\oauth\callback\[provider]\route.ts"
)
function Convert-ShFilesToLf {
param([Parameter(Mandatory = $true)][string]$BaseDir)
Get-ChildItem -Path $BaseDir -Filter "*.sh" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
$text = [System.IO.File]::ReadAllText($_.FullName)
$fixed = $text -replace "`r`n", "`n" -replace "`r", "`n"
if ($text -ne $fixed) {
[System.IO.File]::WriteAllText($_.FullName, $fixed, [System.Text.UTF8Encoding]::new($false))
Write-Host "pack: LF $($_.FullName.Substring($BaseDir.Length).TrimStart('\','/'))"
}
}
}
function Test-RequiredRoutes {
param([string]$BaseDir)
$missing = @()
foreach ($rel in $RequiredRoutes) {
$path = Join-Path $BaseDir $rel
if (-not (Test-Path -LiteralPath $path)) {
$missing += $rel
}
}
if ($missing.Count -gt 0) {
throw "pack: missing dynamic API routes (bracket paths): $($missing -join ', ')"
}
}
$CodeDirs = @(
"src",
"prisma",
"public",
"scripts"
)
foreach ($dir in $CodeDirs) {
$src = Join-Path $Root $dir
if (-not (Test-Path $src)) {
throw "missing dir: $dir"
}
Write-Host "pack: copy $dir"
$dstDir = Join-Path $Code $dir
Copy-TreeLiteral -SourceDir $src -DestDir $dstDir
}
$deploySrc = Join-Path $Root "deploy"
$deployDst = Join-Path $Code "deploy"
New-Item -ItemType Directory -Path $deployDst -Force | Out-Null
Get-ChildItem -Path $deploySrc -Force | Where-Object {
$_.Name -ne "database" -and
$_.Name -ne "pack-server-upload.ps1" -and
$_.Name -ne "pack-readme-template.md"
} | ForEach-Object {
Copy-Item -Path $_.FullName -Destination $deployDst -Recurse -Force
}
$CodeFiles = @(
"package.json",
"pnpm-lock.yaml",
"next.config.ts",
"tsconfig.json",
"tsconfig.worker.json",
"next-env.d.ts",
"Dockerfile",
".dockerignore"
)
foreach ($file in $CodeFiles) {
$src = Join-Path $Root $file
if (Test-Path $src) {
Copy-Item -Path $src -Destination (Join-Path $Code $file) -Force
Write-Host "pack: copy $file"
}
}
$envExample = Join-Path $Root "deploy\.env.bt-host-mysql.example"
if (Test-Path $envExample) {
Copy-Item -Path $envExample -Destination (Join-Path $deployDst ".env.bt-host-mysql.example") -Force
Write-Host "pack: copy deploy/.env.bt-host-mysql.example"
}
$staleEnvPath = Join-Path $Code ".env"
if ((Test-Path -LiteralPath $Code) -and (Test-Path -LiteralPath $staleEnvPath)) {
Remove-Item -LiteralPath $staleEnvPath -Force
Write-Host "pack: removed stale 代码/.env"
}
$DbFiles = @("email_forecast.sql", "email_forecast.sql.gz", "README.md")
foreach ($file in $DbFiles) {
$src = Join-Path $Root "deploy\database\$file"
if (Test-Path $src) {
Copy-Item -Path $src -Destination (Join-Path $Db $file) -Force
Write-Host "pack: database $file"
}
}
function Get-DirSizeBytes($path) {
(Get-ChildItem -Path $path -Recurse -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
}
$codeSize = [math]::Round((Get-DirSizeBytes $Code) / 1MB, 2)
$dbSize = [math]::Round((Get-DirSizeBytes $Db) / 1KB, 1)
$fileCount = (Get-ChildItem -Path $Out -Recurse -File).Count
Write-Host ""
Write-Host "pack: normalize shell scripts (CRLF -> LF)"
Convert-ShFilesToLf -BaseDir $Code
Write-Host ""
Write-Host "pack: verify dynamic API routes"
Test-RequiredRoutes -BaseDir $Code
$ForbiddenInCode = @(
(Join-Path $Code ".env"),
(Join-Path $Code "node_modules"),
(Join-Path $Code ".next"),
(Join-Path $Code "data"),
(Join-Path $Code ".git")
)
foreach ($bad in $ForbiddenInCode) {
if (Test-Path -LiteralPath $bad) {
throw "pack: forbidden path present in 代码/: $bad"
}
}
Write-Host ""
Write-Host "pack: done -> $Out"
Write-Host "pack: files=$fileCount codeMB=$codeSize dbKB=$dbSize"
Write-Host "pack: SAFETY ok (no .env/node_modules/.next/data/.git in 代码/)"
$readmeSrc = Join-Path $Root "deploy\pack-readme-template.md"
if (Test-Path $readmeSrc) {
Copy-Item $readmeSrc (Join-Path $Out "README.md") -Force
}
$releaseNotesSrc = Join-Path $Root "deploy\宝塔部署说明.md"
if (Test-Path $releaseNotesSrc) {
Copy-Item $releaseNotesSrc (Join-Path $Out "RELEASE-NOTES.md") -Force
Write-Host "pack: RELEASE-NOTES.md"
}