-- Refuse to delete the legacy parent if another deployment has attached -- unknown children to it. The failed CHECK deliberately leaves migration -- recovery to an operator instead of silently orphaning those menus. CREATE TABLE IF NOT EXISTS chorus_admin_grouped_navigation_up_guard ( id TINYINT NOT NULL, CONSTRAINT chk_chorus_admin_grouped_navigation_up_guard CHECK (id = 0) ) ENGINE=InnoDB; DELETE FROM chorus_admin_grouped_navigation_up_guard; INSERT INTO chorus_admin_grouped_navigation_up_guard (id) SELECT 1 WHERE EXISTS ( SELECT 1 FROM sys_menu child JOIN sys_menu parent ON parent.menu_id = child.parent_id WHERE parent.path = '/chorus' AND child.path NOT IN ( '/chorus/providers', '/chorus/models', '/chorus/routes', '/chorus/templates', '/chorus/health', '/chorus/generations', '/chorus/users', '/chorus/api-keys' ) ); DROP TABLE chorus_admin_grouped_navigation_up_guard; INSERT INTO sys_menu (menu_name, title, icon, path, paths, menu_type, action, permission, parent_id, no_cache, breadcrumb, component, sort, visible, is_frame) VALUES ('chorus-configuration', '生成配置', 'ri:settings-3-line', '/chorus/configuration', '', 'M', '', 'chorus:configuration:view', 0, FALSE, '生成配置', 'Layout', 900, '0', '0'), ('chorus-monitoring', '运行监控', 'ri:pulse-line', '/chorus/monitoring', '', 'M', '', 'chorus:monitoring:view', 0, FALSE, '运行监控', 'Layout', 910, '0', '0'), ('chorus-access', '用户与访问', 'ri:team-line', '/chorus/access', '', 'M', '', 'chorus:access:view', 0, FALSE, '用户与访问', 'Layout', 920, '0', '0'), ('chorus-system', '系统管理', 'ri:shield-keyhole-line', '/chorus/system', '', 'M', '', 'chorus:system:view', 0, FALSE, '系统管理', 'Layout', 930, '0', '0') ON DUPLICATE KEY UPDATE menu_name = VALUES(menu_name), title = VALUES(title), icon = VALUES(icon), menu_type = VALUES(menu_type), action = VALUES(action), permission = VALUES(permission), parent_id = VALUES(parent_id), no_cache = VALUES(no_cache), breadcrumb = VALUES(breadcrumb), component = VALUES(component), sort = VALUES(sort), visible = VALUES(visible), is_frame = VALUES(is_frame), deleted_at = NULL; UPDATE sys_menu SET paths = CONCAT('/0/', menu_id) WHERE path IN ('/chorus/configuration', '/chorus/monitoring', '/chorus/access', '/chorus/system'); UPDATE sys_menu child JOIN ( SELECT '/chorus/providers' AS child_path, '/chorus/configuration' AS parent_path, '上游服务商' AS title, 10 AS sort_order UNION ALL SELECT '/chorus/models', '/chorus/configuration', '模型配置', 20 UNION ALL SELECT '/chorus/routes', '/chorus/configuration', '路由策略', 30 UNION ALL SELECT '/chorus/templates', '/chorus/configuration', '提示词模板', 40 UNION ALL SELECT '/chorus/health', '/chorus/monitoring', '上游健康', 10 UNION ALL SELECT '/chorus/generations', '/chorus/monitoring', '生成记录', 20 UNION ALL SELECT '/chorus/users', '/chorus/access', '终端用户', 10 UNION ALL SELECT '/chorus/api-keys', '/chorus/access', 'API 密钥', 20 ) desired ON desired.child_path = child.path JOIN sys_menu parent ON parent.path = desired.parent_path SET child.parent_id = parent.menu_id, child.paths = CONCAT(parent.paths, '/', child.menu_id), child.title = desired.title, child.breadcrumb = CONCAT(parent.title, ' / ', desired.title), child.sort = desired.sort_order, child.deleted_at = NULL; DELETE FROM sys_menu WHERE path = '/chorus'; INSERT INTO sys_menu (menu_name, title, icon, path, paths, menu_type, action, permission, parent_id, no_cache, breadcrumb, component, sort, visible, is_frame) SELECT desired.menu_name, desired.title, desired.icon, desired.path, '', 'C', '', desired.permission, parent.menu_id, FALSE, CONCAT(parent.title, ' / ', desired.title), desired.component, desired.sort_order, '0', '0' FROM ( SELECT 'chorus-system-admins' AS menu_name, '管理员账号' AS title, 'ri:admin-line' AS icon, '/chorus/system/admins' AS path, 'chorus:system:admins:view' AS permission, 'admin/sys-user/index' AS component, 10 AS sort_order UNION ALL SELECT 'chorus-system-roles', '角色权限', 'ri:shield-user-line', '/chorus/system/roles', 'chorus:system:roles:view', 'admin/sys-role/index', 20 UNION ALL SELECT 'chorus-system-menus', '菜单结构', 'ri:menu-line', '/chorus/system/menus', 'chorus:system:menus:view', 'admin/sys-menu/index', 30 UNION ALL SELECT 'chorus-system-apis', '接口清单', 'ri:braces-line', '/chorus/system/apis', 'chorus:system:apis:view', 'admin/sys-api/index', 40 UNION ALL SELECT 'chorus-system-login-logs', '登录日志', 'ri:login-box-line', '/chorus/system/login-logs', 'chorus:system:login-logs:view', 'admin/sys-login-log/index', 50 ) desired JOIN sys_menu parent ON parent.path = '/chorus/system' ON DUPLICATE KEY UPDATE menu_name = VALUES(menu_name), title = VALUES(title), icon = VALUES(icon), menu_type = VALUES(menu_type), action = VALUES(action), permission = VALUES(permission), parent_id = VALUES(parent_id), no_cache = VALUES(no_cache), breadcrumb = VALUES(breadcrumb), component = VALUES(component), sort = VALUES(sort), visible = VALUES(visible), is_frame = VALUES(is_frame), deleted_at = NULL; UPDATE sys_menu child JOIN sys_menu parent ON parent.path = '/chorus/system' SET child.paths = CONCAT(parent.paths, '/', child.menu_id) WHERE child.path IN ( '/chorus/system/admins', '/chorus/system/roles', '/chorus/system/menus', '/chorus/system/apis', '/chorus/system/login-logs' ); -- Existing deployments may already have a matching path/action row from a -- go-admin seed. In that case retain it instead of taking ownership of it. INSERT IGNORE INTO sys_api (handle, title, path, type, action) VALUES ('chorus.system.admins.list', '管理员账号列表', '/api/v1/sys-user', 'SYS', 'GET'), ('chorus.system.admins.get', '管理员账号详情', '/api/v1/sys-user/:id', 'SYS', 'GET'), ('chorus.system.admins.create', '创建管理员账号', '/api/v1/sys-user', 'SYS', 'POST'), ('chorus.system.admins.update', '更新管理员账号', '/api/v1/sys-user', 'SYS', 'PUT'), ('chorus.system.admins.delete', '删除管理员账号', '/api/v1/sys-user', 'SYS', 'DELETE'), ('chorus.system.admins.status', '更新管理员状态', '/api/v1/user/status', 'SYS', 'PUT'), ('chorus.system.admins.password-reset', '重置管理员密码', '/api/v1/user/pwd/reset', 'SYS', 'PUT'), ('chorus.system.roles.list', '角色列表', '/api/v1/role', 'SYS', 'GET'), ('chorus.system.roles.get', '角色详情', '/api/v1/role/:id', 'SYS', 'GET'), ('chorus.system.roles.create', '创建角色', '/api/v1/role', 'SYS', 'POST'), ('chorus.system.roles.update', '更新角色', '/api/v1/role/:id', 'SYS', 'PUT'), ('chorus.system.roles.delete', '删除角色', '/api/v1/role', 'SYS', 'DELETE'), ('chorus.system.roles.status', '更新角色状态', '/api/v1/role-status', 'SYS', 'PUT'), ('chorus.system.roles.menu', '角色菜单树', '/api/v1/menurole', 'SYS', 'GET'), ('chorus.system.menus.list', '菜单结构列表', '/api/v1/menu', 'SYS', 'GET'), ('chorus.system.menus.get', '菜单结构详情', '/api/v1/menu/:id', 'SYS', 'GET'), ('chorus.system.apis.list', '接口清单', '/api/v1/sys-api', 'SYS', 'GET'), ('chorus.system.apis.get', '接口详情', '/api/v1/sys-api/:id', 'SYS', 'GET'), ('chorus.system.login-logs.list', '登录日志列表', '/api/v1/sys-login-log', 'SYS', 'GET'), ('chorus.system.login-logs.get', '登录日志详情', '/api/v1/sys-login-log/:id', 'SYS', 'GET'); INSERT IGNORE INTO sys_role_menu (role_id, menu_id) SELECT role_record.role_id, menu.menu_id FROM sys_role role_record JOIN sys_menu menu ON menu.path IN ( '/chorus/configuration', '/chorus/monitoring', '/chorus/access', '/chorus/system', '/chorus/providers', '/chorus/models', '/chorus/routes', '/chorus/templates', '/chorus/health', '/chorus/generations', '/chorus/users', '/chorus/api-keys', '/chorus/system/admins', '/chorus/system/roles', '/chorus/system/menus', '/chorus/system/apis', '/chorus/system/login-logs' ) WHERE role_record.role_key = 'chorus_operator'; INSERT IGNORE INTO sys_menu_api_rule (menu_id, sys_api_id) SELECT menu.menu_id, api.id FROM sys_menu menu JOIN sys_api api ON ( (menu.path = '/chorus/system/admins' AND ( (api.path = '/api/v1/sys-user' AND api.action IN ('GET', 'POST', 'PUT', 'DELETE')) OR (api.path = '/api/v1/sys-user/:id' AND api.action = 'GET') OR (api.path IN ('/api/v1/user/status', '/api/v1/user/pwd/reset') AND api.action = 'PUT') )) OR (menu.path = '/chorus/system/roles' AND ( (api.path = '/api/v1/role' AND api.action IN ('GET', 'POST', 'DELETE')) OR (api.path = '/api/v1/role/:id' AND api.action IN ('GET', 'PUT')) OR (api.path = '/api/v1/role-status' AND api.action = 'PUT') OR (api.path = '/api/v1/menurole' AND api.action = 'GET') )) OR (menu.path = '/chorus/system/menus' AND ( (api.path = '/api/v1/menu' AND api.action = 'GET') OR (api.path = '/api/v1/menu/:id' AND api.action = 'GET') )) OR (menu.path = '/chorus/system/apis' AND ( (api.path = '/api/v1/sys-api' AND api.action = 'GET') OR (api.path = '/api/v1/sys-api/:id' AND api.action = 'GET') )) OR (menu.path = '/chorus/system/login-logs' AND ( (api.path = '/api/v1/sys-login-log' AND api.action = 'GET') OR (api.path = '/api/v1/sys-login-log/:id' AND api.action = 'GET') )) ); INSERT IGNORE INTO sys_casbin_rule (ptype, v0, v1, v2, v3, v4, v5) SELECT DISTINCT 'p', 'chorus_operator', api.path, api.action, '', '', '' FROM sys_menu_api_rule menu_api JOIN sys_menu menu ON menu.menu_id = menu_api.menu_id JOIN sys_api api ON api.id = menu_api.sys_api_id WHERE menu.path LIKE '/chorus/system/%';