Appearance
UGC 文本过滤与审核举报
本文引用的文件
- ugctext.go
- texturemd.go
- skinlib.go
- reports.go
- reports.go
- skinlib.go
- texturepage.go
- 0017_texture_metadata_reports.sql
- 0018_texture_moderation.sql
- SkinlibPage.vue
- TexturesView.vue
- api.js
目录
简介
本文说明皮肤库用户生成内容(UGC)在写入数据库前如何被清洗,以及 Markdown 描述、敏感格式字符、举报和审核流程如何协作,防止 XSS、不当内容和滥用行为进入系统。重点包括:
ugctext对用户提交的名称、描述等文本进行 Unicode 控制字符白名单清理。texturemd使用 blumia/goldmark + bluemonday 对 Markdown 描述进行严格 HTML 白名单渲染。- 举报接口创建
texture_reports记录,并实施用户与材质维度的限流。 - 管理员通过后台面板执行“下架(标记违规)”和“解除违规”操作,更新纹理的
moderation字段。 - 皮肤库详情页提供举报入口;管理员
TexturesView.vue提供列表筛选与审核操作。 - 当前实现中并未维护独立的
report_count或moderation_flag列,而是通过texture_reports表计数与textures.moderation状态表达。
项目结构
与本主题相关的代码分布在服务端 Go 包、迁移脚本、前端 Vue 页面及 API 客户端中:
图表来源
- ugctext.go:1-33
- texturemd.go:1-66
- skinlib.go:1-200
- skinlib.go:200-388
- reports.go:1-200
- reports.go:1-200
- skinlib.go:1-200
- 0017_texture_metadata_reports.sql:1-20
- 0018_texture_moderation.sql:1-5
- SkinlibPage.vue:1-200
- TexturesView.vue:1-200
- api.js:1-161
- texturepage.go:1-200
章节来源
- ugctext.go:1-33
- texturemd.go:1-66
- skinlib.go:1-200
- skinlib.go:200-388
- reports.go:1-200
- reports.go:1-200
- skinlib.go:1-200
- 0017_texture_metadata_reports.sql:1-20
- 0018_texture_moderation.sql:1-5
- SkinlibPage.vue:1-200
- TexturesView.vue:1-200
- api.js:1-161
- texturepage.go:1-200
核心组件
- ugctext:负责去除危险但视觉不可见的 Unicode 控制字符,保留 emoji 序列和合法连字符,避免反向书写、零宽空格等干扰显示或欺骗比对的内容进入存储层。
- texturemd:将用户 Markdown 描述转换为安全 HTML,仅允许标题、段落、加粗、斜体、列表、代码块、表格和 http(s) 链接;拒绝 script、img、iframe、style、javascript 协议等危险内容。
- webapi.skinlib:暴露皮肤库列表、点赞、改名、公开性切换、来源元数据更新和举报接口;对名称、描述、来源 URL 做长度、白名单和可见字符清洗。
- adminapi.reports:管理端举报处理、纹理下架、违规解除、审计日志写入与通知。
- store.reports:举报插入、按用户/材质限流计数、举报列表查询、举报解决、纹理下架事务、违规解除。
- store.skinlib:皮肤库列表与单条查询,应用访问者权限、公开性、类型和关键词过滤。
- 迁移 0017/0018:新增
detail_md、source、source_url、texture_reports表,以及textures.moderation列。 - 前端 SkinlibPage.vue:公开皮肤库页面,负责点赞、收藏语义、错误提示。
- 前端 TexturesView.vue:管理员纹理审核视图,支持搜索、筛选、下架、解除、删除等操作。
- 前端 api.js:封装皮肤库列表、点赞、举报等请求。
- texturepage.go:服务端渲染的皮肤库详情页,消费
texturemd.Render输出安全 HTML。
章节来源
- ugctext.go:1-33
- texturemd.go:1-66
- skinlib.go:1-200
- skinlib.go:200-388
- reports.go:1-200
- reports.go:1-200
- skinlib.go:1-200
- 0017_texture_metadata_reports.sql:1-20
- 0018_texture_moderation.sql:1-5
- SkinlibPage.vue:1-200
- TexturesView.vue:1-200
- api.js:1-161
- texturepage.go:1-200
架构总览
下图展示从用户提交到渲染、举报、审核的关键调用链:
图表来源
- skinlib.go:1-200
- skinlib.go:200-388
- reports.go:1-200
- reports.go:1-200
- skinlib.go:1-200
- api.js:1-161
- TexturesView.vue:1-200
详细组件分析
ugctext 可见字符清洗
ugctext 的目标不是通用敏感词检测,而是防御 Unicode 控制字符带来的显示欺骗和注入风险。它扫描输入中的 Unicode Cf 类字符,除少数连接符外全部移除。测试覆盖了方向控制、隔离符和连字场景,确保正常多语言表情和文字不被破坏。
图表来源
章节来源
texturemd Markdown 安全渲染
texturemd 使用 goldmark 解析 GFM Markdown,再通过 bluemonday 策略只放行安全的 HTML 元素和 http(s) 链接。图片、iframe、表单、内联样式、javascript 协议均被拒绝;链接会附加 nofollow 与 no-referrer,降低外链追踪风险。Excerpt 还会对渲染结果再次走严格策略提取纯文本摘要。
图表来源
章节来源
名称与描述写入流程
公开皮肤库 PATCH 接口允许上传者修改名称、描述、来源、来源 URL 和公开性。名称和描述先经 ugctext.Visible 清洗,再做长度限制;来源 URL 必须为合法的 http(s) 地址;derivative 与 repost 必须附带原始 URL。若材质已被标记违规,则禁止改为公开。
图表来源
章节来源
点赞与收藏语义
公开皮肤库的“点赞”实际上复用收藏语义:skinlibLike 调用 AddClosetEntry,失败时返回未找到或内部错误;成功后返回新的 likes 计数。取消点赞则调用 RemoveClosetEntryByTID,同样返回新的 likes 计数。皮肤库列表通过 user_closet 聚合 likes,而不是依赖 textures.likes 冗余字段。
图表来源
章节来源
举报流程与限流
举报接口要求登录用户,不允许举报自己的材质。允许的原因有版权、色情、暴力、仇恨、垃圾、其他六类;备注最多 500 字符。限流策略为:
- 同一用户 24 小时内最多 10 次举报。
- 同一材质 24 小时内最多 50 次举报。
- 同一用户对同一材质重复举报返回冲突。
举报写入 texture_reports 表,状态为 open,由管理员后续处理。
图表来源
章节来源
管理员审核:下架与解除
管理员有两个关键动作:
- 下架(标记违规):通过
/api/admin/v1/textures/{tid}/strike或举报处理中的 hide 动作,调用StrikeTexture。该函数在事务中将public置为 0,清空public_dedupe,并将moderation设为violation。同时关闭相关举报、写审计日志并通知上传者。 - 解除违规:通过
/api/admin/v1/textures/{tid}/moderationPUT 空值,调用LiftTextureModeration,将moderation恢复为空字符串。注意:这解除的是“禁止重新公开”的限制,并不自动把材质设为公开。
图表来源
章节来源
数据库模型与字段约定
当前仓库并未在 textures 表中维护名为 report_count 或 moderation_flag 的列。相关语义如下:
textures.description:短描述,仍用于历史兼容和搜索。textures.detail_md:长 Markdown 描述。textures.source、textures.source_url:来源与原始链接。textures.public:是否公开。textures.moderation:违规状态,空字符串表示干净,violation表示被下架。texture_reports:举报记录表,含texture_id、reporter_uid、reason、detail、status、handled_by、created_at、handled_at。
图表来源
章节来源
皮肤库详情页举报按钮与 Markdown 渲染
皮肤库详情页由 texturepage.go 服务端渲染,使用 texturemd.Render 将 detail_md 转为安全 HTML。详情页模板中包含举报对话框 UI;前端公开皮肤库页面主要负责列表、点赞和编辑操作,而详情页的举报交互通常由服务端模板与静态资源共同完成。
图表来源
章节来源
管理员 TexturesView.vue 审核操作
TexturesView.vue 提供管理员纹理列表,支持按关键词、类型、状态筛选,并提供以下操作:
- 下架:调用
POST /api/admin/v1/textures/{id}/strike。 - 解除违规:调用
PUT /api/admin/v1/textures/{id}/moderation,body 为{moderation: ""}。 - 删除:调用
DELETE /api/admin/v1/textures/{id},必要时带force=1。
图表来源
章节来源
依赖关系分析
ugctext 与 texturemd 是低耦合工具包,分别承担“输入文本可见性清洗”和“Markdown 输出安全化”。webapi.skinlib 是主要业务编排点,组合 ugctext、store 层与外部鉴权。store.reports 集中了举报与违规状态的事务逻辑。迁移脚本定义最终持久化形态。
图表来源
章节来源
性能与安全性考虑
性能特性
- Markdown 渲染路径在空输入时直接返回空串,避免无意义转换。
- 皮肤库列表不依赖冗余 likes 列,而是实时统计
user_closet,保证一致性但增加一次子查询。 - 举报计数查询按时间窗口聚合,适合高频举报防护,但不作为公开计数器暴露给前端。
- 详情页使用服务器端渲染,便于爬虫抓取真实内容,也减少 SPA 首屏 JavaScript 负担。
安全风险与缓解
- XSS:通过 bluemonday 白名单阻止 script、img、iframe、style、javascript 协议等危险标签。
- 外链追踪与 NSFW:远程图片和非 http(s) 链接被拒绝,链接附加 nofollow/no-referrer。
- Unicode 欺骗:ugctext 清除 Cf 控制字符,防止反向书写和零宽字符绕过。
- 重复举报与刷举报:用户维度 24 小时 10 次、材质维度 24 小时 50 次,重复举报返回冲突。
- 违规材质重发布:
moderation='violation'阻止上传者将材质改回公开,解除后仍需手动公开。
[本节为通用指导,不直接分析具体源码片段]
故障排查指南
常见异常与定位
- 举报原因无效:检查 reason 是否属于允许集合。
- 举报备注过长:detail 超过 500 字符会被拒绝。
- 重复举报:同一用户对同一材质已举报过,返回冲突。
- 举报频率过高:用户或材质 24 小时内超过阈值,返回过多请求。
- 无法举报私有材质:目标材质不存在或为私有。
- 违规材质不能公开:PATCH 时将返回违规禁止公开错误。
- 管理员解除违规后仍不公开:解除只是去掉违规标记,不代表自动设为公开。
章节来源
运营建议
定期清理低质量材质
- 利用管理员纹理视图按状态筛选,优先处理长期无人互动、频繁举报且未处理的材质。
- 结合删除流程与 tombstone 机制,确保删除后仍有审核历史可查。
对高频举报用户限流
- 当前已实现用户 24 小时 10 次、材质 24 小时 50 次上限;可根据运营数据调整阈值。
- 对连续触发限流的用户,可配合账户封禁或限制上传/评论功能。
审计日志记录审核操作
- 下架、解除违规、公开性变更、模型修正均已写入审计日志。
- 建议定期导出审计日志,核对管理员操作、举报处理和通知发送情况。
- 对于高风险操作,应启用更严格的权限校验与双人复核流程。
持续改进敏感词检测
- 当前 ugctext 侧重 Unicode 控制字符,未实现完整敏感词匹配。
- 建议在 ugctext 层增加可扩展的词表匹配模块,并对匹配结果打标签,供举报与审核界面参考。
- 对高频举报原因分类(版权、色情、暴力、仇恨、垃圾、其他)建立统计看板,辅助词表迭代。
[本节为运营实践建议,不直接分析具体源码片段]
结论
本系统的 UGC 文本安全采用“写入前清洗 + 渲染时白名单 + 举报与审核闭环”的分层设计:ugctext 防御 Unicode 控制字符,texturemd 用 goldmark 与 bluemonday 确保 Markdown 输出安全,webapi.skinlib 编排上传、点赞、改名与举报,store.reports 集中处理举报与违规状态,adminapi.reports 提供管理员审核能力。当前实现没有 report_count 或 moderation_flag 字段,而是通过 texture_reports 表和 textures.moderation 表达审核语义。未来可在敏感词检测、举报计数可视化、高频举报治理和审计报表方面进一步扩展。