OpenHabitTracker 1.2.5 配置参数详解
OpenHabitTracker 1.2.5 配置参数详解:环境变量与 appsettings.json 四层配置、账号与 JWT 密钥实测坑位(仅首次生效/轮换登出)、TZ 与日志级别、Settings 表 56 列与 SQLite 数据模型,实测验证。
OpenHabitTracker 1.2.5 配置参数详解
目标:把自托管 OpenHabitTracker 1.2.5 时会用到的全部配置讲透——容器环境变量、内置 appsettings.json 默认值、数据目录里的 SQLite 与密钥文件、应用内设置,以及「改了为什么没生效」的实测结论。全部参数行为在 Ubuntu 22.04 + 官方镜像
jinjinov/openhabittracker:1.2.5(.NET 10.0.12)部署实例上实测验证,服务端口以 8129 为例。
一、环境信息
- 应用版本:OpenHabitTracker 1.2.5(镜像
jinjinov/openhabittracker:1.2.5,程序集OpenHT.dll,运行时 .NET 10.0.12) - 实测环境:Ubuntu 22.04(x86_64)+ Docker 27.3.1,服务端口 8129 → 容器内 8080
- 配置源码基线:1.2.5 tag 的
OpenHabitTracker.Blazor.Web/appsettings.json与Program.cs(GitHub 实查,2026-10-24) - 数据库:SQLite(
OpenHT.db),ASP.NET Core DataProtection 密钥与数据库同目录存放 - 前提:已完成部署并可用(未部署请先按本专栏《OpenHabitTracker Docker Compose 部署指南》完成,本文复用其部署目录与 .env 约定)
二、前置条件
- 能进入部署目录并重建容器(改配置的主要手段是改环境变量后重建):
cd /opt/openhabittracker # 换成你的部署目录
docker compose ps --format '{{.Name}} {{.Image}} {{.Status}}'
实测回显:
oht-1.2.5-server jinjinov/openhabittracker:1.2.5 Up 3 minutes
- 能读取挂载的数据目录(备份与存储布局核对用):
ls -la data/.OpenHabitTracker/
实测回显:
-rw------- 1 root root 1001 Oct 24 12:17 key-a0f0a8f2-ebd0-4156-9f2d-aa9d84ca1b43.xml
-rw-r--r-- 1 root root 143360 Oct 24 12:17 OpenHT.db
- (可选)服务器上有 sqlite3 或 python3,用于只读核对数据表(本文实测用 python3 内置 sqlite3 模块,只读打开,不写库)。
三、参数查看与修改方式
OpenHabitTracker 的配置分四层,优先级从高到低:
| 层 | 载体 | 查看方式 | 修改方式 | 生效时机 |
|---|---|---|---|---|
| 环境变量 | AppSettings__*、TZ、Logging__* 等 | docker inspect <容器> --format '{{.Config.Env}}' | 改 compose/.env 后重建 | docker compose up -d 重建后 |
| 配置文件 | 容器内 /app/appsettings.json | docker exec <容器> cat /app/appsettings.json | 挂载覆盖(不推荐) | 重启后 |
| 数据库层 | SQLite 各表(账号、刷新令牌、应用设置) | 只读查询 OpenHT.db | 应用界面操作(设置页);数据库直改有风险 | 即时/重启后 |
| 应用内设置 | Settings 页 UI | 登录 → Settings | 界面开关 | 保存即生效 |
内置 appsettings.json 全文(1.2.5 实查源码,容器内一致):
{
"AppSettings": {
"UserName": "admin",
"Email": "admin@admin.com",
"Password": "admin",
"JwtSecret": "your-extremely-strong-secret-key"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
环境变量覆盖规则(ASP.NET Core 标准约定):__(双下划线)替代配置层级分隔符,如 AppSettings__JwtSecret 覆盖 AppSettings:JwtSecret。这就是 compose 中四个 AppSettings__* 变量的由来。
四、参数总览
| 参数 | 位置 | 默认值 | 作用 | 风险等级 |
|---|---|---|---|---|
AppSettings__UserName | 环境变量 | admin | 初始账号用户名 + watchdog 日志页账号 | 低(仅首次建库生效) |
AppSettings__Email | 环境变量 | admin@admin.com | 初始账号邮箱 | 低(仅首次建库生效) |
AppSettings__Password | 环境变量 | admin | 初始账号口令 + watchdog 口令 | 高(仅首次建库生效,见 5.1) |
AppSettings__JwtSecret | 环境变量 | your-extremely-strong-secret-key | JWT 签发/校验密钥 | 极高(泄露可伪造令牌;轮换即全端登出,见 5.2) |
TZ | 环境变量 | Etc/UTC | 容器时区 | 中(影响「今天」判定与统计) |
Logging__LogLevel__Default | 环境变量 | Information | 全局日志级别 | 低 |
Logging__LogLevel__Microsoft.AspNetCore | 环境变量 | Warning | 框架日志级别 | 低 |
ASPNETCORE_HTTP_PORTS | 镜像内置 | 8080 | 容器内监听端口 | 低(一般不动,改端口映射即可) |
ASPNETCORE_ENVIRONMENT | 镜像内置 | Production | 运行环境 | 中(Development 会开放异常详情,勿对外) |
AllowedHosts | appsettings.json | * | 允许的 Host 头 | 中(对外可收紧为域名) |
| 应用内 Settings | Settings 页 / Settings 表 | 界面默认 | 主题/语言/通知/过滤等全部行为 | 低 |
五、核心参数分类详解
5.1 账号参数:UserName / Email / Password
三者只在数据库中不存在同名用户时生效:应用每次启动会执行「按 AppSettings.UserName 查用户 → 不存在则用环境变量创建」。启动日志可确认:
docker logs oht-1.2.5-server 2>&1 | grep -E 'Default user'
实测回显(首次启动):
Default user created successfully.
**实测坑位:改环境变量口令不会更新已有账号。**把 .env 中 APPSETTINGS_PASSWORD 改为新口令并重建容器后:
curl -s -X POST http://localhost:8129/api/Auth/jwt-token -H 'Content-Type: application/json' -d '{"username":"admin","password":"q1w2e3r4"}' -o /dev/null -w 'old: %{http_code}\n'
curl -s -X POST http://localhost:8129/api/Auth/jwt-token -H 'Content-Type: application/json' -d '{"username":"admin","password":"NewPass123456"}' -o /dev/null -w 'new: %{http_code}\n'
实测回显:
old: 200
new: 401
结论:口令哈希存于 SQLite 的 AspNetUsers.PasswordHash,环境变量只在创建时用一次。因此首次部署就设最终口令;后续换口令走应用内界面修改。实测改回 .env 原口令并重建后,原口令恢复可用(restored old password: 200)。
账号数据落库实查(只读):
python3 -c "import sqlite3; con=sqlite3.connect('file:data/.OpenHabitTracker/OpenHT.db?mode=ro', uri=True); print([r[1] for r in con.execute('PRAGMA table_info(AspNetUsers)')])"
实测回显:
['Id', 'UserName', 'NormalizedUserName', 'Email', 'NormalizedEmail', 'EmailConfirmed', 'PasswordHash', 'SecurityStamp', 'ConcurrencyStamp', 'PhoneNumber', 'PhoneNumberConfirmed', 'TwoFactorEnabled', 'LockoutEnd', 'LockoutEnabled', 'AccessFailedCount', 'LastChangeAt']
5.2 JwtSecret:令牌签名密钥
用途:登录 API(POST /api/Auth/jwt-token)签发 JWT、服务端校验令牌签名(签发方与校验方同源此密钥,签发的令牌有效期 1 天;刷新令牌有效期 90 天,存 RefreshTokens 表)。
**实测结论:轮换 JwtSecret 会使所有已签发 JWT 立即失效。**轮换前把登录返回的令牌存入 token.txt(获取方式见本专栏《OpenHabitTracker 1.2.5 API 使用详解》认证一节),改密钥重建后用旧令牌调数据接口:
curl -s -o /dev/null -w 'old-token with new secret: %{http_code}\n' http://localhost:8129/api/DataAccess/notes -H "Authorization: Bearer $(cat token.txt)"
实测回显:
old-token with new secret: 401
恢复原密钥并重新登录后,接口恢复 200。运维要点:
- 密钥生成用
openssl rand -base64 32,不要用示例占位值; - 轮换密钥 = 所有客户端(PWA/桌面/手机)同时登出,需重新登录,数据无损;
- 泄露必须轮换(攻击者可自签任意令牌)。
💡 技术拓展:JWT 签名算法常见选择对比——
算法 原理 特点 本文项目实际采用 HS256(HMAC-SHA256) 对称密钥,签发/校验同一把钥匙 简单;密钥必须在签发与校验方共享 是(源码 SecurityAlgorithms.HmacSha256,单服务自洽场景合理)RS256/ES256 非对称,私钥签名公钥校验 适合多服务分发公钥验签 否(单服务无必要) OpenHabitTracker 是单体单用户服务,签发与校验在同一进程,HS256 足够。
5.3 时区:TZ
影响统计的「今天」边界、日历图与通知时间。实测容器内:
docker exec oht-1.2.5-server date
实测回显(TZ=Asia/Shanghai):
Sat Oct 24 12:31:57 CST 2026
不设置时容器走 UTC,国内傍晚产生的记录会被算到「明天」。设为你的 IANA 时区(如 Asia/Shanghai)并在改后重建容器。
5.4 日志参数:Logging__LogLevel__*
默认级别:全局 Information、ASP.NET Core 框架 Warning(appsettings.json 默认)。排障时可临时提高认证日志级别:
environment:
- Logging__LogLevel__Microsoft.AspNetCore.Authentication=Debug
重建后访问一次受保护接口,日志即可看到认证细节(实测节选):
dbug: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[2]
Successfully validated the token.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
AuthenticationScheme: Bearer was challenged.
这两行是定位「令牌有效但接口仍 401」类问题的关键证据(本文实测曾据此区分出「令牌校验失败」与「令牌有效但授权策略不认」两种 401)。排障完建议去掉 Debug 级别。
5.5 运行时参数:端口/环境/Host/HTTPS
ASPNETCORE_HTTP_PORTS=8080:镜像内置监听端口,改宿主机端口只需动 compose 的ports映射(如"8129:8080"),不必动它;ASPNETCORE_ENVIRONMENT:保持Production。生产模式下异常返回精简 JSON(API 路径)或跳转/Error页面;Development 会暴露异常堆栈(应用对/api异常返回 message+stackTrace 的 JSON),切勿对外开放;AllowedHosts: *:默认接受任意 Host 头;已配反向代理 + 域名时建议改为具体域名,防 Host 头注入;- HTTPS:应用启用了
UseHttpsRedirection与 HSTS(源码默认),但官方镜像内无证书,重定向中间件在无配置端口时仅告警不生效(实测启动日志有warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware)。对外服务请在前面挂 Nginx/Caddy 等 TLS 网关终结 HTTPS,容器保持 HTTP 内网通信。
5.6 应用内设置:Settings 表全参数
登录后在 Settings 页调整的项保存在 SQLite Settings 表(按用户一行)。实测表结构共 56 列(只读查询 PRAGMA table_info(Settings)),按用途归类如下:
| 类别 | 代表列 | 说明 |
|---|---|---|
| 外观 | IsDarkMode、Theme、Color(行背景)、HorizontalMargin/VerticalMargin | 26 套 Bootswatch 主题 + 深浅色 |
| 语言与本地化 | Culture、FirstDayOfWeek | 20 种语言;周起始日 |
| 启动行为 | StartPage、StartSidebar、FoldSection | 启动进入哪个页面/侧栏、折叠区 |
| 列表显隐 | ShowItemList、ShowPriority、ShowCategory、ShowColor、ShowCreatedUpdated、HideCompletedTasks、ShowGroupedByCategory、ShowLastTimeDone | 每行显示哪些字段 |
| 日历 | ShowSmallCalendar、ShowLargeCalendar、MaxSmallCalendarDays | 列表旁日历开关与天数 |
| 习惯统计 | ShowHabitStatistics、ShowHabitCharts、SelectedRatio、SelectedRatioMin/Max、ShowOnlyOverSelectedRatioMin、ShowOnlyUnderSelectedRatioMax | 周统计、图表、按超期百分比过滤 |
| 日期过滤 | DoneFromDayOffset/DoneToDayOffset、PlannedFromDayOffset/PlannedToDayOffset、ShowDoneInRange | 「距今天数」式过滤器(保存后不过期) |
| 通知 | NotificationContent、NotificationHabitThreshold、NotificationIncludeOverdueTasks、NotificationLeadMinutes、NotificationMinimumPriority、NotificationTime | 每日摘要内容/时间、任务提醒提前量与优先级下限 |
| 同步 | BaseUrl、RefreshToken、RememberMe、Username | 客户端指向的服务器地址与刷新令牌 |
| 编辑行为 | InsertTabsInNoteContent、DisplayNoteContentAsMarkdown、UncheckAllItemsOnHabitDone、ShowHelp | Markdown 渲染、习惯完成时清单重置等 |
要点:
- 这些设置随 JSON/YAML 备份一并导出并参与同步(官方 README 明确),换设备不必重调;
Settings表按UserId区分——单用户部署就是一行。
5.7 数据存储布局:OpenHT.db 表结构
实测表清单(只读查询 sqlite_master):
tables: ['AspNetRoleClaims', 'AspNetRoles', 'AspNetUserClaims', 'AspNetUserLogins', 'AspNetUserRoles', 'AspNetUserTokens', 'AspNetUsers', 'Categories', 'Contents', 'Items', 'Priorities', 'RefreshTokens', 'Settings', 'Times', '__EFMigrationsHistory', '__EFMigrationsLock', 'sqlite_sequence']
核心业务表解读:
| 表 | 角色 | 实测关键列 |
|---|---|---|
Contents | 习惯/笔记/任务三合一主表(EF Core TPH 继承,Discriminator 区分行类型) | Id, CategoryId, Title, Color, Priority, IsDeleted, CreatedAt, UpdatedAt, Discriminator + 习惯专属(RepeatCount/RepeatInterval/RepeatPeriod/TargetQuantity/DisplayMetric/StartAt)+ 任务专属(PlannedAt, TaskEntity_Duration, StartedAt, CompletedAt)+ 笔记专属(Content) |
Items | 清单项(任务的 checklist) | Id, ParentId, Title, DoneAt |
Times | 习惯完成流水 | Id, HabitId, StartedAt, CompletedAt, Quantity |
Categories / Priorities | 分类与优先级字典 | 用户可自定义扩展 |
Settings | 应用内设置(按用户一行,见 5.6) | 56 列 |
RefreshTokens | 登录刷新令牌(90 天轮换,含旧令牌 60 秒宽限列) | Username, Token, ExpiryDate, PreviousToken, PreviousTokenValidUntil |
AspNetUsers 等 AspNet* | ASP.NET Core Identity 标准表 | 口令哈希、角色、外部登录 |
💡 技术拓展:把多类实体存进一张表的常见方案对比——
方案 原理 取舍 本文项目实际采用 TPH(Table per Hierarchy) 所有子类共用一张表,用鉴别列区分 查询快(无 join)、列有 NULL 冗余 是( Contents.Discriminator)TPT(Table per Type) 每类一张表外键关联 结构干净、查询要 join 否 分表 每类独立表 简单直接,但跨类统一查询/过滤困难 否(而本项目要跨三类统一搜索排序) 笔记/任务/习惯要共用「搜索、过滤、排序、分类、优先级」,TPH 单表最贴合这个产品需求。
备份即拷贝整个 .OpenHabitTracker/ 目录(OpenHT.db + key-*.xml);应用内另提供 JSON/YAML/TSV/Markdown 导出作为跨版本/跨应用迁移格式。
六、生产配置模板
适用场景:个人/家庭单用户自托管(2 核 CPU、1GB 内存、10GB 磁盘即可长期稳定运行;SQLite 单文件随数据量增长,纯文本数据一年量级在数十 MB 内)。
name: oht-1.2.5-stack
services:
openhabittracker:
image: jinjinov/openhabittracker:1.2.5
container_name: oht-1.2.5-server
ports:
- "8129:8080" # 或 "127.0.0.1:8129:8080" 仅本机+反代可达
environment:
- AppSettings__UserName=${APPSETTINGS_USERNAME}
- AppSettings__Email=${APPSETTINGS_EMAIL}
- AppSettings__Password=${APPSETTINGS_PASSWORD}
- AppSettings__JwtSecret=${APPSETTINGS_JWT_SECRET}
- TZ=Asia/Shanghai
volumes:
- ./data/.OpenHabitTracker:/app/.OpenHabitTracker
restart: unless-stopped
敏感四项经 .env 注入(实际值示例,首次部署前替换为你自己的口令与密钥——口令按 5.1 结论一次设对,密钥用 openssl rand -base64 32 现场生成):
APPSETTINGS_USERNAME=admin
APPSETTINGS_EMAIL=admin@admin.com
APPSETTINGS_PASSWORD=q1w2e3r4
APPSETTINGS_JWT_SECRET=BI/pnDHJsnvA4kDltCsULSGvXuyFe5HfFhVJhB7y8OE=
⚠️ 若前面有 TLS 网关,建议把映射改为
127.0.0.1:8129:8080只监听本机回环,由网关对外。
上线前检查清单:口令已设最终值、JwtSecret 已随机生成、TZ 正确(docker exec oht-1.2.5-server date 核对)、数据目录挂载成功(ls data/.OpenHabitTracker/ 有 OpenHT.db)、登录验证通过、备份计划(cron 拷贝目录或 docker compose down 后冷备)已安排。
七、常见问题(FAQ)
Q1:改了环境变量为什么不生效?
环境变量在容器创建时读取,docker compose restart 不重建容器配置——用 docker compose up -d(检测到配置变化会重建)。核对实际注入值:docker inspect <容器> --format '{{.Config.Env}}'。
Q2:改口令环境变量后新口令登不进? 见 5.1 实测:口令只在首次创建账号时生效,之后以数据库哈希为准。换口令走应用界面。
Q3:轮换 JwtSecret 后所有设备被登出? 正常现象(实测旧令牌 401)。重新登录即可,数据无损。这也是密钥泄露后的应急手段。
Q4:统计数据里的「今天」差 8 小时?
TZ 未设或设错(容器默认 UTC)。按 5.3 设置 TZ=Asia/Shanghai 重建。
Q5:直接改 OpenHT.db 行不行?
技术可行(SQLite 标准文件),但表结构受 EF Core 迁移管理、且含口令哈希/令牌等敏感数据。建议只读查询(加 ?mode=ro),写操作走应用界面或 API,避免绕过应用校验造成脏数据。
Q6:如何把数据从浏览器 PWA 迁到自托管? PWA/客户端内 Data 页导出 JSON → 自托管实例登录后导入 JSON(应用内导入导出功能,格式含设置与全部内容)。
Q7:升级版本需要动配置吗? 不需要,配置全在环境变量与数据目录里。升级=改镜像 tag 重建(流程见部署指南第十章);应用启动时自动执行数据库迁移(启动日志可见 migration 过程)。
八、参考链接
- 1.2.5 appsettings.json 源码:https://github.com/Jinjinov/OpenHabitTracker/blob/1.2.5/OpenHabitTracker.Blazor.Web/appsettings.json
- 1.2.5 Program.cs(配置读取/认证/初始化逻辑):https://github.com/Jinjinov/OpenHabitTracker/blob/1.2.5/OpenHabitTracker.Blazor.Web/Program.cs
- 官方 README(环境变量示例):https://github.com/Jinjinov/OpenHabitTracker#docker-image-contains-a-blazor-server-app-for-one-user
- ASP.NET Core 环境变量配置约定(
__层级分隔符):https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/ - 相关篇目:本专栏《OpenHabitTracker Docker Compose 部署指南》(部署主线)、《OpenHabitTracker 1.2.5 API 使用详解》(接口级配置行为)