支持同机双域名部署选号网hfb_show

Caddy按CADDY_SHOW_DOMAIN托管选号网静态并反代API;一键脚本可构建hfb_show并写入配置。
This commit is contained in:
yml2213
2026-07-16 22:24:40 +08:00
parent a4189db4ca
commit a0fc23647b
12 changed files with 267 additions and 38 deletions
+39 -7
View File
@@ -3,14 +3,16 @@
目标访问地址:
```text
https://hfb.221329.cc.cd
https://主站域名 # CADDY_DOMAIN,哈夫币系统
https://选号网域名 # CADDY_SHOW_DOMAIN(可选),hfb_show 静态选号站
https://monitor.主站域名 # Beszel 监控
```
生产部署使用 Caddy 作为公网入口,只暴露宿主机 `80``443` 端口。Caddy 会自动申请和续签 HTTPS 证书,并直接托管前端静态资源(前端 `dist` 在构建 Caddy 镜像时打入);后端、MySQL、Redis、MinIO 均通过 Docker 内网通信。
生产部署使用 Caddy 作为公网入口,只暴露宿主机 `80``443` 端口。Caddy 会自动申请和续签 HTTPS 证书,并直接托管前端静态资源(主站 `dist` 与可选的选号网 `show-dist` 在构建 Caddy 镜像时打入);后端、MySQL、Redis、MinIO 均通过 Docker 内网通信。选号网与主站**共用同一 backend**,浏览器访问选号网域名下的 `/api/*` 由 Caddy 反代到本机 backend**不会直连数据库**。
部署前请确认:
- 域名 A 记录已解析到服务器公网 IP。
- 主站域名 A 记录已解析到服务器公网 IP;启用选号网时,选号网域名也要解析到同一 IP。
- 服务器安全组或防火墙已放行 `80/tcp``443/tcp`
- 服务器上没有其他服务占用 `80``443`
- 国内服务器如需对外访问,请先确认域名备案和云厂商限制。
@@ -36,6 +38,31 @@ STORAGE_ACCESS_KEY_ID
STORAGE_SECRET_ACCESS_KEY
```
### 同机部署选号网(hfb_show,可选)
`backend/.env` 增加(域名不要带 `https://`):
```text
CADDY_SHOW_DOMAIN=show.example.com
HFB_SHOW_DIR=../hfb_show
```
要求:
1. 服务器上 `hfb_show``hfb_sys` 同级目录(或 `HFB_SHOW_DIR` 指向真实路径)。
2. 部署机可执行 `npm`(脚本会在 build Caddy 前构建选号网 dist)。
3. 选号网域名 DNS 已指向本机。
一键脚本会自动:
- `npm ci && npm run build` 选号网;
-`dist` 同步到 `deploy/caddy/show-dist`
- 生成 Caddy 站点配置 `deploy/caddy/conf.d/show.caddy`
- 构建 Caddy 镜像时打入主站 + 选号网静态资源;
- 为选号网域名申请 HTTPS,并把 `/api/*` 反代到 `backend:8080`
不配置 `CADDY_SHOW_DOMAIN` 则行为与原来一致,仅部署主站。
如果正式对外运营,还需要配置真实短信和实名服务,避免继续使用测试配置。
## 2. 一键部署
@@ -102,15 +129,20 @@ docker compose -f deploy/docker-compose.prod.yml run --rm --no-deps backend \
## 5. 验证
```bash
curl https://你的域名/health
curl https://你的域名/api/health
curl https://主站域名/health
curl https://主站域名/api/health
# 若启用了选号网:
curl -I https://选号网域名/
curl https://选号网域名/api/health
curl "https://选号网域名/api/listings?page=1&page_size=1"
```
浏览器访问:
```text
https://你的域名
https://你的域名/你配置的后台入口/login
https://主站域名
https://主站域名/你配置的后台入口/login
https://选号网域名 # 选号网首页
```
Caddy 证书和 ACME 账号数据保存在 Docker 卷 `caddy_data``caddy_config` 中。不要随意删除这两个卷,否则 Caddy 会重新申请证书,频繁重建可能触发证书签发频率限制。
+35 -28
View File
@@ -8,6 +8,35 @@
header_up X-Real-IP {remote_host}
}
# 公开 API 反代片段:主站与选号网共用。
(public_api) {
@api_sse {
path /api/chats/events /api/admin/chats/events
}
handle @api_sse {
reverse_proxy backend:8080 {
flush_interval -1
import backend_proxy
}
}
handle /api/* {
request_body {
max_size 11MB
}
reverse_proxy backend:8080 {
import backend_proxy
}
}
handle /health {
reverse_proxy backend:8080 {
import backend_proxy
}
}
}
{$CADDY_DOMAIN} {
encode zstd gzip
@@ -20,33 +49,7 @@
Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data: blob:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'"
}
# 聊天 SSE 长连接:关闭缓冲,逐条推送。
@api_sse {
path /api/chats/events /api/admin/chats/events
}
handle @api_sse {
reverse_proxy backend:8080 {
flush_interval -1
import backend_proxy
}
}
handle /api/* {
# 后端上传上限 10MB,这里前置拦掉超大请求体(留 1MB 余量)。
request_body {
max_size 11MB
}
reverse_proxy backend:8080 {
import backend_proxy
}
}
handle /health {
reverse_proxy backend:8080 {
import backend_proxy
}
}
import public_api
# 生产后台页面使用构建时配置的隐藏入口,旧 /admin 入口直接返回 404。
@legacy_admin path /admin /admin/*
@@ -54,7 +57,7 @@
respond 404
}
# 其余流量返回前端静态资源,未命中的路径交给 SPA 处理
# 其余流量返回主站前端静态资源。
handle {
root * /srv/dist
try_files {path} /index.html
@@ -62,6 +65,10 @@
}
}
# 选号网(hfb_show):仅当 CADDY_SHOW_DOMAIN 有值时由部署脚本启用 conf.d/show.caddy。
# 未配置时 conf.d 为空,不影响主站。
import /etc/caddy/conf.d/*.caddy
# Beszel 自带登录与权限控制;单独子域避免与业务站点的 CSP、路由相互影响。
monitor.{$CADDY_DOMAIN} {
encode zstd gzip
+5 -2
View File
@@ -1,4 +1,4 @@
# 第一阶段:构建前端静态资源
# 第一阶段:构建主站前端静态资源
FROM node:24-alpine AS build
ARG VITE_ADMIN_BASE_PATH=/manage-7x9k2p
@@ -10,8 +10,11 @@ RUN npm install
COPY frontend/ .
RUN npm run build
# 第二阶段:Caddy 作为公网入口,同时托管前端 dist
# 第二阶段:Caddy 作为公网入口,托管主站 + 选号网静态资源
FROM caddy:2.10-alpine
COPY --from=build /src/dist /srv/dist
# show-dist 由 deploy-prod.sh 在 build 前准备(hfb_show 构建产物或占位页)
COPY deploy/caddy/show-dist /srv/show-dist
EXPOSE 80 443
View File
+4
View File
@@ -0,0 +1,4 @@
# 占位片段:保证 import conf.d/*.caddy 至少匹配一个文件。
# 选号网站点由 deploy-prod.sh 写入 conf.d/show.caddy。
(optional_sites_base) {
}
View File
+32
View File
@@ -0,0 +1,32 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>选号网未构建</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 4rem auto;
padding: 0 1rem;
color: #17233d;
line-height: 1.6;
}
code {
background: #f1f5f9;
padding: 0.1em 0.35em;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>选号网静态资源尚未构建</h1>
<p>
请配置 <code>CADDY_SHOW_DOMAIN</code><code>HFB_SHOW_DIR</code> 后重新执行
<code>./scripts/deploy-prod.sh</code>,或手动构建
<code>hfb_show</code> 并将 <code>dist/</code> 复制到
<code>deploy/caddy/show-dist/</code>
</p>
</body>
</html>
+35
View File
@@ -0,0 +1,35 @@
# 由 scripts/deploy-prod.sh 根据 CADDY_SHOW_DOMAIN 生成到 conf.d/show.caddy
# 选号网静态页 + 同源 /api 反代到 hfb_sys backend(本机 Docker 网络,非公网绕行)
__CADDY_SHOW_DOMAIN__ {
encode zstd gzip
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
}
# 与主站共用后端公开接口;浏览器访问选号网域名下的 /api/* 即可,无需跨域。
handle /api/* {
request_body {
max_size 11MB
}
reverse_proxy backend:8080 {
header_up X-Real-IP {remote_host}
}
}
handle /health {
reverse_proxy backend:8080 {
header_up X-Real-IP {remote_host}
}
}
handle {
root * /srv/show-dist
try_files {path} /index.html
file_server
}
}
+3
View File
@@ -10,11 +10,14 @@ services:
TZ: Asia/Shanghai
CADDY_DOMAIN: ${CADDY_DOMAIN:?请先导出 CADDY_DOMAIN,或使用 scripts/deploy-prod.sh 部署}
CADDY_EMAIL: ${CADDY_EMAIL:?请先导出 CADDY_EMAIL,或使用 scripts/deploy-prod.sh 部署}
# 选号网域名可选;未配置时 conf.d 为空,不影响主站
CADDY_SHOW_DOMAIN: ${CADDY_SHOW_DOMAIN:-}
ports:
- "80:80"
- "443:443"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy/conf.d:/etc/caddy/conf.d:ro
- caddy_data:/data
- caddy_config:/config
deploy: