TraderMemos
自托管

部署

Docker 一体化部署、CDN + API 分离,或边缘重写 —— 选择适合你基础设施的方式。

三种部署方式:

  1. Docker 一体化部署 —— 默认自托管方式:一个 URL、一个数据卷
  2. 静态 SPA + 独立部署的 API —— UI 放在 CDN,API 在你的主机上(CORS + Server URL)
  3. 静态 SPA + 边缘重写 —— UI 放在 CDN,浏览器保持同源

只想一键把 UI 部署到你自己的 Vercel / Cloudflare / Netlify?参见 Fork 并部署

1. Docker 一体化部署(推荐默认方式)

拉取 Docker Hub 上已发布的镜像(sinhong2011/tradermemos-api + …-web)。

# 可选:复制并编辑 Hub 命名空间 / 标签
cp .env.example .env
# DOCKERHUB_USERNAME=sinhong2011   # 如果你发布自己的镜像,填你的 Hub 用户名
# TM_IMAGE_TAG=0.7.0               # 生产环境建议锁定具体版本(默认:latest)

make up            # docker compose up -d(拉取 Hub 镜像,使用 SQLite)
# 打开 http://localhost:3000

make up-postgres   # 同上 + Postgres 覆盖配置
make up-build      # 从本仓库构建 api/web,而非拉取镜像

Docker Hub 用户名的来源

场景设置位置
终端用户 / 自托管根目录 .envDOCKERHUB_USERNAME(Compose 会自动加载)。默认为 sinhong2011
镜像标签根目录 .envTM_IMAGE_TAGlatest 或语义化版本号,如 0.7.0)。
CI 发布到 HubGitHub 仓库密钥 DOCKERHUB_USERNAME + DOCKERHUB_TOKEN

你将获得:

URL服务
http://localhost:3000nginx SPA
http://localhost:3000/api/v1/*代理转发到 Go API
http://localhost:8080API 直连(可选;健康检查、调试用)

登录 / 设置中的 Server 字段留空即可。SPA 使用相对路径 /api/v1,nginx 会将请求代理到 api 容器 —— 无需 CORS 配置。

关键环境变量(compose / 宿主机):

变量用途
TM_JWT_SECRETJWT 签名密钥 —— 生产环境必须设置openssl rand -hex 32
TM_ALLOW_INSECURE_JWTCompose 默认设为 true 以方便首次运行;生产环境应设为 false 或留空
TM_ALLOW_REGISTRATION默认 false。初始化完成后,除非主动开启,否则只存在 owner 账户
TM_DATABASE_URL统一数据库连接串 —— SQLite sqlite:///data/tradermemos.db(默认)或 postgres://user:pass@host:5432/db?sslmode=require
TM_ATTACH_DIR附件存储路径。SQLite 默认使用 <dbDir>/attachments;Postgres 需显式设置
TM_CORS_ORIGINS此模式下留空即可

首次启动: 打开 http://localhost:3000 —— 若数据库中还没有用户,会出现初始化向导, 用于创建 owner(管理员)账户及可选的交易账户。初始化完成后,公开注册将保持关闭。

# 类生产环境的 compose 示例
cp .env.example .env
# 编辑 .env:TM_IMAGE_TAG=0.7.0、TM_JWT_SECRET=…、TM_ALLOW_INSECURE_JWT=false
export TM_JWT_SECRET=$(openssl rand -hex 32)
export TM_ALLOW_INSECURE_JWT=false
make up

数据保存在 tm_data Docker 数据卷中(SQLite + 附件)。

make logs        # 跟踪 compose 日志
make down        # 停止服务栈

生产环境建议:在前面加一层 Caddy / Traefik / nginx 提供 TLS,并只将其指向 web 服务 —— /api 保持同源。切勿在没有 TLS 的情况下将 API 直接暴露到公网。可直接复制的配置: 反向代理与 TLS

内置的认证加固

  • 首用户初始化接口;开放注册 /auth/register 默认关闭
  • 密码长度需 ≥ 10 位(bcrypt)
  • 认证与初始化路由均有限流(每 IP 约 2 请求/秒)
  • Access 与 Refresh JWT 使用不同的 typ 声明
  • 若检测到已知的不安全 JWT 密钥,服务端会拒绝启动,除非设置 TM_ALLOW_INSECURE_JWT=true

API 访问令牌与 OpenAPI 文档

每个实例都在 /docs 提供交互式 OpenAPI 参考文档,并可在设置 → API 中为 MCP、AI 代理 与脚本创建个人访问令牌(tm_pat_…)—— 参见 API 令牌与 OpenAPI

2. 静态 Web(Vercel / Cloudflare Pages / Netlify)+ 独立部署的 API

适用于 UI 部署在 CDN、日志 API 运行在 VPS、Fly、家庭 NAS 等场景。

https://app.example.com     → 静态 SPA(CDN)
https://api.example.com     → Docker/Go API + SQLite 数据卷

API

  1. 运行 API 容器(或二进制文件),确保有可访问的 URL 与持久化磁盘。
  2. 允许 SPA 的源:
TM_CORS_ORIGINS=https://*.vercel.app,https://*.pages.dev,http://localhost:5173
TM_JWT_SECRET=$(openssl rand -hex 32)
# 公网 API 请不要设置 TM_ALLOW_INSECURE_JWT
# TM_ALLOW_REGISTRATION=true   # 仅当你希望通过 UI 添加额外用户时才开启

通配符形式 https://*.vercel.apphttps://*.pages.dev 可匹配预览 / 生产环境的 CDN 主机。 自定义域名请使用精确的源。

在这种模式下使用公开分享链接?请同时把 TM_PUBLIC_WEB_URL 设置为 SPA 的源 —— 否则分享 URL 会基于 API 源生成,而该源在此模式下 并不提供 Web 应用。

Web

构建 SPA 并部署 web/dist

cd web && vp install && vp build

配置 SPA 指向的 API:

方式适用场景
登录 / 设置 → Server / API server用户自带 API(运行时 tm_api_base
构建期 VITE_API=https://api.example.com/api/v1固定的公开 / 演示 API,直接打包进构建产物

仅填源(如 https://api.example.com)时会自动追加 /api/v1

3. 静态 Web + 边缘重写(同源 CDN)

浏览器只访问一个源,由边缘节点将 /api 代理到你的 API。无需 CORS,也无需填写 Server 字段。

Vercel

复制 deploy/vercel.json.example,将 destination 设为你的 API 主机,部署 web/dist (或将 web/ 项目的 outputDirectory 设为 dist 并连接)。

Cloudflare Workers / Pages

vp build 之前,将 deploy/cloudflare/_redirects.example 复制为 web/public/_redirects, 并配置一条指向你 API 的 200 代理规则。SPA 兜底仍由 web/wrangler.tomlnot_found_handling = "single-page-application")负责 —— 不要额外添加 /* /index.html 200(Workers 会将其判定为无限循环并拒绝)。

使用重写方案时,TM_CORS_ORIGINS 留空即可 —— 浏览器不会发起跨源请求。

如何选择部署方式

目标方式
Fork 后一键将 UI 部署到你自己的 Vercel 或 CFFork 并部署
Homelab / VPS / NAS,单一 URL方式 1:Docker
营销 / 演示用 UI 放在 CDN,用户自托管 API方式 2:CDN + CORS
全球 SPA CDN,自建托管 API,Server 字段留空方式 3:边缘重写

不要在 Vercel Serverless 或 Cloudflare Workers 上运行 Go + SQLite 的 API —— 请将 API 放在带有真实磁盘的机器 / 数据卷上。

检查清单

  • 已将 TM_JWT_SECRET 从默认值修改
  • SQLite / 附件存储在持久化数据卷上,并已安排备份
  • Docker 部署:已开放 web 端口;Server 字段留空
  • 分离部署:TM_CORS_ORIGINS 匹配 SPA 的源
  • 上传:nginx / 代理的 client_max_body_size ≥ API 的 TM_*_MAX_BYTES(compose web 镜像使用 20m)

完整环境变量列表:配置参考

本页内容