站点:纯静态 HTML + CSS + 原生 JS,零框架零构建
页面:首页 / 合作品牌 / 荣誉资质 / 客户案例 / 关于我们
内容:
- site/ 站点本体(含已压缩 webp 图片,约 4.8 MB)
- deploy/ Docker Compose / Caddy / Apache 部署配置
- _tools/ 开发自检脚本与部署脚本
- Dockerfile 自建 nginx 镜像方案
- README.md 项目文档
设计:品牌黄 #ffc107 + 白,深蓝仅用于页脚;菜单长期置顶;
交互动效只加在关键词与区块级元素上。
素材说明:原始素材(背景图/老网站/方案/荣誉资质/合作品牌,约 290 MB)
未纳入版本控制,站点使用的是 site/assets 内已处理压缩的图片。
90 lines
3.8 KiB
PowerShell
90 lines
3.8 KiB
PowerShell
# 把站点同步到服务器 10.0.0.131
|
||
#
|
||
# 用法:
|
||
# powershell -ExecutionPolicy Bypass -File _tools\deploy-server.ps1
|
||
#
|
||
# 前置:本机已安装 ssh / scp(Windows 10+ 自带 OpenSSH),且能访问 10.0.0.131:22
|
||
# 说明:
|
||
# - 站点文件通过「本地打 tar 包 -> scp -> 远端解包」同步。
|
||
# 不要用 PowerShell 管道直接传 tar 流,它会破坏二进制内容
|
||
# (会出现 tar: Skipping to next header)。
|
||
# - 更新内容不需要重建镜像或重启容器,覆盖文件即可(bind mount)。
|
||
# - 改了 Caddyfile 才需要 restart。
|
||
|
||
param(
|
||
[string]$Server = "10.0.0.131",
|
||
[string]$User = "root",
|
||
[string]$Remote = "/www/wwwroot/xuanheng-site",
|
||
[switch]$WithConfig # 加这个参数才同步 Caddyfile / docker-compose.yml
|
||
)
|
||
|
||
$ErrorActionPreference = "Stop"
|
||
$Root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
||
$Target = "$User@$Server"
|
||
|
||
# ---------- SSH 免交互登录(askpass 方式,兼容 Windows OpenSSH) ----------
|
||
$Tmp = Join-Path $env:TEMP ("xh-deploy-" + (Get-Random))
|
||
New-Item -ItemType Directory -Force -Path $Tmp | Out-Null
|
||
|
||
if (-not $env:XH_SERVER_PASSWORD) {
|
||
Write-Host "请先设置服务器密码到环境变量(只在本次会话生效):" -ForegroundColor Yellow
|
||
Write-Host ' $env:XH_SERVER_PASSWORD = "你的密码"' -ForegroundColor Yellow
|
||
Write-Host "然后重新运行本脚本。"
|
||
exit 1
|
||
}
|
||
|
||
# askpass 只把密码交给 ssh 自己,不写进命令历史
|
||
Set-Content -Path "$Tmp\askpass.cmd" -Value "@echo off`r`necho $env:XH_SERVER_PASSWORD" -Encoding ascii
|
||
$env:DISPLAY = "localhost:0"
|
||
$env:SSH_ASKPASS = "$Tmp\askpass.cmd"
|
||
$env:SSH_ASKPASS_REQUIRE = "force"
|
||
|
||
$sshOpts = @(
|
||
"-o", "StrictHostKeyChecking=no",
|
||
"-o", "UserKnownHostsFile=$Tmp\known_hosts",
|
||
"-o", "PreferredAuthentications=password",
|
||
"-o", "PubkeyAuthentication=no",
|
||
"-o", "NumberOfPasswordPrompts=1",
|
||
"-o", "ConnectTimeout=20"
|
||
)
|
||
|
||
function Invoke-Remote([string]$Cmd) {
|
||
& ssh @sshOpts $Target $Cmd
|
||
if ($LASTEXITCODE -ne 0) { throw "远程命令失败:$Cmd" }
|
||
}
|
||
|
||
try {
|
||
Write-Host "[1/4] 确认远端目录"
|
||
Invoke-Remote "mkdir -p $Remote/site $Remote/caddy-data $Remote/caddy-config $Remote/logs && chown -R 1000:1000 $Remote/caddy-data $Remote/caddy-config $Remote/logs && echo OK"
|
||
|
||
Write-Host "[2/4] 上传站点文件"
|
||
$tar = Join-Path $Tmp "site.tar"
|
||
Push-Location (Join-Path $Root "site")
|
||
try { & tar -cf $tar . } finally { Pop-Location }
|
||
Write-Host (" 打包完成:{0:N0} KB" -f ((Get-Item $tar).Length / 1KB))
|
||
|
||
& scp @sshOpts $tar "${Target}:/tmp/xh-site.tar"
|
||
if ($LASTEXITCODE -ne 0) { throw "scp 上传失败" }
|
||
Invoke-Remote "cd $Remote/site && tar -xf /tmp/xh-site.tar && rm -f /tmp/xh-site.tar && echo ' 解包完成,共'\$(find . -type f | wc -l)'个文件'"
|
||
|
||
if ($WithConfig) {
|
||
Write-Host "[3/4] 上传配置"
|
||
foreach ($f in @("Caddyfile", "docker-compose.yml")) {
|
||
& scp @sshOpts (Join-Path $Root "deploy\server\$f") "${Target}:$Remote/$f"
|
||
if ($LASTEXITCODE -ne 0) { throw "$f 上传失败" }
|
||
}
|
||
Invoke-Remote "cd $Remote && sed -i '1s/^\xEF\xBB\xBF//' Caddyfile docker-compose.yml && docker run --rm -v ${Remote}:/work caddy:2.11.2 caddy validate --config /work/Caddyfile --adapter caddyfile 2>&1 | tail -2"
|
||
Invoke-Remote "cd $Remote && docker compose up -d && sleep 3 && docker compose ps"
|
||
} else {
|
||
Write-Host "[3/4] 跳过配置同步(未加 -WithConfig)"
|
||
}
|
||
|
||
Write-Host "[4/4] 验证"
|
||
Invoke-Remote "for p in / /healthz /brands /honors /cases /about; do printf ' %-12s ' \$p; curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8081\$p; done"
|
||
|
||
Write-Host "`n完成。站点地址: http://${Server}:8081/" -ForegroundColor Green
|
||
}
|
||
finally {
|
||
Remove-Item -Recurse -Force $Tmp -ErrorAction SilentlyContinue
|
||
}
|