Skip to content

Commit 9bb9061

Browse files
committed
feat(release): 安装包自建服务器托管——下载页直链与自动更新双源
- nginx 新增 /downloads/ 静态托管(单连接限速 2MB/s + 单 IP 4 并发,yml/json 禁缓存) - docker-compose.prod.yml 挂载 /opt/Entropydecrease/downloads(独立于 website 目录) - deploy.sh 预创建静态资源目录,保证 CI 用户可写 - release.yml 发版后同步安装包至服务器:先传 exe/blockmap 后传元数据消除更新 404 窗口期,生成 latest.json 供官网消费,仅保留最近 3 版 - electron-builder 双 publish:generic(自建服务器)为自动更新源,GitHub 保留为灾备源;artifactName 去除空格 - 下载页新增 DownloadCta 组件:latest.json 动态版本 + 服务器直链,失败回退 GitHub 备用源 - 修复 lint-staged 的 website eslint 命令:ESLint 9 需显式 --config 指向 website/eslint.config.mjs
1 parent 705e778 commit 9bb9061

8 files changed

Lines changed: 226 additions & 64 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,62 @@ jobs:
7272
- uses: softprops/action-gh-release@v2
7373
with:
7474
files: release-*/*
75+
76+
# ---------------------------------------------------------------
77+
# 同步 Windows 安装包至自建服务器(国内主下载源 + 自动更新源)
78+
# latest.json 供官网下载页动态展示版本/直链;latest.yml 供 electron-updater
79+
# ---------------------------------------------------------------
80+
- name: Generate latest.json for website
81+
run: |
82+
YML=release-windows-latest/latest.yml
83+
VERSION=$(grep -m1 '^version:' "$YML" | awk '{print $2}')
84+
FILE=$(grep -m1 '^path:' "$YML" | sed 's/^path: //')
85+
SIZE=$(stat -c%s "release-windows-latest/$FILE")
86+
SHA256=$(sha256sum "release-windows-latest/$FILE" | awk '{print $1}')
87+
DATE=$(date -u +%F)
88+
printf '{"version":"%s","fileName":"%s","size":%s,"sha256":"%s","releaseDate":"%s"}\n' \
89+
"$VERSION" "$FILE" "$SIZE" "$SHA256" "$DATE" > release-windows-latest/latest.json
90+
cat release-windows-latest/latest.json
91+
92+
- name: Ensure downloads directory on server
93+
uses: appleboy/ssh-action@v1
94+
with:
95+
host: ${{ secrets.SERVER_HOST }}
96+
username: ${{ secrets.SERVER_USER }}
97+
key: ${{ secrets.SSH_PRIVATE_KEY }}
98+
script: mkdir -p /opt/Entropydecrease/downloads
99+
100+
# 先传二进制(exe/blockmap),后传元数据(yml/json):
101+
# 避免 latest.yml 率先就位时指向尚未上传完成的安装包(更新 404 窗口期)
102+
- name: Upload installer binaries to server
103+
uses: appleboy/scp-action@v0.1.7
104+
with:
105+
host: ${{ secrets.SERVER_HOST }}
106+
username: ${{ secrets.SERVER_USER }}
107+
key: ${{ secrets.SSH_PRIVATE_KEY }}
108+
source: release-windows-latest/*.exe,release-windows-latest/*.blockmap
109+
target: /opt/Entropydecrease/downloads
110+
strip_components: 1
111+
112+
- name: Publish update metadata (last, avoids 404 window)
113+
uses: appleboy/scp-action@v0.1.7
114+
with:
115+
host: ${{ secrets.SERVER_HOST }}
116+
username: ${{ secrets.SERVER_USER }}
117+
key: ${{ secrets.SSH_PRIVATE_KEY }}
118+
source: release-windows-latest/latest.yml,release-windows-latest/latest.json
119+
target: /opt/Entropydecrease/downloads
120+
strip_components: 1
121+
122+
# 保留策略:仅保留最近 3 个版本的安装包,防止磁盘增长
123+
- name: Prune old versions (keep last 3)
124+
uses: appleboy/ssh-action@v1
125+
with:
126+
host: ${{ secrets.SERVER_HOST }}
127+
username: ${{ secrets.SERVER_USER }}
128+
key: ${{ secrets.SSH_PRIVATE_KEY }}
129+
script: |
130+
cd /opt/Entropydecrease/downloads
131+
ls -t -- *.exe 2>/dev/null | tail -n +4 | while read -r f; do
132+
rm -f -- "$f" "$f.blockmap"
133+
done

‎client/electron-builder.yml‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
# Electron Builder 打包配置 - 熵减桌面端
22
appId: com.entropydecrease.app
33
productName: "Entropy decrease"
4+
# 双发布源:第一项(generic = 自建服务器)被写入 app-update.yml 作为
5+
# electron-updater 的自动更新源;GitHub 保留为开源分发/灾备源。
6+
# 注意:存量客户端的 app-update.yml 仍指向 GitHub,过渡期内
7+
# GitHub Release 资产(含 latest.yml)不可停发,否则存量用户无法升级到新源。
48
publish:
5-
provider: github
6-
owner: Aparencia
7-
repo: Entropydecrease
9+
- provider: generic
10+
url: https://entropydecrease.com/downloads
11+
- provider: github
12+
owner: Aparencia
13+
repo: Entropydecrease
14+
# 统一产物命名(无空格):避免默认 "Entropy decrease Setup x.y.z.exe"
15+
# 在 URL/服务器文件名中产生空格转义问题
816
directories:
917
output: release
1018
buildResources: build
19+
artifactName: "Entropydecrease-Setup-${version}.${ext}"
1120
asarUnpack:
1221
- "**/*.node"
1322
files:

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@
3030
},
3131
"lint-staged": {
3232
"client/**/*.{ts,tsx}": "npm --prefix client exec oxlint --",
33-
"website/**/*.{ts,tsx}": "npm --prefix website exec eslint --"
33+
"website/**/*.{ts,tsx}": "npm --prefix website exec eslint -- --config website/eslint.config.mjs"
3434
}
3535
}

‎server/deploy.sh‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,14 @@ fi
6767
log_info "$ENV_FILE 已就绪 ✓"
6868

6969
# ---------------------------------------------------------------------------
70-
# 3. 构建并启动服务
70+
# 3. 准备宿主机目录(Nginx bind mount 依赖,缺失时 Docker 会以 root 自动创建,
71+
# 提前创建保证 CI 用户可写入)
72+
# ---------------------------------------------------------------------------
73+
log_info "准备静态资源目录..."
74+
mkdir -p /opt/Entropydecrease/website /opt/Entropydecrease/downloads
75+
76+
# ---------------------------------------------------------------------------
77+
# 4. 构建并启动服务
7178
# ---------------------------------------------------------------------------
7279
log_info "构建 Docker 镜像(首次构建约需 3-5 分钟)..."
7380

@@ -78,13 +85,13 @@ log_info "启动所有服务..."
7885
docker compose -f docker-compose.prod.yml --env-file "$ENV_FILE" up -d
7986

8087
# ---------------------------------------------------------------------------
81-
# 4. 等待服务就绪
88+
# 5. 等待服务就绪
8289
# ---------------------------------------------------------------------------
8390
log_info "等待服务启动..."
8491
sleep 10
8592

8693
# ---------------------------------------------------------------------------
87-
# 5. 健康检查
94+
# 6. 健康检查
8895
# ---------------------------------------------------------------------------
8996
log_info "执行健康检查..."
9097

‎server/docker-compose.prod.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ services:
146146
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
147147
- ./nginx/certs:/etc/nginx/certs:ro
148148
- /opt/Entropydecrease/website:/usr/share/nginx/html:ro
149+
# 安装包下载目录:独立于 website(其部署会 rm 清空目录),由 release.yml 同步
150+
- /opt/Entropydecrease/downloads:/usr/share/nginx/downloads:ro
149151
depends_on:
150152
- ai-gateway
151153
- sync-service

‎server/nginx/nginx.conf‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ http {
2525
# Rate limiting
2626
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
2727
limit_req_zone $binary_remote_addr zone=ai:10m rate=30r/m;
28+
# 安装包下载并发限制(按 IP),避免大文件下载挤占 API 带宽
29+
limit_conn_zone $binary_remote_addr zone=dl_conn:10m;
2830

2931
# Upstream 后端服务
3032
upstream ai_gateway {
@@ -188,6 +190,26 @@ http {
188190
proxy_set_header Host $host;
189191
}
190192

193+
# -----------------------------------------------------------------
194+
# 安装包下载目录(由 release.yml CI 同步至 /opt/Entropydecrease/downloads)
195+
# ^~ 阻止服务器级正则 location(如图片/字体规则)抢占匹配;
196+
# 同时作为 electron-updater generic provider 的更新源
197+
# -----------------------------------------------------------------
198+
location ^~ /downloads/ {
199+
root /usr/share/nginx;
200+
autoindex off;
201+
# 单连接限速 2MB/s + 单 IP 最多 4 并发,保护 AI 网关/同步服务带宽
202+
limit_rate 2m;
203+
limit_conn dl_conn 4;
204+
205+
# 更新元数据(latest.yml / latest.json)禁止缓存,
206+
# 否则客户端会长期读到旧版本信息;允许 www 子域跨域读取
207+
location ~* \.(yml|json)$ {
208+
add_header Cache-Control "no-cache" always;
209+
add_header Access-Control-Allow-Origin "*" always;
210+
}
211+
}
212+
191213
# -----------------------------------------------------------------
192214
# 官网静态站点(Next.js 静态导出,由 CI 同步至 /opt/Entropydecrease/website)
193215
# -----------------------------------------------------------------

‎website/app/download/page.tsx‎

Lines changed: 5 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// @ai-context
2-
// 下载页:应用窗口 Mockup、下载入口、版本轨迹与开源信息。Download page: app mockup, download CTA, changelog.
2+
// 下载页:应用窗口 Mockup、下载入口(DownloadCta 服务器直链 + GitHub 备用)、版本轨迹与开源信息。
3+
// Download page: app mockup, download CTA (server-hosted with GitHub fallback), changelog.
34
// Why: Mockup 为纯 CSS 风格化示意而非截图,避免版本迭代后截图过期。
45
"use client";
56

67
import { motion } from "framer-motion";
8+
import { DownloadCta } from "@/components/DownloadCta";
79
import { GlowOrb } from "@/components/GlowOrb";
810
import { SectionReveal } from "@/components/SectionReveal";
911

@@ -46,13 +48,6 @@ const CHANGELOG = [
4648
},
4749
];
4850

49-
const SYSTEM_REQ = [
50-
{ label: "操作系统", value: "Windows 10 / 11 (64位)" },
51-
{ label: "处理器", value: "Intel i5 / AMD Ryzen 5 或更高" },
52-
{ label: "内存", value: "8 GB RAM 以上" },
53-
{ label: "存储空间", value: "500 MB 可用空间" },
54-
];
55-
5651
/* ---------- 应用窗口 Mockup ---------- */
5752

5853
function AppMockup() {
@@ -182,57 +177,10 @@ export default function DownloadPage() {
182177
</SectionReveal>
183178
</section>
184179

185-
{/* 下载区 */}
180+
{/* 下载区(服务器直链 + 动态版本 + GitHub 备用源) */}
186181
<section className="max-w-3xl mx-auto px-6 mb-24">
187182
<SectionReveal>
188-
<div
189-
className="rounded-3xl p-10 text-center"
190-
style={{
191-
background: "var(--kb-bg-elevated)",
192-
border: "1px solid var(--kb-glass-border)",
193-
boxShadow: "var(--kb-shadow-brand)",
194-
}}
195-
>
196-
<div className="flex items-center justify-center gap-3 mb-6">
197-
<span
198-
className="w-12 h-12 rounded-2xl flex items-center justify-center text-lg font-bold text-white"
199-
style={{ background: "linear-gradient(135deg, var(--kb-brand-400), var(--kb-accent-500))" }}
200-
>
201-
熵
202-
</span>
203-
<div className="text-left">
204-
<h2 className="font-serif text-xl font-bold text-kb-text">熵减 Entropydecrease</h2>
205-
<p className="text-xs text-kb-text3">v0.15.0 正式版 · 2026-07</p>
206-
</div>
207-
</div>
208-
209-
<a
210-
href="https://github.com/Aparencia/Entropydecrease/releases/latest"
211-
target="_blank"
212-
rel="noopener noreferrer"
213-
className="inline-block px-12 py-4 rounded-2xl text-white font-medium text-lg transition-all duration-500 hover:scale-[1.04] active:scale-[0.97] mb-6"
214-
style={{
215-
background: "linear-gradient(135deg, var(--kb-amber), var(--kb-accent-400))",
216-
boxShadow: "var(--kb-shadow-accent)",
217-
}}
218-
>
219-
⬇ 下载 Windows 版
220-
</a>
221-
222-
<p className="text-xs text-kb-text3 mb-8">
223-
约 120 MB · 适用于 Windows 10/11 (64位) · 免费开源
224-
</p>
225-
226-
{/* 系统要求 */}
227-
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 text-left">
228-
{SYSTEM_REQ.map((req) => (
229-
<div key={req.label} className="rounded-xl p-3.5" style={{ background: "var(--kb-bg-tertiary)" }}>
230-
<p className="text-[10px] text-kb-text3 uppercase tracking-wider mb-1">{req.label}</p>
231-
<p className="text-xs text-kb-text2 leading-snug">{req.value}</p>
232-
</div>
233-
))}
234-
</div>
235-
</div>
183+
<DownloadCta />
236184
</SectionReveal>
237185
</section>
238186

‎website/components/DownloadCta.tsx‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// @ai-context
2+
// 下载 CTA 卡片:从自建服务器 latest.json 动态获取版本信息与安装包直链(国内主源),
3+
// 拉取失败时静默回退 GitHub Releases。Download CTA: server-hosted direct link with GitHub fallback.
4+
// Why: 国内访问 GitHub Release 不稳定,服务器直链提升首下载转化;版本信息动态化消灭硬编码。
5+
"use client";
6+
7+
import { useEffect, useState } from "react";
8+
9+
/** 自建服务器下载源(release.yml CI 发版时同步安装包与 latest.json) */
10+
const DOWNLOAD_BASE = "https://entropydecrease.com/downloads";
11+
/** GitHub Releases 备用源 */
12+
const GITHUB_RELEASES = "https://github.com/Aparencia/Entropydecrease/releases/latest";
13+
14+
/** 兜底展示信息(latest.json 不可达时使用) */
15+
const FALLBACK = { version: "0.15.0", date: "2026-07", sizeMB: 120 };
16+
17+
/** release.yml 生成的 latest.json 结构 */
18+
interface LatestInfo {
19+
version: string;
20+
fileName: string;
21+
size: number;
22+
releaseDate: string;
23+
}
24+
25+
const SYSTEM_REQ = [
26+
{ label: "操作系统", value: "Windows 10 / 11 (64位)" },
27+
{ label: "处理器", value: "Intel i5 / AMD Ryzen 5 或更高" },
28+
{ label: "内存", value: "8 GB RAM 以上" },
29+
{ label: "存储空间", value: "500 MB 可用空间" },
30+
];
31+
32+
export function DownloadCta() {
33+
const [latest, setLatest] = useState<LatestInfo | null>(null);
34+
35+
useEffect(() => {
36+
const controller = new AbortController();
37+
fetch(`${DOWNLOAD_BASE}/latest.json`, { signal: controller.signal, cache: "no-store" })
38+
.then((res) => (res.ok ? res.json() : Promise.reject(new Error(`HTTP ${res.status}`))))
39+
.then((data: LatestInfo) => {
40+
if (data?.version && data?.fileName) setLatest(data);
41+
})
42+
.catch(() => {
43+
/* 服务器源不可达:保持 GitHub 回退,不打扰用户 */
44+
});
45+
return () => controller.abort();
46+
}, []);
47+
48+
// 服务器直链优先;元数据未就绪时回退 GitHub Release 页
49+
const downloadUrl = latest
50+
? `${DOWNLOAD_BASE}/${encodeURIComponent(latest.fileName)}`
51+
: GITHUB_RELEASES;
52+
const version = latest?.version ?? FALLBACK.version;
53+
const date = latest?.releaseDate ?? FALLBACK.date;
54+
const sizeMB = latest ? Math.round(latest.size / 1024 / 1024) : FALLBACK.sizeMB;
55+
56+
return (
57+
<div
58+
className="rounded-3xl p-10 text-center"
59+
style={{
60+
background: "var(--kb-bg-elevated)",
61+
border: "1px solid var(--kb-glass-border)",
62+
boxShadow: "var(--kb-shadow-brand)",
63+
}}
64+
>
65+
<div className="flex items-center justify-center gap-3 mb-6">
66+
<span
67+
className="w-12 h-12 rounded-2xl flex items-center justify-center text-lg font-bold text-white"
68+
style={{ background: "linear-gradient(135deg, var(--kb-brand-400), var(--kb-accent-500))" }}
69+
>
70+
熵
71+
</span>
72+
<div className="text-left">
73+
<h2 className="font-serif text-xl font-bold text-kb-text">熵减 Entropydecrease</h2>
74+
<p className="text-xs text-kb-text3">v{version} 正式版 · {date}</p>
75+
</div>
76+
</div>
77+
78+
<a
79+
href={downloadUrl}
80+
{...(latest ? {} : { target: "_blank", rel: "noopener noreferrer" })}
81+
className="inline-block px-12 py-4 rounded-2xl text-white font-medium text-lg transition-all duration-500 hover:scale-[1.04] active:scale-[0.97] mb-4"
82+
style={{
83+
background: "linear-gradient(135deg, var(--kb-amber), var(--kb-accent-400))",
84+
boxShadow: "var(--kb-shadow-accent)",
85+
}}
86+
>
87+
⬇ 下载 Windows 版
88+
</a>
89+
90+
<p className="text-xs text-kb-text3 mb-2">
91+
约 {sizeMB} MB · 适用于 Windows 10/11 (64位) · 免费开源
92+
</p>
93+
<p className="text-xs mb-8">
94+
<a
95+
href={GITHUB_RELEASES}
96+
target="_blank"
97+
rel="noopener noreferrer"
98+
className="text-kb-text3 underline underline-offset-2 transition-colors duration-300 hover:text-kb-text2"
99+
>
100+
下载缓慢?使用 GitHub 备用源 ↗
101+
</a>
102+
</p>
103+
104+
{/* 系统要求 */}
105+
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 text-left">
106+
{SYSTEM_REQ.map((req) => (
107+
<div key={req.label} className="rounded-xl p-3.5" style={{ background: "var(--kb-bg-tertiary)" }}>
108+
<p className="text-[10px] text-kb-text3 uppercase tracking-wider mb-1">{req.label}</p>
109+
<p className="text-xs text-kb-text2 leading-snug">{req.value}</p>
110+
</div>
111+
))}
112+
</div>
113+
</div>
114+
);
115+
}

0 commit comments

Comments
 (0)