背景
开源版本的FastGPT默认只有一个超级用户root
,为了更好地管理应用和知识库,可以通过操作MongoDB数据库来增加新的用户和团队。
所需环境
- 已安装并运行的FastGPT实例
- MongoDB客户端工具(如Mongo Shell或Robo 3T等)
操作步骤
-
启动MongoDB客户端
在命令行中启动MongoDB客户端,并连接到FastGPT使用的数据库。 -
增加新用户
向users
集合中插入一条新记录,这条记录包含新用户的详细信息,包括用户名、密码等字段。db.getCollection("users").insert({ username: "demo", password: "756bc47cb5215dc3329ca7e1f7be33a2dad68990bb94b76d90aa07f4e44a233a", // 请使用安全的方式加密密码 status: "active", avatar: "/icon/human.svg", balance: NumberInt("100000"), promotionRate: NumberInt("10"), timezone: "Asia/Shanghai", createTime: new ISODate() });
-
创建新团队
向teams
集合中插入一条新记录,这条记录包含团队的基本信息。db.getCollection("teams").insert({ name: "New Team", ownerId: ObjectId("65916f1a52ac39c5d10bb505"), // 新创建用户的ObjectId avatar: "/icon/newteam.svg", createTime: new ISODate(), balance: NumberInt("100000"), maxSize: NumberInt("10"), __v: NumberInt("0") });
-
将用户加入团队
向team_members
集合中插入一条新记录,这条记录关联了用户和团队。db.getCollection("team_members").insert({ teamId: ObjectId("65916f3952ac39c5d10bb506"), // 团队的ObjectId userId: ObjectId("65916f1a52ac39c5d10bb505"), // 用户的ObjectId name: "Owner", role: "owner", status: "active", createTime: new ISODate(), defaultTeam: true });
更方便的方式
有了这么多AI,当然可以直接增加管理页面
- user.tsx
import React, { useState, useEffect } from 'react';
import {
Box,
Button,
FormControl,
FormLabel,
Input,
VStack,
HStack,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
useDisclosure,
useToast,
Select,
} from '@chakra-ui/react';
import { serviceSideProps } from '@/web/common/utils/i18n';
const fetchUsers = async () => {
const response = await fetch('/api/admin/users');
if (!response.ok) throw new Error('Failed to fetch users');
return response.json();
};
const addUser = async (userData) => {
const response = await fetch('/api/admin/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData),
});
if (!response.ok) throw new Error('Failed to add user');
return response.json();
};
const updateUser = async (userId, userData) => {
const response = await fetch(`/api/admin/users/${userId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData),
});
if (!response.ok) throw new Error('Failed to update user');
return response.json();
};
const deleteUser = async (userId) => {
const response = await fetch(`/api/admin/users/${userId}`, { method: 'DELETE' });
if (!response.ok) throw new Error('Failed to delete user');
return response.json();
};
export default function UserManagement() {
const [users, setUsers] = useState([]);
const [currentUser, setCurrentUser] = useState(null);
const { isOpen, onOpen, onClose } = useDisclosure();
const toast = useToast();
useEffect(() => {
loadUsers();
}, []);
const loadUsers = async () => {
try {
const fetchedUsers = await fetchUsers();
setUsers(fetchedUsers);
} catch (error) {
toast({
title: "Failed to load users.",
description: error.message,
status: "error",
duration: 3000,
isClosable: true,
});
}
};
const handleAddUser = async (userData) => {
try {
const newUser = await addUser(userData);
setUsers([...users, newUser.user]);
onClose();
toast({
title: "User added.",
status: "success",
duration: 2000,
isClosable: true,
});
} catch (error) {
toast({
title: "Failed to add user.",
description: error.message,
status: "error",
duration: 3000,
isClosable: true,
});
}
};
const handleUpdateUser = async (userData) => {
try {
const updatedUser = await updateUser(currentUser._id, userData);
setUsers(users.map(user => user._id === updatedUser._id ? updatedUser : user));
onClose();
toast({
title: "User updated.",
status: "success",
duration: 2000,
isClosable: true,
});
} catch (error) {
toast({
title: "Failed to update user.",
description: error.message,
status: "error",
duration: 3000,
isClosable: true,
});
}
};
const handleDeleteUser = async (userId) => {
try {
await deleteUser(userId);
setUsers(users.filter(user => user._id !== userId));
toast({
title: "User deleted.",
status: "success",
duration: 2000,
isClosable: true,
});
} catch (error) {
toast({
title: "Failed to delete user.",
description: error.message,
status: "error",
duration: 3000,
isClosable: true,
});
}
};
const openAddModal = () => {
setCurrentUser(null);
onOpen();
};
const openEditModal = (user) => {
setCurrentUser(user);
onOpen();
};
return (
<Box p={5}>
<Button onClick={openAddModal} colorScheme="blue" mb={4}>
Add User
</Button>
<Table variant="simple">
<Thead>
<Tr>
<Th>Username</Th>
<Th>Status</Th>
<Th>Balance</Th>
<Th>Promotion Rate</Th>
<Th>Timezone</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{users.map((user) => (
<Tr key={user._id}>
<Td>{user.username}</Td>
<Td>{user.status}</Td>
<Td>{user.balance}</Td>
<Td>{user.promotionRate}%</Td>
<Td>{user.timezone}</Td>
<Td>
<HStack spacing={2}>
<Button size="sm" onClick={() => openEditModal(user)}>
Edit
</Button>
<Button size="sm" colorScheme="red" onClick={() => handleDeleteUser(user._id)}>
Delete
</Button>
</HStack>
</Td>
</Tr>
))}
</Tbody>
</Table>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>{currentUser ? 'Edit User' : 'Add User'}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<UserForm user={currentUser} onSubmit={currentUser ? handleUpdateUser : handleAddUser} />
</ModalBody>
</ModalContent>
</Modal>
</Box>
);
}
function UserForm({ user, onSubmit }) {
const [formData, setFormData] = useState(user || {
username: '',
password: '',
status: 'active',
balance: 100000,
promotionRate: 10,
timezone: 'Asia/Shanghai'
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(formData);
};
return (
<form onSubmit={handleSubmit}>
<VStack spacing={4}>
<FormControl isRequired>
<FormLabel>Username</FormLabel>
<Input name="username" value={formData.username} onChange={handleChange} />
</FormControl>
{!user && (
<FormControl isRequired>
<FormLabel>Password</FormLabel>
<Input name="password" type="password" value={formData.password} onChange={handleChange} />
</FormControl>
)}
<FormControl>
<FormLabel>Status</FormLabel>
<Select name="status" value={formData.status} onChange={handleChange}>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</Select>
</FormControl>
<FormControl>
<FormLabel>Balance</FormLabel>
<Input name="balance" type="number" value={formData.balance} onChange={handleChange} />
</FormControl>
<FormControl>
<FormLabel>Promotion Rate (%)</FormLabel>
<Input name="promotionRate" type="number" value={formData.promotionRate} onChange={handleChange} />
</FormControl>
<FormControl>
<FormLabel>Timezone</FormLabel>
<Input name="timezone" value={formData.timezone} onChange={handleChange} />
</FormControl>
<Button type="submit" colorScheme="blue">
{user ? 'Update' : 'Add'} User
</Button>
</VStack>
</form>
);
}
export async function getServerSideProps(content: any) {
return {
props: {
...(await serviceSideProps(content, ['publish', 'user']))
}
};
}
- [id].js
import { connectToDatabase } from '@/service/mongo';
import { hashStr } from '@fastgpt/global/common/string/tools';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
export default async function handler(req, res) {
const {
query: { id },
method,
} = req;
await connectToDatabase();
switch (method) {
case 'GET':
try {
const user = await MongoUser.findById(id).select('-password');
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.status(200).json(user);
} catch (error) {
res.status(500).json({ error: 'Error fetching user' });
}
break;
case 'PUT':
try {
const { username, status, avatar, balance, promotionRate, timezone, password } = req.body;
const updateDoc = {
username,
status,
avatar,
balance,
promotionRate,
timezone,
};
if (password) {
updateDoc.password = hashStr(password);
}
const user = await MongoUser.findByIdAndUpdate(id, updateDoc, {
new: true,
runValidators: true,
}).select('-password');
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.status(200).json(user);
} catch (error) {
res.status(500).json({ error: 'Error updating user' });
}
break;
case 'DELETE':
try {
const deletedUser = await MongoUser.findByIdAndDelete(id);
if (!deletedUser) {
return res.status(404).json({ error: 'User not found' });
}
// Remove user from teams
await MongoTeamMember.deleteMany({ userId: id });
// Delete teams owned by this user
const ownedTeams = await MongoTeam.find({ ownerId: id });
for (const team of ownedTeams) {
await MongoTeamMember.deleteMany({ teamId: team._id });
await MongoTeam.findByIdAndDelete(team._id);
}
res.status(200).json({ success: true });
} catch (error) {
res.status(500).json({ error: 'Error deleting user' });
}
break;
default:
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
- index.js
// pages/api/users/index.js
import { connectToDatabase } from '@/service/mongo';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { hashStr } from '@fastgpt/global/common/string/tools';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
export default async function handler(req, res) {
const { method } = req;
await connectToDatabase();
const { userId } = await authCert({ req, authToken: true });
const curUser = await MongoUser.findById(userId).select('-password');
if (curUser.username !== 'root') {
return res.status(200).json([]);
}
switch (method) {
case 'GET':
try {
console.log('GET /api/users', MongoUser);
const users = await MongoUser.find({ username: { $ne: 'root' } }).select('-password');
res.status(200).json(users);
} catch (error) {
console.log(error);
res.status(500).json({ error: 'Error fetching users' });
}
break;
case 'POST':
try {
const { username, password, status, avatar, balance, promotionRate, timezone } = req.body;
// Check if user already exists
const existingUser = await MongoUser.findOne({ username });
if (existingUser) {
return res.status(400).json({ error: 'Username already exists' });
}
console.log('POST /api/users', password, hashStr(password), hashStr(hashStr(password)));
// Hash password
const hashedPassword = hashStr(password);
const newUser = new MongoUser({
username,
password: hashedPassword,
status,
avatar,
balance,
promotionRate,
timezone,
});
const savedUser = await newUser.save();
// Create a new team for the user
const team = new MongoTeam({
name: `${username}'s Team`,
ownerId: savedUser._id,
});
const savedTeam = await team.save();
// Add user to team_members
const teamMember = new MongoTeamMember({
teamId: savedTeam._id,
userId: savedUser._id,
name: 'Owner',
role: 'owner',
defaultTeam: true,
});
await teamMember.save();
res.status(201).json({ success: true, user: savedUser.toObject({ versionKey: false, transform: (doc, ret) => { delete ret.password; return ret; } }) });
} catch (error) {
console.log(error);
res.status(500).json({ error: 'Error creating user' });
}
break;
default:
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
// pages/api/users/[id].js
- 访问/admin/user