This commit is contained in:
2026-02-01 19:26:44 +08:00
parent 337abd2bc3
commit a0f0f65d20
2 changed files with 70 additions and 1 deletions

View File

@@ -54,6 +54,14 @@ export const getFileList = async (list: any, opts?: { objectName: string; app: s
}); });
}; };
// import { logger } from '@/module/logger.ts'; // import { logger } from '@/module/logger.ts';
/**
* GET 处理 AI 代理请求
* 1. 如果是目录请求,返回目录列表
* 2. 如果是文件请求,返回文件流
*
* 如果是 stat
* 只返回对应的 stat 信息
*/
const getAiProxy = async (req: IncomingMessage, res: ServerResponse, opts: ProxyOptions) => { const getAiProxy = async (req: IncomingMessage, res: ServerResponse, opts: ProxyOptions) => {
const { createNotFoundPage } = opts; const { createNotFoundPage } = opts;
const _u = new URL(req.url, 'http://localhost'); const _u = new URL(req.url, 'http://localhost');
@@ -63,6 +71,7 @@ const getAiProxy = async (req: IncomingMessage, res: ServerResponse, opts: Proxy
const hash = params.get('hash'); const hash = params.get('hash');
let dir = !!params.get('dir'); let dir = !!params.get('dir');
const recursive = !!params.get('recursive'); const recursive = !!params.get('recursive');
const showStat = !!params.get('stat');
const { objectName, app, owner, loginUser, isOwner } = await getObjectName(req); const { objectName, app, owner, loginUser, isOwner } = await getObjectName(req);
if (!dir && _u.pathname.endsWith('/')) { if (!dir && _u.pathname.endsWith('/')) {
dir = true; // 如果是目录请求强制设置为true dir = true; // 如果是目录请求强制设置为true
@@ -103,6 +112,19 @@ const getAiProxy = async (req: IncomingMessage, res: ServerResponse, opts: Proxy
logger.info('no permission', checkPermission, loginUser, owner); logger.info('no permission', checkPermission, loginUser, owner);
return createNotFoundPage('no permission'); return createNotFoundPage('no permission');
} }
if (showStat) {
res.writeHead(200, { 'content-type': 'application/json' });
res.end(
JSON.stringify({
code: 200,
data: {
...stat,
metaData: filterKeys(stat.metaData, ['etag', 'size']),
},
}),
);
return true;
}
if (hash && stat.etag === hash) { if (hash && stat.etag === hash) {
res.writeHead(304); // not modified res.writeHead(304); // not modified
res.end('not modified'); res.end('not modified');
@@ -295,6 +317,48 @@ export const renameProxy = async (req: IncomingMessage, res: ServerResponse, opt
} }
}; };
export const updateMetadataProxy = async (req: IncomingMessage, res: ServerResponse, opts: ProxyOptions) => {
const { objectName, isOwner } = await getObjectName(req);
let oss = opts.oss;
if (!isOwner) {
return opts?.createNotFoundPage?.('no permission');
}
const end = (data: any, message?: string, code = 200) => {
res.writeHead(code, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ code: code, data: data, message: message || 'success' }));
};
const _u = new URL(req.url, 'http://localhost');
const params = _u.searchParams;
const metaParam = params.get('meta');
if (!metaParam) {
return end({ success: false }, 'meta parameter required', 400);
}
const meta = parseSearchValue(metaParam, { decode: true });
try {
const stat = await oss.statObject(objectName);
if (!stat) {
return end({ success: false }, 'object not found', 404);
}
const newMeta = {
"app-source": "user-app",
...meta,
};
console.log('update metadata', objectName, newMeta);
// 过滤掉包含无效字符的 keyS3 元数据头不支持某些字符)
const filteredMeta: Record<string, string> = {};
for (const [key, value] of Object.entries(newMeta)) {
if (/^[\w\-]+$/.test(key)) {
filteredMeta[key] = String(value);
}
}
await oss.replaceObject(objectName, filteredMeta);
end({ success: true, objectName, meta }, 'update metadata success', 200);
} catch (error) {
logger.error('updateMetadataProxy error', error);
end({ success: false, error }, 'update metadata failed', 500);
}
};
export type ProxyOptions = { export type ProxyOptions = {
createNotFoundPage: (msg?: string) => any; createNotFoundPage: (msg?: string) => any;
oss?: OssBase; oss?: OssBase;
@@ -303,8 +367,8 @@ export const aiProxy = async (req: IncomingMessage, res: ServerResponse, opts: P
if (!opts.oss) { if (!opts.oss) {
opts.oss = oss; opts.oss = oss;
} }
const searchParams = new URL(req.url || '', 'http://localhost').searchParams;
if (req.method === 'POST') { if (req.method === 'POST') {
const searchParams = new URL(req.url || '', 'http://localhost').searchParams;
const chunk = searchParams.get('chunk'); const chunk = searchParams.get('chunk');
const chunked = searchParams.get('chunked'); const chunked = searchParams.get('chunked');
if (chunk !== null || chunked !== null) { if (chunk !== null || chunked !== null) {
@@ -316,6 +380,10 @@ export const aiProxy = async (req: IncomingMessage, res: ServerResponse, opts: P
return deleteProxy(req, res, opts); return deleteProxy(req, res, opts);
} }
if (req.method === 'PUT') { if (req.method === 'PUT') {
const meta = searchParams.get('meta');
if (meta) {
return updateMetadataProxy(req, res, opts);
}
return renameProxy(req, res, opts); return renameProxy(req, res, opts);
} }

View File

@@ -298,6 +298,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
username: loginUser?.tokenUser?.username || '', username: loginUser?.tokenUser?.username || '',
password: password, password: password,
}); });
console.log('checkPermission', checkPermission, 'loginUser:', loginUser, password)
if (!checkPermission.success) { if (!checkPermission.success) {
return createNotFoundPage('no permission'); return createNotFoundPage('no permission');
} }