1325 lines
34 KiB
TypeScript
1325 lines
34 KiB
TypeScript
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-nocheck
|
|
// 加载环境变量(必须在其他导入之前)
|
|
import * as dotenv from 'dotenv';
|
|
import * as path from 'path';
|
|
|
|
// 根据 NODE_ENV 加载对应的环境配置文件
|
|
const nodeEnv = process.env.NODE_ENV || 'development';
|
|
const envFile = `.env.${nodeEnv}`;
|
|
// scripts 目录的父目录就是 backend 目录
|
|
const backendDir = path.resolve(__dirname, '..');
|
|
const envPath = path.resolve(backendDir, envFile);
|
|
|
|
// 尝试加载环境特定的配置文件
|
|
dotenv.config({ path: envPath });
|
|
|
|
// 如果环境特定文件不存在,尝试加载默认的 .env 文件
|
|
if (!process.env.DATABASE_URL) {
|
|
dotenv.config({ path: path.resolve(backendDir, '.env') });
|
|
}
|
|
|
|
// 验证必要的环境变量
|
|
if (!process.env.DATABASE_URL) {
|
|
console.error('❌ 错误: 未找到 DATABASE_URL 环境变量');
|
|
console.error(` 请确保存在以下文件之一:`);
|
|
console.error(` - ${envPath}`);
|
|
console.error(` - ${path.resolve(backendDir, '.env')}`);
|
|
console.error(` 或者设置 NODE_ENV 环境变量(当前: ${nodeEnv})`);
|
|
process.exit(1);
|
|
}
|
|
|
|
import { PrismaClient } from '@prisma/client';
|
|
import * as bcrypt from 'bcrypt';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
// 定义所有基础权限
|
|
const permissions = [
|
|
{
|
|
code: 'workbench:read',
|
|
resource: 'workbench',
|
|
action: 'read',
|
|
name: '查看工作台',
|
|
description: '允许查看工作台',
|
|
},
|
|
// 用户管理权限
|
|
{
|
|
code: 'user:create',
|
|
resource: 'user',
|
|
action: 'create',
|
|
name: '创建用户',
|
|
description: '允许创建新用户',
|
|
},
|
|
{
|
|
code: 'user:read',
|
|
resource: 'user',
|
|
action: 'read',
|
|
name: '查看用户',
|
|
description: '允许查看用户列表和详情',
|
|
},
|
|
{
|
|
code: 'user:update',
|
|
resource: 'user',
|
|
action: 'update',
|
|
name: '更新用户',
|
|
description: '允许更新用户信息',
|
|
},
|
|
{
|
|
code: 'user:delete',
|
|
resource: 'user',
|
|
action: 'delete',
|
|
name: '删除用户',
|
|
description: '允许删除用户',
|
|
},
|
|
|
|
// 角色管理权限
|
|
{
|
|
code: 'role:create',
|
|
resource: 'role',
|
|
action: 'create',
|
|
name: '创建角色',
|
|
description: '允许创建新角色',
|
|
},
|
|
{
|
|
code: 'role:read',
|
|
resource: 'role',
|
|
action: 'read',
|
|
name: '查看角色',
|
|
description: '允许查看角色列表和详情',
|
|
},
|
|
{
|
|
code: 'role:update',
|
|
resource: 'role',
|
|
action: 'update',
|
|
name: '更新角色',
|
|
description: '允许更新角色信息',
|
|
},
|
|
{
|
|
code: 'role:delete',
|
|
resource: 'role',
|
|
action: 'delete',
|
|
name: '删除角色',
|
|
description: '允许删除角色',
|
|
},
|
|
{
|
|
code: 'role:assign',
|
|
resource: 'role',
|
|
action: 'assign',
|
|
name: '分配角色',
|
|
description: '允许给用户分配角色',
|
|
},
|
|
|
|
// 权限管理权限
|
|
{
|
|
code: 'permission:create',
|
|
resource: 'permission',
|
|
action: 'create',
|
|
name: '创建权限',
|
|
description: '允许创建新权限',
|
|
},
|
|
{
|
|
code: 'permission:read',
|
|
resource: 'permission',
|
|
action: 'read',
|
|
name: '查看权限',
|
|
description: '允许查看权限列表和详情',
|
|
},
|
|
{
|
|
code: 'permission:update',
|
|
resource: 'permission',
|
|
action: 'update',
|
|
name: '更新权限',
|
|
description: '允许更新权限信息',
|
|
},
|
|
{
|
|
code: 'permission:delete',
|
|
resource: 'permission',
|
|
action: 'delete',
|
|
name: '删除权限',
|
|
description: '允许删除权限',
|
|
},
|
|
|
|
// 菜单管理权限
|
|
{
|
|
code: 'menu:create',
|
|
resource: 'menu',
|
|
action: 'create',
|
|
name: '创建菜单',
|
|
description: '允许创建新菜单',
|
|
},
|
|
{
|
|
code: 'menu:read',
|
|
resource: 'menu',
|
|
action: 'read',
|
|
name: '查看菜单',
|
|
description: '允许查看菜单列表和详情',
|
|
},
|
|
{
|
|
code: 'menu:update',
|
|
resource: 'menu',
|
|
action: 'update',
|
|
name: '更新菜单',
|
|
description: '允许更新菜单信息',
|
|
},
|
|
{
|
|
code: 'menu:delete',
|
|
resource: 'menu',
|
|
action: 'delete',
|
|
name: '删除菜单',
|
|
description: '允许删除菜单',
|
|
},
|
|
|
|
// 数据字典权限
|
|
{
|
|
code: 'dict:create',
|
|
resource: 'dict',
|
|
action: 'create',
|
|
name: '创建字典',
|
|
description: '允许创建新字典',
|
|
},
|
|
{
|
|
code: 'dict:read',
|
|
resource: 'dict',
|
|
action: 'read',
|
|
name: '查看字典',
|
|
description: '允许查看字典列表和详情',
|
|
},
|
|
{
|
|
code: 'dict:update',
|
|
resource: 'dict',
|
|
action: 'update',
|
|
name: '更新字典',
|
|
description: '允许更新字典信息',
|
|
},
|
|
{
|
|
code: 'dict:delete',
|
|
resource: 'dict',
|
|
action: 'delete',
|
|
name: '删除字典',
|
|
description: '允许删除字典',
|
|
},
|
|
|
|
// 系统配置权限
|
|
{
|
|
code: 'config:create',
|
|
resource: 'config',
|
|
action: 'create',
|
|
name: '创建配置',
|
|
description: '允许创建新配置',
|
|
},
|
|
{
|
|
code: 'config:read',
|
|
resource: 'config',
|
|
action: 'read',
|
|
name: '查看配置',
|
|
description: '允许查看配置列表和详情',
|
|
},
|
|
{
|
|
code: 'config:update',
|
|
resource: 'config',
|
|
action: 'update',
|
|
name: '更新配置',
|
|
description: '允许更新配置信息',
|
|
},
|
|
{
|
|
code: 'config:delete',
|
|
resource: 'config',
|
|
action: 'delete',
|
|
name: '删除配置',
|
|
description: '允许删除配置',
|
|
},
|
|
|
|
// 日志管理权限
|
|
{
|
|
code: 'log:read',
|
|
resource: 'log',
|
|
action: 'read',
|
|
name: '查看日志',
|
|
description: '允许查看系统日志',
|
|
},
|
|
{
|
|
code: 'log:delete',
|
|
resource: 'log',
|
|
action: 'delete',
|
|
name: '删除日志',
|
|
description: '允许删除系统日志',
|
|
},
|
|
|
|
// 用户密码管理权限
|
|
{
|
|
code: 'user:password:update',
|
|
resource: 'user',
|
|
action: 'password:update',
|
|
name: '修改用户密码',
|
|
description: '允许修改用户密码',
|
|
},
|
|
|
|
// 学校管理权限
|
|
{
|
|
code: 'school:create',
|
|
resource: 'school',
|
|
action: 'create',
|
|
name: '创建学校',
|
|
description: '允许创建学校信息',
|
|
},
|
|
{
|
|
code: 'school:read',
|
|
resource: 'school',
|
|
action: 'read',
|
|
name: '查看学校',
|
|
description: '允许查看学校信息',
|
|
},
|
|
{
|
|
code: 'school:update',
|
|
resource: 'school',
|
|
action: 'update',
|
|
name: '更新学校',
|
|
description: '允许更新学校信息',
|
|
},
|
|
{
|
|
code: 'school:delete',
|
|
resource: 'school',
|
|
action: 'delete',
|
|
name: '删除学校',
|
|
description: '允许删除学校信息',
|
|
},
|
|
{
|
|
code: 'department:create',
|
|
resource: 'department',
|
|
action: 'create',
|
|
name: '创建部门',
|
|
description: '允许创建部门',
|
|
},
|
|
{
|
|
code: 'department:read',
|
|
resource: 'department',
|
|
action: 'read',
|
|
name: '查看部门',
|
|
description: '允许查看部门列表和详情',
|
|
},
|
|
{
|
|
code: 'department:update',
|
|
resource: 'department',
|
|
action: 'update',
|
|
name: '更新部门',
|
|
description: '允许更新部门信息',
|
|
},
|
|
{
|
|
code: 'department:delete',
|
|
resource: 'department',
|
|
action: 'delete',
|
|
name: '删除部门',
|
|
description: '允许删除部门',
|
|
},
|
|
{
|
|
code: 'grade:create',
|
|
resource: 'grade',
|
|
action: 'create',
|
|
name: '创建年级',
|
|
description: '允许创建年级',
|
|
},
|
|
{
|
|
code: 'grade:read',
|
|
resource: 'grade',
|
|
action: 'read',
|
|
name: '查看年级',
|
|
description: '允许查看年级列表和详情',
|
|
},
|
|
{
|
|
code: 'grade:update',
|
|
resource: 'grade',
|
|
action: 'update',
|
|
name: '更新年级',
|
|
description: '允许更新年级信息',
|
|
},
|
|
{
|
|
code: 'grade:delete',
|
|
resource: 'grade',
|
|
action: 'delete',
|
|
name: '删除年级',
|
|
description: '允许删除年级',
|
|
},
|
|
{
|
|
code: 'class:create',
|
|
resource: 'class',
|
|
action: 'create',
|
|
name: '创建班级',
|
|
description: '允许创建班级',
|
|
},
|
|
{
|
|
code: 'class:read',
|
|
resource: 'class',
|
|
action: 'read',
|
|
name: '查看班级',
|
|
description: '允许查看班级列表和详情',
|
|
},
|
|
{
|
|
code: 'class:update',
|
|
resource: 'class',
|
|
action: 'update',
|
|
name: '更新班级',
|
|
description: '允许更新班级信息',
|
|
},
|
|
{
|
|
code: 'class:delete',
|
|
resource: 'class',
|
|
action: 'delete',
|
|
name: '删除班级',
|
|
description: '允许删除班级',
|
|
},
|
|
{
|
|
code: 'teacher:create',
|
|
resource: 'teacher',
|
|
action: 'create',
|
|
name: '创建教师',
|
|
description: '允许创建教师',
|
|
},
|
|
{
|
|
code: 'teacher:read',
|
|
resource: 'teacher',
|
|
action: 'read',
|
|
name: '查看教师',
|
|
description: '允许查看教师列表和详情',
|
|
},
|
|
{
|
|
code: 'teacher:update',
|
|
resource: 'teacher',
|
|
action: 'update',
|
|
name: '更新教师',
|
|
description: '允许更新教师信息',
|
|
},
|
|
{
|
|
code: 'teacher:delete',
|
|
resource: 'teacher',
|
|
action: 'delete',
|
|
name: '删除教师',
|
|
description: '允许删除教师',
|
|
},
|
|
{
|
|
code: 'student:create',
|
|
resource: 'student',
|
|
action: 'create',
|
|
name: '创建学生',
|
|
description: '允许创建学生',
|
|
},
|
|
{
|
|
code: 'student:read',
|
|
resource: 'student',
|
|
action: 'read',
|
|
name: '查看学生',
|
|
description: '允许查看学生列表和详情',
|
|
},
|
|
{
|
|
code: 'student:update',
|
|
resource: 'student',
|
|
action: 'update',
|
|
name: '更新学生',
|
|
description: '允许更新学生信息',
|
|
},
|
|
{
|
|
code: 'student:delete',
|
|
resource: 'student',
|
|
action: 'delete',
|
|
name: '删除学生',
|
|
description: '允许删除学生',
|
|
},
|
|
|
|
// 赛事管理权限
|
|
{
|
|
code: 'contest:create',
|
|
resource: 'contest',
|
|
action: 'create',
|
|
name: '创建比赛',
|
|
description: '允许创建比赛',
|
|
},
|
|
{
|
|
code: 'contest:read',
|
|
resource: 'contest',
|
|
action: 'read',
|
|
name: '查看比赛',
|
|
description: '允许查看比赛列表和详情',
|
|
},
|
|
{
|
|
code: 'contest:update',
|
|
resource: 'contest',
|
|
action: 'update',
|
|
name: '更新比赛',
|
|
description: '允许更新比赛信息',
|
|
},
|
|
{
|
|
code: 'contest:delete',
|
|
resource: 'contest',
|
|
action: 'delete',
|
|
name: '删除比赛',
|
|
description: '允许删除比赛',
|
|
},
|
|
{
|
|
code: 'contest:publish',
|
|
resource: 'contest',
|
|
action: 'publish',
|
|
name: '发布比赛',
|
|
description: '允许发布比赛',
|
|
},
|
|
{
|
|
code: 'contest:team:create',
|
|
resource: 'contest:team',
|
|
action: 'create',
|
|
name: '创建团队',
|
|
description: '允许创建比赛团队',
|
|
},
|
|
{
|
|
code: 'contest:team:read',
|
|
resource: 'contest:team',
|
|
action: 'read',
|
|
name: '查看团队',
|
|
description: '允许查看团队列表和详情',
|
|
},
|
|
{
|
|
code: 'contest:team:update',
|
|
resource: 'contest:team',
|
|
action: 'update',
|
|
name: '更新团队',
|
|
description: '允许更新团队信息',
|
|
},
|
|
{
|
|
code: 'contest:team:delete',
|
|
resource: 'contest:team',
|
|
action: 'delete',
|
|
name: '删除团队',
|
|
description: '允许删除团队',
|
|
},
|
|
{
|
|
code: 'contest:team:manage',
|
|
resource: 'contest:team',
|
|
action: 'manage',
|
|
name: '管理团队成员',
|
|
description: '允许管理团队成员',
|
|
},
|
|
{
|
|
code: 'contest:review:create',
|
|
resource: 'contest:review',
|
|
action: 'create',
|
|
name: '创建评审规则',
|
|
description: '允许创建评审规则',
|
|
},
|
|
{
|
|
code: 'contest:review:read',
|
|
resource: 'contest:review',
|
|
action: 'read',
|
|
name: '查看评审',
|
|
description: '允许查看评审规则和评审记录',
|
|
},
|
|
{
|
|
code: 'contest:review:update',
|
|
resource: 'contest:review',
|
|
action: 'update',
|
|
name: '更新评审规则',
|
|
description: '允许更新评审规则',
|
|
},
|
|
{
|
|
code: 'contest:review:delete',
|
|
resource: 'contest:review',
|
|
action: 'delete',
|
|
name: '删除评审规则',
|
|
description: '允许删除评审规则',
|
|
},
|
|
{
|
|
code: 'contest:review:assign',
|
|
resource: 'contest:review',
|
|
action: 'assign',
|
|
name: '分配评审任务',
|
|
description: '允许分配评审任务给评委',
|
|
},
|
|
{
|
|
code: 'contest:review:score',
|
|
resource: 'contest:review',
|
|
action: 'score',
|
|
name: '评审打分',
|
|
description: '允许对作品进行评审打分',
|
|
},
|
|
{
|
|
code: 'contest:judge:create',
|
|
resource: 'contest:judge',
|
|
action: 'create',
|
|
name: '添加评委',
|
|
description: '允许添加比赛评委',
|
|
},
|
|
{
|
|
code: 'contest:judge:read',
|
|
resource: 'contest:judge',
|
|
action: 'read',
|
|
name: '查看评委',
|
|
description: '允许查看评委列表',
|
|
},
|
|
{
|
|
code: 'contest:judge:update',
|
|
resource: 'contest:judge',
|
|
action: 'update',
|
|
name: '更新评委',
|
|
description: '允许更新评委信息',
|
|
},
|
|
{
|
|
code: 'contest:judge:delete',
|
|
resource: 'contest:judge',
|
|
action: 'delete',
|
|
name: '删除评委',
|
|
description: '允许删除评委',
|
|
},
|
|
{
|
|
code: 'contest:work:create',
|
|
resource: 'contest:work',
|
|
action: 'create',
|
|
name: '创建作品',
|
|
description: '允许创建参赛作品',
|
|
},
|
|
{
|
|
code: 'contest:work:read',
|
|
resource: 'contest:work',
|
|
action: 'read',
|
|
name: '查看作品',
|
|
description: '允许查看作品列表和详情',
|
|
},
|
|
{
|
|
code: 'contest:work:update',
|
|
resource: 'contest:work',
|
|
action: 'update',
|
|
name: '更新作品',
|
|
description: '允许更新作品信息',
|
|
},
|
|
{
|
|
code: 'contest:work:delete',
|
|
resource: 'contest:work',
|
|
action: 'delete',
|
|
name: '删除作品',
|
|
description: '允许删除作品',
|
|
},
|
|
{
|
|
code: 'contest:work:submit',
|
|
resource: 'contest:work',
|
|
action: 'submit',
|
|
name: '提交作品',
|
|
description: '允许提交作品',
|
|
},
|
|
{
|
|
code: 'contest:work:review',
|
|
resource: 'contest:work',
|
|
action: 'review',
|
|
name: '审核作品',
|
|
description: '允许审核作品状态',
|
|
},
|
|
{
|
|
code: 'contest:registration:create',
|
|
resource: 'contest:registration',
|
|
action: 'create',
|
|
name: '创建报名',
|
|
description: '允许创建报名记录',
|
|
},
|
|
{
|
|
code: 'contest:registration:read',
|
|
resource: 'contest:registration',
|
|
action: 'read',
|
|
name: '查看报名',
|
|
description: '允许查看报名列表和详情',
|
|
},
|
|
{
|
|
code: 'contest:registration:update',
|
|
resource: 'contest:registration',
|
|
action: 'update',
|
|
name: '更新报名',
|
|
description: '允许更新报名信息',
|
|
},
|
|
{
|
|
code: 'contest:registration:delete',
|
|
resource: 'contest:registration',
|
|
action: 'delete',
|
|
name: '删除报名',
|
|
description: '允许删除报名记录',
|
|
},
|
|
{
|
|
code: 'contest:registration:approve',
|
|
resource: 'contest:registration',
|
|
action: 'approve',
|
|
name: '审核报名',
|
|
description: '允许审核报名(通过/拒绝)',
|
|
},
|
|
{
|
|
code: 'contest:notice:create',
|
|
resource: 'contest:notice',
|
|
action: 'create',
|
|
name: '创建公告',
|
|
description: '允许创建比赛公告',
|
|
},
|
|
{
|
|
code: 'contest:notice:read',
|
|
resource: 'contest:notice',
|
|
action: 'read',
|
|
name: '查看公告',
|
|
description: '允许查看公告列表和详情',
|
|
},
|
|
{
|
|
code: 'contest:notice:update',
|
|
resource: 'contest:notice',
|
|
action: 'update',
|
|
name: '更新公告',
|
|
description: '允许更新公告信息',
|
|
},
|
|
{
|
|
code: 'contest:notice:delete',
|
|
resource: 'contest:notice',
|
|
action: 'delete',
|
|
name: '删除公告',
|
|
description: '允许删除公告',
|
|
},
|
|
{
|
|
code: 'contest:notice:publish',
|
|
resource: 'contest:notice',
|
|
action: 'publish',
|
|
name: '发布公告',
|
|
description: '允许发布公告',
|
|
},
|
|
|
|
// 作业管理权限
|
|
{
|
|
code: 'homework:create',
|
|
resource: 'homework',
|
|
action: 'create',
|
|
name: '创建作业',
|
|
description: '允许创建作业',
|
|
},
|
|
{
|
|
code: 'homework:read',
|
|
resource: 'homework',
|
|
action: 'read',
|
|
name: '查看作业',
|
|
description: '允许查看作业列表和详情',
|
|
},
|
|
{
|
|
code: 'homework:update',
|
|
resource: 'homework',
|
|
action: 'update',
|
|
name: '更新作业',
|
|
description: '允许更新作业信息',
|
|
},
|
|
{
|
|
code: 'homework:delete',
|
|
resource: 'homework',
|
|
action: 'delete',
|
|
name: '删除作业',
|
|
description: '允许删除作业',
|
|
},
|
|
|
|
// 租户查看权限(所有租户都可以查看租户列表,用于发布赛事选择公开范围等)
|
|
{
|
|
code: 'tenant:read',
|
|
resource: 'tenant',
|
|
action: 'read',
|
|
name: '查看租户',
|
|
description: '允许查看租户列表和详情',
|
|
},
|
|
|
|
// 评委管理权限(简化前缀)
|
|
{
|
|
code: 'judge:create',
|
|
resource: 'judge',
|
|
action: 'create',
|
|
name: '添加评委',
|
|
description: '允许添加评委',
|
|
},
|
|
{
|
|
code: 'judge:read',
|
|
resource: 'judge',
|
|
action: 'read',
|
|
name: '查看评委',
|
|
description: '允许查看评委列表和详情',
|
|
},
|
|
{
|
|
code: 'judge:update',
|
|
resource: 'judge',
|
|
action: 'update',
|
|
name: '更新评委',
|
|
description: '允许更新评委信息',
|
|
},
|
|
{
|
|
code: 'judge:delete',
|
|
resource: 'judge',
|
|
action: 'delete',
|
|
name: '删除评委',
|
|
description: '允许删除评委',
|
|
},
|
|
|
|
// 公告管理权限(简化前缀)
|
|
{
|
|
code: 'notice:create',
|
|
resource: 'notice',
|
|
action: 'create',
|
|
name: '创建公告',
|
|
description: '允许创建公告',
|
|
},
|
|
{
|
|
code: 'notice:read',
|
|
resource: 'notice',
|
|
action: 'read',
|
|
name: '查看公告',
|
|
description: '允许查看公告列表和详情',
|
|
},
|
|
{
|
|
code: 'notice:update',
|
|
resource: 'notice',
|
|
action: 'update',
|
|
name: '更新公告',
|
|
description: '允许更新公告信息',
|
|
},
|
|
{
|
|
code: 'notice:delete',
|
|
resource: 'notice',
|
|
action: 'delete',
|
|
name: '删除公告',
|
|
description: '允许删除公告',
|
|
},
|
|
|
|
// 作品管理权限(简化前缀)
|
|
{
|
|
code: 'work:read',
|
|
resource: 'work',
|
|
action: 'read',
|
|
name: '查看作品',
|
|
description: '允许查看参赛作品',
|
|
},
|
|
];
|
|
|
|
// 超级管理员专属权限(只有超级租户才能拥有)
|
|
const superAdminPermissions = [
|
|
{
|
|
code: 'tenant:create',
|
|
resource: 'tenant',
|
|
action: 'create',
|
|
name: '创建租户',
|
|
description: '允许创建租户(仅超级管理员)',
|
|
},
|
|
{
|
|
code: 'tenant:update',
|
|
resource: 'tenant',
|
|
action: 'update',
|
|
name: '更新租户',
|
|
description: '允许更新租户信息(仅超级管理员)',
|
|
},
|
|
{
|
|
code: 'tenant:delete',
|
|
resource: 'tenant',
|
|
action: 'delete',
|
|
name: '删除租户',
|
|
description: '允许删除租户(仅超级管理员)',
|
|
},
|
|
];
|
|
|
|
/**
|
|
* 仅初始化 admin 角色的权限(不创建用户、角色和菜单)
|
|
*/
|
|
async function initTenantAdminPermissionsOnly(tenantCode: string) {
|
|
try {
|
|
console.log(`🚀 开始为租户 "${tenantCode}" 的 admin 角色初始化权限...\n`);
|
|
|
|
// 1. 查找租户
|
|
console.log(`📋 步骤 1: 查找租户 "${tenantCode}"...`);
|
|
const tenant = await prisma.tenant.findUnique({
|
|
where: { code: tenantCode },
|
|
});
|
|
|
|
if (!tenant) {
|
|
console.error(`❌ 错误: 租户 "${tenantCode}" 不存在!`);
|
|
console.error(' 请先创建租户后再运行此脚本');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (tenant.validState !== 1) {
|
|
console.error(`❌ 错误: 租户 "${tenantCode}" 状态无效!`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const isSuperTenant = tenant.isSuper === 1;
|
|
console.log(`✅ 找到租户: ${tenant.name} (${tenant.code})${isSuperTenant ? ' [超级租户]' : ''}\n`);
|
|
|
|
// 2. 检查 admin 角色是否存在
|
|
console.log(`👤 步骤 2: 检查 admin 角色是否存在...`);
|
|
const adminRole = await prisma.role.findFirst({
|
|
where: {
|
|
tenantId: tenant.id,
|
|
code: 'admin',
|
|
},
|
|
});
|
|
|
|
if (!adminRole) {
|
|
console.error(`❌ 错误: 租户 "${tenantCode}" 的 admin 角色不存在!`);
|
|
console.error(' 请先运行完整初始化脚本创建 admin 角色');
|
|
console.error(` 使用方法: pnpm init:tenant-admin ${tenantCode}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`✅ 找到 admin 角色: ${adminRole.name} (${adminRole.code})\n`);
|
|
|
|
// 3. 初始化租户权限(如果不存在则创建)
|
|
// 超级租户拥有所有权限,普通租户只拥有基础权限
|
|
const allPermissions = isSuperTenant
|
|
? [...permissions, ...superAdminPermissions]
|
|
: permissions;
|
|
|
|
console.log(`📝 步骤 3: 初始化租户权限...${isSuperTenant ? '(包含超级管理员权限)' : ''}`);
|
|
const createdPermissions = [];
|
|
|
|
for (const perm of allPermissions) {
|
|
// 检查权限是否已存在
|
|
const existingPermission = await prisma.permission.findFirst({
|
|
where: {
|
|
tenantId: tenant.id,
|
|
code: perm.code,
|
|
},
|
|
});
|
|
|
|
if (!existingPermission) {
|
|
// 创建权限
|
|
const permission = await prisma.permission.create({
|
|
data: {
|
|
tenantId: tenant.id,
|
|
code: perm.code,
|
|
resource: perm.resource,
|
|
action: perm.action,
|
|
name: perm.name,
|
|
description: perm.description,
|
|
validState: 1,
|
|
},
|
|
});
|
|
createdPermissions.push(permission);
|
|
console.log(` ✓ 创建权限: ${perm.code} - ${perm.name}`);
|
|
} else {
|
|
// 更新现有权限(确保信息是最新的)
|
|
const permission = await prisma.permission.update({
|
|
where: { id: existingPermission.id },
|
|
data: {
|
|
name: perm.name,
|
|
resource: perm.resource,
|
|
action: perm.action,
|
|
description: perm.description,
|
|
validState: 1,
|
|
},
|
|
});
|
|
createdPermissions.push(permission);
|
|
}
|
|
}
|
|
|
|
console.log(`✅ 共确保 ${createdPermissions.length} 个权限存在\n`);
|
|
|
|
// 获取租户的所有有效权限
|
|
const tenantPermissions = await prisma.permission.findMany({
|
|
where: {
|
|
tenantId: tenant.id,
|
|
validState: 1,
|
|
},
|
|
});
|
|
|
|
// 4. 为 admin 角色分配所有权限
|
|
console.log(`🔗 步骤 4: 为 admin 角色分配所有权限...`);
|
|
const existingRolePermissions = await prisma.rolePermission.findMany({
|
|
where: { roleId: adminRole.id },
|
|
select: { permissionId: true },
|
|
});
|
|
const existingPermissionIds = new Set(
|
|
existingRolePermissions.map((rp) => rp.permissionId),
|
|
);
|
|
|
|
let addedCount = 0;
|
|
for (const permission of tenantPermissions) {
|
|
if (!existingPermissionIds.has(permission.id)) {
|
|
await prisma.rolePermission.create({
|
|
data: {
|
|
roleId: adminRole.id,
|
|
permissionId: permission.id,
|
|
},
|
|
});
|
|
addedCount++;
|
|
}
|
|
}
|
|
|
|
if (addedCount > 0) {
|
|
console.log(`✅ 为 admin 角色添加了 ${addedCount} 个权限`);
|
|
console.log(`✅ admin 角色现在拥有 ${tenantPermissions.length} 个权限\n`);
|
|
} else {
|
|
console.log(
|
|
`✅ admin 角色已拥有所有权限(${tenantPermissions.length} 个)\n`,
|
|
);
|
|
}
|
|
|
|
// 5. 验证结果
|
|
console.log('🔍 步骤 5: 验证结果...');
|
|
const roleWithPermissions = await prisma.role.findUnique({
|
|
where: { id: adminRole.id },
|
|
include: {
|
|
permissions: {
|
|
include: {
|
|
permission: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const permissionCodes = new Set<string>();
|
|
roleWithPermissions?.permissions.forEach((rp) => {
|
|
permissionCodes.add(rp.permission.code);
|
|
});
|
|
|
|
console.log(`\n📊 初始化结果:`);
|
|
console.log(` 租户名称: ${tenant.name}`);
|
|
console.log(` 租户编码: ${tenant.code}`);
|
|
console.log(` 角色名称: ${adminRole.name}`);
|
|
console.log(` 角色编码: ${adminRole.code}`);
|
|
console.log(` 权限数量: ${permissionCodes.size}`);
|
|
if (permissionCodes.size > 0) {
|
|
console.log(` 权限列表:`);
|
|
Array.from(permissionCodes)
|
|
.sort()
|
|
.forEach((code) => {
|
|
console.log(` - ${code}`);
|
|
});
|
|
}
|
|
console.log(`\n✅ admin 角色权限初始化完成!`);
|
|
} catch (error) {
|
|
console.error('❌ 初始化失败:', error);
|
|
throw error;
|
|
} finally {
|
|
await prisma.$disconnect();
|
|
}
|
|
}
|
|
|
|
async function initTenantAdmin(tenantCode: string) {
|
|
try {
|
|
console.log(`🚀 开始为租户 "${tenantCode}" 初始化 admin 账号...\n`);
|
|
|
|
// 1. 查找租户
|
|
console.log(`📋 步骤 1: 查找租户 "${tenantCode}"...`);
|
|
const tenant = await prisma.tenant.findUnique({
|
|
where: { code: tenantCode },
|
|
});
|
|
|
|
if (!tenant) {
|
|
console.error(`❌ 错误: 租户 "${tenantCode}" 不存在!`);
|
|
console.error(' 请先创建租户后再运行此脚本');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (tenant.validState !== 1) {
|
|
console.error(`❌ 错误: 租户 "${tenantCode}" 状态无效!`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const isSuperTenant = tenant.isSuper === 1;
|
|
console.log(`✅ 找到租户: ${tenant.name} (${tenant.code})${isSuperTenant ? ' [超级租户]' : ''}\n`);
|
|
|
|
// 2. 检查是否已存在 admin 用户
|
|
console.log(`👤 步骤 2: 检查 admin 用户是否已存在...`);
|
|
const existingAdmin = await prisma.user.findFirst({
|
|
where: {
|
|
tenantId: tenant.id,
|
|
username: 'admin',
|
|
},
|
|
});
|
|
|
|
if (existingAdmin) {
|
|
console.log(`⚠️ 警告: 租户 "${tenantCode}" 已存在 admin 用户`);
|
|
console.log(` 用户ID: ${existingAdmin.id}`);
|
|
console.log(` 用户名: ${existingAdmin.username}`);
|
|
console.log(` 昵称: ${existingAdmin.nickname}`);
|
|
console.log(` 将更新密码和权限...\n`);
|
|
}
|
|
|
|
// 3. 初始化租户权限(如果不存在则创建)
|
|
// 超级租户拥有所有权限,普通租户只拥有基础权限
|
|
const allPermissions = isSuperTenant
|
|
? [...permissions, ...superAdminPermissions]
|
|
: permissions;
|
|
|
|
console.log(`📝 步骤 3: 初始化租户权限...${isSuperTenant ? '(包含超级管理员权限)' : ''}`);
|
|
const createdPermissions = [];
|
|
|
|
for (const perm of allPermissions) {
|
|
// 检查权限是否已存在
|
|
const existingPermission = await prisma.permission.findFirst({
|
|
where: {
|
|
tenantId: tenant.id,
|
|
code: perm.code,
|
|
},
|
|
});
|
|
|
|
if (!existingPermission) {
|
|
// 创建权限
|
|
const permission = await prisma.permission.create({
|
|
data: {
|
|
tenantId: tenant.id,
|
|
code: perm.code,
|
|
resource: perm.resource,
|
|
action: perm.action,
|
|
name: perm.name,
|
|
description: perm.description,
|
|
validState: 1,
|
|
},
|
|
});
|
|
createdPermissions.push(permission);
|
|
console.log(` ✓ 创建权限: ${perm.code} - ${perm.name}`);
|
|
} else {
|
|
// 更新现有权限(确保信息是最新的)
|
|
const permission = await prisma.permission.update({
|
|
where: { id: existingPermission.id },
|
|
data: {
|
|
name: perm.name,
|
|
resource: perm.resource,
|
|
action: perm.action,
|
|
description: perm.description,
|
|
validState: 1,
|
|
},
|
|
});
|
|
createdPermissions.push(permission);
|
|
}
|
|
}
|
|
|
|
console.log(`✅ 共确保 ${createdPermissions.length} 个权限存在\n`);
|
|
|
|
// 获取租户的所有有效权限
|
|
const tenantPermissions = await prisma.permission.findMany({
|
|
where: {
|
|
tenantId: tenant.id,
|
|
validState: 1,
|
|
},
|
|
});
|
|
|
|
// 4. 创建或获取 admin 角色
|
|
console.log(`👤 步骤 4: 创建或获取 admin 角色...`);
|
|
let adminRole = await prisma.role.findFirst({
|
|
where: {
|
|
tenantId: tenant.id,
|
|
code: 'admin',
|
|
},
|
|
});
|
|
|
|
if (!adminRole) {
|
|
adminRole = await prisma.role.create({
|
|
data: {
|
|
tenantId: tenant.id,
|
|
name: '管理员',
|
|
code: 'admin',
|
|
description: '租户管理员角色,拥有租户的所有权限',
|
|
validState: 1,
|
|
},
|
|
});
|
|
console.log(
|
|
`✅ admin 角色已创建: ${adminRole.name} (${adminRole.code})\n`,
|
|
);
|
|
} else {
|
|
// 更新角色信息
|
|
adminRole = await prisma.role.update({
|
|
where: { id: adminRole.id },
|
|
data: {
|
|
name: '管理员',
|
|
description: '租户管理员角色,拥有租户的所有权限',
|
|
validState: 1,
|
|
},
|
|
});
|
|
console.log(
|
|
`✅ admin 角色已更新: ${adminRole.name} (${adminRole.code})\n`,
|
|
);
|
|
}
|
|
|
|
// 5. 为 admin 角色分配所有权限
|
|
console.log(`🔗 步骤 5: 为 admin 角色分配所有权限...`);
|
|
const existingRolePermissions = await prisma.rolePermission.findMany({
|
|
where: { roleId: adminRole.id },
|
|
select: { permissionId: true },
|
|
});
|
|
const existingPermissionIds = new Set(
|
|
existingRolePermissions.map((rp) => rp.permissionId),
|
|
);
|
|
|
|
let addedCount = 0;
|
|
for (const permission of tenantPermissions) {
|
|
if (!existingPermissionIds.has(permission.id)) {
|
|
await prisma.rolePermission.create({
|
|
data: {
|
|
roleId: adminRole.id,
|
|
permissionId: permission.id,
|
|
},
|
|
});
|
|
addedCount++;
|
|
}
|
|
}
|
|
|
|
if (addedCount > 0) {
|
|
console.log(`✅ 为 admin 角色添加了 ${addedCount} 个权限`);
|
|
console.log(`✅ admin 角色现在拥有 ${tenantPermissions.length} 个权限\n`);
|
|
} else {
|
|
console.log(
|
|
`✅ admin 角色已拥有所有权限(${tenantPermissions.length} 个)\n`,
|
|
);
|
|
}
|
|
|
|
// 6. 创建或更新 admin 用户
|
|
console.log(`👤 步骤 6: 创建或更新 admin 用户...`);
|
|
const password = `admin@${tenantCode}`;
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
|
|
let adminUser;
|
|
if (existingAdmin) {
|
|
adminUser = await prisma.user.update({
|
|
where: { id: existingAdmin.id },
|
|
data: {
|
|
password: hashedPassword,
|
|
nickname: '管理员',
|
|
email: `admin@${tenantCode}.com`,
|
|
validState: 1,
|
|
},
|
|
});
|
|
console.log(
|
|
`✅ 用户已更新: ${adminUser.username} (${adminUser.nickname})\n`,
|
|
);
|
|
} else {
|
|
adminUser = await prisma.user.create({
|
|
data: {
|
|
tenantId: tenant.id,
|
|
username: 'admin',
|
|
password: hashedPassword,
|
|
nickname: '管理员',
|
|
email: `admin@${tenantCode}.com`,
|
|
validState: 1,
|
|
},
|
|
});
|
|
console.log(
|
|
`✅ 用户已创建: ${adminUser.username} (${adminUser.nickname})\n`,
|
|
);
|
|
}
|
|
|
|
// 7. 为 admin 用户分配 admin 角色
|
|
console.log(`🔗 步骤 7: 为 admin 用户分配 admin 角色...`);
|
|
const existingUserRole = await prisma.userRole.findUnique({
|
|
where: {
|
|
userId_roleId: {
|
|
userId: adminUser.id,
|
|
roleId: adminRole.id,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!existingUserRole) {
|
|
await prisma.userRole.create({
|
|
data: {
|
|
userId: adminUser.id,
|
|
roleId: adminRole.id,
|
|
},
|
|
});
|
|
console.log(`✅ 角色分配成功\n`);
|
|
} else {
|
|
console.log(`✅ 用户已拥有 admin 角色\n`);
|
|
}
|
|
|
|
// 8. 验证结果
|
|
console.log('🔍 步骤 8: 验证结果...');
|
|
const userWithRoles = await prisma.user.findUnique({
|
|
where: { id: adminUser.id },
|
|
include: {
|
|
roles: {
|
|
include: {
|
|
role: {
|
|
include: {
|
|
permissions: {
|
|
include: {
|
|
permission: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const roleCodes = userWithRoles?.roles.map((ur) => ur.role.code) || [];
|
|
const permissionCodes = new Set<string>();
|
|
userWithRoles?.roles.forEach((ur) => {
|
|
ur.role.permissions.forEach((rp) => {
|
|
permissionCodes.add(rp.permission.code);
|
|
});
|
|
});
|
|
|
|
console.log(`\n📊 初始化结果:`);
|
|
console.log(` 租户名称: ${tenant.name}`);
|
|
console.log(` 租户编码: ${tenant.code}`);
|
|
console.log(` 用户名: ${adminUser.username}`);
|
|
console.log(` 昵称: ${adminUser.nickname}`);
|
|
console.log(` 密码: ${password}`);
|
|
console.log(` 角色: ${roleCodes.join(', ')}`);
|
|
console.log(` 权限数量: ${permissionCodes.size}`);
|
|
if (permissionCodes.size > 0) {
|
|
console.log(` 权限列表:`);
|
|
Array.from(permissionCodes)
|
|
.sort()
|
|
.forEach((code) => {
|
|
console.log(` - ${code}`);
|
|
});
|
|
}
|
|
console.log(`\n✅ 租户 admin 账号初始化完成!`);
|
|
console.log(`\n💡 现在可以使用以下凭据登录:`);
|
|
console.log(` 租户编码: ${tenant.code}`);
|
|
console.log(` 用户名: ${adminUser.username}`);
|
|
console.log(` 密码: ${password}`);
|
|
} catch (error) {
|
|
console.error('❌ 初始化失败:', error);
|
|
throw error;
|
|
} finally {
|
|
await prisma.$disconnect();
|
|
}
|
|
}
|
|
|
|
// 获取命令行参数
|
|
// 支持两种调用方式:
|
|
// 1. pnpm init:tenant-admin tenant1 --permissions-only
|
|
// 2. pnpm init:tenant-admin:permissions tenant1 (--permissions-only 在 argv[2])
|
|
let tenantCode: string | undefined;
|
|
let permissionsOnly = false;
|
|
|
|
// 检查是否有 --permissions-only 标志
|
|
if (process.argv[2] === '--permissions-only') {
|
|
permissionsOnly = true;
|
|
tenantCode = process.argv[3];
|
|
} else if (process.argv[3] === '--permissions-only') {
|
|
permissionsOnly = true;
|
|
tenantCode = process.argv[2];
|
|
} else {
|
|
tenantCode = process.argv[2];
|
|
}
|
|
|
|
if (!tenantCode) {
|
|
console.error('❌ 错误: 请提供租户编码作为参数');
|
|
console.error(' 使用方法:');
|
|
console.error(' 完整初始化: pnpm init:tenant-admin <租户编码>');
|
|
console.error(
|
|
' 仅初始化权限: pnpm init:tenant-admin <租户编码> --permissions-only',
|
|
);
|
|
console.error(' 或: pnpm init:tenant-admin:permissions <租户编码>');
|
|
console.error(' 示例:');
|
|
console.error(' pnpm init:tenant-admin tenant1');
|
|
console.error(' pnpm init:tenant-admin tenant1 --permissions-only');
|
|
console.error(' pnpm init:tenant-admin:permissions tenant1');
|
|
process.exit(1);
|
|
}
|
|
|
|
// 执行初始化
|
|
const initFunction = permissionsOnly
|
|
? initTenantAdminPermissionsOnly
|
|
: initTenantAdmin;
|
|
|
|
initFunction(tenantCode)
|
|
.then(() => {
|
|
console.log('\n🎉 初始化脚本执行完成!');
|
|
process.exit(0);
|
|
})
|
|
.catch((error) => {
|
|
console.error('\n💥 初始化脚本执行失败:', error);
|
|
process.exit(1);
|
|
});
|