完善全新部署数据保护与域名配置
Some checks are pending
Security Checks / security (push) Waiting to run

补充部署时的历史运行数据保护,移除前端旧域名兜底,并将 Nginx 模板改为用户确认域名占位符。
This commit is contained in:
smail 2026-10-01 11:24:07 +08:00
parent bb7e73643e
commit 0c4ad939aa
6 changed files with 62 additions and 13 deletions

View File

@ -40,7 +40,7 @@ foreach ($item in $receipt.files) {
}
& node (Join-Path $PSScriptRoot 'build-state.mjs') verify --deployed
if ($LASTEXITCODE -ne 0) { throw '部署校验失败,禁止启动;旧程序备份保留在 out/deployment-history' }
Write-Output '最新编译包已部署到 yunx-test;配置、证书、数据库未被构建过程覆盖。'
Write-Output '最新程序已部署到 yunx-test;正常升级不会覆盖配置、证书或数据库。全新部署必须使用独立的空运行目录。'
} finally {
if ($lock) { $lock.Dispose() }
}

View File

@ -66,6 +66,29 @@ if [ -L /run/smail/admin-control.sock ] || { [ -e /run/smail/admin-control.sock
exit 1
fi
# 全新安装不继承目标目录中的配置、证书或业务数据,也不自动清理它们。
for directory in config data ssl log plugins; do
target="$runtime_dir/$directory"
if [ -d "$target" ] && [ -n "$(find "$target" -mindepth 1 -maxdepth 1 -print -quit)" ]; then
echo "全新部署已停止:$target 非空。请先确认并处理旧数据,再通过初始化页面重新配置。" >&2
exit 1
fi
done
for file in config.json config.dev.json smail.db smail-state.db; do
if [ -e "$runtime_dir/$file" ] || [ -L "$runtime_dir/$file" ]; then
echo "全新部署已停止:发现历史运行文件 $file,不会自动继承或删除。" >&2
exit 1
fi
done
if [ -e /etc/smail/smail.env ]; then
echo "全新部署已停止:请先确认并处理 /etc/smail/smail.env 中的旧部署环境配置。" >&2
exit 1
fi
if [ -d /etc/systemd/system/smail.service.d ] && [ -n "$(find /etc/systemd/system/smail.service.d -mindepth 1 -maxdepth 1 -print -quit)" ]; then
echo "全新部署已停止:请先确认并处理 Smail 的旧服务覆盖配置。" >&2
exit 1
fi
# 安装备份存放在系统备份目录,不在部署根目录遗留 .bak 文件。
backup_dir="$(mktemp -d /var/backups/smail-deploy-XXXXXXXX)"
for name in smail smail-admin; do

View File

@ -115,6 +115,26 @@ $adminSource = Resolve-PackageFile -Candidates @(
) -Description "smail-admin.exe"
$resolvedInstallRoot = [IO.Path]::GetFullPath($InstallRoot)
if (Test-Path -LiteralPath $resolvedInstallRoot) {
$rootInfo = Get-Item -LiteralPath $resolvedInstallRoot -Force
if (-not $rootInfo.PSIsContainer -or $rootInfo.Attributes -band [IO.FileAttributes]::ReparsePoint) {
throw "安装目录不能是文件或链接"
}
}
foreach ($name in @('config', 'data', 'ssl', 'log', 'plugins')) {
$existing = Join-Path $resolvedInstallRoot $name
if (-not (Test-Path -LiteralPath $existing)) { continue }
$info = Get-Item -LiteralPath $existing -Force
if (-not $info.PSIsContainer -or $info.Attributes -band [IO.FileAttributes]::ReparsePoint -or
(Get-ChildItem -LiteralPath $existing -Force | Select-Object -First 1)) {
throw "全新部署已停止:$existing 不是空目录。请先确认并处理旧数据,再通过初始化页面重新配置。"
}
}
foreach ($name in @('config.json', 'config.dev.json', 'smail.db', 'smail-state.db')) {
if (Test-Path -LiteralPath (Join-Path $resolvedInstallRoot $name)) {
throw "全新部署已停止:发现历史运行文件 $name,不会自动继承或删除。"
}
}
New-Item -ItemType Directory -Path $resolvedInstallRoot -Force | Out-Null
New-Item -ItemType Directory -Path (Join-Path $resolvedInstallRoot "config") -Force | Out-Null
New-Item -ItemType Directory -Path (Join-Path $resolvedInstallRoot "data") -Force | Out-Null

View File

@ -1,3 +1,5 @@
# 部署前必须将域名占位符替换为用户在初始化页面确认的邮件主域名和后台域名。
# 此模板不包含测试域名;不要将未填写的模板直接安装到 Nginx。
server {
listen 80 default_server;
listen [::]:80 default_server;
@ -8,7 +10,7 @@ server {
server {
listen 80;
listen [::]:80;
server_name solit.cn mail.solit.cn web.solit.cn smtp.solit.cn imap.solit.cn pop.solit.cn;
server_name __SMAIL_MAIL_DOMAIN__ __SMAIL_WEB_DOMAIN__;
location ^~ /.well-known/acme-challenge/ {
root /var/lib/smail-acme;
@ -17,7 +19,7 @@ server {
}
location / {
return 308 https://web.solit.cn$request_uri;
return 308 https://__SMAIL_WEB_DOMAIN__$request_uri;
}
}
@ -31,16 +33,16 @@ server {
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name solit.cn mail.solit.cn smtp.solit.cn imap.solit.cn pop.solit.cn;
server_name __SMAIL_MAIL_DOMAIN__;
ssl_certificate /opt/smail/ssl/custom.crt;
ssl_certificate_key /opt/smail/ssl/custom.key;
return 308 https://web.solit.cn$request_uri;
return 308 https://__SMAIL_WEB_DOMAIN__$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name web.solit.cn;
server_name __SMAIL_WEB_DOMAIN__;
ssl_certificate /opt/smail/ssl/custom.crt;
ssl_certificate_key /opt/smail/ssl/custom.key;
@ -54,7 +56,7 @@ server {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Connection "";
proxy_ssl_server_name on;
proxy_ssl_name web.solit.cn;
proxy_ssl_name __SMAIL_WEB_DOMAIN__;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;

View File

@ -86,6 +86,7 @@ export function usePage() {
}
const loadBaseInfo = async () => {
currentDomain.value = ''
try {
const infoRes = await http.post('/api/v1/user/info', {})
const email = infoRes?.data?.email

View File

@ -156,7 +156,8 @@
link
size="small"
class="btn-card-copy"
@click="copyText(`imap.${currentDomain || 'solit.cn'}:993`, 'IMAP 参数')"
:disabled="!currentDomain"
@click="copyText(`imap.${currentDomain}:993`, 'IMAP 参数')"
>
复制
</el-button>
@ -165,7 +166,7 @@
<div class="proto-fields">
<div class="field-item">
<span class="field-k">服务器</span>
<span class="field-v font-mono">imap.{{ currentDomain || 'solit.cn' }}</span>
<span class="field-v font-mono">{{ currentDomain ? `imap.${currentDomain}` : '未配置邮件域名' }}</span>
</div>
<div class="field-item">
<span class="field-k">端口</span>
@ -193,7 +194,8 @@
link
size="small"
class="btn-card-copy"
@click="copyText(`pop.${currentDomain || 'solit.cn'}:995`, 'POP3 参数')"
:disabled="!currentDomain"
@click="copyText(`pop.${currentDomain}:995`, 'POP3 参数')"
>
复制
</el-button>
@ -202,7 +204,7 @@
<div class="proto-fields">
<div class="field-item">
<span class="field-k">服务器</span>
<span class="field-v font-mono">pop.{{ currentDomain || 'solit.cn' }}</span>
<span class="field-v font-mono">{{ currentDomain ? `pop.${currentDomain}` : '未配置邮件域名' }}</span>
</div>
<div class="field-item">
<span class="field-k">端口</span>
@ -230,7 +232,8 @@
link
size="small"
class="btn-card-copy"
@click="copyText(`smtp.${currentDomain || 'solit.cn'}:465`, 'SMTP 参数')"
:disabled="!currentDomain"
@click="copyText(`smtp.${currentDomain}:465`, 'SMTP 参数')"
>
复制
</el-button>
@ -239,7 +242,7 @@
<div class="proto-fields">
<div class="field-item">
<span class="field-k">服务器</span>
<span class="field-v font-mono">smtp.{{ currentDomain || 'solit.cn' }}</span>
<span class="field-v font-mono">{{ currentDomain ? `smtp.${currentDomain}` : '未配置邮件域名' }}</span>
</div>
<div class="field-item">
<span class="field-k">端口</span>