Skip to content

fix(audit): collection - #8070

Open
shortlight5980 wants to merge 3 commits into
labring:mainfrom
shortlight5980:audit-collection
Open

shortlight5980 wants to merge 3 commits into
labring:mainfrom
shortlight5980:audit-collection

Conversation

@shortlight5980

Copy link
Copy Markdown
Collaborator

审计操作 触发场景 日志文案
创建集合 手动新建集合或文件夹、文本 / 链接 / 文件 / 图片集导入、备份与模板导入、新建知识库时附带文件 【张三】在产品手册/技术文档中创建了【文件】【API 设计.pdf】
更新集合属性 集合改名、移动到其他目录、启用或禁用 【张三】将【产品手册】中【API 设计.pdf】的【名称】从【旧名称】调整为【新名称】
导出集合内容 导出集合内的问答数据 【张三】导出了【产品手册】中【API 设计.pdf】的集合内容
同步集合 对链接集合、API 文件集合执行同步 【张三】同步了【产品手册】中【API 设计.pdf】
添加集合标签 单个或批量给集合打标签 【张三】为【产品手册】中【API 设计.pdf】添加了标签【已审核】
移除集合标签 单个或批量移除集合的标签 【张三】移除了【产品手册】中【API 设计.pdf】的标签【已审核】

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 59.13% 6188 / 10464
🔵 Statements 58.75% 6543 / 11136
🔵 Functions 55.05% 1171 / 2127
🔵 Branches 53.41% 3295 / 6169
File CoverageNo changed files found.
Generated in workflow #4865 for commit d3516d2 by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

✅ Admin Preview Image Ready!

ghcr.io/labring/fastgpt-pr:admin_d3516d2cff294fa6b8072ed8dd07c3775d290ef5

🕒 Time: 2026-10-26 14:13:54 (UTC+8)

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

✅ Build and publish successful - Preview fastgpt Image:

ghcr.io/labring/fastgpt-pr:fastgpt_d3516d2cff294fa6b8072ed8dd07c3775d290ef5

🕒 Time: 2026-10-26 14:17:35 (UTC+8)

Comment thread packages/global/core/dataset/constants.ts Outdated
Comment thread packages/service/core/dataset/collection/paths.ts
Comment thread packages/service/support/user/audit/schema.ts Outdated

Copy link
Copy Markdown
Collaborator

基于提交 8ba19ca9b4a7b270109a3db67821b262ca036d15 的变更梳理,已展开检查 pro 子模块。建议合并前处理以下问题。

1. [P1] 新审计事件会导致团队审计列表读取失败

Mongo 审计 schema 已接入 DatasetAuditEventList,但 AuditListItemSchema 仍只接受旧 AuditEventEnum。pro 的团队审计列表会查询到新事件,随后 AuditListResponseSchema.parse() 在 list[n].event 处失败,导致整页无法读取。直接加载该提交的真实 schema 复现,7 个新事件全部被拒绝。

同一文件的筛选入参、TeamAuditSchemaType / TeamAuditListItemType 和前端事件筛选菜单也未同步。建议统一汇总成员审计事件,供 Mongo schema、Zod、共享类型和筛选菜单复用,并保留旧事件的历史兼容。

2. [P2] 属性审计新增的翻译 key 会被清理脚本误删

collection/update.ts:155 等位置直接保存 account_team:dataset_content_change_item_*、account_team:dataset_content_status_* 裸字符串,没有使用 i18nT('固定 key') 标记。四种语言目前齐全,但用现有清理器的扫描逻辑验证,这 5 个 key 全部被判为未使用,后续执行清理会导致审计文案裸显 key。建议为各个固定 key 补上静态标记。

3. [P2] 批量标签审计未核对每个集合的实际变化

pro/admin/src/pages/api/core/dataset/tag/batchSetCollectionTags.ts:59 对全部请求集合记录同一组标签名称。使用真实 controller、模拟数据库读写复现:集合原本没有该标签,移除前后 tags 都为空,仍会写入 dataset.collection.tag.removed。不同集合只有部分标签时,日志也无法准确反映各自的变更。建议业务层从事务内快照计算并返回逐集合的实际差异,提交成功后据此写入审计。

4. [P2] 同步返回失败时仍记录“已同步”

collection/sync.ts:33 在 syncCollection() 返回后无条件记录 dataset.collection.synced。该函数可以正常返回 failed 而不抛异常;隔离执行真实路由已确认,响应为 failed 时仍生成“已同步”日志,且没有结果字段。建议按结果记录,或明确记录操作尝试并附带结果。导出异常路径同样需要统一这一语义。

风格、验证与合并事项

  • 审计 constants.ts:136 新增 export type { DatasetAuditEvent },违反仓库禁止 re-export 的规范,使用方应直接从定义模块导入。
  • 已做只读源码复现和主仓库、子模块 git diff --check,未在本地运行 Vitest。需要补充“新事件写入后读取及筛选”、标签无实际变化、同步失败和导出中断的覆盖。
  • 当前 test-app CI 失败:fileSourceLifecycle.test.ts 的 logger mock 缺少 LogCategories.INFRA.MONGO,新增审计模块导入后在测试收集阶段报错。该 CI 中更新、恢复继承和路径测试已通过。
  • 主仓库未发现明显无关改动。pro 差异中的网站爬虫早停修复已在当前主线存在;处理当前合并冲突时,需要保留主线已有的两个 workflow 修复,再更新最终子模块指针。

需要明确的语义:审计记录“操作尝试”还是“成功变更”?若覆盖失败或无实际变化的请求,应补充结果字段和对应文案。

@shortlight5980

Copy link
Copy Markdown
Collaborator Author

基于提交 8ba19ca9b4a7b270109a3db67821b262ca036d15 的变更梳理,已展开检查 pro 子模块。建议合并前处理以下问题。

1. [P1] 新审计事件会导致团队审计列表读取失败

Mongo 审计 schema 已接入 DatasetAuditEventList,但 AuditListItemSchema 仍只接受旧 AuditEventEnum。pro 的团队审计列表会查询到新事件,随后 AuditListResponseSchema.parse() 在 list[n].event 处失败,导致整页无法读取。直接加载该提交的真实 schema 复现,7 个新事件全部被拒绝。

同一文件的筛选入参、TeamAuditSchemaType / TeamAuditListItemType 和前端事件筛选菜单也未同步。建议统一汇总成员审计事件,供 Mongo schema、Zod、共享类型和筛选菜单复用,并保留旧事件的历史兼容。

2. [P2] 属性审计新增的翻译 key 会被清理脚本误删

collection/update.ts:155 等位置直接保存 account_team:dataset_content_change_item_*、account_team:dataset_content_status_* 裸字符串,没有使用 i18nT('固定 key') 标记。四种语言目前齐全,但用现有清理器的扫描逻辑验证,这 5 个 key 全部被判为未使用,后续执行清理会导致审计文案裸显 key。建议为各个固定 key 补上静态标记。

3. [P2] 批量标签审计未核对每个集合的实际变化

pro/admin/src/pages/api/core/dataset/tag/batchSetCollectionTags.ts:59 对全部请求集合记录同一组标签名称。使用真实 controller、模拟数据库读写复现:集合原本没有该标签,移除前后 tags 都为空,仍会写入 dataset.collection.tag.removed。不同集合只有部分标签时,日志也无法准确反映各自的变更。建议业务层从事务内快照计算并返回逐集合的实际差异,提交成功后据此写入审计。

4. [P2] 同步返回失败时仍记录“已同步”

collection/sync.ts:33 在 syncCollection() 返回后无条件记录 dataset.collection.synced。该函数可以正常返回 failed 而不抛异常;隔离执行真实路由已确认,响应为 failed 时仍生成“已同步”日志,且没有结果字段。建议按结果记录,或明确记录操作尝试并附带结果。导出异常路径同样需要统一这一语义。

风格、验证与合并事项

  • 审计 constants.ts:136 新增 export type { DatasetAuditEvent },违反仓库禁止 re-export 的规范,使用方应直接从定义模块导入。
  • 已做只读源码复现和主仓库、子模块 git diff --check,未在本地运行 Vitest。需要补充“新事件写入后读取及筛选”、标签无实际变化、同步失败和导出中断的覆盖。
  • 当前 test-app CI 失败:fileSourceLifecycle.test.ts 的 logger mock 缺少 LogCategories.INFRA.MONGO,新增审计模块导入后在测试收集阶段报错。该 CI 中更新、恢复继承和路径测试已通过。
  • 主仓库未发现明显无关改动。pro 差异中的网站爬虫早停修复已在当前主线存在;处理当前合并冲突时,需要保留主线已有的两个 workflow 修复,再更新最终子模块指针。

需要明确的语义:审计记录“操作尝试”还是“成功变更”?若覆盖失败或无实际变化的请求,应补充结果字段和对应文案。

Resloved

@shortlight5980
shortlight5980 force-pushed the audit-collection branch 2 times, most recently from 7a911d6 to b08d341 Compare October 22, 2026 09:25
@c121914yu c121914yu self-assigned this Oct 26, 2026
@c121914yu

Copy link
Copy Markdown
Collaborator

先 rebase 这个吧

@shortlight5980
shortlight5980 force-pushed the audit-collection branch 2 times, most recently from c3d2204 to a4d7525 Compare October 26, 2026 03:46

@c121914yu c121914yu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查:fix(audit): collection

重要发现

  1. [P2,必须修复] 更新审计使用事务外的旧快照,可能漏掉真实状态变更。 主仓库 collection/update.ts:242–258。collection 来自事务前的鉴权查询,事务内按 _id 写入后仍以这份快照判断是否变化。例如两个请求都读到 forbid=false,A 先提交禁用,B 随后提交 forbid=false 恢复启用;B 将请求值与过期的 false 比较,得到空差异并跳过日志。最终集合已启用,审计却只留下禁用事件。旧实现每次更新都会留一条日志,本次新增的无变化过滤引入了这个漏记窗口。已用原 handler 与原差异函数做可控交错复现。建议在写入事务内读取旧值、计算差异并返回最终成功事务的审计快照,提交后再落日志,同时覆盖并发与事务重试。

  2. [P2,必须修复] 标签选择入口按客户端列表推导审计,会记录并未发生的标签移除。 固定 pro 2816b01e6639edd9d8edf602c1b379511960fee4 的 admin/src/pages/api/core/dataset/tag/addToCollections.ts:71–85。collectionsToAddTag/collectionsToRemoveTag 只由请求中的新旧 ID 列表计算,addTagToCollections 没有返回数据库实际差异,路由却直接为这些 ID 写日志。成功移除后重试同一请求,或客户端旧列表已过期时,数据库中本来就没有标签,也会新增一条“移除了标签”的事件;原 handler 和原 service 的隔离执行已确认此行为。它也与本 PR 另两个标签入口“只记录实际变化”的处理不一致。建议让该 service 也从事务内旧值与实际写入值返回逐集合差异,再据此生成审计,补充重复提交与过期列表覆盖。此问题位于 pro 内部,因此只写入总报告,不伪造主仓库行级定位。

需求变更

为集合创建、改名/移动/启停、内容导出、同步、标签增删补充团队审计。创建、更新由旧枚举切换到 dataset.collection.* 事件,旧映射保留用于历史日志;一次属性更新按改名、位置、状态分别记录。恢复集合权限继承改用独立事件,并保存知识库、集合、继承来源名称。

关键实现路径

  • 集合创建及各种文件、文本、链接、图片、备份/模板导入入口,在创建完成后写审计;createWithFiles 在事务成功后按文件写集合日志。audit/collection.ts 统一对象类型、位置与属性差异,collection/paths.ts 从原 API 提取祖先查询并补充环检测。
  • collection/update.ts 协调鉴权、移动/状态级联事务和变更日志;export.ts 在游标结束或错误路径用一次性开关记录;sync.ts 在同步返回后记录操作,包含 sameRaw/failed 返回结果。
  • 公开集合更新接口与 pro 的单集合、批量标签设置共用 audit/utils.ts 的差异函数,在事务内比较规范化前后标签值,提交后按集合记录;标签选择入口存在上面的差异缺口。日志最终由 addAuditLog(s) 写入 operationLogs,团队列表通过共享事件全集校验,前端专用 processor 保留名称、URL、标签和路径原值,只翻译展示标签。

影响范围与越界判断

合理扩散。 主仓库 36 个文件以及 pro 8 个文件的实际 diff 均围绕集合审计、共享事件展示和局部验证展开。祖先路径提取供 API 与审计复用,共享标签差异函数服务于公开更新接口和 pro 的逐集合审计,审计常量合并至 metadata.ts 后所有 TypeScript 引用已同步;未发现应拆出的独立功能或部署配置变更。没有改变工作流、计费或外部 API 请求结构;标签单集合设置新增事务边界,批量路径跳过相同内容的写入。

代码质量

属性差异作为独立函数、路径查询带环检测、日志写入在业务提交后执行、失败记录由公共审计工具兜底,职责总体清楚。主要风险是上面两处旧快照与实际写入不一致;应统一使用事务内实际差异,避免不同入口采用不同审计口径。创建、更新旧事件的枚举和展示映射仍保留。

代码风格一致性

沿用 parseApiInput、现有鉴权、mongoSessionRun 和审计工具,新增核心函数有职责与边界注释。非阻塞建议: processDatasetCollectionSpecific 新增 t(metadata.objectType/changeItem/oldValue/newValue),与本次审查技能要求的静态 literal key 形式不一致,建议对有限展示字段使用显式映射。目前相关已知 key 在服务端已有静态 i18nT('...') 引用,四种语言的新增 19 个 key 与插值变量均一致,未发现当前文案缺失。

测试与验证缺口

实际执行:最新快照 12 个局部测试文件、157 项测试全部通过,使用仓库的 pnpm test <指定文件...> 入口,关闭覆盖率、单 worker、按 workspace 顺序运行:

  • service:audit/collection.test.ts、audit/config.test.ts,26 项通过。
  • app:集合 update、paths、resumeInheritPermission、create/fileSourceLifecycle,以及前端 dataset/audit、processors/index,58 项通过。覆盖属性与公开标签审计、无变化跳过、权限拒绝、继承来源缺失、路径环检测、文件清理和名称/URL 展示。
  • 固定 pro:标签 controller、batchSetCollectionTags、setCollectionTags、团队 audit list,73 项通过。覆盖实际标签差异、规范化、写入失败回滚、旧格式兼容与列表事件校验。

另外执行: 对最新快照的原 handler、原属性差异函数和原标签 service 做 Node 隔离验证(替换鉴权、数据库、事务依赖)。并发交错探针得到 finalForbid=false 且仅有 disable 日志;标签缺席的重复移除探针得到 tags=[] 却新增 dataset.collection.tag.remove 日志。这是代码分支的隔离复现,不等价于真实 Mongo 并发回归测试;现有局部测试没有覆盖这两条异常路径。

发布前 CI 状态:两项镜像构建及 global、web、workflow-core/cli 测试通过;app、service、pro 测试仍在运行。CI 状态与本次审查结论分别记录。

完整阅读主仓库 merge-base 到 head 的 diff,以及 pro 两端 gitlink 的真实 diff、提交和调用方;检查了创建持久化/队列边界、集合鉴权与状态级联、审计存储/列表/前端消费。git diff --check 在两仓库均通过。未运行全量测试、完整类型检查或浏览器端验证;流式导出和同步的完整异常路径未做端到端验证。

需要作者确认的问题

无阻碍定位上述缺陷的需求歧义。

审查结论

needs_changes。两项 P2 审计准确性问题需要修复;本次提交的是 COMMENT Review,不执行批准、请求修改或代码修复。

  • 主仓库 head:a4d7525a4eaed4347f8dfb574124f2f3658dfc35
  • 实际 base 分支:main;获取时 base:5b5cabb9786189ae29850cb8250300ad59e3da3f
  • merge-base:749809070b75278e11a1576a8ef05ca90acb2d08
  • pro:b2a36080f37af4582c09f4ca1ea36bc85cf3c01d → 2816b01e6639edd9d8edf602c1b379511960fee4,指针发生变化;两端代码均可获取,head 已核对检出且递归子模块无缺失。
  • 审查与验证 worktree:/Users/yjl/.codex/experiments/pr-review-client-probe/worktrees/pr-8070-53391933eb8d/tmp/pr-8070-latest。任务开始时复用宿主准备的父目录;PR 随后更新,因此在同一指定目录内建立新 detached worktree,原检出及用户主工作区保持原样。依赖锁文件未变化,复用父目录已安装依赖;两个独立快照均保留供复核。

Comment thread projects/app/src/pages/api/core/dataset/collection/update.ts Outdated

@c121914yu c121914yu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查:fix(audit): collection

重要发现

  1. [P2] 新增快照查询阻止系统 root 更新其他团队的集合。 update.ts:152 将鉴权返回的操作者 teamId 用于集合查询,但 authDatasetCollection / authDatasetCollectionMove 明确允许系统 root 跨团队访问,且仍返回 root 当前团队的 teamId。root 修改其他团队已有集合的名称、状态或位置时,鉴权通过后会在这里查不到集合并返回 fileNotFound。旧实现使用已鉴权的集合执行更新,没有这道错误的团队过滤。应按已鉴权资源的 collection.teamId 读取事务快照,保留 root 旁路与租户边界,并补充跨团队 root 回归覆盖。

  2. [P2] 外链导出被错误归到发布者本人名下。 export.ts:132–140 对普通导出与外链引用导出统一使用 tmbId 写成员审计。但外链分支的 authChatTargetCrud 返回 tmbId = outLinkConfig.tmbId(发布者),实际访客在 uid 中;需登录外链的 uid 还是已验证的访客成员 ID。当前导出代码丢弃 uid/authType,新日志既没有访客身份,也只写 exportScope: chat_quote。pro 审计列表再按该 tmbId 填充 metadata.name,最终会显示成发布者下载了文件。此前该入口没有这条审计,新增加的错误归因会影响下载追溯。应区分资源归属身份与操作人,需登录外链记录验证过的访客,匿名外链明确记录访客来源/标识,避免显示为发布者操作。

需求变更

为集合创建、改名/移动/启停、导出、同步、标签增删及恢复权限继承补齐团队审计。创建覆盖手动集合/文件夹、文件、文本、链接、图片集、备份、模板和新建知识库附带文件;一次属性更新按实际变更逐条记录,标签按每个集合的实际差异记录。旧的创建/更新事件继续保留供历史日志展示。

关键实现路径

  • 主应用创建入口 → 集合创建服务成功提交 → getCollectionAuditLocation 展开父目录 → addAuditLog(s) 写入 operationLogs。
  • collection/update → 事务内重新读取集合、解析标签、计算属性及标签差异 → 提交成功后写审计;回滚不留日志,重试只采用最终成功的差异。路径查询从 API 提取到 service,并加入 session 透传和环检测。
  • pro 三个标签入口 → 单集合/批量标签 controller → 共享 diffCollectionTags → 事务成功后按集合写日志;规范化、重复提交、选项顺序和裁剪均有对应处理。
  • 共享事件枚举/展示映射 → pro 团队审计列表 schema 和筛选 → 主应用专用 metadata processor / 四语言文案。名称、路径及标签原值保留,状态与类型按当前语言显示。

影响范围与越界判断

事件注册、service 差异计算、各创建入口、pro 标签写入及前端展示属于合理扩散。agentLoopCore/boundary.test.ts 放宽指定 helper 的直接导入与集合审计没有直接关系,属于可疑越界的独立测试维护,建议在 PR 中解释关联或拆分;未发现该测试调整改变运行时行为。其余未发现大范围整理或依赖升级。

代码质量

事务内生成差异、事务外记录审计的职责清楚;标签值先规范化再比较,查询展示名称失败回退 ID,路径成环时终止,均有明确边界。必须修复的问题是上面的 root 回归与外链操作人错误;不能仅凭新增测试通过认定这两条未覆盖路径正确。

代码风格一致性

API 请求边界沿用 parseApiInput,共享逻辑从实际定义模块导入,新增复杂分支有职责说明。非阻塞建议:processDatasetCollectionSpecific 新增 t(metadata.objectType/changeItem/oldValue/newValue) 动态 key 调用,与现行静态字面量 key 规则不一致;建议有限枚举采用静态映射。当前 key 另有后端 i18nT('literal') 标记,四种语言新增项和插值一致,本次未发现实际翻译缺失。

测试与验证缺口

实际执行结果:

  • 为独立 worktree 安装依赖,并完成 SDK 与 workflow 包构建。按受影响范围串行运行 service 3 个文件 / 31 项、主应用 9 个文件 / 77 项、pro 5 个文件 / 83 项,合计 17 个文件、191 项全部通过。
  • 另用真实 Mongo 事务及 API 调用补做临时回归:系统 root 尝试改名其他团队的集合,实际返回 code: 500, error: fileNotFound,应成功的断言失败,证实发现 1。验证文件已移除,业务代码未改动。
  • 对固定版本 authChatTargetCrud 与导出 handler 做独立 Node 探针(仅将数据库、外链验证及审计写入等外部依赖替换为可控实现):访客 UID 与发布者不同,完成导出后审计仍只保存发布者 tmbId,访客 UID 完全未进入记录,证实发现 2。这是调用链复现,并非真实外链浏览器端到端测试。

完整审阅主仓库 merge-base 到 head 的 37 个文件,以及 pro 两个 gitlink 之间的 9 个文件和调用链。主仓库与 pro 的 git diff --check 均通过;四语言各新增 19 个 key,插值集合一致。未运行全量测试、浏览器端到端验证或外部对象存储/爬虫的真实集成。

需要作者确认的问题

请明确匿名外链下载的审计身份展示约定;无论采用何种展示方式,都应区分访客与发布者本人。工作流边界测试调整与本需求的关联也建议补充说明。

审查结论

needs_changes:存在上述两个需要修复的问题。CI 在读取时全部成功,该状态独立于本次代码审查结论。

  • 主仓库 head:d3516d2cff294fa6b8072ed8dd07c3775d290ef5
  • merge-base:5fdc8d17e0d40b161a6aa54897d85d9b87cbcba5(目标分支 main)
  • pro:b2a36080f37af4582c09f4ca1ea36bc85cf3c01d → 08e2f72052df109a88898592d99eca39519736a4,已审阅实际差异。
  • worktree:/Users/yjl/.codex/experiments/pr-review-client-probe/worktrees/pr-8070-36837731b836;复用宿主快照,保留目录,未修改业务代码。

tagChanges: ReturnType<typeof diffCollectionTags>;
} = await mongoSessionRun(async (session) => {
// 所有变更共用事务内旧快照;重试时重新读取,避免并发操作导致漏记或沿用已回滚的差异。
const previousCollection = await MongoDatasetCollection.findOne({ _id: id, teamId }, null, {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查 · [P2] 按资源团队读取 root 已鉴权的快照

这里的 teamId 是鉴权返回的操作者当前团队。authDatasetCollection 和移动鉴权都允许系统 root 跨团队访问,却不会把这个字段改成资源团队,因此 root 更新其他团队集合时,会在鉴权通过后被这条新增查询挡成 fileNotFound;旧版本可以完成该更新。建议使用已鉴权集合的 collection.teamId 读取事务快照,并覆盖 root 与资源团队不一致的请求。

auditFinished = true;
void addAuditLog({
teamId: String(userTeamId),
tmbId,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查 · [P2] 外链导出不要归因到发布者本人

此入口也接受外链引用导出。authChatTargetCrud 的外链分支将发布者 outLinkConfig.tmbId 返回为 tmbId,实际访客在 uid 中(需登录外链时是已验证的访客成员 ID)。这里把前者写为成员审计且丢掉访客身份,pro 列表又按它补操作人名称,导致访客下载显示成发布者本人下载。请区分访客身份与发布归属:已登录外链使用验证过的访客,匿名外链保留访客来源/标识并明确展示,避免错误成员归因。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants