mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-16 08:01:18 +00:00
后台 (#77)
This commit is contained in:
201
admin/LICENSE
Normal file
201
admin/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
15
admin/README.md
Normal file
15
admin/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## fastgpt-admin
|
||||
原作地址:https://github.com/c121914yu/FastGPT/
|
||||
|
||||
## 项目原理
|
||||
使用tushan项目做前端,然后构造了一个与mongodb做沟通的API做后端,可以做到创建、修改和删除用户
|
||||
|
||||
## 使用方法
|
||||
1. 修改根目录下的server.js文件中的mongodb数据库连接地址。
|
||||
2. pnpm i && pnpm dev
|
||||
3. 默认账号密码为tushan
|
||||
|
||||
|
||||
## 可能会有的功能
|
||||
1. 对接数据库中的其他数据
|
||||
2. tokens充值功能
|
13
admin/index.html
Normal file
13
admin/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!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>
|
30
admin/package.json
Normal file
30
admin/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
10
admin/public/logo.svg
Normal file
10
admin/public/logo.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<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>
|
After Width: | Height: | Size: 2.4 KiB |
329
admin/server.js
Normal file
329
admin/server.js
Normal file
@@ -0,0 +1,329 @@
|
||||
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}`);
|
||||
});
|
||||
|
72
admin/src/App.tsx
Normal file
72
admin/src/App.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
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;
|
33
admin/src/auth.ts
Normal file
33
admin/src/auth.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
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(''),
|
||||
};
|
46
admin/src/fields.ts
Normal file
46
admin/src/fields.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
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' }),
|
||||
];
|
7
admin/src/main.tsx
Normal file
7
admin/src/main.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<App />
|
||||
);
|
1
admin/src/vite-env.d.ts
vendored
Normal file
1
admin/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
21
admin/tsconfig.json
Normal file
21
admin/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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" }]
|
||||
}
|
9
admin/tsconfig.node.json
Normal file
9
admin/tsconfig.node.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
7
admin/vite.config.ts
Normal file
7
admin/vite.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
});
|
Reference in New Issue
Block a user