monorepo packages (#344)

This commit is contained in:
Archer
2023-09-24 18:02:09 +08:00
committed by GitHub
parent a4ff5a3f73
commit 3d7178d06f
535 changed files with 12048 additions and 227 deletions

View File

@@ -0,0 +1,30 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { GridFSStorage } from '@/service/lib/gridfs';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { fileId } = req.query as { fileId: string };
if (!fileId) {
throw new Error('fileId is empty');
}
const { userId } = await authUser({ req });
const gridFs = new GridFSStorage('dataset', userId);
await gridFs.delete(fileId);
jsonRes(res);
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}

View File

@@ -0,0 +1,45 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { GridFSStorage } from '@/service/lib/gridfs';
import { authFileToken } from './readUrl';
import jschardet from 'jschardet';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { token } = req.query as { token: string };
const { fileId, userId } = await authFileToken(token);
if (!fileId) {
throw new Error('fileId is empty');
}
const gridFs = new GridFSStorage('dataset', userId);
const [file, buffer] = await Promise.all([
gridFs.findAndAuthFile(fileId),
gridFs.download(fileId)
]);
const encoding = jschardet.detect(buffer)?.encoding;
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
res.setHeader('Cache-Control', 'public, max-age=3600');
res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.filename)}"`);
res.end(buffer);
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}
export const config = {
api: {
responseLimit: '32mb'
}
};

View File

@@ -0,0 +1,75 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import jwt from 'jsonwebtoken';
import { ERROR_ENUM } from '@/service/errorCode';
import { GridFSStorage } from '@/service/lib/gridfs';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { fileId } = req.query as { fileId: string };
if (!fileId) {
throw new Error('fileId is empty');
}
const { userId } = await authUser({ req });
// auth file
const gridFs = new GridFSStorage('dataset', userId);
await gridFs.findAndAuthFile(fileId);
const token = await createFileToken({
userId,
fileId
});
jsonRes(res, {
data: `/api/support/file/read?token=${token}`
});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}
export const createFileToken = (data: { userId: string; fileId: string }) => {
if (!process.env.FILE_TOKEN_KEY) {
return Promise.reject('System unset FILE_TOKEN_KEY');
}
const expiredTime = Math.floor(Date.now() / 1000) + 60 * 30;
const key = process.env.FILE_TOKEN_KEY as string;
const token = jwt.sign(
{
...data,
exp: expiredTime
},
key
);
return Promise.resolve(token);
};
export const authFileToken = (token?: string) =>
new Promise<{ userId: string; fileId: string }>((resolve, reject) => {
if (!token) {
return reject(ERROR_ENUM.unAuthFile);
}
const key = process.env.FILE_TOKEN_KEY as string;
jwt.verify(token, key, function (err, decoded: any) {
if (err || !decoded?.userId || !decoded?.fileId) {
reject(ERROR_ENUM.unAuthFile);
return;
}
resolve({
userId: decoded.userId,
fileId: decoded.fileId
});
});
});

View File

@@ -0,0 +1,111 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { GridFSStorage } from '@/service/lib/gridfs';
import { customAlphabet } from 'nanoid';
import multer from 'multer';
import path from 'path';
const nanoid = customAlphabet('1234567890abcdef', 12);
type FileType = {
fieldname: string;
originalname: string;
encoding: string;
mimetype: string;
filename: string;
path: string;
size: number;
};
/**
* Creates the multer uploader
*/
const maxSize = 50 * 1024 * 1024;
class UploadModel {
uploader = multer({
limits: {
fieldSize: maxSize
},
preservePath: true,
storage: multer.diskStorage({
filename: (_req, file, cb) => {
const { ext } = path.parse(decodeURIComponent(file.originalname));
cb(null, nanoid() + ext);
}
})
}).any();
async doUpload(req: NextApiRequest, res: NextApiResponse) {
return new Promise<{ files: FileType[]; metadata: Record<string, any> }>((resolve, reject) => {
// @ts-ignore
this.uploader(req, res, (error) => {
if (error) {
return reject(error);
}
resolve({
files:
// @ts-ignore
req.files?.map((file) => ({
...file,
originalname: decodeURIComponent(file.originalname)
})) || [],
metadata: (() => {
if (!req.body?.metadata) return {};
try {
return JSON.parse(req.body.metadata);
} catch (error) {
console.log(error);
return {};
}
})()
});
});
});
}
}
const upload = new UploadModel();
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { userId } = await authUser({ req, authToken: true });
const { files, metadata } = await upload.doUpload(req, res);
const gridFs = new GridFSStorage('dataset', userId);
const upLoadResults = await Promise.all(
files.map((file) =>
gridFs.save({
path: file.path,
filename: file.originalname,
metadata: {
...metadata,
contentType: file.mimetype,
userId
}
})
)
);
jsonRes(res, {
data: upLoadResults
});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}
export const config = {
api: {
bodyParser: false
}
};

View File

@@ -0,0 +1,28 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OpenApi } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { id } = req.query as { id: string };
if (!id) {
throw new Error('缺少参数');
}
const { userId } = await authUser({ req, authToken: true });
await connectToDatabase();
await OpenApi.findOneAndRemove({ _id: id, userId });
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,25 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OpenApi } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import type { GetApiKeyProps } from '@/api/support/openapi/index.d';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { appId } = req.query as GetApiKeyProps;
const { userId } = await authUser({ req, authToken: true });
await connectToDatabase();
const findResponse = await OpenApi.find({ userId, appId }).sort({ _id: -1 });
jsonRes(res, {
data: findResponse.map((item) => item.toObject())
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,45 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OpenApi } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { customAlphabet } from 'nanoid';
import type { EditApiKeyProps } from '@/api/support/openapi/index.d';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { appId, name, limit } = req.body as EditApiKeyProps;
const { userId } = await authUser({ req, authToken: true });
await connectToDatabase();
const count = await OpenApi.find({ userId, appId }).countDocuments();
if (count >= 10) {
throw new Error('最多 10 组 API 秘钥');
}
const nanoid = customAlphabet(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
Math.floor(Math.random() * 14) + 24
);
const apiKey = `${global.systemEnv?.openapiPrefix || 'fastgpt'}-${nanoid()}`;
await OpenApi.create({
userId,
apiKey,
appId,
name,
limit
});
jsonRes(res, {
data: apiKey
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,32 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OpenApi } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import type { EditApiKeyProps } from '@/api/support/openapi/index.d';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { _id, name, limit } = req.body as EditApiKeyProps & { _id: string };
const { userId } = await authUser({ req, authToken: true });
await connectToDatabase();
await OpenApi.findOneAndUpdate(
{
_id,
userId
},
{
...(name && { name }),
...(limit && { limit })
}
);
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,44 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OutLink } from '@/service/mongo';
import { authApp, authUser } from '@/service/utils/auth';
import type { OutLinkEditType } from '@/types/support/outLink';
import { customAlphabet } from 'nanoid';
import { OutLinkTypeEnum } from '@/constants/chat';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
/* create a shareChat */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { appId, ...props } = req.body as OutLinkEditType & {
appId: string;
type: `${OutLinkTypeEnum}`;
};
await connectToDatabase();
const { userId } = await authUser({ req, authToken: true });
await authApp({
appId,
userId,
authOwner: false
});
const shareId = nanoid();
await OutLink.create({
shareId,
userId,
appId,
...props
});
jsonRes(res, {
data: shareId
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,29 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OutLink } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
/* delete a shareChat by shareChatId */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { id } = req.query as {
id: string;
};
const { userId } = await authUser({ req, authToken: true });
await OutLink.findOneAndRemove({
_id: id,
userId
});
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,60 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OutLink, User } from '@/service/mongo';
import type { InitShareChatResponse } from '@/api/response/chat';
import { authApp } from '@/service/utils/auth';
import { HUMAN_ICON } from '@/constants/chat';
import { getChatModelNameList, getSpecialModule } from '@/components/ChatBox/utils';
/* init share chat window */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
let { shareId } = req.query as {
shareId: string;
};
if (!shareId) {
throw new Error('params is error');
}
await connectToDatabase();
// get shareChat
const shareChat = await OutLink.findOne({ shareId });
if (!shareChat) {
return jsonRes(res, {
code: 501,
error: '分享链接已失效'
});
}
// 校验使用权限
const [{ app }, user] = await Promise.all([
authApp({
appId: shareChat.appId,
userId: String(shareChat.userId),
authOwner: false
}),
User.findById(shareChat.userId, 'avatar')
]);
jsonRes<InitShareChatResponse>(res, {
data: {
userAvatar: user?.avatar || HUMAN_ICON,
app: {
...getSpecialModule(app.modules),
chatModels: getChatModelNameList(app.modules),
name: app.name,
avatar: app.avatar,
intro: app.intro
}
}
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,32 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OutLink } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { hashPassword } from '@/service/utils/tools';
/* get shareChat list by appId */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { appId } = req.query as {
appId: string;
};
const { userId } = await authUser({ req, authToken: true });
const data = await OutLink.find({
appId,
userId
}).sort({
_id: -1
});
jsonRes(res, { data });
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,25 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, OutLink } from '@/service/mongo';
import type { OutLinkEditType } from '@/types/support/outLink';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { _id, name, responseDetail, limit } = req.body as OutLinkEditType & {};
await OutLink.findByIdAndUpdate(_id, {
name,
responseDetail,
limit
});
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}