Karakeep 自托管部署用户管理与密码重置实战指南(FAQ 精讲)
【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarder
自托管部署 Karakeep 后,最常遇到的运维问题几乎都集中在用户管理上:管理员忘了密码怎么办、如何给第二个管理员授权、关闭开放注册后如何添加新用户。本文以官方 FAQ 文档为骨架,结合仓库源码(数据库表结构、认证逻辑、管理端 tRPC 路由与测试用例)逐项讲解这些场景的标准操作步骤、底层原理与安全注意事项,读完即可独立完成 Karakeep 的用户生命周期管理。
一、用户管理与角色模型概览
Karakeep 的用户体系包含两种角色:user(普通用户)与admin(管理员),角色定义在 packages/shared/types/admin.ts 中:
export const zRoleSchema = z.object({ role: z.enum(["user", "admin"]), });对应的数据库表user定义在 packages/db/schema.ts,核心字段包括:
id:主键,默认由createId()生成;email:唯一索引,登录凭证;password:可空文本字段(OAuth 用户可能没有密码);salt:密码盐,默认空字符串;role:枚举["admin", "user"],默认user。
值得注意的默认规则:第一个注册的用户会被自动提升为管理员。该逻辑位于 packages/trpc/models/users.ts 的createRaw中:
let userRole = input.role; if (!userRole) { const [{ count: userCount }] = trx .select({ count: count() }) .from(users) .all(); userRole = userCount === 0 ? "admin" : "user"; }即当用户表中没有任何记录时,首个创建的账号角色为admin。这一行为在 packages/trpc/routers/users.test.ts 的"first user is admin"测试用例中得到验证:第一个用户role为admin,第二个用户role为user。
二、忘记密码怎么办
2.1 非管理员用户:请管理员重置
如果你不是管理员,密码重置只能由管理员在管理界面完成:
- 打开
Admin Settings(管理设置)页面; - 在
Users List(用户列表)中找到目标用户; - 在
Actions列点击重置密码按钮; - 输入新密码并点击
Reset; - 新密码即刻生效;
- 出于隐私考虑,你可以在登录后前往
User Settings(用户设置)再次修改密码,这样管理员也无法得知你的新密码。
管理员重置密码的后端接口位于 packages/trpc/routers/users.ts,其输入校验在 packages/shared/types/admin.ts 中定义:
export const resetPasswordSchema = z .object({ userId: z.string(), newPassword: z.string().min(8).max(PASSWORD_MAX_LENGTH), newPasswordConfirm: z.string(), }) .refine((data) => data.newPassword === data.newPasswordConfirm, { message: "Passwords don't match", path: ["newPasswordConfirm"], });可以看到新密码有强制约束:最短 8 位,且必须与确认密码一致,否则接口会直接拒绝。重置后,服务端会在 packages/trpc/models/users.ts 处以同样的哈希流程写入新密码。
2.2 管理员自己忘记密码:直接操作数据库
如果管理员本人丢失了密码,就需要绕过登录流程,直接在数据库中重置。官方 FAQ 给出的步骤是:
- 准备一个数据库连接工具:
- Linux 上使用
sqlite3:运行apt-get install sqlite3(具体命令依你的包管理器而定); - Windows 上可使用
dbeaver等图形化工具。
- Linux 上使用
- 关闭 Karakeep 服务(防止服务运行中数据库文件被占用或缓存不一致)。
- 连接数据库文件
db.db——它位于你挂载到 Docker 容器的data目录中:- 在
data目录下直接运行sqlite3 db.db; - 或通过
dbeaver的界面定位并连接该文件。
- 在
- 执行 SQL 更新密码:
update user set password='$2a$10$5u40XUq/cD/TmLdCOyZ82ePENE6hpkbodJhsp7.e/BgZssUO5DDTa', salt='' where email='<YOUR_EMAIL_HERE>';注意:把
<YOUR_EMAIL_HERE>替换成你自己的邮箱地址。该命令将你的密码重置为预设值adminadmin。
- 重新启动 Karakeep。
- 使用邮箱地址与密码
adminadmin登录,然后立即在User Settings中把密码改掉。
2.3 密码哈希原理:为什么这样一条 SQL 就能生效
这条 SQL 之所以能奏效,是因为 Karakeep 的密码存储格式是固定的。查看 packages/trpc/auth.ts:
export async function hashPassword(password: string, salt: string | null) { return await bcrypt.hash(password + (salt ?? ""), BCRYPT_SALT_ROUNDS); }密码校验时(packages/trpc/auth.ts)执行的是:
const validation = await bcrypt.compare( password + (user.salt ?? ""), user.password, );也就是说,最终比较的是密码明文 + salt的 bcrypt 哈希。FAQ 中给出的$2a$10$5u40XUq/cD/TmLdCOyZ82ePENE6hpkbodJhsp7.e/BgZssUO5DDTa正是"adminadmin" + ""(空 salt)经过 bcrypt(cost 因子 10)计算后的合法哈希,因此把password设为该值、salt清空后,adminadmin就能通过登录校验。
源码中的防御性设计也值得了解:当用户不存在或没有密码(如纯 OAuth 账号)时,服务端仍会执行一次针对固定DUMMY_PASSWORD_HASH的 bcrypt 比较(见 packages/trpc/auth.ts),用以隐藏"用户是否存在"的信息,抵御时序攻击。
2.4 安全注意事项
- 务必在操作前关闭 Karakeep,并在修改完成后立刻启动服务并登录验证;
- 登录成功后第一时间把
adminadmin改为强密码; - 该方式直改数据库,属于"最后手段",操作前建议先备份
data目录(Karakeep 的备份与迁移说明可参考 docs/docs/06-administration/06-server-migration.md); - 从源码可推断(packages/shared/types/admin.ts),正常的密码策略要求新密码最短 8 位,重置后也请遵循这一要求。
三、如何添加第二个管理员
默认情况下只有第一个注册用户是管理员。要给其他用户授予管理员权限:
- 使用管理员账号进入
Admin Settings页面; - 在
Users List中找到目标用户; - 在
Actions列点击修改角色(Change Role)按钮; - 将角色改为
Admin; - 点击
Change确认; - 被提升的用户需要先退出登录、再重新登录,新的角色才会生效。
底层实现上,角色修改通过 packages/trpc/routers/users.ts 中的管理端过程完成,其更新模型在 packages/shared/types/admin.ts:
export const updateUserSchema = z.object({ userId: z.string(), role: z.enum(["user", "admin"]).optional(), bookmarkQuota: z.number().int().min(0).nullable().optional(), storageQuota: z.number().int().min(0).nullable().optional(), browserCrawlingEnabled: z.boolean().nullable().optional(), });可以看出,管理员的权限远不止改角色:还可以调整bookmarkQuota(书签配额)、storageQuota(存储配额)、browserCrawlingEnabled(是否允许浏览器爬取)等管理员专属设置,这些字段也同步定义在数据库表user中(packages/db/schema.ts)。
"重新登录后角色生效"这一现象源于会话建立机制:登录时服务端会把用户角色写入会话上下文(见 packages/trpc/index.ts 的role: "admin" | "user" | null),旧会话中缓存的角色不会实时刷新,因此需要重新登录。
四、关闭注册后如何添加新用户
Karakeep 支持通过环境变量DISABLE_SIGNUPS关闭开放注册(对应配置解析位于 packages/shared/config.ts 的disableSignups: val.DISABLE_SIGNUPS)。当注册关闭后,普通用户无法自助注册,服务端会在 packages/trpc/routers/users.ts 直接抛出FORBIDDEN错误:
if ( serverConfig.auth.disableSignups || serverConfig.auth.disablePasswordAuth ) { ... throw new TRPCError({ code: "FORBIDDEN", message: "Signups are disabled in server config", }); }但管理员随时可以手动创建账号,流程如下:
- 进入
Admin Settings页面; - 打开
Users List; - 点击
Create User(创建用户)按钮; - 填写用户信息(姓名、邮箱、密码等);
- 点击
create确认; - 新用户即可直接登录。
这一路径走的是users.create的管理端变体,创建时在 packages/trpc/models/users.ts 中会生成随机盐并调用hashPassword保存密码;同时该接口还支持role参数,管理员在创建时即可决定新用户是普通用户还是管理员。
五、常见问题排查速查
| 场景 | 推荐处理方式 | 依据 |
|---|---|---|
| 普通用户忘记密码 | 管理员在Admin Settings → Users List中重置 | packages/shared/types/admin.ts |
| 管理员忘记密码 | 停服后直连db.db执行 SQL 重置为adminadmin | 本文 2.2 节 |
| 需要第二个管理员 | 管理员修改目标用户角色为Admin,对方重新登录 | packages/shared/types/admin.ts |
| 关闭注册后加人 | 管理员在Admin Settings中Create User | packages/trpc/routers/users.ts |
| 新密码设置失败 | 检查是否满足最短 8 位且两次输入一致 | resetPasswordSchema校验 |
六、小结
Karakeep 的用户管理机制整体设计得简洁而安全:以user/admin双角色为核心,首个注册用户自动成为管理员;密码采用bcrypt(密码 + salt)哈希存储,并内置了防时序攻击的 dummy 比较;管理端对用户的新增、删除、改密、配额调整都封装为带校验的 tRPC 接口。日常运维中只要遵循本文的流程——忘密码走管理端重置或数据库直改、提权后重新登录、关闭注册后用管理员手工建号——即可平稳应对绝大多数账号问题。若遇到登录、OAuth 或邮箱验证等更复杂的认证配置,可继续查阅 docs/docs/03-configuration/01-environment-variables.md 与 docs/docs/02-installation/01-docker.md。
【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarder
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考