📋 通用指南

保康县 AI 编程人才孵化基地 & 统计局 · 技术文档

更新:2026-07-07 · 保康县

🏗️ 部署架构指南

Cloudflare Tunnel + Docker Nginx 反向代理 · 域名体系 · 网络兜底

📐 整体架构

所有站点采用统一的 Cloudflare Tunnel → Docker Nginx → 静态HTML/容器 三层架构。 公网零暴露端口,全部流量经 Cloudflare Edge 加密隧道进入。

用户 ── HTTPS ──▶ Cloudflare Edge │ (Tunnel TLS) ▼ cloudflared (Docker) │ HTTP :80 ▼ nginx-proxy (Docker) │ ┌──────────┼──────────┐ ▼ ▼ ▼ 静态HTML 反向代理 SSI包含 /usr/share/nginx/html/ → Docker容器 服务端边
ℹ️ 运行环境

宿主机:WSL2 (Ubuntu) on Windows 11
容器编排:Docker Compose · nginx 版本 1.31 (alpine)
内网互联:Tailscale (备用通道)
存储挂载:Windows 宿主机 /mnt/c/AI-Workspace/docker-stack/nginx/html/

🌐 域名体系

域名用途说明
bearqh.fun 技术 & 工具 主技术域名,托管 AI 孵化基地、Gitea、Portainer、n8n、Dify、MinIO 等
保康.top 政府统计 官方站点 tj.保康.top、NB保康 nb.保康.top、AI孵化基地 ai.保康.top
熊.online 个人项目 个人门户、知识库浏览器 vault.熊.online、股票看板、论坛、用户管理、短链等

🔁 域名映射流程

用户请求经过的链路:CF Tunnel 的 public hostnamenginx server_nameroot / proxy_pass

Cloudflare Tunnel · config.yml
# 每个 public hostname 映射一个 nginx 子站
tunnel: xxxx-xxxx-xxxx
credentials-file: /home/nonroot/.cloudflared/xxxx.json

ingress:
  # bearqh.fun 子站
  - hostname: ai.bearqh.fun
    service: http://nginx-tj:80
  - hostname: gitea.bearqh.fun
    service: http://nginx-tj:80
  - hostname: n8n.bearqh.fun
    service: http://nginx-tj:80

  # 保康.top 子站
  - hostname: tj.保康.top
    service: http://nginx-tj:80
  - hostname: nb.保康.top
    service: http://nginx-tj:80

  # 熊.online 子站
  - hostname: vault.熊.online
    service: http://nginx-tj:80
  - hostname: chat.熊.online
    service: http://nginx-tj:80

  # 兜底:所有未匹配的 hostname
  - service: http_status:404
Nginx · server_name 示例
# 静态 HTML 站点 (info站点)
server {
    listen 80;
    server_name info.熊.online;

    root /usr/share/nginx/html/info;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

# 反向代理到 Docker 容器 (股票看板API)
server {
    listen 80;
    server_name xn--2vx.online;  # 熊.online 的 punycode

    # 股票搜索接口 → 东方财富 suggest
    location /api/stock/search {
        proxy_pass https://searchadapter.example.com;
        proxy_set_header Host searchadapter.example.com;
        proxy_set_header X-Real-IP $remote_addr;

        # CORS 头注入
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';

        # OPTIONS 预检请求直接返回
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
            add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 204;
        }
    }
}

🔓 CORS 处理策略

浏览器端 JavaScript 调用第三方 API 时面临跨域限制。采用以下三级策略:

策略适用场景实现方式
方案A 自有nginx代理的接口 proxy_pass + add_header Access-Control-Allow-Origin *
方案B API 本身支持 CORS 前端直接 fetch(),无需代理
方案C 不支持 CORS 的外部 API 切换数据源或使用 JSONP 方式(降级)

🌍 网络兜底策略

⚠️ 中国 API HTTPS 连接不稳定

部分中国境内 API(如东方财富、腾讯行情)通过 HTTPS 访问时,因网络环境(Tailscale、WSL 路由、防火墙等)可能出现 TLS 握手失败、连接重置 等问题。

备选方案 — 腾讯 HTTP API:

JavaScript · 兜底切换逻辑
// 腾讯行情 API(HTTP)
const TENCENT_QT_API = 'http://qt.gtimg.cn/q=';

// 腾讯 K线 API(HTTP)
const TENCENT_KLINE_API = 'http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param=';

// 自动降级逻辑
async function fetchWithFallback(primaryUrl, fallbackUrl) {
    try {
        const res = await fetch(primaryUrl, { signal: AbortSignal.timeout(5000) });
        if (!res.ok) throw new Error('HTTP ' + res.status);
        return await res.text();
    } catch (e) {
        console.warn('Primary API failed, falling back: ', e.message);
        const res2 = await fetch(fallbackUrl);
        return await res2.text();
    }
}

核心原则:HTTPS 失败自动降级到 HTTP,优先使用腾讯 API 系列(速度最快、数据最全)。

🧠 Obsidian 知识工作流

知识包含模型 · 五槽模型 · 标签体系 · Web 发布 · 日报与决策日志

📚 知识包含模型(L0–Ln 六层架构)

知识库采用分层架构,从原始记录到最终输出逐层提炼:

层级名称内容示例
L0原始未加工的数据、截图、链接网页剪藏、API 返回结果
L1摘要精炼要点,去冗余会议纪要、文章摘要
L2结构化分类、表格、流程图技术架构图、对比表格
L3关联跨笔记链接、双向关系知识图谱、[[双链]]
L4融合多来源综合,形成新认知研究报告、方案设计
Ln输出对外发布的成品博客文章、技术文档

🧩 五槽模型

每条笔记在 Obsidian 中通过五个维度进行标注(Frontmatter 或 inline fields):

① 包含
Concepts · 概念定义、关键词
② 量化
Data · 数字、指标、频率
③ 状态
Status · 进行中 / 已完成 / 待办
④ 功能
Action · 能做什么,怎么用
⑤ Diff/关系
Relation · 与其它笔记的差异和关联

🏷️ 标签体系

标签含义使用场景
#待完善 草稿 / 不完整 笔记尚未完成,需要后续补充
#已验证 已确认可用 代码片段、配置经过实际测试
#经验 踩坑 / 心得 部署过程中的教训和解决方案
#项目 归档项目 完整项目文档,含架构和决策记录

🌐 Web 发布

Obsidian 知识库通过 vault.熊.online 发布为可浏览的 Web 页面。 支持全文搜索、双向链接导航。目前收录 64+ 篇笔记,涵盖技术、金融、AI 等领域。

📝 日报自动编写 + 决策日志

每日自动生成日报模板,记录当日工作内容、进度和遇到的问题。 决策日志(Decision Log)记录关键架构和技术选型的 背景、方案、结论

📋 决策日志模板

日期:2026-07-07
决策:选择腾讯 HTTP API 作为股票数据兜底方案
背景:HTTPS 连接在某些 WSL 网络环境下不稳定
方案对比:① 腾讯 HTTP(无CORS问题,速度最快)② 东方财富(需nginx代理)③ 新浪(数据延迟)
结论:腾讯 HTTP 为首选降级方案,nginx 代理东方财富为辅

🔧 站点运维指南

站点清单 · Docker · Nginx · Cloudflare Tunnel · 域名新增

📋 所有站点列表

子域名类型目标说明
ai.bearqh.fun反向代理静态HTMLAI编程人才孵化基地(公益)
gitea.bearqh.funDocker容器gitea:latest自托管 Git 服务
n8n.bearqh.funDocker容器n8n:latest工作流自动化引擎
dify.bearqh.funDocker容器dify:1.15AI应用开发平台
minio.bearqh.funDocker容器minio:latest对象存储控制台
portainer.bearqh.funDocker容器portainer:latestDocker管理面板
tj.保康.top静态HTMLnginx静态文件保康统计局官网
nb.保康.top静态HTMLnginx静态文件NB保康展示页
vault.熊.online静态HTMLnginx静态文件Obsidian 知识库 Web 版
chat.熊.onlineDocker容器open-webuiAI 聊天界面
info.熊.online静态HTMLnginx静态文件本文档所在站点
xn--2vx.online/stock/静态HTMLnginx静态文件股票看板 (子路径)

🐳 Docker 容器管理常用命令

Shell · docker-compose
# 查看所有运行容器
docker ps

# 查看所有容器(含停止)
docker ps -a

# 进入容器内部
docker exec -it <container_name> sh

# 重启 nginx
docker exec nginx-proxy nginx -s reload
# 或
docker restart nginx-proxy

# 查看nginx日志
docker logs nginx-proxy --tail 50 -f

# 查看容器资源占用
docker stats

# 启动/停止/重启整个栈
docker compose -f /path/to/docker-compose.yml up -d
docker compose -f /path/to/docker-compose.yml down
docker compose -f /path/to/docker-compose.yml restart

📄 Nginx 配置要点

Nginx · 通用配置模板
# 静态站点配置
server {
    listen 80;
    server_name info.熊.online;

    # 根目录(容器内路径)
    root /usr/share/nginx/html/info;
    index index.html;

    # 共享资源(common.css 等)
    location /_shared/ {
        alias /usr/share/nginx/html/_shared/;
        expires 7d;
        add_header Cache-Control "public, immutable";
    }

    location / {
        try_files $uri $uri/ =404;
    }

    # 安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
}

# 反向代理到 Docker 容器
server {
    listen 80;
    server_name gitea.bearqh.fun;

    location / {
        proxy_pass http://gitea:3000;
        proxy_set_header Host $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;

        # WebSocket 支持
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

🔄 Cloudflare Tunnel 重启流程

Shell · cloudflared
# 查找 cloudflared 容器
docker ps | grep cloudflared

# 重启 tunnel
docker restart cloudflared

# 查看日志确认隧道状态
docker logs cloudflared --tail 20

# 如果容器内手动启动
docker exec -it cloudflared cloudflared tunnel run <tunnel_name>

# 验证隧道是否正常运行(日志中应有)
# "Connection xxxxxxxx registered" 或 "Connected to ... "

➕ 域名新增流程

新增一个子站点需要完成以下 4 步:

  1. DNS 解析:在 Cloudflare Dashboard 添加 CNAME 记录,指向隧道域名(如 xxx-xxx-xxx.cfargotunnel.com
  2. CF Tunnel 配置:在 cloudflared config.ymlingress 中添加 hostnameservice: http://nginx-tj:80
  3. Nginx 配置:/etc/nginx/conf.d/ 下新增 server 块,指定 server_namerootproxy_pass
  4. 重启服务:docker restart cloudflared + docker exec nginx-proxy nginx -s reload
💡 验证新站

使用 curl -H "Host: 新域名" http://localhost 在宿主机直接测试 nginx 是否响应正确,排除隧道问题后再测试公网访问。

📈 炒股看板技术文档

数据源 · CORS 处理 · WSL 网络问题 · 搜索功能

📡 数据源清单

功能API协议CORS说明
搜索 东方财富 suggest HTTPS 需代理 股票代码/名称模糊搜索 + 拼音缩写
K线 腾讯 HTTP/HTTPS 直接调用 日/周/月 K 线,支持前复权
行情 腾讯 QT HTTP 直接调用 实时五档行情、盘口数据
详情 腾讯 QT HTTP 直接调用 公司信息、财务指标、分红送配
指数 东方财富 → 腾讯(兜底) HTTPS/HTTP 需代理/直连 大盘指数行情,东方财富为主

🔓 CORS 处理方案

前端 JavaScript 无法直接调用不支持 CORS 的 API,采用以下分级方案:

方案 1:nginx 代理(适用于东方财富)

Nginx · 东方财富搜索代理
location /api/stock/search {
    # 东方财富 suggest 接口
    proxy_pass https://searchadapter.eastmoney.com/api/suggest/get_v2;
    proxy_set_header Host searchadapter.eastmoney.com;
    proxy_set_header Referer https://quote.eastmoney.com/;
    proxy_set_header User-Agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64)";

    # CORS 头
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';

    # 预检请求
    if ($request_method = 'OPTIONS') {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header Content-Length 0;
        add_header Content-Type text/plain;
        return 204;
    }
}

方案 2:有 CORS 的直接调用(腾讯 API)

JavaScript · 腾讯行情直连
// 腾讯行情 QT API — 支持 CORS,可直接 fetch
const url = `http://qt.gtimg.cn/q=sh600519,sz000001`;

async function fetchStocks(codes) {
    const res = await fetch(`http://qt.gtimg.cn/q=${codes.join(',')}`);
    const text = await res.text();
    return parseQtData(text);  // 自行解析 ~ 格式特殊
}

// 腾讯 K线 API
async function fetchKline(code, period = 'day') {
    const url = `http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param=${code},${period},,,20,qfq`;
    const res = await fetch(url);
    return await res.json();
}

方案 3:数据源切换

当主数据源不可用时,自动切换到备用数据源:

搜索兜底
东方财富 → 腾讯 suggest
K线兜底
腾讯 → 新浪财经
行情兜底
腾讯 QT → 东方财富
指数兜底
东方财富 → 腾讯 QT

⚠️ WSL 网络问题:Tailscale 导致 HTTPS reset

⚠️ 已知问题

在 WSL2 环境下启用 Tailscale 后,部分 HTTPS 请求出现 TCP 连接重置 (RST)。 表现为浏览器中跨域 HTTPS API 调用失败,尤其是对中国境内 API。

兜底策略:

  • 优先使用 HTTP 协议调用腾讯 API(腾讯 QT 同时支持 HTTP/HTTPS)
  • 使用 nginx 代理转发 HTTPS 请求,避免浏览器直接发起到目标地址的 HTTPS 连接
  • 临时关闭 Tailscale 的 MagicDNS / 路由功能(tailscale down 后测试)
  • 最终方案:股票看板前端实现 自动降级逻辑,HTTPS 失败后自动切换到 HTTP

🔍 搜索功能

股票搜索组件支持三种搜索方式,通过 nginx 代理统一入口调用:

功能说明示例
代码搜索 输入股票代码(如 600519 跳转至贵州茅台详情
名称搜索 输入中文名称(如 贵州茅台 东方财富 suggest 返回匹配
拼音缩写 输入首字母缩写(如 gzmt → 贵州茅台) 东方财富 suggest 内置支持

防抖处理

搜索输入框采用 200ms 防抖,避免每次按键都触发 API 请求:

JavaScript · 防抖搜索
// 防抖函数
function debounce(fn, delay = 200) {
    let timer = null;
    return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
    };
}

// 搜索函数
async function doSearch(keyword) {
    if (!keyword || keyword.length < 1) return;
    const url = `/api/stock/search?input=${encodeURIComponent(keyword)}`;
    const res = await fetch(url);
    const data = await res.json();
    // 渲染搜索结果...
}

// 绑定防抖
searchInput.addEventListener('input', debounce((e) => {
    doSearch(e.target.value);
}, 200));

拼音缩写支持

东方财富 suggest 接口原生支持拼音首字母搜索,无需额外逻辑。 例如输入 gzmt 自动匹配 贵州茅台 (600519)zgrm 匹配 中国人民保险 等。

💡 搜索 API 请求示例

GET /api/stock/search?input=gzmt
→ nginx 代理转发至 https://searchadapter.eastmoney.com/api/suggest/get_v2?input=gzmt
→ 返回 JSON 包含股票代码、名称、类型等信息