资讯动态

Civitai Moderator 独立应用拆分的共享模块边界:22 个审核页面的依赖分层与渐进式迁移路线

发布时间:2026/9/18 13:47:38 来源:尧图企业网站定制
Civitai Moderator 独立应用拆分的共享模块边界22 个审核页面的依赖分层与渐进式迁移路线【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai导读当监管审核Moderation页面必须搬进一个独立的卫星应用satellite app、同时又与主应用共享同一套 Postgres / Redis / ClickHouse 连接时最大的工程难题不是写页面而是划定共享代码与各应用私有代码的边界。本文以 Civitai 仓库中的模块边界分析文档为核心完整展开 22 个审核页面的依赖地图六层依赖分级、逐页可移植性评估、三个候选共享模块的设计以及从 Phase 0 到 Phase 6 的渐进式拆分路线并结合仓库后续的 monorepo 演进文档与实际落地的apps/moderator应用说明这套分析如何在真实迁移中被修订与兑现。读者可以从中得到一套先做导入闭包分析、再做按需提取的 monorepo 多应用拆分方法论。一、目标让 22 个审核页面在独立应用中可构建关联文档 docs/moderator-app-shared-modules.md 提出的核心目标是让下列 22 个 moderator 页面能够在一个独立的 Next.js 应用中构建该应用与主应用共享 Postgres / Redis / ClickHouse 连接并识别出为了做到这一点、无需 fork 整个仓库就必须抽取到 git submodule或若干 submodule中的既有代码。拆分动机的完整背景见 docs/monorepo-split-overview.md 的说明脉络而数据契约子模块是拆分的先决条件。需要明确的是原文档撰写于 monorepo 化早期其假设的git submodule civitai-schema-common方案在后续决策中被否决详见本文八、从分析到落地但文档中关于依赖分层与逐页可移植性的分析结论在演进后依然成立是后续所有决策的出发点。22 个在范围内页面清单业务域页面内容审核Content reviewimages、images/to-ingest、image-tags、image-rating-review、downleveled-review、ingestion-error-review、articles、models/index、comics-review、reports、tags、blocklists、auditor、strikes扫描器审计Scanner auditscanner-audit/index、scanner-audit/[mode]/index、scanner-audit/[mode]/[label]含逐标签策略编辑训练Trainingtraining-models、review/training-data/index、review/training-data/[versionId]CSAMcsam/index、csam/[userId]生成Generationgeneration、generation-config、generation-restrictions后续的边界复核文档 docs/moderator-app-package-boundary.md 进一步确认了范围这约 22 页是内容审核子集/moderator下的商务/管理页cosmetic-store、rewards、challenges、paddle、cash-management、auctions、contests、code-gifts、home-blocks暂时留在主应用。二、依赖地图22 页的导入被分成六个层级文档从 import 分析出发把这 22 个页面引入的代码划分为六个层级Tier。表格中的Pages列表示 22 个页面中有多少个页面导入了该项。Tier A — 模式 / 数据契约由civitai-schema-common覆盖ImportPages~/shared/utils/prisma/enums14~/server/common/enumsNsfwLevel、BlockedReason、BlocklistType 等6~/shared/constants/browsingLevel.constants3~/server/common/constants2~/server/schema/report.schema、strike.schema、image.schema、scanner-review.schema5~/shared/constants/basemodel.constants1generation-config~/shared/constants/mime-types、~/shared/utils/report-helpers2结论Verdict以上都应归属civitai-schema-common。其中 Prisma 侧的~/server/schema/*.schema.tszod 模式是边界情形——它们既是 tRPC 输入契约又是许多表的形状事实来源source of truth。文档建议把与 moderator 相关的部分纳入共享因为两个应用需要在这些形状上达成一致。Tier B — tRPC 客户端 认证胶水按消费方各自持有不共享ImportPages~/utils/trpc22全部页面~/server/utils/server-side-helperscreateServerSideProps6~/providers/FeatureFlagsProvider、~/hooks/useFeatureFlags5~/hooks/useCurrentUser、useIsMobile、useInView、useStepper7合计~/types/router推断出的 tRPC 类型2结论每个应用需要自己的副本。卫星应用建立指向自己 router或代理到主应用 router的独立 tRPC 客户端useCurrentUser/useIsMobile这类小 hook 直接 vendor-copy 即可。后来的演进文档 docs/moderator-app-package-boundary.md 称之为per-app glueutils/trpc绑定到~/server/routers的 AppRouter而 moderator 应用有自己的 router 类型因此这类代码天然属于各自应用而非共享包。Tier C — moderator 专用共享组件候选进 moderator 子模块ImportPages~/components/Meta/Meta9~/components/AppLayout/Page、AppLayout/NotFound7~/components/Moderation/*ScannerAuditLayout、ScannerPolicySidebar、FlaggedModelsList、RuleDefinitionPopover、GenerationStatusCard、UserGenerationsDrawer5~/components/Csam/*CsamProvider、CsamDetailsForm、CsamImageSelection、useCsamImageSelectStore4~/components/Dialog/dialogStore、Dialog/Common/TosViolationDialog、Dialog/triggers/*6~/components/Profile/UserBanModal2~/store/select.store2结论这些组件主要由 moderator 页面使用天然适合放进 moderator 专用子模块。仓库中这些组件至今仍以源码形式存在于主应用例如 src/components/Csam/CsamProvider.tsx、src/components/Csam/CsamImageSelection.tsx、src/components/Csam/useCsamImageSelect.store.ts、src/components/Moderation/FlaggedModelsList.tsx、src/components/Moderation/GenerationStatusCard.tsx以及 src/store/select.store.ts。Tier D — Civitai UI 基础词汇尴尬的中间地带这类组件并非 moderator 专用主应用处处在用但 moderator 页面离开它们就无法渲染ImportPages~/components/NextLink/NextLink16~/components/EdgeMedia/*EdgeMedia、EdgeVideo、EdgeVideoBase8~/components/NoContent/NoContent6~/components/LegacyActionIcon/LegacyActionIcon6~/components/InView/InViewLoader4~/components/ImageGuard/ImageGuard23~/components/ImageMeta/ImageMeta、VotableTags/VotableTags、ImageHash/ImageHash、MasonryColumns/*、ContentClamp/ContentClamp、RenderHtml/RenderHtml、DescriptionTable/DescriptionTable、PopConfirm/PopConfirm、CivitaiWrapped/ButtonTooltip16合计文档给出三个选项并明确推荐选项 3vendor-copy 进卫星应用—— 简单但两应用 UI 会随时间视觉漂移提升为civitai-ui-common子模块—— 长期干净但抽取量巨大主应用所有导入这些文件的调用点都要重写先 vendor-copy后期再提升—— 早期承担重复成本漂移变得痛苦时再提升。Tier E — 领域工具函数vendor-copy 或复制ImportPages~/utils/notificationsshowError/SuccessNotification15~/utils/string-helpers9~/utils/date-helpers6~/libs/formForm、InputTextArea、InputNumber、InputSelect、useForm3~/utils/moderators/moderator.util、number-helpers、file-utils、lazy、training、type-guards、normalize-text、metadata/audit、~/client-utils/cf-images-utils、~/hooks/useCheckProfanity分散结论多为小型纯函数工具最省事的是 vendor-copy重度复用的可随 Tier D 一起在适当时机提升到civitai-ui-common。例外是useCheckProfanity——它命中服务端端点hook 必须随 auditor 页面一起迁移且卫星应用需要配套端点。Tier F — 移植阻塞点提取前必须先就地重构ImportPageBlocker~/server/services/image.service类型导入image-rating-review、downleveled-review、ingestion-error-review目前是类型导入但底层函数必须在某处运行。要么卫星应用托管这些 tRPC 路由拖入 image.service要么卫星应用通过 HTTP 调用主应用。~/server/services/scanner-review.service、scanner-content.servicescanner-audit/[mode]/[label]扫描器审计队列 逐标签内容拉取与 xguard orchestrator 回调绑定。需要厘清哪些是只读查询、哪些是 orchestrator 交互。~/server/common/moderation-helpersunpublishReasonsarticles、modelsmoderator 专属领域逻辑实际是稳定的枚举应提升进 Tier Aschema-common。该文件现仍存在src/server/common/moderation-helpers.ts。~/components/Image/PromptHighlight/PromptHighlight、useReportCsamImagesimages.tsxPromptHighlight 拖入 metadata 审计工具CSAM hook 与主应用的 dialog/notification 机制耦合。重构方向抽取纯高亮器CSAM hook 基于卫星应用自己的 dialog 系统重写。三、逐页可移植性总结7 易 / 11 中 / 6 难文档对 22 个页面逐一给出导入数评估与可移植性分级PageImportsPortabilityimages/to-ingest5Easyimage-rating-review15Easydownleveled-review11Easyingestion-error-review9Easycomics-review18Easystrikes17Easyreview/training-data/index12Easyimage-tags21Mediumarticles20MediumunpublishReasonsmodels/index16MediumFlaggedModelsListreports28Mediumtags20Mediumblocklists11Mediumtraining-models16Mediumreview/training-data/[versionId]19Mediumcsam/index8Mediumcsam/[userId]15Mediumgeneration-config9Mediumbasemodel.constantsimages34HardPromptHighlight、CSAM hooksauditor4HarduseCheckProfanityscanner-audit/[mode]/index14HardScannerAuditLayout、xguardscanner-audit/[mode]/[label]18Hardscanner-content servicegeneration-restrictions14HardUserGenerationsDrawer总计7 Easy / 11 Medium / 6 Hard。一个值得注意的规律Hard 页面大多因为单个具体耦合点而变难如 auditor 只因useCheckProfanityimages只因 PromptHighlight 与 CSAM hooks一旦这些耦合被解掉难度会迅速降回 Medium——这正是分阶段迁移按先易后难排序的依据。四、推荐的模块结构三个子模块渐进引入文档建议拆出三个子模块按增量节奏引入1.civitai-schema-common—— 已规划覆盖 Tier A是下面一切的前置条件。本分析对其原计划的两点补充纳入 moderator 相关的~/server/schema/*.schema.tszod 文件report、strike、image、scanner-review、buzz-withdrawal-request、model-version提升~/server/common/moderation-helpers.unpublishReasons及其他稳定的枚举形态导出NsfwLevel、BlockedReason、BlocklistType、MAX_APPEAL_MESSAGE_LENGTH等来自~/server/common/enums与~/server/common/constants。2.civitai-moderator-common—— 新增承载 Tier C 内容以及一旦可移植后的 22 个页面本身。卫星应用的src/pages/moderator/*只做薄重导出thin re-exportscivitai-moderator-common/ ├── components/ │ ├── Moderation/ # ScannerAuditLayout, ScannerPolicySidebar, │ │ FlaggedModelsList, RuleDefinitionPopover, │ │ GenerationStatusCard, UserGenerationsDrawer │ ├── Csam/ # CsamProvider, CsamDetailsForm, │ │ CsamImageSelection, useCsamImageSelectStore │ ├── Dialog/ # dialogStore moderator-specific dialogs │ └── Profile/UserBanModal/ ├── pages/ # the 22 page components (after refactor) ├── server/ │ ├── routers/ # moderator-* tRPC routers │ └── services/ # moderator-specific service code that satellite │ must run locally (or, alternatively, thin │ clients that call main apps tRPC) └── README.md3.civitai-ui-common—— 延迟承载 Tier D 内容EdgeMedia、ImageGuard2、MasonryColumns、NextLink 等从 vendor-copy 开始漂移变得痛苦时再提升。文档预估这是6–12 个月的后续事项不阻塞初始拆分。五、分阶段落地路线Phase 0–6阶段内容Phase 0按既有计划phases 1–5先发布civitai-schema-common。卫星应用在此之前无法启动。Phase 1在主应用内就地重构 6 个 Tier F 阻塞点抽取PromptHighlight脱离 metadata-audit 耦合重构useReportCsamImages使其依赖 dialog 系统接口而非主应用具体 dialog store决策 scanner-content service 归卫星所有还是 tRPC 代理到主应用决策 image.service 的 moderator 查询归卫星还是代理。注useUnsupportedResources抽取项已解决——该 hook 及其唯一消费方/moderator/generation随生成黑名单迁移到ModelVersionFlag.GenerationDisabled位而一并移除。Phase 2建立卫星 Next.js 应用接入civitai-schema-common子模块vendor-copy Tier DUI 原语与 Tier E工具函数接受重复自带 tRPC 客户端 认证设置先移植 7 个 Easy 页面作为概念验证proof-of-concept。Phase 3创建civitai-moderator-common子模块把 Tier C 组件移入moderator 页面随可移植进度移入。过渡期主应用与卫星应用同时从子模块导入主应用最终弃用这些页面。Phase 4Tier F 重构完成后迁移 11 个 Medium 页面。Phase 5迁移 6 个 Hard 页面——多数只因单一具体耦合而难耦合解决后即降为 Medium。Phase 6延迟当重复摩擦真实存在时把 Tier D 提升进civitai-ui-common。六、开放问题Phase 1 前需决策文档末尾以dev:*标注了五个必须在 Phase 1 之前拍板的决策tRPC 拓扑卫星应用是自建 router 直连共享 DB会把服务拖进子模块还是通过 HTTP 调主应用 tRPC更简单但增加网络跳数与停机耦合civitai-moderator-common的范围只含组件 服务端代码还是连 22 个页面一起过渡期的页面归属页面从主应用迁往子模块时是两边同时保持可用还是硬切换认证卫星应用如何判断此用户是 moderator共享主应用的 NextAuth session cookie同域、用自己的登录还是调主应用/api/auth/session校验CSAM 页面处理CSAM 是整个列表中最敏感的部分需确认是否应迁到卫星应用还是出于安全审查/审计追踪理由留在主应用。七、方法论要点导入闭包测试是整个游戏边界复核文档 docs/moderator-app-package-boundary.md 把这份分析背后最核心的规则提炼成一条治理规则governing rule一个包只能导入外部 npm 依赖与其他包civitai/*绝不能导入应用代码~/…。因此对每个拟共享文件问题不是它算不算 moderator 相关而是该文件完整的传递性~/…导入闭包是否也可移动只要有一个叶子摸到 zustand store、tRPC 客户端、provider 或image.service整个候选就被阻塞直到该叶子被处理。这解释了为什么 Tier F 中体型巨大的image.service当前仓库中仍为 src/server/services/image.service.ts约 7982 行会成为全部分析的枢纽它导入 14 个兄弟服务post.service双向互导、report.service、tag.service、notification.service、cosmetic.service、nsfwLevels.service、image-flag.service、games/new-order.service、moderator.service、tagsOnImageNew.service、feature-flags.service、storage-resolver、orchestrator/orchestrator.service等。整体拉出image.service或任何导入它的东西 导入主应用的整个内容图feed 缓存失效、NSFW 重排队、化妆品、游戏——这不可能放进一个包里。80/20 的解法拆分读队列与跨图写入边界文档给出的最终解法是把 moderator 的服务端需求分解为两种截然不同的形态(i) 读队列 —— 自己拥有。审核面本质上就是 SQLgetImageModerationReviewQueue、getImageRatingRequests、getDownleveledImages、getIngestionErrorImages、scanner 队列、reports 列表、strikes 排行榜/历史、CSAM 报告分页、训练队列、flagged-models 列表。它们今天大多定义在image.service/report.service内部但实际只需要dbRead selectors enums。把这些查询函数提升为只导入civitai/dbcivitai/db-schema的查询模块——这是机械抽取而非重新设计SQL 不变。(ii) 跨图写入 —— 不拥有整个服务。少数变更确实触碰主应用图moderateImages→post.service.bustCachesForPosts的 feed 缓存updateImageNsfwLevel→nsfwLevels.service的漫画重排队report 状态变更 → 多个服务。对这类变更按动作选择便宜 → 昂贵仅 Redis 缓存失效bustCachesForPosts归根结底只是失效 Redis 键。若 moderator 写入自己做 DB 变更后失效同一批civitai/redis键像指南对REDIS_KEYS做的那样通过子路径暴露键构建器就无需导入post.service。适用于副作用是失效缓存 X的场景。把动作代理到主应用 tRPC对少数副作用是真实编排的写入漫画重排队、通知扇出、games/new-ordermoderator router 调用主应用的一个 procedure主应用拥有图。为这些低频 moderator 点击接受一次网络跳数。底线自有 router 共享 DB对读与隔离服务完全可行对写入是逐动作的选择——要么复刻缓存失效Redis 键、包内要么代理编排一次对主应用的 tRPC 调用。没有任何理由也不应该把image.service/post.service/model.service/generation.service塞进包。同时六个隔离服务零或近零服务间耦合已逐一核实可以直接原样抽进 moderator 服务层moderator.service审计日志80 行仅dbWrite、blocklist.service125 行仅 db redis constants、scanner-content.service380 行仅orchestrator/client、scanner-review.service591 行仅scanner-content、training.service923 行仅orchestrator/client、strike.service776 行近净——见下。strike.service会拉入notification.service本身零依赖与user.service的一个窄切片getById/updateUserById正确做法是把notification.service一并移动并为 strike 需要的函数抽一个小型user-ops.ts而不是拖入整个auth/session/preferences 密集的user.service。八、从分析到落地方案如何在 monorepo 演进中被修订原文档撰写时假设git submodule civitai-schema-common包后续的迁移交接文档 docs/monorepo-bootstrap-handoff.md 明确记录了这两项均被否决五个基础包不是六个。没有civitai-schema-common。本会住进该包的只有领域常量browsingLevel.constants.ts这类位标志解释、air.ts这类标识符格式、通用flags.ts助手——它们不是基础设施而是领域约定暂留主应用src/shared/直到卫星应用真正需要再评估。基础包仅限基础设施civitai-db、civitai-redis、civitai-clickhouse、civitai-axiom、civitai-telemetry各自封装一块运行时基础设施。基础包互不导入但有一个向下依赖的例外——契约/叶子层civitai/db-schema纯 schema 生成类型产物、无运行时基础包可向下依赖它类似依赖prisma/client。这一决策直接对应原文档 Tier A 的最大条目~/shared/utils/prisma/enums14 页导入如今已是重导出垫片re-export shim→civitai/db-schema/enums两应用今天导入同一套枚举该文件实际存在于 packages/civitai-db-schema/src/enums.ts。原文档中 Tier A 因此坍缩为zod schema 少数server/common枚举/常量。边界复核文档 docs/moderator-app-package-boundary.md 又把包推荐收敛为三个域/特性包civitai/moderator-server、civitai/shared-schema、civitai/ui-common随后的最小抽取计划 docs/moderator-app-package-extraction-plan.md 再将其收紧为一个强制的civitai/domain契约包8 个整文件移动 2 个成员级抽取ReportEntity→enums.ts、CacheTTL→cache.ts均采用纯 rename 提交 垫片提交的两步提交纪律以保全 git 历史 一个小的civitai/orchestrator基础设施包其余一律复制copy/ 留在主应用代理proxy/ 应用内抽取干净切片——理由正是原文档的方法论单消费者代码进应用不打包。当前仓库的落地状态从当前仓库可以看到这套分析最终的实际兑现形态apps/moderator已作为一个SvelteKit 应用而非文档早期设想的 Next.js落地其 apps/moderator/package.json 依赖了civitai/auth、civitai/db、civitai/db-schema、civitai/clickhouse、civitai/redis、civitai/mod-utils、civitai/moderation、civitai/shared、civitai/ui等 workspace 包并拥有自己的 apps/moderator/prisma/schema.prismamoderation 专属数据introspect 自数据库非手写。其路由目录 apps/moderator/src/routes 已包含images/、articles/、models/、reports/、blocklists/、comics-review/、audit/、users/、xguard/、abuse/等大量审核面apps/moderator/CLAUDE.md 还记录了该应用特有的治理规则页面访问集中门控于hooks.server.ts、页面授权与动作授权双轴独立、权限 id 是持久化值不可改名、新页面默认不可达等。主应用侧文档中列出的 Tier C 组件Csam/*、Moderation/*、store/select.store、Tier F 阻塞点image.service、moderation-helpers、src/server/schema/report.schema.ts都仍以源码存在——这与主应用先就地重构、卫星应用按需迁移的渐进路线一致。九、结语这套分析的复用价值moderator-app-shared-modules.md之所以值得细读不在于其早期方案submodule、schema-common被原样采纳而在于它确立了一套可以复用到任何 monorepo 多应用拆分的思考框架先画依赖地图再谈拆分——把待迁移页面的全部导入按契约 / 胶水 / 域组件 / 通用 UI / 工具 / 阻塞点六层归类用多少个页面导入量化每项的共享价值用导入闭包测试做唯一裁决——该文件的传递~/…闭包是否也可移动比这算不算 moderator 相关更有判别力契约按共享数据裁定代码按消费者数量裁定——手写契约枚举、常量、持久化 JSON 形状双应用读写必须打包单消费者代码一律留在应用内UI 原语先 vendor-copy 后提升巨型服务用读队列自持 跨图写入代理拆解——机械抽取查询、逐动作选择 Redis 缓存失效或 tRPC 代理避免拖入主应用内容图按可移植性分级分批迁移——先易后难用 7 个 Easy 页面做概念验证Hard 页面等具体耦合点解掉后再动。最终Civitai 用五个基础设施包 civitai/db-schema契约层 落地为 SvelteKit 的apps/moderator兑现了这份早期分析里独立应用、共享数据库连接、不 fork 整个仓库的全部目标——而这份文档中那些被验证为正确的判断六层依赖、7/11/6 分级、dialog 系统是最高杠杆重构点、vendor-copy 先行至今仍能在 apps/moderator/CLAUDE.md 与 docs/moderator-app-package-extraction-plan.md 的后续实践里看到回响。【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价