# Mgnt Storage Migration (MySQL → MongoDB `box_admin`) ## Scope - Target: Move **box-mgnt-api** admin storage from MySQL to MongoDB database `box_admin`. - Keep **box_stats** unchanged (MongoStats stays as-is). - Read-only analysis; no code modifications. ## 1) MySQL Prisma Models Used by box-mgnt-api at Runtime ### Used models (direct Prisma access in mgnt services) - `User` (`prisma/mysql/schema/user.prisma`) - `Role` (`prisma/mysql/schema/role.prisma`) - `UserRole` (`prisma/mysql/schema/user-role.prisma`) - `Menu` (`prisma/mysql/schema/menu.prisma`) - `RoleMenu` (`prisma/mysql/schema/role-menu.prisma`) - `ApiPermission` (`prisma/mysql/schema/api-permission.prisma`) - `RoleApiPermission` (`prisma/mysql/schema/role-api-permission.prisma`) - `LoginLog` (`prisma/mysql/schema/login-log.prisma`) - `OperationLog` (`prisma/mysql/schema/operation-log.prisma`) - `QuotaLog` (`prisma/mysql/schema/quota-log.prisma`) - `CacheSyncAction` (`prisma/mysql/schema/cache-sync-action.prisma`) ### Not used by box-mgnt-api runtime (present in schema only) - `ImageConfig` (schema deleted; no runtime references; table removed before Mongo migration) - `ProviderVideoSync` (model data flows from provider sync code and now lives under `prisma/mongo/schema/sys-providerVideoSync.prisma`) ## 2) Constraints per Model (PK, Unique, Relations) ### Summary table | Model | Primary Key | Unique Constraints | Relations | |---|---|---|---| | User | `id` (auto-increment int) | `username` | `UserRole.userId -> User.id` | | Role | `id` | `name` | `UserRole.roleId -> Role.id`, `RoleMenu.roleId -> Role.id`, `RoleApiPermission.roleId -> Role.id` | | UserRole | `id` | `(userId, roleId)` | `userId -> User`, `roleId -> Role` | | Menu | `id` | `frontendAuth` | self relation (`parentId -> Menu.id`), `RoleMenu.menuId -> Menu.id`, `ApiPermission.menuId -> Menu.id` | | RoleMenu | `id` | `(roleId, menuId)` | `roleId -> Role`, `menuId -> Menu` | | ApiPermission | `id` | `(menuId, path, method)` | `menuId -> Menu`, `RoleApiPermission.apiPermissionId -> ApiPermission.id` | | RoleApiPermission | `id` | `(roleId, apiPermissionId)` | `roleId -> Role`, `apiPermissionId -> ApiPermission` | | LoginLog | `id` | none | none | | OperationLog | `id` | none | `menuId` is nullable but not enforced by FK in schema | | QuotaLog | `id` | none | none | | CacheSyncAction | `id` (BigInt) | none | none; indexed by `status,nextAttemptAt` and `entityType,entityId` | ### Additional field constraints (from schema) - Enums used in mgnt: - `MenuType` (`DIRECTORY`, `MENU`, `SUBMENU`, `BUTTON`) - `LoginType` / `LoginStatus` - `OperationType` - JSON fields: - `User.twoFARecoveryCodes`, `User.allowIps` - `Menu.meta` - `OperationLog.body`, `OperationLog.response` - `CacheSyncAction.payload` - Special uniqueness / lookup usage in code: - `Menu.frontendAuth` queried by `findUnique` in `apps/box-mgnt-api/src/mgnt-backend/core/menu/menu.service.ts` ## 3) Proposed Mongo Collection Schemas + Indexes (`box_admin`) ### Collections (1-to-1 with current MySQL models) - `sys_user` - Fields: `id` (int), `username`, `password`, `status`, `nick`, `photo`, `remark`, `twoFA`, `twoFALastUsedStep`, `twoFARecoveryCodes`, `allowIps`, `jwtToken`, `oAuthJwtToken`, `lastLoginTime`, `create_time`, `update_time` - Indexes: unique on `username`, index on `status` (optional for listing) - `sys_role` - Fields: `id` (int), `name`, `status`, `remark`, `create_time`, `update_time` - Indexes: unique on `name` - `sys_user_role` - Fields: `id` (int), `user_id`, `role_id`, `create_time`, `update_time` - Indexes: unique compound `(user_id, role_id)`; index on `user_id`, `role_id` - `sys_menu` - Fields: `id` (int), `parent_id`, `title`, `status`, `type`, `order`, `frontend_auth`, `path`, `name`, `icon`, `redirect`, `component_key`, `meta`, `canView`, `canCreate`, `canUpdate`, `canDelete`, `create_time`, `update_time` - Indexes: unique on `frontend_auth`, index on `parent_id`, index on `type` - `sys_role_menu` - Fields: `id` (int), `role_id`, `menu_id`, `canView`, `canCreate`, `canUpdate`, `canDelete`, `create_time`, `update_time` - Indexes: unique compound `(role_id, menu_id)`; index on `role_id`, `menu_id` - `sys_api_permission` - Fields: `id` (int), `menu_id`, `path`, `method`, `create_time`, `update_time` - Indexes: unique compound `(menu_id, path, method)`; index on `menu_id` - `sys_role_api_permission` - Fields: `id` (int), `role_id`, `api_permission_id`, `create_time`, `update_time` - Indexes: unique compound `(role_id, api_permission_id)`; index on `role_id`, `api_permission_id` - `sys_login_log` - Fields: `id` (int), `type`, `status`, `username`, `ip_address`, `user_agent`, `create_time`, `update_time` - Indexes: index on `username`, `create_time` - `sys_operation_log` - Fields: `id` (int), `username`, `menu_id`, `description`, `type`, `status`, `method`, `path`, `body`, `response`, `ip_address`, `call_method`, `create_time`, `update_time` - Indexes: index on `username`, `menu_id`, `create_time` - `sys_quota_log` - Fields: `id` (int), `username`, `op_username`, `amount`, `is_inc`, `quota`, `remark`, `create_time`, `update_time` - Indexes: index on `username`, `create_time` - `cache_sync_action` - Fields: `id` (long), `entityType`, `entityId`, `operation`, `status`, `attempts`, `nextAttemptAt`, `lastError`, `payload`, `createdAt`, `updatedAt` - Indexes: `(status, nextAttemptAt)`, `(entityType, entityId)` ### Mongo Prisma schema filenames & mappings - `prisma/mongo/schema/sys-user.prisma` → maps to `sys_user` (mirrors `prisma/mysql/schema/user.prisma`) - `prisma/mongo/schema/sys-role.prisma` → maps to `sys_role` - `prisma/mongo/schema/sys-user-role.prisma` → maps to `sys_user_role` - `prisma/mongo/schema/sys-menu.prisma` → maps to `sys_menu` - `prisma/mongo/schema/sys-role-menu.prisma` → maps to `sys_role_menu` - `prisma/mongo/schema/sys-api-permission.prisma` → maps to `sys_api_permission` - `prisma/mongo/schema/sys-role-api-permission.prisma` → maps to `sys_role_api_permission` - `prisma/mongo/schema/sys-login-log.prisma` → maps to `sys_login_log` - `prisma/mongo/schema/sys-operation-log.prisma` → maps to `sys_operation_log` - `prisma/mongo/schema/sys-cache-sync-action.prisma` → maps to `sys_cacheSyncAction` (preserves BigInt PK/indexes from the MySQL schema) - `prisma/mongo/schema/sys-providerVideoSync.prisma` → maps to `sys_providerVideoSync` (moved out of `prisma/mysql/schema/main.prisma` so Mongo owns the provider sync store) - `sys_quota_log` intentionally remains only in MySQL; no Mongo schema file is created so Mongo services keep relying on the existing `prisma/mysql/schema/quota-log.prisma`. Every Mongo schema file uses `@@map("sys_