1. 网站信息设置之图标设置
在当前后端项目中,网站图标(Favicon)的处理方式为“动态生成外部 URL 链接”。
以下是具体的处理逻辑:
1.1. 图标 URL 的生成逻辑 (utils/faviconUtils.js)
后端不再将图标下载到本地服务器,而是根据网站域名生成指向第三方图标服务的 URL:
- 优先使用 DuckDuckGo 服务:生成的格式为
https://icons.duckduckgo.com/ip3/{domain}.ico。 - Google 服务兜底:如果 URL 解析出现异常,会退而使用 Google 的图标 API:
https://www.google.com/s2/favicons?sz=64&domain_url={websiteUrl}。 - 支持自定义:如果用户手动提供了图标 URL,后端会优先采用用户提供的地址。
1.2. 业务层的处理流程 (services/websiteService.js)
- 创建网站时:系统会自动调用
fetchFavicon方法,根据用户输入的网站地址生成对应的图标 URL,并将其存入数据库。 - 更新网站时:
- 如果用户修改了网站的
url(导致域名变化),或者当前记录中缺失图标,系统会重新生成图标 URL。 - 如果域名未发生变化,则继续沿用现有的图标 URL,避免不必要的更新。
- 如果用户修改了网站的
- 删除网站时:由于不再存储本地物理文件,删除操作仅需清理数据库记录,无需处理文件删除逻辑(相关方法已变更为 no-op)。
1.3. 数据存储
图标信息直接以字符串形式存储在 SQLite 数据库 websites 表的 favicon_url 字段中。
1.4. 自定义设置技巧(可选操作,可以不填,自动获取)
1.4.1. 方式一:使用网络图片 URL
- GitHub 图片的正确地址格式:
- Raw 格式(推荐):
https://raw.githubusercontent.com/你的用户名/仓库名/main/图片路径(main也可能是master,看仓库主支名字) - jsDelivr CDN 格式(更快):
https://cdn.jsdelivr.net/gh/你的用户名/仓库名@main/图片路
- Raw 格式(推荐):
- 示例:
- 假设你的 GitHub 用户名是
zhangsan,仓库名是my-icons,图片在仓库根目录叫logo.png,那么填写:https://raw.githubusercontent.com/zhangsan/my-icons/main/logo.png - 或者用 CDN 加速:
https://cdn.jsdelivr.net/gh/zhangsan/my-icons@main/logo.png - 在添加网站的表单中,找到"图标地址"或"Favicon"输入框,粘贴上述地址即可。
- 假设你的 GitHub 用户名是
- 获取网站的真实favicon地址
1.4.2. 方式二:存放在项目后端服务中(仅仅对非cf部署情况有效)
- 在图标地址栏输入相对路径,如
/data/icons/custom.ico - 要使用这个相对路径,需要确保,后端服务器
/data目录下有相应的文件或者文件夹。可以在部署的时候将/data/incos/这个文件夹挂载到本地某个具有图片资源的文件夹
2. nginx配置模版
2.1. ✅ 优化后的完美模板
server {
listen 80;
server_name localhost; # 监听域名,可以根据需要修改
root /usr/share/nginx/html; # 前端静态文件根目录
index index.html;
# =======================================================
# 处理 favicon.ico 请求(直接提供文件)
location = /favicon.ico {
expires 30d;
add_header Cache-Control "public, immutable";
}
# =======================================================
# 个人导航主站 (必须放在特定的 location 后面)
location / {
try_files $uri $uri/ /index.html; # SPA 应用,处理路由问题
}
location /api/ {
# 直接代理到变量地址,完美支持 IP 或 域名
proxy_pass ${BACKEND_PROTO}://${BACKEND_ADDR};
# 强制设置 Host 头,对云服务/外部域名极其重要
proxy_set_header Host ${BACKEND_HOST};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 开启 SNI 支持:如果是 https 域名,这行保命;如果是 http IP,这行无副作用
proxy_ssl_server_name on;
}
}
2.2. 💡 变量赋值示例(供参考验证)
当使用这个完美模板时,针对两种场景,环境变量应该这样传:
2.2.1. 场景 1:后端是局域网 IP(比如你 PVE 里的另一个容器)
BACKEND_PROTO=httpBACKEND_ADDR=192.168.3.165:8080BACKEND_HOST=192.168.3.165:8080(或者直接用$host)
3. docker-compose
前端容器启动后,其内置的 Nginx 会充当反向代理,将请求跨过互联网转发给后端的 VPS。
3.1. 🚀 远程镜像模式 (docker-compose.yml)
version: '3.8'
services:
sitebox-frontend:
image: your-username/sitebox-frontend:latest
container_name: sitebox-frontend
restart: always
ports:
- "80:80"
environment:
# 【关键】这里填后端 VPS 的公网 IP 或域名
- BACKEND_PROTO=http
- BACKEND_ADDR=1.2.3.4:3000 # 替换为后端 VPS 的真实公网 IP
- BACKEND_HOST=1.2.3.4 # 对应 IP
# 如果后端 VPS 套了 Nginx 且有域名和 HTTPS,则改为:
# - BACKEND_PROTO=https
# - BACKEND_ADDR=api.yourdomain.com
# - BACKEND_HOST=api.yourdomain.com
运行命令:
# 启动
docker-compose up -d
# 查看日志
docker-compose logs -f
3.2. 🛠️ 本地构建模式 (docker-compose.build.yml)
在前端 VPS 上直接从源码构建:
version: '3.8'
services:
sitebox-frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: sitebox-frontend
restart: always
ports:
- "80:80"
environment:
- BACKEND_PROTO=http
- BACKEND_ADDR=1.2.3.4:3000
- BACKEND_HOST=1.2.3.4
运行命令:
# 启动
docker-compose -f docker-compose.build.yml up -d --build
# 查看日志
docker-compose logs -f
4. 容器环境部署的时候的环境变量
4.1. 地址变量(可选)
可选的原因
- 可以在浏览器直接设置地址,如果设置了就不走nginx去往后端
- 镜像内默认使用
ENV BACKEND_PROTO=httpENV BACKEND_ADDR=localhost:3000ENV BACKEND_HOST=localhost
4.2. nginx规则文件(可选)
没有特殊需求,可以不修改配置文件
4.2.1. 常规docker容器
先在宿主机
/mnt/pve/app/npm/目录下新建文件default.conf.template然后将宿主机:
/mnt/pve/app/npm/挂载到 容器内:/etc/nginx/templates/权限检查
4.2.2. PVE-9.1版本lxc直接运行镜像部署方案
对于pve9.1版本开始的,原生运行 OCI/Docker 镜像到 LXC 容器 的新特性,不需要安装 Docker 引擎,直接用镜像构建 LXC 根文件系统。
预设我们在
id=106的lxc容器内部署镜像挂载目录:
pct set 106 -mp0 /mnt/pve/app/npm/,mp=/etc/nginx/templates/权限设置:
chown -R 100000:100000 /mnt/pve/app/npm/配置文件创建:
/mnt/pve/app/npm/目录下新建文件default.conf.template检查权限:
ls -lh /mnt/pve/app/npm/原因:lxc容器的root对应到pve宿主机,也只是一个特殊的普通用户
5. 非容器环境部署
cd ./frontend
npm install
npx http-server -p 8080
6. 前端后端地址设置(重点)
前端支持在页面右下角「后端地址设置」中配置 API 主机地址。
6.1. 填写规则
- 只填主机地址,不要填
/api - 正确示例:
https://api.example.comhttp://192.168.1.10:3000https://sb.sb.dpdns.org
- 错误示例:
https://api.example.com/api(会重复拼接)https://api.example.com/v1(不支持带业务路径)
系统会自动拼接 /api。
6.2. 具体场景分析
- 场景 A:使用域名 + HTTPS
services:
frontend-nginx:
build: ./frontend
ports:
- "80:80"
environment:
- BACKEND_ADDR=sb.nuaa.dpdns.org
- BACKEND_PROTO=https
- BACKEND_HOST=sb.nuaa.dpdns.org
- 场景 B:使用域名 + HTTP
environment:
- BACKEND_ADDR=sb.nuaa.dpdns.org:80
- BACKEND_PROTO=http
- BACKEND_HOST=sb.nuaa.dpdns.org
- 场景 C:使用 IP + HTTP
environment:
- BACKEND_ADDR=192.168.1.100:3000
- BACKEND_PROTO=http
- BACKEND_HOST=192.168.1.100
- 场景 D:使用 IP + HTTPS
environment:
- BACKEND_ADDR=192.168.1.100:443
- BACKEND_PROTO=https
- BACKEND_HOST=192.168.1.100
关键点说明
| 环境变量 | 说明 | 示例值 |
|---|---|---|
BACKEND_ADDR | 后端地址(域名或IP:端口) | sb.nuaa.dpdns.org 或 192.168.1.100:3000 |
BACKEND_PROTO | 协议 | http 或 https |
BACKEND_HOST | Host 头(通常与 BACKEND_ADDR 域名部分相同) | sb.nuaa.dpdns.org 或 192.168.1.100 |
6.3. 清空设置后行为
- 清空会移除 localStorage 中的
backendUrl - 前端回退到默认
/api
6.4. 对于nginx已经设置了地址的情况
- 首次打开页面的时候还是会检查缓存,如果没有地址,就会有设置界面弹出
- 如果在设置界面设置了地址,就会根据设置的地址进行请求,nginx的地址将会无效原理
- 如果继续使用nginx的地址,界面不设置地址,关闭之后,会缓存
backendSettingDismissed=true,记录到本页面曾打开过,后续刷新页面不再弹窗显示,直到清空浏览器缓存
6.5. 对于前端部署在cf或者远端vps无法访问本地docker的情况
- 提供docker实时信息是否获取设置按钮,会记录选择缓存到本地,清空浏览器缓存需要重新设置
6.6. 浏览器填写地址对于nginx的影响
情景说明:
- 前端部署在192.168.3.165
- 后端部署在192.168.3.166
6.6.1. 默认情况(不填写地址):走 Nginx 代理
config.js代码逻辑是:export const backendUrl = '/api';
- 浏览器请求地址:
http://192.168.3.165/api/xxxxxxxx - 谁处理: 浏览器发现请求的是
.165(即前端 Nginx 所在的服务器)。 - Nginx 行为: 匹配到
location /api/,根据proxy_pass把请求转发给.166。 - 结论: Nginx 代理生效。
6.6.2. 手动填写地址后:绕过 Nginx
在设置页面填了 http://192.168.3.166:8080,你的代码逻辑变为:backendUrl = 'http://192.168.3.166:8080/api';
- 浏览器请求地址:
http://192.168.3.166:8080/api/xxxxxxxx - 谁处理: 浏览器发现这个请求的目的地是
.166,于是它直接通过网络把数据包发给.166。 - Nginx 行为: 部署在
.165上的 Nginx 根本看不见这个请求,因为它压根没经过.165。 - 结论: Nginx 代理被跳过(不起作用)。
7. 常见问题
7.1. 防火墙开放端口 (Firewall)
在后端 VPS 上,你必须通过云服务商的控制台(安全组)或 Linux 自带命令开放 3000 端口,否则前端没法访问。
7.2. 关于跨域 (CORS) 与安全
由于前端和后端 IP 不同,浏览器会认为这是跨域请求。
好消息: 如果你的前端容器里有一个 Nginx 反向代理,浏览器发出的请求是发给前端 IP 的 /api,由前端 Nginx 在后台去请求后端 IP。
结果: 浏览器认为这是“同源”的,你不需要在 Node.js 后端专门去配置复杂的 CORS 跨域规则。这就是使用 Nginx 模板代理的最大好处。
7.3. HTTPS 的必要性
如果前端 VPS 使用了域名并配置了 HTTPS(比如 https://site.com)。
根据浏览器安全策略,你的后端也必须使用 HTTPS,否则请求会被以 Mixed Content 错误拦截。
建议方案: 在后端 VPS 前面也套一个轻量级的 Nginx (或者使用 Caddy/Nginx Proxy Manager) 来申请 SSL 证书。
