feat: add permission for link data, and default public
This commit is contained in:
		
							
								
								
									
										12
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
									
									
									
									
								
							| @@ -30,28 +30,28 @@ | ||||
|   "author": "", | ||||
|   "license": "ISC", | ||||
|   "devDependencies": { | ||||
|     "@rollup/plugin-commonjs": "^28.0.2", | ||||
|     "@rollup/plugin-commonjs": "^28.0.3", | ||||
|     "@rollup/plugin-json": "^6.1.0", | ||||
|     "@rollup/plugin-node-resolve": "^16.0.0", | ||||
|     "@rollup/plugin-node-resolve": "^16.0.1", | ||||
|     "@rollup/plugin-typescript": "^12.1.2", | ||||
|     "@types/http-proxy": "^1.17.16", | ||||
|     "@types/node": "^22.13.9", | ||||
|     "@types/node": "^22.13.10", | ||||
|     "@types/send": "^0.17.4", | ||||
|     "concurrently": "^9.1.2", | ||||
|     "cross-env": "^7.0.3", | ||||
|     "nodemon": "^3.1.9", | ||||
|     "rollup": "^4.34.9", | ||||
|     "rollup": "^4.35.0", | ||||
|     "tslib": "^2.8.1", | ||||
|     "typescript": "^5.8.2" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@kevisual/code-center-module": "0.0.11-alpha.3", | ||||
|     "@kevisual/code-center-module": "0.0.13", | ||||
|     "@kevisual/router": "0.0.9", | ||||
|     "@kevisual/use-config": "^1.0.9", | ||||
|     "archiver": "^7.0.1", | ||||
|     "ioredis": "^5.6.0", | ||||
|     "minio": "^8.0.4", | ||||
|     "nanoid": "^5.1.2", | ||||
|     "nanoid": "^5.1.3", | ||||
|     "send": "^1.1.0", | ||||
|     "sequelize": "^6.37.6" | ||||
|   }, | ||||
|   | ||||
| @@ -8,6 +8,8 @@ import { pipeline } from 'stream'; | ||||
| import { promisify } from 'util'; | ||||
| import { fetchApp, fetchDomain, fetchTest } from './query/get-router.ts'; | ||||
| import { getAppLoadStatus, setAppLoadStatus, AppLoadStatus } from './redis/get-app-status.ts'; | ||||
| import { bucketName, minioClient, minioResources } from './minio.ts'; | ||||
| import { downloadFileFromMinio } from './proxy/http-proxy.ts'; | ||||
|  | ||||
| const pipelineAsync = promisify(pipeline); | ||||
|  | ||||
| @@ -391,13 +393,18 @@ export const deleteUserAppFiles = async (user: string, app: string) => { | ||||
|   } | ||||
|   // console.log('deleteUserAppFiles', res); | ||||
| }; | ||||
|  | ||||
| async function downloadFile(fileUrl: string, destFile: string) { | ||||
|   if (fileUrl.startsWith(minioResources)) { | ||||
|     await downloadFileFromMinio(fileUrl, destFile); | ||||
|     return; | ||||
|   } | ||||
|   console.log('destFile', destFile, 'fileUrl', fileUrl); | ||||
|   const res = await fetch(fileUrl); | ||||
|  | ||||
|   if (!res.ok) { | ||||
|     throw new Error(`Failed to fetch ${fileUrl}: ${res.statusText}`); | ||||
|   } | ||||
|   console.log('destFile', destFile); | ||||
|   const destStream = fs.createWriteStream(destFile); | ||||
|  | ||||
|   // 使用 `pipeline` 将 `res.body` 中的数据传递给 `destStream` | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import { getContentType } from './get-content-type.ts'; | ||||
| import { createRefreshHtml } from './html/create-refresh-html.ts'; | ||||
| import { fileProxy } from './proxy/file-proxy.ts'; | ||||
| import net from 'net'; | ||||
| import { httpProxy } from './proxy/http-proxy.ts'; | ||||
|  | ||||
| const api = config?.api || { host: 'kevisual.xiongxiao.me', path: '/api/router' }; | ||||
| const domain = config?.proxy?.domain || 'kevisual.xiongxiao.me'; | ||||
| @@ -125,7 +126,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR | ||||
|   const url = pathname; | ||||
|   if (!domainApp && noProxyUrl.includes(url)) { | ||||
|     if (url === '/') { | ||||
|       // 获取一下登陆用户,如果没有登陆用户,重定向到ai-chat页面 | ||||
|       // TODO: 获取一下登陆用户,如果没有登陆用户,重定向到ai-chat页面 | ||||
|       // 重定向到 | ||||
|       res.writeHead(302, { Location: home }); | ||||
|       return res.end(); | ||||
| @@ -168,7 +169,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR | ||||
|     res.write('Server Error\n'); | ||||
|     res.end(); | ||||
|   }; | ||||
|   const createNotFoundPage = (msg?: string) => { | ||||
|   const createNotFoundPage = async (msg?: string) => { | ||||
|     res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' }); | ||||
|     res.write(msg || 'Not Found App\n'); | ||||
|     res.end(); | ||||
| @@ -208,27 +209,11 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR | ||||
|         return createNotFoundPage('Invalid proxy url'); | ||||
|       } | ||||
|       console.log('proxyUrl', appFileUrl, proxyUrl); | ||||
|       let protocol = proxyUrl.startsWith('https') ? https : http; | ||||
|       // 代理 | ||||
|       const proxyReq = protocol.request(proxyUrl, (proxyRes) => { | ||||
|         res.writeHead(proxyRes.statusCode, { | ||||
|           ...proxyRes.headers, | ||||
|       httpProxy(req, res, { | ||||
|         proxyUrl, | ||||
|         userApp, | ||||
|         createNotFoundPage, | ||||
|       }); | ||||
|         if (proxyRes.statusCode === 404) { | ||||
|           userApp.clearCacheData(); | ||||
|           return createNotFoundPage('Invalid proxy url'); | ||||
|         } | ||||
|         if (proxyRes.statusCode === 302) { | ||||
|           res.writeHead(302, { Location: proxyRes.headers.location }); | ||||
|           return res.end(); | ||||
|         } | ||||
|         proxyRes.pipe(res, { end: true }); | ||||
|       }); | ||||
|       proxyReq.on('error', (err) => { | ||||
|         console.error(`Proxy request error: ${err.message}`); | ||||
|         userApp.clearCacheData(); | ||||
|       }); | ||||
|       proxyReq.end(); | ||||
|       // userApp.clearCacheData() | ||||
|       return; | ||||
|     } | ||||
|   | ||||
| @@ -5,8 +5,10 @@ type MinioConfig = { | ||||
|   minio: ClientOptions & { bucketName: string }; | ||||
| }; | ||||
| const config = useConfig<MinioConfig>(); | ||||
|  | ||||
| const { bucketName, ...minioRest } = config.minio; | ||||
| const { port, endPoint, useSSL } = minioRest; | ||||
| export const minioUrl = `http${useSSL ? 's' : ''}://${endPoint}:${port || 9000}`; | ||||
| export const minioResources = `${minioUrl}/resources`; | ||||
| export const minioClient = new Client(minioRest); | ||||
| export { bucketName }; | ||||
| if (!minioClient) { | ||||
|   | ||||
							
								
								
									
										89
									
								
								src/module/proxy/http-proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/module/proxy/http-proxy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| import { pipeline } from 'stream'; | ||||
| import { promisify } from 'util'; | ||||
| import { bucketName, minioClient, minioResources } from '../minio.ts'; | ||||
| import fs from 'fs'; | ||||
| import { IncomingMessage, ServerResponse } from 'http'; | ||||
| import http from 'http'; | ||||
| import https from 'https'; | ||||
| import { UserApp } from '../get-user-app.ts'; | ||||
|  | ||||
| const pipelineAsync = promisify(pipeline); | ||||
|  | ||||
| export async function downloadFileFromMinio(fileUrl: string, destFile: string) { | ||||
|   const objectName = fileUrl.replace(minioResources + '/', ''); | ||||
|   const objectStream = await minioClient.getObject(bucketName, objectName); | ||||
|   const destStream = fs.createWriteStream(destFile); | ||||
|   await pipelineAsync(objectStream, destStream); | ||||
|   console.log(`minio File downloaded to ${minioResources}/${objectName} \n ${destFile}`); | ||||
| } | ||||
| export async function minioProxy( | ||||
|   req: IncomingMessage, | ||||
|   res: ServerResponse, | ||||
|   opts: { | ||||
|     proxyUrl: string; | ||||
|     userApp: UserApp; | ||||
|     createNotFoundPage: (msg?: string) => any; | ||||
|   }, | ||||
| ) { | ||||
|   const fileUrl = opts.proxyUrl; | ||||
|   const { userApp, createNotFoundPage } = opts; | ||||
|   const objectName = fileUrl.replace(minioResources + '/', ''); | ||||
|   try { | ||||
|     const stat = await minioClient.statObject(bucketName, objectName); | ||||
|     if (stat.size === 0) { | ||||
|       return createNotFoundPage('Invalid proxy url'); | ||||
|     } | ||||
|     const contentLength = stat.size; | ||||
|     const etag = stat.etag; | ||||
|     const lastModified = stat.lastModified.toISOString(); | ||||
|     // console.log('contentType', stat.metaData); | ||||
|     res.writeHead(200, { | ||||
|       'Content-Length': contentLength, | ||||
|       etag, | ||||
|       'last-modified': lastModified, | ||||
|       ...stat.metaData, | ||||
|     }); | ||||
|     const objectStream = await minioClient.getObject(bucketName, objectName); | ||||
|     objectStream.pipe(res, { end: true }); | ||||
|   } catch (error) { | ||||
|     console.error(`Proxy request error: ${error.message}`); | ||||
|     userApp.clearCacheData(); | ||||
|     return createNotFoundPage('Invalid proxy url'); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const httpProxy = async ( | ||||
|   req: IncomingMessage, | ||||
|   res: ServerResponse, | ||||
|   opts: { | ||||
|     proxyUrl: string; | ||||
|     userApp: UserApp; | ||||
|     createNotFoundPage: (msg?: string) => any; | ||||
|   }, | ||||
| ) => { | ||||
|   const { proxyUrl, userApp, createNotFoundPage } = opts; | ||||
|   if (proxyUrl.startsWith(minioResources)) { | ||||
|     return minioProxy(req, res, opts); | ||||
|   } | ||||
|   let protocol = proxyUrl.startsWith('https') ? https : http; | ||||
|   // 代理 | ||||
|   const proxyReq = protocol.request(proxyUrl, (proxyRes) => { | ||||
|     res.writeHead(proxyRes.statusCode, { | ||||
|       ...proxyRes.headers, | ||||
|     }); | ||||
|     if (proxyRes.statusCode === 404) { | ||||
|       userApp.clearCacheData(); | ||||
|       return createNotFoundPage('Invalid proxy url'); | ||||
|     } | ||||
|     if (proxyRes.statusCode === 302) { | ||||
|       res.writeHead(302, { Location: proxyRes.headers.location }); | ||||
|       return res.end(); | ||||
|     } | ||||
|     proxyRes.pipe(res, { end: true }); | ||||
|   }); | ||||
|   proxyReq.on('error', (err) => { | ||||
|     console.error(`Proxy request error: ${err.message}`); | ||||
|     userApp.clearCacheData(); | ||||
|   }); | ||||
|   proxyReq.end(); | ||||
| }; | ||||
| @@ -8,6 +8,7 @@ app | ||||
|   .route({ | ||||
|     path: 'app', | ||||
|     key: 'auth-admin', | ||||
|     id: 'auth-admin', | ||||
|   }) | ||||
|   .define(async (ctx) => { | ||||
|     const { user } = ctx.query; | ||||
| @@ -116,7 +117,7 @@ app | ||||
|   .route({ | ||||
|     path: 'app', | ||||
|     key: 'status', | ||||
|     middleware: ['auth-admin'], | ||||
|     middleware: [], | ||||
|   }) | ||||
|   .define(async (ctx) => { | ||||
|     const { user, app } = ctx.query; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user