feat: 上传文件到minio
This commit is contained in:
		| @@ -5,6 +5,10 @@ import path from 'path'; | |||||||
| import { IncomingForm } from 'formidable'; | import { IncomingForm } from 'formidable'; | ||||||
| import { checkToken } from '@abearxiong/auth'; | import { checkToken } from '@abearxiong/auth'; | ||||||
| import { useConfig } from '@abearxiong/use-config'; | import { useConfig } from '@abearxiong/use-config'; | ||||||
|  | import { minioClient } from '@/app.ts'; | ||||||
|  | import { bucketName } from '@/modules/minio.ts'; | ||||||
|  | import { getContentType } from '@/utils/get-content-type.ts'; | ||||||
|  | import { User } from '@/models/user.ts'; | ||||||
| const { tokenSecret } = useConfig<{ tokenSecret: string }>(); | const { tokenSecret } = useConfig<{ tokenSecret: string }>(); | ||||||
| const filePath = useFileStore('upload'); | const filePath = useFileStore('upload'); | ||||||
| // curl -X POST http://localhost:4000/api/upload -F "file=@readme.md" | // curl -X POST http://localhost:4000/api/upload -F "file=@readme.md" | ||||||
| @@ -15,12 +19,12 @@ const filePath = useFileStore('upload'); | |||||||
| //   -F "username=testuser" | //   -F "username=testuser" | ||||||
|  |  | ||||||
| export const uploadMiddleware = async (req: http.IncomingMessage, res: http.ServerResponse) => { | export const uploadMiddleware = async (req: http.IncomingMessage, res: http.ServerResponse) => { | ||||||
|   if (req.method === 'GET' && req.url === '/api/upload') { |   if (req.method === 'GET' && req.url === '/api/app/upload') { | ||||||
|     res.writeHead(200, { 'Content-Type': 'text/plain' }); |     res.writeHead(200, { 'Content-Type': 'text/plain' }); | ||||||
|     res.end('Upload API is ready'); |     res.end('Upload API is ready'); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   if (false && req.method === 'POST' && req.url === '/api/upload') { |   if (false && req.method === 'POST' && req.url === '/api/app/upload') { | ||||||
|     res.writeHead(200, { 'Content-Type': 'text/plain' }); |     res.writeHead(200, { 'Content-Type': 'text/plain' }); | ||||||
|  |  | ||||||
|     // 检查 Content-Type 是否为 multipart/form-data |     // 检查 Content-Type 是否为 multipart/form-data | ||||||
| @@ -78,61 +82,70 @@ export const uploadMiddleware = async (req: http.IncomingMessage, res: http.Serv | |||||||
|       res.end(uploadResults.join('\n')); |       res.end(uploadResults.join('\n')); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|   if (req.method === 'POST' && req.url === '/api/upload') { |   if (req.method === 'POST' && req.url === '/api/app/upload') { | ||||||
|     res.writeHead(200, { 'Content-Type': 'text/plain' }); |     res.writeHead(200, { 'Content-Type': 'application/json' }); | ||||||
|     const authroization = req.headers?.['Authorization'] as string; |     const authroization = req.headers?.['authorization'] as string; | ||||||
|  |     const error = (msg: string) => { | ||||||
|  |       return JSON.stringify({ code: 500, message: msg }); | ||||||
|  |     }; | ||||||
|     if (!authroization) { |     if (!authroization) { | ||||||
|       res.statusCode = 401; |       res.statusCode = 401; | ||||||
|       res.end('Invalid authorization'); |       res.end(error('Invalid authorization')); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     const token = authroization.split(' ')[1]; |     const token = authroization.split(' ')[1]; | ||||||
|     const tokenUser = await checkToken(token, tokenSecret); |     let tokenUser; | ||||||
|     if (!tokenUser) { |     try { | ||||||
|  |       tokenUser = await User.verifyToken(token); | ||||||
|  |     } catch (e) { | ||||||
|       res.statusCode = 401; |       res.statusCode = 401; | ||||||
|       res.end('Invalid token'); |       res.end(error('Invalid token')); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     //  |     // | ||||||
|     // 使用 formidable 解析 multipart/form-data |     // 使用 formidable 解析 multipart/form-data | ||||||
|     const form = new IncomingForm({ |     const form = new IncomingForm({ | ||||||
|       multiples: true, // 支持多文件上传 |       multiples: true, // 支持多文件上传 | ||||||
|       uploadDir: filePath, // 上传文件存储目录 |       uploadDir: filePath, // 上传文件存储目录 | ||||||
|     }); |     }); | ||||||
|     // 解析上传的文件 |     // 解析上传的文件 | ||||||
|     form.parse(req, (err, fields, files) => { |     form.parse(req, async (err, fields, files) => { | ||||||
|       if (err) { |       if (err) { | ||||||
|         res.end(`Upload error: ${err.message}`); |         res.end(error(`Upload error: ${err.message}`)); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       console.log('fields', fields); |       console.log('fields', fields); | ||||||
|  |       const { appKey, version } = fields; | ||||||
|       // 逐个处理每个上传的文件 |       // 逐个处理每个上传的文件 | ||||||
|       const uploadedFiles = Array.isArray(files.file) ? files.file : [files.file]; |       const uploadedFiles = Array.isArray(files.file) ? files.file : [files.file]; | ||||||
|       const uploadResults = []; |       const uploadResults = []; | ||||||
|  |       for (let i = 0; i < uploadedFiles.length; i++) { | ||||||
|       uploadedFiles.forEach((file) => { |         const file = uploadedFiles[i]; | ||||||
|         // @ts-ignore |         // @ts-ignore | ||||||
|         const tempPath = file.filepath; // 文件上传时的临时路径 |         const tempPath = file.filepath; // 文件上传时的临时路径 | ||||||
|         const relativePath = file.originalFilename; // 保留表单中上传的文件名 (包含文件夹结构) |         const relativePath = file.originalFilename; // 保留表单中上传的文件名 (包含文件夹结构) | ||||||
|         uploadResults.push(`File ${relativePath} uploaded successfully. ${tempPath}`); |         // 比如 child2/b.txt | ||||||
|  |         const minioPath = `${tokenUser.username}/${appKey}/${version}/${relativePath}`; | ||||||
|         // 上传到 MinIO 并保留文件夹结构 |         // 上传到 MinIO 并保留文件夹结构 | ||||||
|         // minioClient.fPutObject(bucketName, relativePath, tempPath, {}, (err, etag) => { |         const isHTML = relativePath.endsWith('.html'); | ||||||
|         //   fs.unlinkSync(tempPath); // 删除临时文件 |         await minioClient.fPutObject(bucketName, minioPath, tempPath, { | ||||||
|  |           'Content-Type': getContentType(relativePath), | ||||||
|         //   if (err) { |           'app-source': 'user-app', | ||||||
|         //     uploadResults.push(`Upload error for ${relativePath}: ${err.message}`); |           'Cache-Control': isHTML ? 'no-cache' : 'max-age=31536000, immutable', // 缓存一年 | ||||||
|         //   } else { |         }); | ||||||
|         //     uploadResults.push(`File ${relativePath} uploaded successfully. ETag: ${etag}`); |         uploadResults.push({ | ||||||
|         //   } |           name: relativePath, | ||||||
|  |           path: minioPath, | ||||||
|         //   // 如果所有文件都处理完毕,返回结果 |         }); | ||||||
|         //   if (uploadResults.length === uploadedFiles.length) { |         fs.unlinkSync(tempPath); // 删除临时文件 | ||||||
|         //     res.writeHead(200, { 'Content-Type': 'text/plain' }); |       } | ||||||
|         //     res.end(uploadResults.join('\n')); |       // 修改header | ||||||
|         //   } |       // res.writeHead(200, { 'Content-Type': 'text/plain' }); | ||||||
|         // }); |       const data = { | ||||||
|       }); |         code: 200, | ||||||
|       res.end(uploadResults.join('\n')); |         data: uploadResults, | ||||||
|  |       }; | ||||||
|  |       res.end(JSON.stringify(data)); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -10,10 +10,15 @@ app | |||||||
|   }) |   }) | ||||||
|   .define(async (ctx) => { |   .define(async (ctx) => { | ||||||
|     const tokenUser = ctx.state.tokenUser; |     const tokenUser = ctx.state.tokenUser; | ||||||
|  |     const data = ctx.query.data || {}; | ||||||
|  |     if (!data.key) { | ||||||
|  |       throw new CustomError('key is required'); | ||||||
|  |     } | ||||||
|     const list = await AppListModel.findAll({ |     const list = await AppListModel.findAll({ | ||||||
|       order: [['updatedAt', 'DESC']], |       order: [['updatedAt', 'DESC']], | ||||||
|       where: { |       where: { | ||||||
|         uid: tokenUser.id, |         uid: tokenUser.id, | ||||||
|  |         key: data.key, | ||||||
|       }, |       }, | ||||||
|     }); |     }); | ||||||
|     ctx.body = list; |     ctx.body = list; | ||||||
| @@ -61,6 +66,10 @@ app | |||||||
|       } |       } | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!rest.key) { | ||||||
|  |       throw new CustomError('key is required'); | ||||||
|  |     } | ||||||
|     const app = await AppListModel.create({ data, ...rest, uid: tokenUser.id }); |     const app = await AppListModel.create({ data, ...rest, uid: tokenUser.id }); | ||||||
|     ctx.body = app; |     ctx.body = app; | ||||||
|     return ctx; |     return ctx; | ||||||
| @@ -82,7 +91,9 @@ app | |||||||
|     if (!app) { |     if (!app) { | ||||||
|       throw new CustomError('app not found'); |       throw new CustomError('app not found'); | ||||||
|     } |     } | ||||||
|     await app.destroy(); |     await app.destroy({ | ||||||
|  |       force: true, | ||||||
|  |     }); | ||||||
|     ctx.body = 'success'; |     ctx.body = 'success'; | ||||||
|     return ctx; |     return ctx; | ||||||
|   }) |   }) | ||||||
|   | |||||||
| @@ -11,8 +11,7 @@ export class AppListModel extends Model { | |||||||
|   declare id: string; |   declare id: string; | ||||||
|   declare data: AppData; |   declare data: AppData; | ||||||
|   declare version: string; |   declare version: string; | ||||||
|   declare appType: AppType; |   declare key: string; | ||||||
|   declare type: string; |  | ||||||
|   declare uid: string; |   declare uid: string; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -32,13 +31,8 @@ AppListModel.init( | |||||||
|       type: DataTypes.STRING, |       type: DataTypes.STRING, | ||||||
|       defaultValue: '', |       defaultValue: '', | ||||||
|     }, |     }, | ||||||
|     appType: { |     key: { | ||||||
|       type: DataTypes.STRING, |       type: DataTypes.STRING, | ||||||
|       defaultValue: '', |  | ||||||
|     }, |  | ||||||
|     type: { |  | ||||||
|       type: DataTypes.STRING, |  | ||||||
|       defaultValue: '', |  | ||||||
|     }, |     }, | ||||||
|     uid: { |     uid: { | ||||||
|       type: DataTypes.UUID, |       type: DataTypes.UUID, | ||||||
|   | |||||||
| @@ -14,6 +14,8 @@ export type App = Partial<InstanceType<typeof AppModel>>; | |||||||
| export class AppModel extends Model { | export class AppModel extends Model { | ||||||
|   declare id: string; |   declare id: string; | ||||||
|   declare data: AppData; |   declare data: AppData; | ||||||
|  |   declare title: string; | ||||||
|  |   declare description: string; | ||||||
|   declare version: string; |   declare version: string; | ||||||
|   declare domain: string; |   declare domain: string; | ||||||
|   declare appType: string; |   declare appType: string; | ||||||
| @@ -21,6 +23,7 @@ export class AppModel extends Model { | |||||||
|   declare type: string; |   declare type: string; | ||||||
|   declare uid: string; |   declare uid: string; | ||||||
|   declare user: string; |   declare user: string; | ||||||
|  |   declare status: string; | ||||||
| } | } | ||||||
| AppModel.init( | AppModel.init( | ||||||
|   { |   { | ||||||
| @@ -30,6 +33,14 @@ AppModel.init( | |||||||
|       defaultValue: DataTypes.UUIDV4, |       defaultValue: DataTypes.UUIDV4, | ||||||
|       comment: 'id', |       comment: 'id', | ||||||
|     }, |     }, | ||||||
|  |     title: { | ||||||
|  |       type: DataTypes.STRING, | ||||||
|  |       defaultValue: '', | ||||||
|  |     }, | ||||||
|  |     description: { | ||||||
|  |       type: DataTypes.STRING, | ||||||
|  |       defaultValue: '', | ||||||
|  |     }, | ||||||
|     data: { |     data: { | ||||||
|       type: DataTypes.JSON, |       type: DataTypes.JSON, | ||||||
|       defaultValue: {}, |       defaultValue: {}, | ||||||
| @@ -48,7 +59,7 @@ AppModel.init( | |||||||
|     }, |     }, | ||||||
|     key: { |     key: { | ||||||
|       type: DataTypes.STRING, |       type: DataTypes.STRING, | ||||||
|       unique: true, |       // 和 uid 组合唯一 | ||||||
|     }, |     }, | ||||||
|     type: { |     type: { | ||||||
|       type: DataTypes.STRING, |       type: DataTypes.STRING, | ||||||
| @@ -58,11 +69,25 @@ AppModel.init( | |||||||
|       type: DataTypes.UUID, |       type: DataTypes.UUID, | ||||||
|       allowNull: true, |       allowNull: true, | ||||||
|     }, |     }, | ||||||
|  |     user: { | ||||||
|  |       type: DataTypes.STRING, | ||||||
|  |       allowNull: true, | ||||||
|  |     }, | ||||||
|  |     status: { | ||||||
|  |       type: DataTypes.STRING, | ||||||
|  |       defaultValue: 'running', // stop, running | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     sequelize, |     sequelize, | ||||||
|     tableName: 'kv_app', |     tableName: 'kv_app', | ||||||
|     paranoid: true, |     paranoid: true, | ||||||
|  |     indexes: [ | ||||||
|  |       { | ||||||
|  |         unique: true, | ||||||
|  |         fields: ['key', 'uid'], | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|   }, |   }, | ||||||
| ); | ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -48,6 +48,8 @@ app | |||||||
|     middleware: ['auth'], |     middleware: ['auth'], | ||||||
|   }) |   }) | ||||||
|   .define(async (ctx) => { |   .define(async (ctx) => { | ||||||
|  |     const tokenUser = ctx.state.tokenUser; | ||||||
|  |  | ||||||
|     const { data, id, ...rest } = ctx.query.data; |     const { data, id, ...rest } = ctx.query.data; | ||||||
|     if (id) { |     if (id) { | ||||||
|       const app = await AppModel.findByPk(id); |       const app = await AppModel.findByPk(id); | ||||||
| @@ -60,8 +62,19 @@ app | |||||||
|       } |       } | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     const tokenUser = ctx.state.tokenUser; |     if (!rest.key) { | ||||||
|     const app = await AppModel.create({ data, ...rest, uid: tokenUser.id }); |       throw new CustomError('key is required'); | ||||||
|  |     } | ||||||
|  |     const findApp = await AppModel.findOne({ where: { key: rest.key, uid: tokenUser.id } }); | ||||||
|  |     if (findApp) { | ||||||
|  |       throw new CustomError('key already exists'); | ||||||
|  |     } | ||||||
|  |     const app = await AppModel.create({ | ||||||
|  |       data: { files: [] }, | ||||||
|  |       ...rest, | ||||||
|  |       uid: tokenUser.id, | ||||||
|  |       user: tokenUser.username, | ||||||
|  |     }); | ||||||
|     ctx.body = app; |     ctx.body = app; | ||||||
|     return ctx; |     return ctx; | ||||||
|   }) |   }) | ||||||
|   | |||||||
| @@ -1,8 +1,25 @@ | |||||||
| import { app } from '@/app.ts'; | import { app } from '@/app.ts'; | ||||||
| import { getMinioList } from './module/get-minio-list.ts'; | import { getFileStat, getMinioList } from './module/get-minio-list.ts'; | ||||||
| import path from 'path'; | import path from 'path'; | ||||||
| import { CustomError } from '@abearxiong/router'; | import { CustomError } from '@abearxiong/router'; | ||||||
|  | import { get } from 'http'; | ||||||
|  |  | ||||||
|  | const handlePrefix = (prefix: string) => { | ||||||
|  |   // 清理所有的 '..' | ||||||
|  |   if (!prefix) return ''; | ||||||
|  |   if (prefix.includes('..')) { | ||||||
|  |     throw new CustomError('invalid prefix'); | ||||||
|  |   } | ||||||
|  |   return prefix; | ||||||
|  | }; | ||||||
|  | const getPrefixByUser = (data: { prefix: string }, tokenUser: { username: string }) => { | ||||||
|  |   const prefixBase = '/' + tokenUser.username; | ||||||
|  |   const _prefix = handlePrefix(data.prefix); | ||||||
|  |   return { | ||||||
|  |     len: prefixBase.length, | ||||||
|  |     prefix: path.join(prefixBase, './', _prefix), | ||||||
|  |   }; | ||||||
|  | }; | ||||||
| app | app | ||||||
|   .route({ |   .route({ | ||||||
|     path: 'file', |     path: 'file', | ||||||
| @@ -12,19 +29,37 @@ app | |||||||
|   .define(async (ctx) => { |   .define(async (ctx) => { | ||||||
|     const tokenUser = ctx.state.tokenUser; |     const tokenUser = ctx.state.tokenUser; | ||||||
|     const data = ctx.query.data || {}; |     const data = ctx.query.data || {}; | ||||||
|     const prefixBase = '/' + tokenUser.username; |     const { len, prefix } = getPrefixByUser(data, tokenUser); | ||||||
|     const handlePrefix = (prefix: string) => { |  | ||||||
|       // 清理所有的 '..' |  | ||||||
|       if (prefix.includes('..')) { |  | ||||||
|         throw new CustomError('invalid prefix'); |  | ||||||
|       } |  | ||||||
|       return prefix; |  | ||||||
|     }; |  | ||||||
|     const _prefix = handlePrefix(data.prefix); |  | ||||||
|     const prefix = path.join(prefixBase, './', _prefix); |  | ||||||
|     const recursive = data.recursive; |     const recursive = data.recursive; | ||||||
|     const list = await getMinioList({ prefix: prefix.slice(1), recursive: recursive }); |     const list = await getMinioList({ prefix: prefix.slice(1), recursive: recursive }); | ||||||
|     ctx.body = list; |  | ||||||
|  |     ctx.body = list.map((item) => { | ||||||
|  |       if ('prefix' in item) { | ||||||
|  |         return { | ||||||
|  |           ...item, | ||||||
|  |           prefix: item.prefix.slice(len), | ||||||
|  |         }; | ||||||
|  |       } else { | ||||||
|  |         return { ...item, name: item.name.slice(len) }; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     return ctx; | ||||||
|  |   }) | ||||||
|  |   .addTo(app); | ||||||
|  |  | ||||||
|  | app | ||||||
|  |   .route({ | ||||||
|  |     path: 'file', | ||||||
|  |     key: 'stat', | ||||||
|  |     middleware: ['auth'], | ||||||
|  |   }) | ||||||
|  |   .define(async (ctx) => { | ||||||
|  |     const tokenUser = ctx.state.tokenUser; | ||||||
|  |     const data = ctx.query.data || {}; | ||||||
|  |     const { prefix } = getPrefixByUser(data, tokenUser); | ||||||
|  |     console.log('prefix', prefix); | ||||||
|  |     const stat = await getFileStat(prefix.slice(1)); | ||||||
|  |     ctx.body = stat; | ||||||
|     return ctx; |     return ctx; | ||||||
|   }) |   }) | ||||||
|   .addTo(app); |   .addTo(app); | ||||||
|   | |||||||
| @@ -5,17 +5,17 @@ type MinioListOpt = { | |||||||
|   prefix: string; |   prefix: string; | ||||||
|   recursive?: boolean; |   recursive?: boolean; | ||||||
| }; | }; | ||||||
| type MinioFile = { | export type MinioFile = { | ||||||
|   name: string; |   name: string; | ||||||
|   size: number; |   size: number; | ||||||
|   lastModified: Date; |   lastModified: Date; | ||||||
|   etag: string; |   etag: string; | ||||||
| }; | }; | ||||||
| type MinioDirectory = { | export type MinioDirectory = { | ||||||
|   prefix: string; |   prefix: string; | ||||||
|   size: number; |   size: number; | ||||||
| }; | }; | ||||||
| type MinioList = (MinioFile | MinioDirectory)[]; | export type MinioList = (MinioFile | MinioDirectory)[]; | ||||||
| export const getMinioList = async (opts: MinioListOpt): Promise<MinioList> => { | export const getMinioList = async (opts: MinioListOpt): Promise<MinioList> => { | ||||||
|   const prefix = opts.prefix; |   const prefix = opts.prefix; | ||||||
|   const recursive = opts.recursive ?? false; |   const recursive = opts.recursive ?? false; | ||||||
| @@ -41,3 +41,15 @@ export const getMinioList = async (opts: MinioListOpt): Promise<MinioList> => { | |||||||
|       }); |       }); | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
|  | export const getFileStat = async (prefix: string): Promise<any> => { | ||||||
|  |   try { | ||||||
|  |     const obj = await minioClient.statObject(bucketName, prefix); | ||||||
|  |     return obj; | ||||||
|  |   } catch (e) { | ||||||
|  |     if (e.code === 'NotFound') { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     console.error('get File Stat Error not handle', e); | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { bucketName, minioClient } from '@/modules/minio.ts'; | import { bucketName, minioClient } from '@/modules/minio.ts'; | ||||||
|  | import { S3Error } from 'minio'; | ||||||
| const main = async () => { | const main = async () => { | ||||||
|   const res = await new Promise((resolve, reject) => { |   const res = await new Promise((resolve, reject) => { | ||||||
|     let res: any[] = []; |     let res: any[] = []; | ||||||
| @@ -24,4 +24,17 @@ const main = async () => { | |||||||
|   }); |   }); | ||||||
|   console.log(res); |   console.log(res); | ||||||
| }; | }; | ||||||
| main(); | // main(); | ||||||
|  |  | ||||||
|  | const main2 = async () => { | ||||||
|  |   try { | ||||||
|  |     const obj = await minioClient.statObject(bucketName, 'root/codeflow/0.0.1/README.md'); | ||||||
|  |  | ||||||
|  |     console.log(obj); | ||||||
|  |   } catch (e) { | ||||||
|  |     console.log('', e.message, '\n\r', e.code); | ||||||
|  |     // console.error(e); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | main2(); | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								src/utils/get-content-type.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/utils/get-content-type.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | import path from 'path'; | ||||||
|  | // 获取文件的 content-type | ||||||
|  | export const getContentType = (filePath: string) => { | ||||||
|  |   const extname = path.extname(filePath); | ||||||
|  |   const contentType = { | ||||||
|  |     '.html': 'text/html', | ||||||
|  |     '.js': 'text/javascript', | ||||||
|  |     '.css': 'text/css', | ||||||
|  |     '.json': 'application/json', | ||||||
|  |     '.png': 'image/png', | ||||||
|  |     '.jpg': 'image/jpg', | ||||||
|  |     '.gif': 'image/gif', | ||||||
|  |     '.svg': 'image/svg+xml', | ||||||
|  |     '.wav': 'audio/wav', | ||||||
|  |     '.mp4': 'video/mp4', | ||||||
|  |   }; | ||||||
|  |   return contentType[extname] || 'application/octet-stream'; | ||||||
|  | }; | ||||||
| @@ -1,2 +0,0 @@ | |||||||
| code的flow流程成图 |  | ||||||
|  |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| // Generated by dts-bundle-generator v9.5.1 |  | ||||||
|  |  | ||||||
| export type RouterCode = { |  | ||||||
| 	id: string; |  | ||||||
| 	path: string; |  | ||||||
| 	key: string; |  | ||||||
| 	active: boolean; |  | ||||||
| 	project: string; |  | ||||||
| 	code: string; |  | ||||||
| 	exec: string; |  | ||||||
| 	type: RouterCodeType; |  | ||||||
| 	middleware: string[]; |  | ||||||
| 	next: string; |  | ||||||
| 	data: any; |  | ||||||
| 	validator: any; |  | ||||||
| }; |  | ||||||
| declare enum RouterCodeType { |  | ||||||
| 	route = "route", |  | ||||||
| 	middleware = "middleware" |  | ||||||
| } |  | ||||||
| declare enum CodeStatus { |  | ||||||
| 	running = "running", |  | ||||||
| 	stop = "stop", |  | ||||||
| 	fail = "fail" |  | ||||||
| } |  | ||||||
| export type CodeManager = { |  | ||||||
| 	fn?: any; |  | ||||||
| 	status?: CodeStatus; |  | ||||||
| 	errorMsg?: string; |  | ||||||
| 	lock?: boolean; |  | ||||||
| } & Partial<RouterCode>; |  | ||||||
| export interface ContainerData { |  | ||||||
| 	style?: { |  | ||||||
| 		[key: string]: string; |  | ||||||
| 	}; |  | ||||||
| 	className?: string; |  | ||||||
| 	showChild?: boolean; |  | ||||||
| 	shadowRoot?: boolean; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export {}; |  | ||||||
		Reference in New Issue
	
	Block a user