开源版本的FastGPT默认只有一个超级用户root,为了更好地管理应用和知识库,可以通过操作MongoDB数据库来增加新的用户和团队。
- 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()
- });
-
- 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")
- });
-
- 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,当然可以直接增加管理页面
- 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']))
- }
- };
- }
-
-
- 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`);
- }
- }
-
- // 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
-
-