YouYiDianNotificationCenter/scripts/deploy-material-host.sh
2026-07-15 18:06:20 +08:00

236 lines
6.6 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
target=/mnt/PoolOne/apps/youyidian-notification-center
archive=/tmp/youyidian-notification-center-release.tar.gz
source_env=/mnt/PoolOne/apps/youyidian-3d/.env
backup_root=/mnt/PoolOne/apps/youyidian-notification-center-backups
test -f "$archive"
test -f "$source_env"
if tar -tzf "$archive" | grep -Eq '(^/|(^|/)\.\.(/|$))'; then
echo '安装包包含不安全路径' >&2
exit 1
fi
mkdir -p "$target" "$backup_root"
if test -f "$target/package.json"; then
stamp="$(date +%Y%m%d%H%M%S)"
tar -czf "$backup_root/source-$stamp.tar.gz" --exclude=.env --exclude=data -C "$target" .
fi
tar -xzf "$archive" -C "$target"
python3 - <<'PY'
from pathlib import Path
import json
import os
import secrets
import shutil
import time
def parse_env(path: Path):
result = {}
if not path.exists():
return result
for raw in path.read_text(encoding='utf-8').splitlines():
line = raw.strip()
if not line or line.startswith('#') or '=' not in line:
continue
key, value = line.split('=', 1)
key = key.strip()
value = value.strip()
if value.startswith('"') and value.endswith('"'):
try:
value = json.loads(value)
except Exception:
value = value[1:-1]
elif value.startswith("'") and value.endswith("'"):
value = value[1:-1]
result[key] = value
return result
def quoted(value):
return json.dumps(str(value), ensure_ascii=False)
source_path = Path('/mnt/PoolOne/apps/youyidian-3d/.env')
target_path = Path('/mnt/PoolOne/apps/youyidian-notification-center/.env')
source = parse_env(source_path)
existing = parse_env(target_path)
app_key = (
source.get('DINGTALK_WORK_NOTICE_CLIENT_ID')
or source.get('DINGTALK_WORK_NOTICE_APP_KEY')
or source.get('DINGTALK_CLIENT_ID')
or source.get('DINGTALK_APP_KEY')
)
app_secret = (
source.get('DINGTALK_WORK_NOTICE_CLIENT_SECRET')
or source.get('DINGTALK_WORK_NOTICE_APP_SECRET')
or source.get('DINGTALK_CLIENT_SECRET')
or source.get('DINGTALK_APP_SECRET')
)
robot_code = source.get('DINGTALK_ROBOT_CODE')
if not app_key or not app_secret or not robot_code:
raise SystemExit('素材库生产环境缺少统一机器人配置')
try:
old_projects = json.loads(existing.get('YYD_NOTIFICATION_PROJECTS_JSON', '[]'))
except Exception:
old_projects = []
by_code = {
str(item.get('code')): item
for item in old_projects
if isinstance(item, dict)
}
def secret_for(code):
old = str(by_code.get(code, {}).get('secret') or '')
return old if len(old) >= 24 else secrets.token_hex(32)
projects = [
{
'code': 'material_library',
'name': '素材库',
'secret': secret_for('material_library'),
'allowedHosts': ['sc.jiansun.vip'],
},
{
'code': 'fixed_asset',
'name': '固定资产',
'secret': secret_for('fixed_asset'),
'allowedHosts': ['www.jsgdzc.cn', 'jsgdzc.cn'],
},
]
center_lines = [
'PORT=3088',
'DINGTALK_APP_KEY=' + quoted(app_key),
'DINGTALK_APP_SECRET=' + quoted(app_secret),
'DINGTALK_ROBOT_CODE=' + quoted(robot_code),
'YYD_NOTIFICATION_PROJECTS_JSON=' + quoted(
json.dumps(projects, ensure_ascii=False, separators=(',', ':'))
),
'WORKER_ENABLED=true',
'WORKER_INTERVAL_MS=1000',
'READ_STATUS_INTERVAL_MS=5000',
]
target_path.write_text('\n'.join(center_lines) + '\n', encoding='utf-8')
os.chmod(target_path, 0o600)
material_secret = next(
item['secret'] for item in projects if item['code'] == 'material_library'
)
updates = {
'YYD_NOTIFICATION_CENTER_URL': 'https://sc.jiansun.vip/notification-center',
'YYD_NOTIFICATION_CENTER_PROJECT_CODE': 'material_library',
'YYD_NOTIFICATION_CENTER_PROJECT_SECRET': material_secret,
'YYD_NOTIFICATION_CENTER_TEST_USER_IDS': '',
}
backup = source_path.with_name(
'.env.before-yyd-notification-' + time.strftime('%Y%m%d%H%M%S')
)
shutil.copy2(source_path, backup)
os.chmod(backup, 0o600)
lines = source_path.read_text(encoding='utf-8').splitlines()
seen = set()
output = []
for line in lines:
if '=' in line and not line.lstrip().startswith('#'):
key = line.split('=', 1)[0].strip()
if key in updates:
output.append(key + '=' + quoted(updates[key]))
seen.add(key)
continue
output.append(line)
for key, value in updates.items():
if key not in seen:
output.append(key + '=' + quoted(value))
source_path.write_text('\n'.join(output) + '\n', encoding='utf-8')
os.chmod(source_path, 0o600)
print('通知中心与素材库适配配置已写入')
PY
cd "$target"
image="youyidian-notification-center:${YYD_NOTIFICATION_IMAGE_TAG:-latest}"
if docker image inspect "$image" >/dev/null 2>&1; then
docker compose up -d --no-build
else
docker compose up -d --build
fi
healthy=0
for _ in $(seq 1 30); do
if curl -fsS --max-time 3 http://127.0.0.1:3088/health >/tmp/yyd-notification-health.json; then
healthy=1
break
fi
sleep 2
done
if test "$healthy" != 1; then
docker compose logs --tail 120
exit 1
fi
python3 - <<'PY'
from pathlib import Path
source = Path('/etc/frp/frpc-sc-entry.toml').read_text(encoding='utf-8')
header = source.split('[[proxies]]', 1)[0]
header = header.replace(
'/root/frp/frpc-sc-entry.log',
'/root/frp/frpc-notify-entry.log',
)
proxy = '''[[proxies]]
name = "youyidian-notification-center"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3088
remotePort = 2326
healthCheck.type = "http"
healthCheck.path = "/health"
healthCheck.intervalSeconds = 15
healthCheck.maxFailed = 3
healthCheck.timeoutSeconds = 5
'''
Path('/etc/frp/frpc-notify-entry.toml').write_text(
header.rstrip() + '\n\n' + proxy,
encoding='utf-8',
)
PY
chmod 600 /etc/frp/frpc-notify-entry.toml
cat > /etc/systemd/system/frpc-notify-entry.service <<'EOF'
[Unit]
Description=FRP Client for Youyidian notification center
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service
[Service]
Type=simple
Restart=always
RestartSec=10s
LimitNOFILE=65535
ExecStart=/root/frp-upgrade/frp_0.67.0_linux_amd64/frpc -c /etc/frp/frpc-notify-entry.toml
StandardOutput=append:/root/frp/frpc-notify-entry.service.log
StandardError=append:/root/frp/frpc-notify-entry.service.log
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now frpc-notify-entry.service
sleep 3
systemctl is-active --quiet frpc-notify-entry.service
rm -f "$archive"
curl -fsS http://127.0.0.1:3088/health
echo
printf 'CENTER_CONTAINER='
docker inspect -f '{{.State.Status}}' youyidian-notification-center
printf 'FRP_SERVICE='
systemctl is-active frpc-notify-entry.service