Commit 23786158 by stakeswky Committed by GitHub

后台 (#77)

parent ba9d9c3d
This diff is collapsed. Click to expand it.
## fastgpt-admin
原作地址:https://github.com/c121914yu/FastGPT/
## 项目原理
使用tushan项目做前端,然后构造了一个与mongodb做沟通的API做后端,可以做到创建、修改和删除用户
## 使用方法
1. 修改根目录下的server.js文件中的mongodb数据库连接地址。
2. pnpm i && pnpm dev
3. 默认账号密码为tushan
## 可能会有的功能
1. 对接数据库中的其他数据
2. tokens充值功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tushan</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
{
"name": "kbgpt-deafult",
"private": true,
"version": "0.0.0",
"type": "module",
"author": "anonymous",
"scripts": {
"dev": "concurrently \"vite\" \"npm run start:api\"",
"build": "tsc && vite build",
"preview": "vite preview",
"start:api": "node server.js"
},
"dependencies": {
"concurrently": "^8.1.0",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^7.2.2",
"react": "^18.2.0",
"react-admin": "^4.11.0",
"react-dom": "^18.2.0",
"tushan": "^0.2.13"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^3.1.0",
"typescript": "^4.9.3",
"vite": "^4.2.1"
}
}
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28 88L49.5 57L118.5 29.5L248 51L323.5 122.5L360.5 324L301 421.5L164.5 412.5L118.5 324L127.5 225.5L143.5 184.5L151.5 130.5L127.5 95L82.5 80L49.5 95L28 88Z" fill="#DFDFDF"/>
<path d="M144.734 22.04C139.186 22.0047 133.638 22.1568 128.1 22.496C84.33 25.196 40.5 49 24.238 67.492C7.97598 85.984 4 91.601 4 91.601C4 91.601 34.922 98.392 57 97.5C79.078 96.608 111.355 88.82 127.692 104.564C144.032 120.309 151.428 146.017 135.232 175.709C116.062 210.852 102.516 271.862 115.086 332.235C127.656 392.609 168.054 451.995 254.814 478.007C288.29 488.043 333.639 494.757 376.459 485.673C420.966 476.885 472.309 450.915 483.351 422.563C474.101 431.448 463.911 437.703 453.149 442.353C471.455 421.433 484.884 392.621 489.939 354.179L492.469 334.939L476.147 345.435C465.644 352.19 455.562 358.838 446.054 363.831C448.692 357.959 451.092 350.611 453.784 341.054C442.687 356.244 430.054 366.409 415.186 372.526C405.952 372.023 396.833 367.659 385.976 356.429C374.618 344.682 367.856 324.334 363.513 298.763C359.169 273.191 357.053 242.836 352.845 211.886C344.425 149.984 326.933 84.013 263.105 50.851C226.15 31.651 184.013 22.274 144.733 22.038L144.734 22.04ZM144.611 40.05C181.073 40.305 220.721 49.115 254.808 66.824C311.201 96.124 326.802 153.964 335.011 214.312C339.115 244.487 341.197 274.866 345.769 301.777C347.085 309.53 348.604 317.019 350.462 324.162C335.014 324.202 323.208 315.855 308.758 299.445C316.143 329.855 320.748 335.979 334.463 354.995C306.243 346.76 273.823 320.255 253.513 290.932C250.239 330.979 273.736 362.506 286.788 374.862C261.612 360.666 226.075 333.326 202.165 286.207C201.149 327.633 214.095 373.939 238.615 402.672C204.1 391.136 173.645 303.2 153.195 275.039C140.155 308.256 150.247 364.124 169.267 405.161C149.639 382.323 138.38 355.786 132.712 328.565C121.188 273.223 134.462 214.718 151.037 184.327C170.587 148.485 161.952 112.577 140.187 91.601C118.419 70.625 66 81 53.633 83.286C41.266 85.572 31 83.286 31 83.286C31 83.286 41.3371 75.1684 48 70C74.6656 49.3155 88.786 42.954 129.211 40.461C134.263 40.149 139.406 40.011 144.614 40.047L144.611 40.05Z" fill="url(#paint0_linear_1104_3)"/>
<defs>
<linearGradient id="paint0_linear_1104_3" x1="384.5" y1="480" x2="256" y2="256" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF6011"/>
<stop offset="1" stop-color="#FF9411"/>
</linearGradient>
</defs>
</svg>
import express from 'express';
import mongoose from 'mongoose';
import cors from 'cors';
const app = express();
app.use(cors());
app.use(express.json());
const mongoURI = '';//在这里填入mongodb的连接地址
mongoose.connect(mongoURI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connected to MongoDB successfully!'))
.catch((err) => console.log(`Error connecting to MongoDB: ${err}`));
const userSchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
username: String,
password: String,
balance: Number,
promotion: {
rate: Number,
},
openaiKey: String,
avatar: String,
createTime: Date,
});
// 新增: 定义 pays 模型
const paySchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
userId: mongoose.Schema.Types.ObjectId,
price: Number,
orderId: String,
status: String,
createTime: Date,
__v: Number,
});
// 新增: 定义 kb 模型
const kbSchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
userId: mongoose.Schema.Types.ObjectId,
avatar: String,
name: String,
tags: [String],
updateTime: Date,
__v: Number,
});
const modelSchema = new mongoose.Schema({
userId: mongoose.Schema.Types.ObjectId,
name: String,
avatar: String,
status: String,
chat: {
relatedKbs: [mongoose.Schema.Types.ObjectId],
searchMode: String,
systemPrompt: String,
temperature: Number,
chatModel: String
},
share: {
isShare: Boolean,
isShareDetail: Boolean,
intro: String,
collection: Number
},
security: {
domain: [String],
contextMaxLen: Number,
contentMaxLen: Number,
expiredTime: Number,
maxLoadAmount: Number
},
updateTime: Date
});
const Model = mongoose.model('Model', modelSchema);
const Kb = mongoose.model('Kb', kbSchema);
const User = mongoose.model('User', userSchema, 'users');
const Pay = mongoose.model('Pay', paySchema, 'pays');
// 获取用户列表
app.get('/users', async (req, res) => {
try {
const start = parseInt(req.query._start) || 0;
const end = parseInt(req.query._end) || 20;
const order = req.query._order === 'DESC' ? -1 : 1;
const sort = req.query._sort || '_id';
const usersRaw = await User.find()
.skip(start)
.limit(end - start)
.sort({ [sort]: order });
const users = usersRaw.map((user) => {
const obj = user.toObject();
obj.id = obj._id;
delete obj._id;
return obj;
});
const totalCount = await User.countDocuments();
res.header('Access-Control-Expose-Headers', 'X-Total-Count');
res.header('X-Total-Count', totalCount);
res.json(users);
} catch (err) {
console.log(`Error fetching users: ${err}`);
res.status(500).json({ error: 'Error fetching users' });
}
});
// 创建用户
app.post('/users', async (req, res) => {
try {
const { username, password, balance, promotion, openaiKey = '', avatar = '/icon/human.png' } = req.body;
if (!username || !password || !balance) {
return res.status(400).json({ error: 'Invalid user information' });
}
const existingUser = await User.findOne({ username });
if (existingUser) {
return res.status(400).json({ error: 'Username already exists' });
}
const user = new User({
_id: new mongoose.Types.ObjectId(),
username,
password,
balance,
promotion: {
rate: promotion?.rate || 0,
},
openaiKey,
avatar,
createTime: new Date(),
});
const result = await user.save();
res.json(result);
} catch (err) {
console.log(`Error creating user: ${err}`);
res.status(500).json({ error: 'Error creating user' });
}
});
// 修改用户信息
app.put('/users/:id', async (req, res) => {
try {
const _id = req.params.id;
const result = await User.updateOne({ _id: _id }, { $set: req.body });
res.json(result);
} catch (err) {
console.log(`Error updating user: ${err}`);
res.status(500).json({ error: 'Error updating user' });
}
});
// 删除用户
app.delete('/users/:id', async (req, res) => {
try {
const _id = req.params.id;
if (!mongoose.Types.ObjectId.isValid(_id)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
const result = await User.deleteOne({ _id: _id });
res.json(result);
} catch (err) {
console.log(`Error deleting user: ${err}`);
res.status(500).json({ error: 'Error deleting user' });
}
});
// 新增: 获取 pays 列表
app.get('/pays', async (req, res) => {
try {
const start = parseInt(req.query._start) || 0;
const end = parseInt(req.query._end) || 20;
const order = req.query._order === 'DESC' ? -1 : 1;
const sort = req.query._sort || '_id';
const paysRaw = await Pay.find()
.skip(start)
.limit(end - start)
.sort({ [sort]: order });
const usersMap = new Map();
const pays = [];
for (const payRaw of paysRaw) {
const pay = payRaw.toObject();
if (!usersMap.has(pay.userId.toString())) {
const user = await User.findById(pay.userId);
usersMap.set(pay.userId.toString(), user.username);
}
const orderedPay = {
id: pay._id.toString(),
name: usersMap.get(pay.userId.toString()),
price: pay.price,
orderId: pay.orderId,
status: pay.status,
createTime: pay.createTime
};
pays.push(orderedPay);
}
const totalCount = await Pay.countDocuments();
res.header('Access-Control-Expose-Headers', 'X-Total-Count');
res.header('X-Total-Count', totalCount);
res.json(pays);
} catch (err) {
console.log(`Error fetching pays: ${err}`);
res.status(500).json({ error: 'Error fetching pays', details: err.message });
}
});
// 获取用户知识库列表
app.get('/kbs', async (req, res) => {
try {
const start = parseInt(req.query._start) || 0;
const end = parseInt(req.query._end) || 20;
const order = req.query._order === 'DESC' ? -1 : 1;
const sort = req.query._sort || '_id';
const kbsRaw = await Kb.find()
.skip(start)
.limit(end - start)
.sort({ [sort]: order });
const usersMap = new Map();
const kbs = [];
for (const kbRaw of kbsRaw) {
const kb = kbRaw.toObject();
if (!usersMap.has(kb.userId.toString())) {
const user = await User.findById(kb.userId);
usersMap.set(kb.userId.toString(), user.username);
}
const orderedKb = {
id: kb._id.toString(),
user: usersMap.get(kb.userId.toString()),
name: kb.name,
tags: kb.tags,
avatar: kb.avatar
};
kbs.push(orderedKb);
}
const totalCount = await Kb.countDocuments();
res.header('Access-Control-Expose-Headers', 'X-Total-Count');
res.header('X-Total-Count', totalCount);
res.json(kbs);
} catch (err) {
console.log(`Error fetching kbs: ${err}`);
res.status(500).json({ error: 'Error fetching kbs', details: err.message });
}
});
// 获取AI助手列表
app.get('/models', async (req, res) => {
try {
const start = parseInt(req.query._start) || 0;
const end = parseInt(req.query._end) || 20;
const order = req.query._order === 'DESC' ? -1 : 1;
const sort = req.query._sort || '_id';
const modelsRaw = await Model.find()
.skip(start)
.limit(end - start)
.sort({ [sort]: order });
const usersMap = new Map();
const models = [];
for (const modelRaw of modelsRaw) {
const model = modelRaw.toObject();
if (!usersMap.has(model.userId.toString())) {
const user = await User.findById(model.userId);
usersMap.set(model.userId.toString(), user.username);
}
// 获取与模型关联的知识库名称
const kbNames = [];
for (const kbId of model.chat.relatedKbs) {
const kb = await Kb.findById(kbId);
kbNames.push(kb.name);
}
const orderedModel = {
id: model._id.toString(),
user: usersMap.get(model.userId.toString()),
name: model.name,
relatedKbs: kbNames, // 将relatedKbs的id转换为相应的Kb名称
searchMode: model.chat.searchMode,
systemPrompt: model.chat.systemPrompt,
temperature: model.chat.temperature,
isShare: model.share.isShare,
isShareDetail: model.share.isShareDetail,
avatar: model.avatar
};
models.push(orderedModel);
}
const totalCount = await Model.countDocuments();
res.header('Access-Control-Expose-Headers', 'X-Total-Count');
res.header('X-Total-Count', totalCount);
res.json(models);
} catch (err) {
console.log(`Error fetching models: ${err}`);
res.status(500).json({ error: 'Error fetching models', details: err.message });
}
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
import {
createTextField,
jsonServerProvider,
ListTable,
Resource,
Tushan,
} from 'tushan';
import { authProvider } from './auth';
import { userFields,payFields,kbFields,ModelFields } from './fields';
const dataProvider = jsonServerProvider('http://localhost:3001');
function App() {
return (
<Tushan
basename="/"
header={'fastgpt-admin'}
footer={'Build with stakeswky'}
dataProvider={dataProvider}
authProvider={authProvider}
>
<Resource
name="users"
label="用户信息"
list={
<ListTable
filter={[
createTextField('q', {
label: 'Query',
}),
]}
fields={userFields}
action={{ create: true, detail: true, edit: true, delete: true }}
/>
}
/>
<Resource
name="pays"
label="支付记录"
list={
<ListTable
fields={payFields}
action={{ detail: true }}
/>
}
/>
<Resource
name="kbs"
label="知识库"
list={
<ListTable
fields={kbFields}
action={{ detail: true }}
/>
}
/>
<Resource
name="models"
label="Ai模型"
list={
<ListTable
fields={ModelFields}
action={{ detail: true }}
/>
}
/>
</Tushan>
);
}
export default App;
import { AuthProvider } from 'tushan';
export const authProvider: AuthProvider = {
login: ({ username, password }) => {
if (username !== 'tushan' || password !== 'tushan') {
return Promise.reject();
}
localStorage.setItem('username', username);
return Promise.resolve();
},
logout: () => {
localStorage.removeItem('username');
return Promise.resolve();
},
checkAuth: () =>
localStorage.getItem('username') ? Promise.resolve() : Promise.reject(),
checkError: (error) => {
const status = error.status;
if (status === 401 || status === 403) {
localStorage.removeItem('username');
return Promise.reject();
}
return Promise.resolve();
},
getIdentity: () =>
Promise.resolve({
id: '0',
fullName: 'Admin',
}),
getPermissions: () => Promise.resolve(''),
};
import {
createTextField,
createUrlField,
createNumberField,
createAvatarField
} from 'tushan';
export const userFields = [
createTextField('id', { label: 'ID' }),
createTextField('username', { label: '用户名', list: { sort: true } }),
createTextField('password', { label: '密码(加密)' }),
createNumberField('balance', { label: '余额' }),
createTextField('openaiKey', { label: 'OpenAI Key' }),
createTextField('createTime', { label: 'Create Time' }),
createAvatarField('avatar', { label: 'Avatar' }),
];
export const payFields = [
createTextField('id', { label: 'ID' }),
createTextField('name', { label: '用户名', list: { sort: true } }),
createNumberField('price', { label: '支付金额' }),
createTextField('orderId', { label: 'orderId' }),
createTextField('status', { label: '状态' }),
createTextField('createTime', { label: 'Create Time' }),
];
export const kbFields = [
createTextField('id', { label: 'ID' }),
createTextField('user', { label: '所属用户' }),
createTextField('name', { label: '知识库', list: { sort: true } }),
createTextField('tags', { label: 'Tags' }),
createAvatarField('avatar', { label: 'Avatar' }),
];
export const ModelFields = [
createTextField('id', { label: 'ID' }),
createTextField('name', { label: 'Ai助手', list: { sort: true } }),
createTextField('user', { label: '所属用户' }),
createTextField('relatedKbs', { label: '引用的知识库' }),
createTextField('searchMode', { label: '搜索模式' }),
createTextField('systemPrompt', { label: '提示词' }),
createTextField('temperature', { label: '温度' }),
createTextField('isShare', { label: '是否分享' }),
createTextField('isShareDetail', { label: '分享详情' }),
createAvatarField('avatar', { label: 'Avatar' }),
];
\ No newline at end of file
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<App />
);
/// <reference types="vite/client" />
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment