527 lines
13 KiB
TypeScript
527 lines
13 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: '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: '允许修改用户密码',
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
// 根据路由配置定义的菜单数据
|
||
|
|
const menus = [
|
||
|
|
// 顶级菜单:仪表盘
|
||
|
|
{
|
||
|
|
name: '仪表盘',
|
||
|
|
path: '/dashboard',
|
||
|
|
icon: 'DashboardOutlined',
|
||
|
|
component: 'dashboard/Index',
|
||
|
|
parentId: null,
|
||
|
|
sort: 1,
|
||
|
|
},
|
||
|
|
// 父菜单:系统管理
|
||
|
|
{
|
||
|
|
name: '系统管理',
|
||
|
|
path: '/system',
|
||
|
|
icon: 'SettingOutlined',
|
||
|
|
component: null, // 父菜单不需要组件
|
||
|
|
parentId: null,
|
||
|
|
sort: 10,
|
||
|
|
children: [
|
||
|
|
{
|
||
|
|
name: '用户管理',
|
||
|
|
path: '/system/users',
|
||
|
|
icon: 'UserOutlined',
|
||
|
|
component: 'system/users/Index',
|
||
|
|
sort: 1,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: '角色管理',
|
||
|
|
path: '/system/roles',
|
||
|
|
icon: 'TeamOutlined',
|
||
|
|
component: 'system/roles/Index',
|
||
|
|
sort: 2,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: '菜单管理',
|
||
|
|
path: '/system/menus',
|
||
|
|
icon: 'MenuOutlined',
|
||
|
|
component: 'system/menus/Index',
|
||
|
|
sort: 3,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: '数据字典',
|
||
|
|
path: '/system/dict',
|
||
|
|
icon: 'BookOutlined',
|
||
|
|
component: 'system/dict/Index',
|
||
|
|
sort: 4,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: '系统配置',
|
||
|
|
path: '/system/config',
|
||
|
|
icon: 'ToolOutlined',
|
||
|
|
component: 'system/config/Index',
|
||
|
|
sort: 5,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: '日志记录',
|
||
|
|
path: '/system/logs',
|
||
|
|
icon: 'FileTextOutlined',
|
||
|
|
component: 'system/logs/Index',
|
||
|
|
sort: 6,
|
||
|
|
},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
async function initAdmin() {
|
||
|
|
try {
|
||
|
|
console.log('🚀 开始初始化超级管理员...\n');
|
||
|
|
|
||
|
|
// 1. 创建或获取所有权限
|
||
|
|
console.log('📝 步骤 1: 创建基础权限...');
|
||
|
|
const createdPermissions = [];
|
||
|
|
for (const perm of permissions) {
|
||
|
|
const permission = await prisma.permission.upsert({
|
||
|
|
where: { code: perm.code },
|
||
|
|
update: perm,
|
||
|
|
create: perm,
|
||
|
|
});
|
||
|
|
createdPermissions.push(permission);
|
||
|
|
console.log(` ✓ ${perm.code} - ${perm.name}`);
|
||
|
|
}
|
||
|
|
console.log(`✅ 共创建/更新 ${createdPermissions.length} 个权限\n`);
|
||
|
|
|
||
|
|
// 2. 创建或获取超级管理员角色
|
||
|
|
console.log('👤 步骤 2: 创建超级管理员角色...');
|
||
|
|
const adminRole = await prisma.role.upsert({
|
||
|
|
where: { code: 'super_admin' },
|
||
|
|
update: {
|
||
|
|
name: '超级管理员',
|
||
|
|
description: '拥有系统所有权限的超级管理员角色',
|
||
|
|
},
|
||
|
|
create: {
|
||
|
|
name: '超级管理员',
|
||
|
|
code: 'super_admin',
|
||
|
|
description: '拥有系统所有权限的超级管理员角色',
|
||
|
|
permissions: {
|
||
|
|
create: createdPermissions.map((perm) => ({
|
||
|
|
permission: { connect: { id: perm.id } },
|
||
|
|
})),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
console.log(
|
||
|
|
`✅ 超级管理员角色已创建/更新: ${adminRole.name} (${adminRole.code})\n`,
|
||
|
|
);
|
||
|
|
|
||
|
|
// 3. 创建或获取 admin 用户
|
||
|
|
console.log('👤 步骤 3: 创建 admin 用户...');
|
||
|
|
const hashedPassword = await bcrypt.hash('cms@admin', 10);
|
||
|
|
|
||
|
|
const adminUser = await prisma.user.upsert({
|
||
|
|
where: { username: 'admin' },
|
||
|
|
update: {
|
||
|
|
password: hashedPassword,
|
||
|
|
nickname: '超级管理员',
|
||
|
|
validState: 1,
|
||
|
|
},
|
||
|
|
create: {
|
||
|
|
username: 'admin',
|
||
|
|
password: hashedPassword,
|
||
|
|
nickname: '超级管理员',
|
||
|
|
email: 'admin@example.com',
|
||
|
|
validState: 1,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
console.log(
|
||
|
|
`✅ 用户已创建/更新: ${adminUser.username} (${adminUser.nickname})\n`,
|
||
|
|
);
|
||
|
|
|
||
|
|
// 4. 给 admin 用户分配超级管理员角色
|
||
|
|
console.log('🔗 步骤 4: 分配角色...');
|
||
|
|
await prisma.userRole.upsert({
|
||
|
|
where: {
|
||
|
|
userId_roleId: {
|
||
|
|
userId: adminUser.id,
|
||
|
|
roleId: adminRole.id,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
update: {},
|
||
|
|
create: {
|
||
|
|
user: { connect: { id: adminUser.id } },
|
||
|
|
role: { connect: { id: adminRole.id } },
|
||
|
|
},
|
||
|
|
});
|
||
|
|
console.log(`✅ 角色分配成功\n`);
|
||
|
|
|
||
|
|
// 5. 初始化菜单数据
|
||
|
|
console.log('📋 步骤 5: 初始化菜单数据...');
|
||
|
|
|
||
|
|
// 递归创建菜单
|
||
|
|
async function createMenu(menuData: any, parentId: number | null = null) {
|
||
|
|
const { children, ...menuFields } = menuData;
|
||
|
|
|
||
|
|
// 查找是否已存在相同名称和父菜单的菜单
|
||
|
|
const existingMenu = await prisma.menu.findFirst({
|
||
|
|
where: {
|
||
|
|
name: menuFields.name,
|
||
|
|
parentId: parentId,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
let menu;
|
||
|
|
if (existingMenu) {
|
||
|
|
// 更新现有菜单
|
||
|
|
menu = await prisma.menu.update({
|
||
|
|
where: { id: existingMenu.id },
|
||
|
|
data: {
|
||
|
|
name: menuFields.name,
|
||
|
|
path: menuFields.path || null,
|
||
|
|
icon: menuFields.icon || null,
|
||
|
|
component: menuFields.component || null,
|
||
|
|
parentId: parentId,
|
||
|
|
sort: menuFields.sort || 0,
|
||
|
|
validState: 1,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
// 创建新菜单
|
||
|
|
menu = await prisma.menu.create({
|
||
|
|
data: {
|
||
|
|
name: menuFields.name,
|
||
|
|
path: menuFields.path || null,
|
||
|
|
icon: menuFields.icon || null,
|
||
|
|
component: menuFields.component || null,
|
||
|
|
parentId: parentId,
|
||
|
|
sort: menuFields.sort || 0,
|
||
|
|
validState: 1,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 如果有子菜单,递归创建
|
||
|
|
if (children && children.length > 0) {
|
||
|
|
for (const child of children) {
|
||
|
|
await createMenu(child, menu.id);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return menu;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 创建所有菜单
|
||
|
|
for (const menu of menus) {
|
||
|
|
await createMenu(menu);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 统计菜单数量
|
||
|
|
const menuCount = await prisma.menu.count();
|
||
|
|
const topLevelMenuCount = await prisma.menu.count({
|
||
|
|
where: { parentId: null },
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log(
|
||
|
|
`✅ 菜单初始化完成: 共 ${menuCount} 个菜单(${topLevelMenuCount} 个顶级菜单)\n`,
|
||
|
|
);
|
||
|
|
|
||
|
|
// 6. 验证结果
|
||
|
|
console.log('🔍 步骤 6: 验证结果...');
|
||
|
|
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(` 用户名: ${adminUser.username}`);
|
||
|
|
console.log(` 昵称: ${adminUser.nickname}`);
|
||
|
|
console.log(` 密码: cms@admin`);
|
||
|
|
console.log(` 角色: ${roleCodes.join(', ')}`);
|
||
|
|
console.log(` 权限数量: ${permissionCodes.size}`);
|
||
|
|
console.log(` 菜单数量: ${menuCount} (${topLevelMenuCount} 个顶级菜单)`);
|
||
|
|
console.log(`\n✅ 超级管理员和菜单数据初始化完成!`);
|
||
|
|
console.log(`\n💡 现在可以使用以下凭据登录:`);
|
||
|
|
console.log(` 用户名: admin`);
|
||
|
|
console.log(` 密码: cms@admin`);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('❌ 初始化失败:', error);
|
||
|
|
throw error;
|
||
|
|
} finally {
|
||
|
|
await prisma.$disconnect();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 执行初始化
|
||
|
|
initAdmin()
|
||
|
|
.then(() => {
|
||
|
|
console.log('\n🎉 初始化脚本执行完成!');
|
||
|
|
process.exit(0);
|
||
|
|
})
|
||
|
|
.catch((error) => {
|
||
|
|
console.error('\n💥 初始化脚本执行失败:', error);
|
||
|
|
process.exit(1);
|
||
|
|
});
|