Commit Graph

6536 Commits

Author SHA1 Message Date
LineWalker 353e64c58b ops: 把 114 deploy.sh 纳入 git 管理,修复 CI 找不到部署脚本
deploy.sh 原本是 114 上 untracked 的运维脚本,CI SSH 过来调
`bash /opt/bisheng/deploy.sh` 执行部署。由于未入 git:

- 被 rsync --delete / git clean -fd / 误手 rm 都会导致文件消失
- 消失后 CI 直接 exit 127,需要人工 scp 回来才能恢复
- 今天已经因此阻塞两次

纳入 git 后:
- 每次 deploy.sh 自己跑 git reset --hard origin/2.5.0-PM 会自动保证
  脚本最新版存在于 /opt/bisheng/deploy.sh,不会再意外消失
- Linux 下执行中的脚本解析器已把内容加载到内存,reset 覆盖不影响
  当前这次执行
- 脚本变更走 PR 可审查

脚本职责(保持与上次临时版一致):
- git fetch + reset --hard origin/2.5.0-PM(不清 untracked)
- uv sync --frozen 对齐依赖
- kill & restart uvicorn + knowledge_celery + workflow_celery
- curl /health 探活 30s

不做:alembic 迁移(破坏性 DDL 应人工审核)、前端重启(vite HMR)、
     celery beat、Linsight worker

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:18:47 +08:00
LineWalker 852ae0baab merge: 合并 origin/2.5.0-PM (admin 密码登录修复) 到 F012 分支
原因:
origin/2.5.0-PM 比本地 F011/F012 分叉走了 2 个 hotfix commit (e863d987f + 33bce04ef, PR #1985) 修复 admin 密码登录回归;这些修复在 F012 合并回 2.5.0-PM 前必须先吸收,否则 merge 时会有线性 history 断档 + alembic 双头分叉。

解决冲突 (3 处):

1. src/backend/bisheng/user/domain/services/user.py (CONFLICT content)
   - 两边都在 UserService.create_user 加了 source='local' + external_id=req_data.user_name,只是注释文案微差异;
   - 保留 origin 版注释(含 commit SHA 94323e3ec 定位便于未来回查)。

2. Alembic revision 双头分叉(正规 merge revision 解决):
   - 现状:origin 的 f012_backfill_local_external_id 与本地 F011/F012 链路均以 f011_backfill_create_knowledge_web_menu 为 parent,形成两个 head。
   - 解决:新增 v2_5_1_f012_merge_heads.py(revision='f012_merge_heads', down_revision=('f012_user_token_version', 'f012_backfill_local_external_id'))空-op merge revision,恢复 origin migration 的原 down_revision='f011_backfill_create_knowledge_web_menu'。
   - 运行时效果:对已在 f012_backfill_local_external_id 的 DB(114 dev server 现状),alembic upgrade head 会沿另一支路补跑 f011_tenant_tree → f012_user_token_version 后 land 在 f012_merge_heads;任何支路先行都能自洽。

3. F011 dedup 辅助函数改 Python 迭代(latent bug 修复):
   - 原 alembic_helpers/f011.py::deduplicate_multi_active_user_tenants 用 UPDATE ... WHERE user_id IN (SELECT ... FROM user_tenant) 自引用子查询,MySQL 8 报 1093 "You can't specify target table for update in FROM clause"(即使内部 SELECT 返回空也在 parser 层被拒),SQLite 则通过。
   - 修复:改用 SELECT 拉取候选 user_id + 逐用户比较 last_access_time 后 UPDATE WHERE id IN (...),在 pymysql/aiomysql/SQLite 均兼容;同时处理 datetime(MySQL driver)与 ISO 字符串(SQLite driver)两种 last_access_time 返回类型。
   - F011 原 5 个迁移单测绿色维持。

114 dev server 实机验证:
- alembic upgrade head: f011_backfill_create_knowledge_web_menu → f011_tenant_tree → f012_user_token_version → f012_merge_heads (3 个 revision 连续跑通)
- smoke: UserDao.aget_token_version(1)=0, TenantResolver.resolve_user_leaf_tenant(1)={id=1, status=active, parent=None}, alist_users_paginated 返回 3 个用户,token_version=0 列已建立。

回归: F011 85 + F012 77 tests passed(含 F011 迁移测试 5 条重构后全绿)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:58:00 +08:00
LineWalker b8ad65f7de feat(F012-v2.5.1): 叶子 Tenant 派生 + JWT token_version + sync 主部门变更
v2.5.0 的扁平多租户模型没有"用户归属到哪个 Tenant"的源头——登录时 tenant_id 仍走 DEFAULT_TENANT_ID=1 兜底,主部门调岗后旧 JWT 继续有效。F011 已把 Tenant 树结构(Root + 0~N Child)落地但缺用户侧映射;F012 补齐这一层,同时为 F013/F016/F019/F020 铺好 ContextVar 契约。

核心变更:
- 新增 TenantResolver:沿主部门 path 反向找最近 is_tenant_root=1 的挂载点,返回其 Child Tenant;disabled/archived/orphaned 跳过;最终回落 Root(1);带循环保护
- 新增 UserTenantSyncService.sync_user:resolve → 比对当前 leaf → 计数旧 Tenant 下资源 → 切归属 + 递增 token_version + 重写 FGA tenant#member 元组(crash_safe=True)+ DEL Redis 缓存 + 写 user.tenant_relocated audit
- 新增 UserDepartmentService.change_primary_department:事务内 demote/promote + 触发 sync(trigger=dept_change)
- user 表加 token_version INT NOT NULL DEFAULT 0(Alembic v2_5_1_f012_user_token_version,down=f011_tenant_tree)
- UserDao.aget_token_version (Redis 缓存 300s) + aincrement_token_version (原子 UPDATE) + alist_users_paginated
- LoginUser.token_version 字段 + create_access_token/init_login_user/get_login_user/get_login_user_from_ws 全链路透传
- 登录流程 (user_login) 集成 sync_user(LOGIN) + aget_token_version 刷新版本
- core/context/tenant.py 扩展 4 ContextVar:visible_tenant_ids / _strict_tenant_filter / _admin_scope_tenant_id / _is_management_api;新 strict_tenant_filter() CM;get_current_tenant_id() 优先级 admin_scope>current_tenant_id(v2.5.0 签名保留不变)
- CustomMiddleware 原地增强:_validate_token_version(401 + 19103)+ _check_is_global_super(FGA + Redis 缓存)+ _compute_visible_tenant_ids 计算 {leaf,1} / {1} / None 三档
- GET /api/v1/user/current-tenant:返 {leaf_tenant_id, is_child, mounted_department_id, root_tenant_id}(handler 抽到 current_tenant.py 便于单测)
- Celery 6h reconcile:worker.tenant_reconcile.tasks.reconcile_user_tenant_assignments,分页 batch=500 扫全量 user 表,beat crontab 0 */6 * * *
- UserTenantSyncConf 新配置类(enforce_transfer_before_relocate,默认 False)注册到 Settings
- TenantAuditAction 枚举加 USER_TENANT_RELOCATED / USER_TENANT_RELOCATE_BLOCKED;新 UserTenantSyncTrigger 枚举(LOGIN/DEPT_CHANGE/CELERY_RECONCILE/MANUAL)
- 错误码模块 191 tenant_resolver (19101-19104): TenantRelocateBlockedError / TenantResolveFailedError / TokenVersionMismatchError / TenantCycleDetectedError

F011/F012 共用重构:
- 抽取 tenant/domain/services/inbox_helper.py:集中 send_inbox_notice + list_global_super_admin_ids,替代 F011 department_deletion_handler 内的 _send_inbox_notice/_list_global_super_admin_ids;F011 handler 改为从 helper 导入(保留 __all__ re-export 给 F011 旧测试兼容)
- F012 UserTenantSyncService._notify_resource_owner_relocation 复用 send_inbox_notice

关键不变量:
- 资源不随归属迁移(PRD Review P0-C,INV-T4)——主部门跨 Tenant 变更后 sync_user 仅更新 user_tenant + JWT,资源 tenant_id 保持不动
- enforce_transfer_before_relocate=true 时名下有资源即阻断(409 + 19101 + audit user.tenant_relocate_blocked),降低误操作成本
- FGA 元组重写走 crash_safe failed_tuples 补偿;主事务不因 FGA 失败回滚(归属切换优先成功)
- visible_tenant_ids 为 F013 IN-list 过滤的契约;F019 AdminScopeMiddleware 之后通过 _admin_scope_tenant_id 覆盖 get_current_tenant_id()

测试(87 passed / 10 files):
- test_user_token_version_dao.py (6): ORM 默认 / NOT NULL / UPDATE 原子 / 重复 +1
- test_tenant_context_vars.py (16): 4 ContextVar 默认 + set/reset + 优先级 + strict CM 嵌套/异常安全 + v2.5.0 签名守护
- test_tenant_resolver.py (12): 路径派生 / disabled 跳过 / 多层取最近 / 无主部门 / cycle 检测 / Tenant 缺失回落
- test_user_tenant_sync_service.py (10): 无变化直返 / 阻断 / 告警不阻断 / FGA 重写 delete+write / token_version 递增 / Redis DEL / 首次同步无 delete / FGA 故障容错 / reason=no_primary_department
- test_auth_jwt_token_version.py (7): payload 含字段 / classmethod 读 User.token_version / 兼容 v2.5.0 无字段 User / 显式参数覆盖
- test_middleware_token_version.py (13): visible 计算 5 档 / token_version 匹配/不匹配/fail-open / apply 401 / super None / child {leaf,1}
- test_user_department_service.py (5): 同主部门 no-op / 跨 Tenant 触发 sync(DEPT_CHANGE) / 首次 primary / Blocked 透传 / 枚举值
- test_current_tenant_api.py (3): Root/Child 两档 + switch-tenant 410 回归
- test_tenant_reconcile_task.py (5): 单批/多批分页 / Blocked 吞掉 / 泛型异常继续 / 空表 no-op
- test_department_deletion_handler.py (10) [F011 回归]: handler 使用 inbox_helper 后原测试全绿

回归:
- F011 85 tests passed(含 department_deletion_handler 10 条,重构后无损)
- v2.5.0 基线维持(553 passed + 16 pre-existing failures 皆与 F012 无关)

SDD 产物:
- features/v2.5.1/012-tenant-resolver/tasks.md(12 任务全部完成 + 实际偏差记录)
- features/v2.5.1/012-tenant-resolver/ac-verification.md(AC-01~AC-11 → 测试映射 + spec §8 手工 QA 清单)

下游解锁:
- F013-tenant-fga-tree:读 get_visible_tenant_ids() 做 IN-list FGA check
- F014-sso-org-realtime-sync:直接调 UserTenantSyncService.sync_user(trigger=DEPT_CHANGE)
- F016-tenant-quota-hierarchy:strict_tenant_filter() 精确配额计数
- F019-admin-tenant-scope:set_admin_scope_tenant_id() + set_is_management_api() 已就位
- F020-llm-tenant-isolation:LLMDao 查询读 visible_tenant_ids

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:27:46 +08:00
芥子观须弥 33bce04efd fix(user): 回填本地账号 external_id,修复 admin 密码登录失败 (#1985)
commit 94323e3ec 把登录查询从 user_name 切到 external_id,但未对已有本地 账号做数据迁移——源于
admin 及所有旧本地用户的 external_id=NULL,密码
登录一律返回 10600 "Account or password error"。

- Alembic f012: 为 source='local' 且 external_id IS NULL 的活跃用户回填
external_id = user_name。uk_user_source_external_id(source,external_id)
复合唯一键天然继承原 user_name 唯一性语义。
- UserService.create_user: 新建本地账号时自动把 external_id 填成 user_name, 堵住 UI
新建用户后无法登录的漏洞(SSO 路径走 org_sync,不经此函数)。
2026-04-19 00:06:26 -07:00
LineWalker e863d987f3 fix(user): 回填本地账号 external_id,修复 admin 密码登录失败
commit 94323e3ec 把登录查询从 user_name 切到 external_id,但未对已有本地
账号做数据迁移——源于 admin 及所有旧本地用户的 external_id=NULL,密码
登录一律返回 10600 "Account or password error"。

- Alembic f012: 为 source='local' 且 external_id IS NULL 的活跃用户回填
  external_id = user_name。uk_user_source_external_id(source,external_id)
  复合唯一键天然继承原 user_name 唯一性语义。
- UserService.create_user: 新建本地账号时自动把 external_id 填成 user_name,
  堵住 UI 新建用户后无法登录的漏洞(SSO 路径走 org_sync,不经此函数)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:03:05 +08:00
LineWalker 8a111ad2ae feat(F011-v2.5.1): Tenant 树形数据模型 + Root 保护 + 资源下沉 API
将 v2.5.0 的扁平多租户模型重写为私有化部署两层 Tenant 树(Root + 0~N Child),
为 v2.5.1 后续 Feature(F012 resolver / F013 FGA / F018 交接)铺路。

核心变更:
- tenant 表加 parent_tenant_id + share_default_to_children;status 枚举含 orphaned
- user_tenant 加 is_active + uk_user_active(user_id, is_active) 唯一叶子约束
- department 加 is_tenant_root + mounted_tenant_id(挂载点标记)
- audit_log 追加结构化列:tenant_id/operator_tenant_id/action/target_type/target_id/reason/metadata
- 新增 TenantMountService(mount/unmount 三策略/resource migrate-from-root)
- 新增 DepartmentDeletionHandler(SSO/Celery/manual 三源孤儿处理)
- POST /tenants + /user/switch-tenant 改 HTTP 410 Gone(AC-15)
- PUT /tenants/1/status + DELETE /tenants/1 → 403 + 22008(INV-T11)
- 新增 4 个 API 端点:POST/DELETE /departments/{id}/mount-tenant、POST /tenants/{child}/resources/migrate-from-root
- 错误码模块 220 tenant_tree (22001-22011),原 spec 190 与 permission F004 冲突重分配

Alembic:
- v2_5_1_f011_tenant_tree.py:ALTER 四张既有表 + Root 回填 + uk_user_tenant→uk_user_active
- 业务逻辑抽到 alembic_helpers/f011.py 便于单测

测试(117 passed / 0 regressions):
- test_f011_migration.py:Root 回填 + is_active 回填 + 多 active dedup
- test_tenant_tree_dao.py / test_user_tenant_leaf.py / test_audit_log_v2.py:ORM + DAO SELECT 语义
- test_tenant_service_root_protect.py / test_deprecated_tenant_endpoints.py:Root 保护 + 410
- test_tenant_mount_service.py / test_department_deletion_handler.py:Service 单元
- test_tenant_mount_api.py:TestClient 端到端
- test_abulk_update_tenant_id.py:AC-04a 原子性(rollback on mid-loop failure)

SDD 产出:
- features/v2.5.1/release-contract.md 表 3 模块编码 190→220
- features/v2.5.1/011-tenant-tree-model/{spec,tasks,ac-verification}.md
- CLAUDE.md 模块编码清单 + v2.5 Tenant 树形上下文
- F012-F021 spec 骨架同步入仓

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 12:13:34 +08:00
30388 909b22f4ff feat(perm): ReBAC 应用列表异步化与部门管理员隐式范围修复
- WorkFlowService.get_all_flows 改为 async,使用 aget_all_apps 与
  aget_merged_rebac_app_resource_ids,避免线程池内跨 event loop 导致 FGA 失败
- chat/workflow 路由直接 await,移除 asyncio.to_thread 包装
- LoginUser 新增 get_merged_rebac_app_resource_ids / aget_merged_rebac_app_resource_ids
- DepartmentDao 子树 ID 查询结果归一化为 int,修复 Row/元组导致集合判断错误
- list_objects 缓存命中时与部门管理员隐式资源 ID 取并集;隐式范围增加调试日志

Made-with: Cursor
2026-04-19 01:43:40 +08:00
30388 6037855213 feat(权限): 完善 permission_service ReBAC/部门逻辑,同步 PRD 并补充本地测试数据备忘
Made-with: Cursor
2026-04-19 00:53:12 +08:00
30388 b2d81ce8e1 feat(权限与知识库): 复制授权对齐、授权列表Tab、用户组列表与FGA解析修复
- 知识库复制:POST /knowledge/copy 与 /qa/copy 校验 create_knowledge + 源资源读权限;文档库/QA 列表复制入口与「创建」菜单对齐

- 授权管理:当前权限支持用户/部门/用户组 Tab;空 Tab 可停留;OpenFGA 列表正确解析 department/user_group 的 #member 主体

- 用户组:getUserGroupsApi 统一返回 records 数组,修复添加权限时列表为空;各调用方适配

- 登录公钥:get_rsa_publish_key 使用同步 Redis,避免 Windows 上 async Redis 与事件循环不一致

- 部门/知识列表等:与本次权限改造相关的后端与 PRD、i18n 同步

Made-with: Cursor
2026-04-18 23:34:38 +08:00
30388 b142817199 feat(2.5-permission): app copy, user list union, knowledge routes & audit
- Frontend: copy app with ReBAC aligned to create_app; canEdit for publish/switch; SubjectSearchUser paging; i18n and routes.

- Backend: user list merges dept subtree with group admins; audit/knowledge/auth/role_access updates; F011 alembic; F006 checkpoint.

- PRD updates; AGENTS.md; ignore local *.msi and /query.

Made-with: Cursor
2026-04-18 20:53:53 +08:00
芥子观须弥 c2b60926c3 fix: F006 migration querying wrong table name (role_access → roleaccess) (#1984)
## Summary
F006 RBAC→ReBAC migration had **two** table/schema-mismatch bugs that
were masked by permissive SQLite test fixtures. Fixing both here, plus a
reusable end-to-end verification harness that catches similar
regressions.

### Bug 1: `role_access` → `roleaccess` table name
SQLModel auto-tablenames `RoleAccess` as `roleaccess` (no underscore).
Migration SQL hardcoded `FROM role_access`, causing startup to fail with
`Table 'bisheng.role_access' doesn't exist`. Test fixture mirrored the
typo (`CREATE TABLE role_access`), so tests passed against the fictional
table while production consistently hit the missing one.

### Bug 2: Step 4 case-sensitivity mismatch
Production MySQL declares:
```
business_type  enum('SPACE','CHANNEL')
user_role      enum('CREATOR','ADMIN','MEMBER')
```
But `SCM_ROLE_MAPPING` / `SCM_TYPE_MAPPING` use **lowercase** keys.
`.get('CREATOR')` returned `None`, so Step 4 silently skipped every
active space/channel member row. On 114 this meant **19 real membership
records were being dropped** — those users would have lost their access
after migration.

Test fixture uses `VARCHAR(16)` + lowercase test data, which masked the
bug. Fix: `.lower()` before mapping lookup (executor + verify_all both).
Added regression test inserting uppercase enum values to guard against
recurrence.

### Verification harness (`scripts/verify_f006_migration.py`)
Self-contained script that seeds all 9 legacy tables with diverse mock
data covering every code path in F006, then runs `migration → --verify →
reconcile → cleanup`. Designed to be reusable for future migrations.

Mock data covers:
- All 10 non-menu `AccessType` values (1, 3, 5, 6, 7, 8, 9, 10, 11, 12)
- Skip boundaries: `type=99` (WEB_MENU), `role_id=1` (admin), PENDING /
REJECTED SCM status, `is_delete=1` tool, `flow_type=15`, non-numeric
`file_level_path`
- viewer+editor dedup on the same (user, resource)
- 3-level folder hierarchy under knowledge_space → folder → folder →
file

Asserts 47 `must_have` tuples present and 17 `must_not_have` tuples
absent in OpenFGA, plus built-in `--verify` regression == 0.

## Validation on 114
```
Step 1 super_admin:  2    Step 4 SCM:       25  (mock 6 + prod 19)
Step 2 user_group:  13    Step 5 owners:    96
Step 3 role_access: 17    Step 6 folder:    14
Total tuples: 167    --verify regression: 0
must_have 47/47    must_not_have 17/17    OVERALL: PASS
```

## Scope
| File | Change |
|---|---|
| `bisheng/permission/migration/migrate_rbac_to_rebac.py` | 2 SQL table
names (role_access → roleaccess), Step 4 case normalization (executor +
verifier) |
| `test/fixtures/table_definitions.py` | Fixture table name aligned |
| `test/test_f006_permission_migration.py` | 7 `insert_rows` table names
aligned + new `test_uppercase_enum_values` |
| `test/test_infrastructure_smoke.py` | Expected set aligned |
| `scripts/verify_f006_migration.py` | **NEW** — reusable E2E
verification harness |

## Test plan
- [ ] `pytest test/test_f006_permission_migration.py
test/test_infrastructure_smoke.py` passes
- [ ] On a clean environment, backend startup runs F006 without error
and Step 4 tuple count matches active `space_channel_member` rows
- [ ] `scripts/verify_f006_migration.py` returns exit 0 on PASS and
cleans up mock data
2026-04-17 09:15:53 -07:00
LineWalker cc70a33086 Fix F006 Step 4 case sensitivity + add end-to-end verification script
Seeding the live 114 MySQL with realistic mock data and running the full
F006 migration surfaced a second latent bug: SCM_ROLE_MAPPING and
SCM_TYPE_MAPPING use lowercase keys (creator/admin/member, space/channel),
but production MySQL stores the columns as ENUM('SPACE','CHANNEL') and
ENUM('CREATOR','ADMIN','MEMBER') — uppercase. Step 4 was silently dropping
every real space/channel member row (19 rows on 114) because .get(role)
returned None and the code fell into the "skip unknown role" branch.

Test fixtures had masked the issue: the SQLite schema uses VARCHAR(16)
with lowercase test data, so .get(role.lower_value) happened to work in
tests but never in production.

Fix:
- migrate_rbac_to_rebac.py step4_space_channel_members: normalize role
  and biz_type to lower() before mapping lookup
- migrate_rbac_to_rebac.py verify_all: same normalization on scm_set
  (so old-system membership comparison stays case-insensitive)
- test_f006_permission_migration.py: new TestStep4.test_uppercase_enum_values
  guarding against regressions by inserting the exact values production's
  enum would store

Also adds scripts/verify_f006_migration.py — a reusable end-to-end
verification harness that seeds all 9 legacy tables with diverse mock
data (all 10 AccessType values, 6 SCM role/status combinations, 3-level
folder hierarchy, skip-boundary cases for type=99 / role_id=1 /
PENDING / REJECTED / is_delete=1 / flow_type=15 / non-numeric paths),
runs the migration, invokes --verify mode, reconciles 47 must_have and
17 must_not_have tuples against OpenFGA, and cleans up.

Validation on 114 after the fixes:
  Step 1 super_admin:  2   Step 4 SCM:      25  (mock 6 + prod 19)
  Step 2 user_group:  13   Step 5 owners:   96
  Step 3 role_access: 17   Step 6 folder:   14
  Total tuples: 167     --verify regression: 0
  must_have 47/47     must_not_have 17/17     OVERALL: PASS
2026-04-18 00:06:42 +08:00
LineWalker 4d2f8aa2d9 Fix F006 migration table name: role_access -> roleaccess
SQLModel defaults RoleAccess.__tablename__ to 'roleaccess' (no underscore),
but the F006 RBAC->ReBAC migration queried 'role_access', causing the
migration to fail at startup with "Table 'bisheng.role_access' doesn't
exist". The test fixture mirrored the same typo, so tests passed against
the fictional table while production consistently hit the missing one.

Why: confirmed against the live 114 schema — tables 'roleaccess',
'userrole', 'usergroup', 'groupresource' all lack underscores; only
'space_channel_member', 'user_tenant', 'user_department', 'failed_tuple'
use underscores via explicit __tablename__ overrides. Migration already
had 'userrole'/'usergroup' correct — only 'role_access' was wrong.

After fix, first execution on 114 wrote 101 tuples (1 super_admin,
8 user_group, 83 resource_owners, 9 folder_hierarchy).
2026-04-17 22:51:03 +08:00
30388 94323e3ec8 fix(permission): dept admin role list, org access, user groups
- Department admin: FGA admin check with DB parent-chain fallback

- User groups: public groups manageable by dept admins; member-edit empty hint

- Roles: list_roles uses same admin-dept query as org UI; subtree filter + global read-only

- Role scope full path; hide edit/delete for readonly rows

- Alembic v2_5_0_f010 user name non-unique; client/platform URL and i18n updates

Made-with: Cursor
2026-04-17 22:00:29 +08:00
LineWalker d574df9f76 docs: systematize dashboard upgrade guide for both OSS and commercial
Expand section 10 of the v2.5 migration plan to serve as the authoritative
upgrade reference for the open-source community:

- OSS vs commercial dataset breakdown (4 vs 7 datasets)
- Separate upgrade paths (1 image for OSS, 2 images for commercial)
- Auto-upgrade mechanism for dashboard_dataset table
- Verification checklist and troubleshooting
- Safe rollback strategy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 20:52:57 +08:00
Y1fe1Zh0u 5be6bb3ac0 Merge remote-tracking branch 'upstream/2.5.0-PM' into 2.5.0-PM 2026-04-17 14:34:37 +08:00
Y1fe1Zh0u fafea3e907 Align knowledge-space permissions around existing view/edit/delete semantics
The knowledge-space permission rollout was functionally working, but a few
adjacent behaviors still looked like separate privileges when they should have
followed existing product semantics. This commit collapses tag management and
retry back into edit-level behavior, keeps chat on top of existing view
permissions, and wires the platform relation-model editor to read the backend
knowledge-space permission template instead of relying only on its local copy.

Constraint: Preserve the agreed product rule that question-answer access follows visibility and maintenance actions follow edit rather than introducing extra permission ids
Rejected: Keep manage_*_tags and retry_file as standalone actions | that would over-model implementation details as product-level privileges
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Prefer reusing established permission ids for adjacent behaviors unless product explicitly wants a separately toggleable capability
Tested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py src/backend/test/test_knowledge_space_chat_service.py src/backend/test/test_permission_service.py src/backend/test/test_permission_relation_bindings.py src/backend/test/test_knowledge_space_upload_regressions.py -q; frontend platform TS diagnostics for permission.ts and RolesAndPermissions.tsx
Not-tested: Full frontend interaction flow for the platform relation-model editor consuming the backend template endpoint
2026-04-17 14:07:57 +08:00
Y1fe1Zh0u 11477ecbde Complete backend knowledge-space permission-first action coverage
Knowledge-space runtime authorization still had several coarse-grained holdouts:
tag management and retry flows were effectively edit-tier only, and folder/file
chat paths were not consistently tied to read permissions. This change removes
chat-specific permission ids, makes chat depend on existing view permissions,
and extends action-level permission checks so tags and retry are governed by the
backend canonical knowledge-space template as part of the permission-first
model.

Constraint: Keep question-answer access tied to existing view permissions rather than inventing separate chat privileges
Rejected: Add chat_file/chat_folder permission ids | question-answer access should follow visibility, not introduce a second read model
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Reuse existing view/edit/delete permission ids for adjacent behaviors unless product semantics explicitly require a new action id
Tested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py src/backend/test/test_permission_service.py src/backend/test/test_permission_relation_bindings.py src/backend/test/test_knowledge_space_upload_regressions.py -q; src/backend/.venv/bin/python -m py_compile src/backend/bisheng/knowledge/domain/services/knowledge_space_chat_service.py src/backend/bisheng/knowledge/domain/services/knowledge_space_service.py src/backend/bisheng/permission/domain/knowledge_space_permission_template.py src/backend/bisheng/permission/api/endpoints/resource_permission.py
Not-tested: Dedicated chat-service unit tests and full backend suite with manual/env-dependent tests
2026-04-17 13:23:46 +08:00
Y1fe1Zh0u c94ec024f9 Make knowledge-space permission-first semantics explicit
The runtime and template work was already converging on permission-first
authorization, but the contract was still implicit and easy to regress.
This commit locks the backend-owned knowledge-space permission template in
place, adds the template API for future frontend reuse, and documents in the
service layer that relation-based defaults are legacy compatibility only while
permissions[] is the authoritative runtime source when bindings exist.

Constraint: Keep current backend behavior stable while clarifying the intended permission-first contract
Rejected: Leave the canonical template implied in service code only | future frontend/backend drift would remain likely
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Treat relation fallback as compatibility code; do not add new knowledge-space actions without updating the canonical backend template
Tested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py -q
Not-tested: Full backend suite and frontend consumption of the new permission template endpoint
2026-04-17 13:04:01 +08:00
Y1fe1Zh0u 0dcb9abacb Drive knowledge-space runtime auth from canonical permission templates
Knowledge-space permissions were still split between coarse OpenFGA relation
checks and UI-only relation-model permissions metadata. This change promotes a
backend-owned canonical knowledge-space permission template, exposes it through
an API for future frontend reuse, and makes runtime checks consume relation
bindings plus permission ids so actions such as delete/download/rename are no
longer governed only by can_read/can_edit/can_manage.

The implementation keeps knowledge_space as the top boundary, applies folder and
knowledge_file resource checks for child operations, and revokes child direct
tuples when membership is removed so child resources do not outlive space
membership by default.

Constraint: Preserve existing knowledge-space APIs while making fine-grained permission ids authoritative at runtime
Rejected: Keep permissions[] as UI metadata only | relation models would continue to misrepresent actual runtime behavior
Confidence: medium
Scope-risk: broad
Reversibility: messy
Directive: New knowledge-space actions must be added to the canonical backend permission template before wiring UI or runtime checks
Tested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py src/backend/test/test_permission_service.py src/backend/test/test_permission_relation_bindings.py src/backend/test/test_knowledge_space_upload_regressions.py -q
Not-tested: Full backend suite with external/manual tests and frontend consumers of the new permission template endpoint
2026-04-17 12:56:08 +08:00
LineWalker a970393056 chore: retrigger CI after adding /opt/bisheng/deploy.sh on 114
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:06:45 +08:00
30388 7c1e89f231 fix(dept): FGA admin on member remove; member edit UI cleanup
- aremove_member: drop department admin tuple when removing member from dept
- department_service: align primary-dept move with admin/member FGA (existing logic)
- OrganizationMemberEditDialog: remove user group and role hint lines (local/synced/affiliate)
- locales: remove unused memberEditUserGroupsHint / memberEditRolesHint keys

Made-with: Cursor
2026-04-17 11:44:27 +08:00
Y1fe1Zh0u 5934982ac3 Initialize child-resource tuples for knowledge-space files and folders
Knowledge-space ReBAC support still stopped at the space itself. Folder and
knowledge_file resources existed in the schema, but runtime create/delete flows
weren't writing parent or owner tuples, which left file-level permissions and
hierarchical inheritance incomplete for newly created items. This initializes
folder/file parent+owner tuples on creation and cleans them up on file/folder
removal, including recursive folder deletes and batch file deletes.

The regression tests extend the knowledge-space service suite to lock the child
resource tuple lifecycle down.

Constraint: Preserve existing knowledge-file/folder CRUD behavior and avoid changing external APIs
Rejected: Leave folder/file tuples to migration only | newly created resources would never participate in ReBAC hierarchy
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Any runtime path that creates or deletes folder/knowledge_file records must update FGA tuples in the same transaction window
Tested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py src/backend/test/test_permission_service.py src/backend/test/test_permission_relation_bindings.py src/backend/test/test_knowledge_space_upload_regressions.py -q
Not-tested: End-to-end permission-management flows on folder/file resources via UI
2026-04-17 11:44:14 +08:00
Y1fe1Zh0u 170571a4a2 Tighten knowledge-space manage boundaries and expose pending-space actions
Knowledge-space settings and member listing were still using edit-level
permissions even when the action was really about membership policy. The
square and preview drawer also stranded users in joined, pending, or rejected
states despite the backend already supporting leave, withdraw, and reapply.
This narrows the backend checks to can_manage where appropriate and wires the
existing frontend flows to the available APIs, including space-tag deletion.

Constraint: Reuse the current client-side knowledge-space surfaces and existing APIs without introducing a new permission-management shell
Rejected: Leave joined/pending/rejected actions disabled in the square | backend unsubscribe and resubscribe flows would remain unreachable
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Any UI state that reflects a backend transition should keep an actionable path when the API already supports it
Tested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py src/backend/test/test_permission_service.py src/backend/test/test_permission_relation_bindings.py src/backend/test/test_knowledge_space_upload_regressions.py -q; frontend TS diagnostics clean for changed knowledge-space files; locale JSON parse
Not-tested: Browser-interactive client flows and generic permission-management entry points for knowledge space/folder/file
2026-04-17 11:33:56 +08:00
Y1fe1Zh0u 4265834bf9 Keep knowledge-space membership tuples aligned with space lifecycle
Knowledge-space permissions were still drifting away from member state after
the earlier destructive-operation fixes. Public subscriptions did not write
viewer tuples, visibility changes did not reconcile active members, and
creators could remove their own membership record while remaining the DB owner.
This keeps tuple state and membership state in sync across subscribe,
unsubscribe, and visibility transitions.

The tests extend the focused knowledge-space service coverage so these ReBAC
consistency cases stay guarded without needing the full application stack.

Constraint: Preserve existing knowledge-space API responses and subscription statuses
Rejected: Rely only on approval handlers for tuple repair | public subscriptions and visibility switches would still drift
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Membership status transitions must update FGA tuples in the same code path that mutates SCM records
Tested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py src/backend/test/test_permission_service.py src/backend/test/test_permission_relation_bindings.py src/backend/test/test_knowledge_space_upload_regressions.py -q
Not-tested: End-to-end approval message flows and live share-link behavior
2026-04-17 11:24:16 +08:00
Y1fe1Zh0u 461214dde1 Close knowledge-space permission gaps on destructive operations
The ReBAC migration wired core relation checks into knowledge spaces, but
several endpoints still trusted caller-supplied space IDs or creator ownership.
This normalizes delete authorization onto can_delete and validates that file
and folder IDs actually belong to the target space before reading, deleting,
or downloading them.

The change also adds focused regression tests for the P0 cases so these
cross-space and missing-permission regressions stay locked down.

Constraint: Keep the current knowledge-space API surface and error types stable
Rejected: Fix only delete_space | cross-space file and folder operations would remain exploitable
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Any endpoint accepting both space_id and file_id/folder_id must verify resource ownership before acting
Tested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py -q
Not-tested: Full backend suite and live MinIO/download integration flows
2026-04-17 11:16:19 +08:00
Y1fe1Zh0u 4bc9aa7b4e Merge remote-tracking branch 'upstream/2.5.0-PM' into 2.5.0-PM 2026-04-17 02:44:43 +08:00
Y1fe1Zh0u 986e5f3848 Document the current knowledge-space permission model
This note records how knowledge-space permissions actually work today:
coarse-grained relation levels are enforced at runtime, while the
relation-model permissions list is saved and displayed but not yet wired
into per-action checks. The goal is to make the current behavior explicit
before further knowledge-space review and testing.

Constraint: Describe present behavior only and avoid implying a future product decision
Rejected: Leave the explanation only in chat context | it would be easy to lose the current-state understanding during follow-up work
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Treat this as a current-state note, not a final permission design contract
Tested: Documentation-only change
Not-tested: N/A
2026-04-17 02:02:20 +08:00
Y1fe1Zh0u 6676c076c2 Keep owner available in permission-model fallback options
When the grantable relation-model API failed, the permission grant UI fell
back to viewer/editor/manager only, which temporarily removed the ability
to grant owner access. This restores owner to the fallback model list and
aligns the shared RelationSelect fallback options with the full built-in
relation set.

Constraint: Preserve the existing fallback flow while keeping the built-in permission levels complete
Rejected: Patch only PermissionGrantTab | other fallback consumers would still present an incomplete relation set
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Fallback relation-model lists should mirror the full built-in relation set unless the backend intentionally restricts them
Tested: npm test -- --run src/test/smoke.test.ts
Not-tested: Interactive permission-grant flow while the relation-model API is unavailable
2026-04-17 01:54:44 +08:00
Y1fe1Zh0u f4acc3daa7 Save role settings and menu permissions atomically
The role editor was persisting role metadata first and menu permissions in a
second request, which left partial updates behind whenever the second step
failed. This extends the v2 role create/update payloads to carry menu_ids
and handles menu replacement in the same backend transaction as the role
record update. The editor now submits one save request for both pieces of
state.

Constraint: Preserve backward compatibility for callers that still use the separate menu endpoint
Rejected: Frontend-only compensation after menu save failure | still leaves edit updates non-atomic and vulnerable to partial commits
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Persist role metadata and role web-menu access in one transaction whenever they originate from the same form
Tested: pytest src/backend/test/test_role_service.py -q; npm test -- --run src/test/smoke.test.ts
Not-tested: End-to-end role create/edit flows against a live backend
2026-04-17 01:53:39 +08:00
Y1fe1Zh0u b5355e3baf Stop falling back to default menus when role menu loading fails
Editing an existing role used to treat a failed or empty menu fetch as the
same thing as the default menu set, which could overwrite the role's saved
menu permissions on the next save. This keeps default menus only for create
mode, tracks menu loading state for edit mode, and disables saving until the
actual menu data has been loaded or reloaded successfully.

Constraint: Preserve create-mode defaults while preventing edit-mode overwrites from incomplete menu state
Rejected: Silently keep the old fallback and rely on users not to save | one transient fetch failure could rewrite role menus incorrectly
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Do not reuse create-mode defaults as an edit-mode fallback for persisted role settings
Tested: npm test -- --run src/test/smoke.test.ts
Not-tested: Interactive role-edit retry flow in the browser
2026-04-17 01:49:39 +08:00
Y1fe1Zh0u 80787c2f88 Load all user-group members before sync-based saves
The user-group edit page initialized its member selection from only the
first 500 members, then saved through a full-replacement sync endpoint.
Large groups could therefore lose members that were never loaded into the
UI. This adds a paginated helper that fetches all member pages before the
edit form builds its selection state, plus a small frontend regression test
for the pagination helper.

Constraint: Keep the existing sync-based save contract while eliminating truncated initialization data
Rejected: Raise the single-request limit only | groups larger than the new cap would still be vulnerable to silent removals
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Any UI that performs full-sync saves must hydrate from the full dataset, not a capped first page
Tested: npm test -- --run src/test/userGroups.test.ts; npm test -- --run src/test/smoke.test.ts
Not-tested: Manual editing of a very large user group in the browser
2026-04-17 01:47:54 +08:00
Y1fe1Zh0u 06f2a65fee Back out explicit admin_user_ids rejection while the model is undecided
The repository currently has mixed signals around user-group admins between
the top-level PRD, the older F003 spec, and the partial creator-centric UI
and service changes. Since this topic is paused pending a product-level
alignment, the earlier fail-fast rejection of extra admin_user_ids is backed
out to avoid forcing one semantic direction in code.

Constraint: Keep the user-group admin question open without adding further silent behavior changes
Rejected: Leave the rejection in place while the semantics are still under review | it would keep pushing implementation toward one disputed model
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Do not harden user-group admin semantics further until product direction is explicitly chosen
Tested: pytest src/backend/test/test_user_group_service.py -q; pytest src/backend/test/test_user_group_api.py -q
Not-tested: Legacy clients that submit admin_user_ids during create while semantics remain unresolved
2026-04-17 01:46:18 +08:00
Y1fe1Zh0u 9af3e8b161 Rewrite the user-group admin note as a state report
The previous note stated a target conclusion, but this document is meant to
capture the current repository state instead. This rewrites it into a
neutral status report that separates the top-level PRD, the older F003
spec, the current UI, and the mixed backend compatibility state without
choosing the future direction.

Constraint: Keep the note descriptive and avoid turning it into a product decision document
Rejected: Leave the earlier conclusion wording in place | it overstates one possible direction as current truth
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Use this note to describe the present mixed state only; future direction still needs an explicit product decision
Tested: Documentation-only change
Not-tested: N/A
2026-04-17 01:44:28 +08:00
Y1fe1Zh0u fe1eac9839 Document the intended user-group admin model before further changes
The current branch contains mixed signals between the top-level PRD,
legacy F003 specs, and implementation work around user-group admins.
This note records the working product conclusion for future changes:
user groups should support independent admins just like departments,
while this topic is paused for now to avoid pushing the codebase further
in the wrong direction.

Constraint: Preserve the current pause state and avoid implying that creator-only user groups are the final design
Rejected: Leave the conclusion only in chat context | future code changes would re-open the same ambiguity without a repo-local reference
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Treat user-group creator and user-group admin as separate concepts in future design work unless product direction changes explicitly
Tested: Documentation-only change
Not-tested: N/A
2026-04-17 01:41:49 +08:00
Y1fe1Zh0u 8a6b8d01f4 Reject unsupported extra admin_user_ids during group creation
The create-group API schema still accepted admin_user_ids even though the
new user-group model no longer supports separate group admins. That made the
request succeed while silently discarding extra admin assignments. This now
fails fast when callers submit admins beyond the creator, while still
allowing the creator's own ID for backward compatibility.

Constraint: Keep creator-only creation payloads working while eliminating silent data loss
Rejected: Continue accepting and ignoring extra admin_user_ids | callers cannot tell the request was only partially applied
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If an API field is no longer supported, reject it explicitly rather than silently dropping it
Tested: pytest src/backend/test/test_user_group_service.py -q; pytest src/backend/test/test_user_group_api.py -q
Not-tested: Legacy external clients that may still submit extra admin_user_ids on create
2026-04-17 01:32:15 +08:00
Y1fe1Zh0u 0bf2a63bc4 Restore legacy user-group admin rows for newly created groups
The new user-group service relied on OpenFGA admin tuples only, but several
legacy auth and info paths still identify group admins from the
user_group.is_group_admin rows. That left newly created groups invisible to
those older permission checks. This writes the creator's legacy admin row at
creation time while keeping the FGA admin tuple path in place.

Constraint: Preserve the newer creator-based/FGA model without breaking existing admin-group readers
Rejected: Rewrite every legacy admin lookup in one pass | broader migration with higher regression risk
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Until all legacy auth reads are removed, group creation must keep FGA admin and user_group admin rows aligned
Tested: pytest src/backend/test/test_user_group_service.py -q; pytest src/backend/test/test_user_group_api.py -q
Not-tested: Mixed old/new user-group management flows in a live environment
2026-04-17 01:31:04 +08:00
Y1fe1Zh0u 19f18dd55b Preserve permission tuples when rebinding relation models
Relation-model rebinding was treating a same-relation model switch as a
grant plus revoke against the same FGA tuple, which removed the actual
permission. Binding records also dropped include_children scope, so list
revokes and model deletion could revoke broader department grants than the
original authorization. This keeps same-relation model switches binding-only,
persists scoped binding metadata, and forwards department include_children
when modifying or revoking from the permission list.

Constraint: Maintain compatibility with legacy binding keys already stored in config
Rejected: Fix only the frontend request payload | model deletion and backend binding lookups would still over-revoke
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Relation-model metadata must never change FGA tuples unless relation or include_children actually changes
Tested: pytest src/backend/test/test_permission_relation_bindings.py -q; pytest src/backend/test/test_permission_service.py -q
Not-tested: Interactive permission-management UI flow across all resource types
2026-04-17 01:29:18 +08:00
Y1fe1Zh0u e77472a4c3 Enforce department-admin scope on role mutations and detail access
Role management only filtered department admins by subtree in the list view,
while direct detail, update, delete, and menu operations still accepted any
tenant role ID. This adds explicit subtree checks for create and single-role
operations, and marks out-of-scope tenant roles read-only in list responses.

Constraint: Preserve tenant-admin behavior while tightening only department-admin scope
Rejected: Rely on list filtering alone | direct role endpoints remain callable with guessed IDs
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Any single-role endpoint reachable by department admins must re-check subtree ownership server-side
Tested: pytest src/backend/test/test_role_service.py -q
Not-tested: Full role-management UI flow for department-admin accounts
2026-04-17 01:26:31 +08:00
Y1fe1Zh0u 87fd7790c8 Route department member group edits through user-group permissions
Department member editing was deriving manageable groups from visibility and
then writing membership rows directly, which bypassed the user-group
mutation rules and skipped the existing group change handling path. This
narrows editable groups to the caller's true mutation scope and routes the
membership replacement through UserGroupService so permission checks and
FGA sync stay consistent.

Constraint: Preserve non-manageable memberships while updating only the caller's editable slice
Rejected: Keep direct DAO writes with a narrower visibility filter | still bypasses group service invariants and tuple sync
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Membership writes that affect user_group permissions should go through UserGroupService, not raw DAO diffs
Tested: pytest src/backend/test/test_user_group_service.py -q; pytest src/backend/test/test_user_group_api.py -q
Not-tested: End-to-end organization member edit flow in UI
2026-04-17 01:24:47 +08:00
Y1fe1Zh0u 435e811f2b Restore department member removal from the organization table
The member table lost its direct remove action during the edit-dialog
refactor, which left third-party primary members and affiliate members
without any UI path to leave a department. This restores the existing
API-backed removal entry in the table while keeping local-account
delete flows unchanged.

Constraint: Keep the fix UI-only and reuse the existing remove-member API
Rejected: Add remove logic only inside the edit dialog | still leaves key member types without an obvious table action
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Member removal and local-account deletion are distinct flows; preserve both entry points
Tested: Frontend smoke test via vitest smoke suite
Not-tested: Interactive DepartmentPage manual removal flow
2026-04-17 01:22:54 +08:00
Y1fe1Zh0u a424a66c90 fix(perm-2.5): address code review — purge safety, permission leak, archived write-protect
HIGH: purge now deletes scoped roles instead of setting department_id=NULL
(prevents privilege escalation to tenant-wide). Blocks purge when archived
children exist (prevents orphan nodes).

HIGH: replace _get_dept_or_raise + _check_permission with combined helper
that returns PermissionDenied for both not-found and no-access (non-admin),
preventing resource existence enumeration.

HIGH: _manageable_group_options now filters to groups where user is admin
or creator, not just visible groups.

MEDIUM: archived departments are now fully read-only — backend rejects
update/set-admins; frontend hides admin and default-role sections.

MEDIUM: fix useCallback stale closure for adminPicks in CreateDepartmentDialog.

LOW: filter archived departments from parent selector and TreeDepartmentSelect.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 01:18:29 +08:00
Y1fe1Zh0u 9a39c7e18f feat(perm-2.5): quota enforcement, dept default roles, dept admin on create, archived dept purge, configurable dept ID prefix
P0: Fix require_quota decorator for sync/async compat, apply to 6 resource
creation endpoints (knowledge_space, knowledge_file, workflow, assistant,
channel, tool). Add default role selector UI to DepartmentSettings.

P1: Add admin selection to CreateDepartmentDialog. Add physical deletion
for archived departments (purge endpoint, OpenFGA cleanup, tree shows
archived nodes dimmed).

P2: Make dept ID prefix configurable via system config (dept_id_prefix).
Resource permission template CRUD already complete — no changes needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 01:06:41 +08:00
LineWalker f44ce8ee4a feat: add department dimension to dashboard telemetry + docs updates
- Add user_department_infos to telemetry schema, ES mappings, and mid-table sync
- Update UserDao/UserRepositoryImpl to load department info for telemetry
- Add DepartmentDao.get_by_ids and UserDepartmentDao.get_by_user_ids sync methods
- Update 4 Celery sync tasks to include department info
- Add department dimension i18n labels (zh/en/ja)
- Add v2.5 upgrade migration plan for dashboard department dimension
- Include misc docs (architecture, PRD, deployment diagrams) and frontend updates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 23:19:11 +08:00
30388 89649ffd79 feat(perm-2.5): org member edit, primary dept, delete guard, router order
- Department: member edit-form/apply-edit, local primary dept change, delete-check and local-account purge
- Register member_router before department_router to fix GET .../members/.../edit-form 404
- Platform: OrganizationMemberEditDialog, MemberTable tooltips, TreeDepartmentSelect, i18n
- Docker: remove broken OpenFGA HEALTHCHECK (distroless has no shell)
- Scripts: default user group removal and sample SQL for secondary dept

Made-with: Cursor
2026-04-16 21:59:15 +08:00
Y1fe1Zh0u 3d2edd9270 Restore platform permission API compatibility
The platform permission UI was importing legacy helpers from
controllers/API/permission.ts, but that module only exported the
ReBAC schema fetcher. Vite then failed at runtime when the page tried
to import getDepartmentTree and related permission helpers.

This change reintroduces the compatibility exports for department tree,
resource permission listing, authorize calls, and permission checks so
existing permission components load again without changing their call
sites.

Constraint: Keep the fix limited to the platform API adapter layer so Drone can redeploy without broader frontend churn
Rejected: Refactor all permission consumers to new import paths | unnecessary for production recovery
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If permission APIs are reorganized again, preserve a compatibility adapter until all platform imports are migrated together
Tested: cd src/frontend/platform && npm test -- --run
Tested: cd src/frontend/platform && npm run build
Not-tested: Browser verification after Drone redeploy
2026-04-16 16:38:45 +08:00
Y1fe1Zh0u 2a896448d6 Restore knowledge-space file visibility after upload failures
The knowledge-space file queries could crash after successful reprocessing because
ResourceTypeEnum was referenced without being imported, and duplicate detection
continued to treat failed/timeout records as active conflicts. The frontend also
hid the real parse failure details behind generic failed states.

This change restores the query path, excludes failed/timeout records from
duplicate checks, and surfaces backend failure reasons from remark/error_message
in the knowledge-space file views.

Constraint: Keep the fix small and compatible with the current 2.5.0-PM knowledge-space flow
Rejected: Rework upload flow end-to-end | too broad for the production issue at hand
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: If duplicate semantics change again, keep failed/timeout records out of active conflict checks unless retry flow is redesigned
Tested: src/backend/.venv/bin/python -m pytest src/backend/test/test_knowledge_space_upload_regressions.py
Not-tested: Live browser verification against the remote server UI
2026-04-16 15:26:00 +08:00
LineWalker f7dd34469f chore: add no-op comment to retrigger CI
Add a harmless inline comment in roles page only to trigger a fresh CI run while investigating runner connectivity issues.

Made-with: Cursor
2026-04-15 16:42:32 +08:00
LineWalker 30106794e2 feat: align system management with PRD 2.5 roles and department local members
Implement role management on the new role APIs, add department local-member creation flow with assignable roles and synced-readonly rules, and align user group visibility plus ReBAC schema read view to unblock end-to-end org/member setup.

Made-with: Cursor
2026-04-15 16:20:49 +08:00
LineWalker 5848ae7e69 docs: add gateway commercial deployment guide
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:07:51 +08:00