Repository navigation
bridge: 账号列表支持删除/清理重复条目,复制 server config 可选 host:port - #4
Open
std-external wants to merge 1 commit into
Open
std-external wants to merge 1 commit into
std-external wants to merge 1 commit into
Conversation
std-external
force-pushed
the
feat/account-cleanup
branch
from
October 9, 2026 16:56
86fcf71 to
b1c5b52
Compare
账号列表管理: - 卡片新增复选框与 Delete account,多选后可 Delete selected。删除都经过确认弹窗: 列出具体条目、写明会有几个已登录的 Telegram 客户端被登出;条目不由配置文件管理时 直接禁用确认按钮 - findDuplicateAccounts() 只报能证明的重复:adapter 解析到同一个平台用户 (platformKind + userId),或该条目因虚拟手机号被另一条目占用而 provision 失败 (VirtualPhoneClaimedError)。保留客户端已登录的那条,平手按 platformId 稳定排序。 页面的 Select duplicates 只做选中,看到确认列表后才删 - deleteAccounts() 先校验全部目标,再停订阅、清理平台会话/虚拟号/TOTP secret/两步 验证密码/binding/客户端授权、吊销对应 auth key,最后通过所属的 loader 树删掉 config entry(app.yml 会写回,重启后不会再被 provision)。消息与会话历史保留 - 新增 account-removal.ts / account-duplicates.ts 及单元测试;另有直接跑真实 Loader + Include 的测试,覆盖嵌套树的 entry key 与删除写回 复制 server config: - dashboard 新增 serverEndpoints:主地址在前,altEndpoints 按配置顺序解析后跟在后面并去重 - Copy 按钮左侧新增 Endpoint 下拉(只有一个地址时不显示,默认主地址),只改写 host/port 与 dcs[].ip/port;页面展示与剪贴板仍是同一份文档,格式与 main 一致
std-external
force-pushed
the
feat/account-cleanup
branch
from
October 9, 2026 18:03
b1c5b52 to
9b70886
Compare
Author
|
已重新基于
#3 的建议写在描述末尾:这一页的改动已经完全独立,建议关掉它;若要保留单行剪贴板,单独合掉即可(同一处需要一次很小的 rebase)。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
需求与来源
两个需求都在
/platform-accounts这一页,报告来源是 QQ 群「Koishi On Topic」的群管理员:「我现在列表里创建了几个重复的,是不是加一个删除功能好一些」
altEndpoints)只能复制出来再手改:「之前有做一个 alt endpoint 的功能,可以在复制配置左边加一个下拉菜单 控制复制出哪个 host:port 不然需要复制出来再手改」
一、删除账号与清理重复条目
「账号」就是一条 Cordis platform entry
docs/IM_PLATFORM.zh.md:同一个 Cordis platform entry 对应一个账号和一个虚拟手机号。列表页的platformId就是 entry 的配置id(
resolvePlatformPluginId())。所以只清数据库是治不好的:entry 还在的话,下次 provision 又把账号建回来。删除必须把那条 config entry 一起删掉。
删除的语义
deleteAccounts(platformIds):mtproto_platform_session、mtproto_auth_session(虚拟号 + TOTP secret + 两步验证密码)、mtproto_auth_binding、mtproto_client_authorization、mtproto_authorization_settings不删消息/会话历史:它们按 platform session 隔离,条目删掉后不再可达;连带删历史既不是这次的需求,风险也大。
如果以后用同一个 entry id 再配回来,
mtproto_im_user身份行还在,旧消息不会变成孤儿。每次删除都会弹二次确认,内容来自后端
describeAccountRemoval():单条删除(卡片上的
Delete account)和多选删除(勾选后Delete selected)走同一个确认弹窗和同一个 RPC。重复判定口径
findDuplicateAccounts()只报能证明的重复,两类:identityplatformKind和userId都相同)virtual-phone888{qq},占错本身就证明是同一个账号)Error,现在换成VirtualPhoneClaimedError并把占用者的 platformId 带出来,所以这类重复是「由数据本身证明」的,不是猜的。
Select duplicates只做选中,用户看到列表确认后才删;没有「一键直接删」的路径。为什么不做全自动去重:删除会登出客户端、删掉虚拟号和两步验证密码,而「同名」或「同 server config」都不足以判定重复
(同一份 config 合法地跑多个不同账号是正常的)。所以最终一步必须落到人看得到的确认列表上。
一个真实 loader 的坑(已修)
app.yml 在生产里是经
@cordisjs/plugin-cli-cordis→@cordisjs/plugin-include加载的,条目在 loader 里的 key 是<config-tree>:<configured-id>(例如7ffc5890:qqnt),而 bridge 里的platformId是配置里的 id(qqnt):entry.id匹配会找不到条目 → 改成同时接受entry.options.id;ctx.loader.remove(<qualified>)在嵌套树里会静默不删(EntryGroup.remove用的是所属子树的 store)。现在改为通过条目所属的树删
options.id,那也正是负责写回 app.yml 的那棵树。这两点是靠一个直接跑真实
Loader+Include的测试守住的(删完app.yml里确实没有这条了),只 mock loader 是测不出来的。二、复制 server config 时可选 host:port
serverEndpoints:主地址(serverHost:serverPort)在前,altEndpoints按配置顺序解析后跟在后面,去重。Copy server configuration左侧出现Endpoint下拉:只有一个地址时不显示(行为与之前完全一致),默认选中主地址。host/port和dcs[].ip/port,不改别的字段;页面<pre>展示的是当前选中地址的版本(所见即所复制)。main的JSON.stringify(config, null, 2)),只把host/port与dcs[].ip/port换成选中的地址,其他字段逐字不变。改动
packages/bridge/src/account-removal.ts(新)packages/bridge/src/account-duplicates.ts(新)packages/bridge/src/platform-account.tsVirtualPhoneClaimedError(带占用者 id)packages/bridge/src/account-dashboard.tsmakeServerEndpoints()packages/bridge/src/dashboard-types.tsserverEndpoints、findDuplicateAccounts、describeAccountRemoval、deleteAccounts的线上契约packages/bridge/src/index.tspackages/bridge/client/accounts.tsxDelete account、多选工具条、重复提示条、确认弹窗、Endpoint下拉packages/bridge/client/bridge-model.tsformatEndpoint、withServerEndpoint、duplicateOwners/duplicatePlatformIds/describeDuplicateGrouppackages/bridge/client/style.css验证
遗留 session 缺失的旧数据)、真实 loader 上的条目查找与删除(含 app.yml 写回)、endpoint 文档改写(只改 host/port 与 dcs)。
Endpoint默认是主地址、切到backup.test:8443后剪贴板里host/port/dcs都跟着变(其余字段不变)、无 Clipboard API 的回退路径复制出同样内容;重复提示条与卡片上的Duplicate of;打开
offline的删除确认会提示「不由配置文件管理」且确认按钮禁用;打开qq-main的删除确认会提示「2 个客户端会被登出」;
Select duplicates→ 确认 → 该条目从列表里消失、删除请求只带这一个 id。回归:把
withServerEndpoint()从复制路径去掉,e2e 里备用地址的断言立刻失败;重复清理那条如果不做确认,也会被「确认按钮禁用」的断言拦住。
全量:
npx vitest run→ 1741 passed / 1 failed(voice-worker-client.test.ts需要cargo,本机没装,spawn cargo ENOENT,与本次改动无关);
npx vitest run --config vitest.webui-solid.config.mts→ 85 passed / 1 failed(
loader.browser.e2e.test.ts的 loader 页横向溢出,未改动的main上同样失败)。截图
以下是 e2e 跑出来的真实页面(截图放在 fork 的分支上,避免往仓库里塞二进制):
复制配置前(主地址,默认):
切到备用地址后(文本里的 host/port 与 dcs 已经跟着变):
(Chromium 没法截原生
<select>展开后的弹层,所以这里是「选中前 / 选中后」两张,选中后顺带展开配置文件对照。)重复条目提示条:
重复条目卡片:
删除确认:
#3 怎么办:建议关掉
main上也不存在 bridge: 复制 server config 时输出单行 JSON(Android 端粘贴报「非法 JSON」) #3 的代码,所以 bridge: 复制 server config 时输出单行 JSON(Android 端粘贴报「非法 JSON」) #3 不再是任何 PR 的前置。服务端产物:真实 crossgram-android 的
Server.parseUserInput用 Androidorg.json解析 pretty JSON、CRLF、BOM、尾随换行都能过,唯一能复现的坏变形是缩进空格被换成 NBSP(见本地报告
reports/crossgram-62-android-parse-probe/)。也就是说「服务端只发单行」是更抗粘贴链路搬运,但不是修某个必然失败的 bug。
后合的那次需要一次很小的 rebase;也可以照着这个 PR 的
withServerEndpoint()写法把它并进来。边界与未纳入
bridgeManagement那层 seam 没动),管理 Bot 里没有加删除按钮。identity口径下被发现;adapter 报错(离线、unsupported)而拿不到平台用户 id 的条目不算重复,需要手动多选删除。
mtproto_im_user等历史数据不删(见上);如果以后要「连历史一起删」,建议单开一个 PR。