# 复制成 docker/.env 后填上真实值,然后 docker compose up -d # (要用自带的数据库容器就加 `--profile local-db`,见下面「库和缓存」那段)。 # # 这个文件(.env.example)可以入库,因为它只有占位符; # docker/.env 已加入 .gitignore,**绝不要提交**。 # # 生成随机值: # openssl rand -hex 32 # APP_SECRET_KEY 用这个(64 位十六进制) # openssl rand -base64 18 # 其余口令用这个 # ============================================================================ # 库和缓存:两种跑法,只填你要用的那一种 # ============================================================================ # # A. 用外部 MySQL / Redis(默认) # docker compose up -d --build # 把下面「外部实例」那几项填成你的地址;自带的 mysql / redis 容器不会启动。 # 外部 MySQL 得自己先把库建好(容器版是自动灌 schema.sql 的): # mysql --default-character-set=utf8mb4 -u root -p sms_gateway < backend/sql/schema.sql # 库和表必须是 utf8mb4。 # # B. 用自带的 MySQL / Redis 容器 # docker compose --profile local-db up -d --build # 「外部实例」那几项留空即可(留空 = 用容器,也就不用管它们)。 # # ── 外部实例(用自带容器时全部留空 —— 留空就回落到容器)───────────────────── # 下面是「整个 JDBC 地址」。末尾的 characterEncoding 必须是 **UTF-8**(Java 字符集名, # 不是 MySQL 的 utf8mb4)—— 写错后端起不来,而且日志里只有一句 Hibernate 的 # 「Unable to determine Dialect」,跟真正的病因隔着好几层。照这行改 IP 即可: # jdbc:mysql://192.168.1.20:3306/sms_gateway?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8 SPRING_DATASOURCE_URL= SPRING_DATASOURCE_USERNAME= SPRING_REDIS_HOST= SPRING_REDIS_PORT= # ── 口令(两种跑法都要)──────────────────────────────────────────────────── # 连数据库用的账号口令。B 模式下它同时用来初始化容器里的那个账号。 MYSQL_PASSWORD=change-me-app # Redis 口令。Redis 里明文存着管理员令牌和调用方 API Key,没口令 + 端口外放 # 等于把管理后台拱手让人。 # B 模式(自带容器):**必填**,留空的话容器直接拒绝启动。 # A 模式(外部实例):允许留空 —— 外部 Redis 常常没设 requirepass —— 但那意味着 # 上面那句话对你也成立,请确认那台 Redis 只在内网、且另有访问控制。 REDIS_PASSWORD=change-me-redis # 只有 B 模式用它来初始化自带容器的 root 账号,A 模式用不到。 # 留空且真去起了容器时,MySQL 镜像会拒绝初始化并明确报错(不会悄悄起一个没口令的库)。 # 反过来说,B 模式下别把这个占位值原样留着 —— 那就是一个真口令。 MYSQL_ROOT_PASSWORD=change-me-root # ============================================================================ # 系统主密钥。**这一项最要紧。** # # 设备令牌 = HMAC-SHA256(deviceId, 这个值),是确定性推导而非随机签发 —— # 也就是说,知道它的人可以对任意 deviceId 直接算出合法令牌, # 冒充任何设备上报短信、并读走那台设备的全部记录。 # # 所以它必须足够随机,且绝不能是仓库里的默认值。 # 更换它会让**所有已签发的设备令牌立即失效**,每台设备都需要重新注册 —— # 趁设备还少的时候把它定下来。 # ============================================================================ APP_SECRET_KEY=change-me-64-hex-chars # 仅在 admin_user 表为空(全新库)时用于创建管理员账号。 APP_ADMIN_DEFAULT_PASSWORD=change-me-admin # 需要轮换一个**已存在**的管理员账号的密码时,临时设上这个、重启、确认能登录后删掉。 # 留空则什么都不做。这是目前唯一能改管理员密码的途径: # 管理接口里没有改密码的功能,而 APP_ADMIN_DEFAULT_PASSWORD 只在建号时生效。 # APP_ADMIN_RESET_PASSWORD= # 时区。容器基础镜像默认 UTC,而代码里所有「今天/现在」都用 JVM 默认时区, # 不设的话统计与列表时间会整体偏 8 小时。 TZ=Asia/Shanghai # ============================================================================ # 管理后台容器 # ============================================================================ # 浏览器访问管理后台的端口 —— 也是这套系统对外的**唯一**入口: # 设备的 /api/... 也走它转给后端(后端只监听回环,不对公网开放)。 # # 所以公网/安全组里只放行这一个端口;以后加域名 + HTTPS 就是在这个前面再放一层反代 # (或者直接给这层 nginx 配上证书)。 FRONTEND_PORT=8081 # 后端端口。只监听回环,nginx 把 /api 转到它上面;两个容器都在 host 网络, # 所以这个端口在宿主机上要没被占用。想换就改这里 —— 后端与 nginx 同时跟着变。 BACKEND_PORT=8080 # 注意:**「设备要访问的地址」不在这里配**。二维码里写的是「管理员访问后台用的那个 # origin」(前端是唯一入口,见 management/src/api/device.ts),所以以后上域名是自动的。 # 代价:别用 SSH 隧道 / 内网 IP 打开后台去生成二维码 —— 那样写进二维码的地址手机连不上, # 而二维码本身看起来一切正常。生成前弹窗里会显示地址,扫之前看一眼。 # # 用 localhost / 127.0.0.1 打开后台时,那两个功能会直接报错、拒绝生成二维码(原因是 # 回环地址对手机来说指向它自己)。开发时想看二维码:`npm run dev -- --host`, # 再从局域网 IP 访问即可。 # ============================================================================ # 镜像从哪来(只放 compose + 本文件到服务器时,这就是部署的全部) # ============================================================================ # 打 tag 后 CI 会把两个镜像推到 ghcr.io(见 .github/workflows/release.yml), # 服务器那句 `docker compose pull && docker compose up -d` 拉的就是它们 —— # 那边不用放源码,也不用装 JDK / Maven。默认取 latest;要钉死某一版就写全: # BACKEND_IMAGE=ghcr.io/hearthealt/sms-gateway-backend:v1.0.0 # FRONTEND_IMAGE=ghcr.io/hearthealt/sms-gateway-frontend:v1.0.0 # # 镜像里不含任何部署相关的信息(设备地址是上面那个运行期变量),所以同一份镜像放到 # 哪台机器上都对 —— CI 那边不需要配任何仓库变量或 build arg。 # # 换成别的镜像仓库(比如 ghcr.io 拉不动、改用 Docker Hub)时,这两行跟着换。 # ============================================================================ # 消息转发(v2,可选) # ============================================================================ # 短信到达后主动推送到群机器人 / 个人 IM。配置在管理后台的「转发渠道」页, # 这里只放进程级的开关与密钥;各渠道怎么配见 docs/notify-channel-setup.md。 # 渠道凭据(webhook 地址)的加密密钥,Base64 编码的 32 字节。 # 生成:openssl rand -base64 32 # # 刻意没有默认值:跟着仓库走的默认密钥等于没有密钥。 # # **这里没有「转发总开关」** —— 它在管理后台「系统设置」页,改完立即生效、不用重启。 # 留空时后端照常启动,只是转发不可启用(界面上打开开关时会提示你来配这一项)。 # # 注意:这个密钥丢了,所有渠道凭据都要重新配置 —— 密文在库里,没有密钥读不出来。 NOTIFY_ENCRYPT_KEY=