nemu-x 说明 mikan 用户导入功能的审查修正要点
nemu-x 在 mikan(mihomo 内核 VPN 面板)Marzban/PasarGuard/Remnawave 用户导入 PR #37 中说明已 rebase 到 dev 并逐条修正评审意见:schema 移入 postgres/0003_legacy_sub_tokens.sql 且带 Down(若 #31 先合并则改号 0004),SQLite 导入测试回滚到导入基线;Reissue 在同一事务删除旧 legacy_sub_tokens;shortUuid 须匹配 ^[A-Za-z0-9_-]{16,128}$,否则用户导入后无链接;分页按 len(page.Users) 推进、空页或到 total 停止,少于 total 报 import_incomplete;预览亦为后台任务(POST /import/preview 返 202,结果走 GET /import/status),名称按 1000 一批检查,各列表上限 50 条加计数;RefillFor(n) 导入前补一次槽位并发一次 SlotsChanged;混用类型返回 409 import_kind_conflict,Remnawave 导入不改 legacy_sub_kind;上游两 API 均无 sub_revoked_at,故每条链接记录 not_before 并拒绝更早签发的 token,此后的吊销无法感知,已在包文档与预览警告提示重新签发;任务 panic 记为 import_failed、DELETE /import 可取消、Run 作为面板 worker 在关闭时取消并等待,中途取消或超时保留已建用户与报告;并修正 on-hold 溢出与地址校验细节。28 个 Go 包对 PostgreSQL 18 全通过,Web 应用类型检查与构建通过。
作者原文@Nemu-xThanks. Rebased on
dev(ea9db8a, after #30); every point is addressed in f8e5ed3 and 0bd2fd3.Blocker: schema
0001_baseline.sqlandpostgres_test.goare back todev, andmigrations/0019_legacy_sub_tokens.sqlis gone.- The table is in
postgres/0003_legacy_sub_tokens.sql, with a Down. #31 also adds a0003_promocodes.sql, so whichever merges second has to renumber. I will move mine to0004if #31 goes first.- The store's SQLite import tests ran into a fully migrated schema and failed on the new table. They now roll the fixture back to the import baseline, which is what
MigrateandRestoreSQLitedo.High
- Reissue deletes the user's
legacy_sub_tokensin the same transaction (TestReissueDropsOldLinks).- Weak
shortUuid. A token must match^[A-Za-z0-9_-]{16,128}$. Otherwise the user is imported without a link and listed under "no link" in the preview and the report (TestRemnawaveWeakToken).- Paging. The offset advances by
len(page.Users), and the loop stops on an empty page or attotal. Fewer users thantotalfails the job withimport_incomplete(TestFetchMarzbanCappedLimit, where the fake caps every page at 2).Medium
- Preview is a background job too.
POST /import/previewreturns 202 and the result arrives inGET /import/status. Names are checked withname = ANY($1)in chunks of 1000. The taken, invalid, skipped, no-link and failed lists are capped at 50 names plus a count.- Slots.
Users.RefillFor(n)refills the missing slots once before the import and sends oneSlotsChanged(TestRefillForTheImport).- Mixing kinds. A Marzban import over PasarGuard links, or the other way round, is refused with 409
import_kind_conflict(TestImportRefusesMixingSignedPanels). Remnawave links need no secret, so a Remnawave import leaveslegacy_sub_kindalone.sub_revoked_atis in neither Marzban's nor PasarGuard's API. What I did:- Each link stores
not_before, the user's creation time in the old panel, and a token signed before it is refused.- A revoke after that cannot be seen. The package doc and the preview warning say so, and tell the admin to reissue those users' links.
- Job.
recoverturns a panic into a failed job (import_failed).DELETE /importcancels a running job.Importer.Runis one of the panel's workers: on shutdown it cancels the job and waits for it.- A cancel or timeout mid-import keeps the users made and their report, and
Donestill runs whenever users were created.- Tests:
TestJobCancel,TestJobCancelKeepsThePartialReport,TestJobPanic,TestJobTimeoutandTestJobStopsWithThePanel.Low
- On-hold overflow. The duration is checked against the 20-year limit before the multiplication, and out of range is invalid (
TestOnHoldOverflow).AddressOKblocksfd00:ec2::254. NAT6464:ff9b::/96is checked by the IPv4 inside it, so a NAT64 form of 169.254.169.254 is refused and one of 10.0.0.1 is allowed. CGNAT stays allowed, with a comment: Tailscale and similar overlays put an old panel there (TestAddressOKSpecial).- API errors are codes only. The remote text and
Locationgo to the log.All 28 Go packages pass in a Linux container against PostgreSQL 18. The web app typechecks and builds.