From c6ed02dee258aa2b47ff925e5f9fcb9a1b486a41 Mon Sep 17 00:00:00 2001 From: zhou <85232349+Fantasy-Ke@users.noreply.github.com> Date: Wed, 5 Mar 2025 17:36:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6=E5=A4=B9?= =?UTF-8?q?=E7=9A=84=E8=B7=AF=E5=BE=84=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/manage/block/{[id].js => [[path]].js} | 15 +++-- .../manage/delete/{[id].js => [[path]].js} | 14 ++-- .../api/manage/white/{[id].js => [[path]].js} | 15 +++-- functions/file/{[id].js => [[path]].js} | 48 ++++++-------- functions/upload.js | 64 +++++-------------- 5 files changed, 62 insertions(+), 94 deletions(-) rename functions/api/manage/block/{[id].js => [[path]].js} (67%) rename functions/api/manage/delete/{[id].js => [[path]].js} (87%) rename functions/api/manage/white/{[id].js => [[path]].js} (67%) rename functions/file/{[id].js => [[path]].js} (93%) diff --git a/functions/api/manage/block/[id].js b/functions/api/manage/block/[[path]].js similarity index 67% rename from functions/api/manage/block/[id].js rename to functions/api/manage/block/[[path]].js index b4b86616..d2128916 100644 --- a/functions/api/manage/block/[id].js +++ b/functions/api/manage/block/[[path]].js @@ -13,17 +13,20 @@ export async function onRequest(context) { // 组装 CDN URL const url = new URL(request.url); - const cdnUrl = `https://${url.hostname}/file/${params.id}`; - - // 解码params.id - params.id = decodeURIComponent(params.id); + + if (params.path) { + params.path = String(params.path).split(',').join('/'); + } + const cdnUrl = `https://${url.hostname}/file/${params.path}`; + // 解码params.path + params.path = decodeURIComponent(params.path); //read the metadata - const value = await env.img_url.getWithMetadata(params.id); + const value = await env.img_url.getWithMetadata(params.path); //change the metadata value.metadata.ListType = "Block" - await env.img_url.put(params.id,"",{metadata: value.metadata}); + await env.img_url.put(params.path,"",{metadata: value.metadata}); const info = JSON.stringify(value.metadata); // 清除CDN缓存 diff --git a/functions/api/manage/delete/[id].js b/functions/api/manage/delete/[[path]].js similarity index 87% rename from functions/api/manage/delete/[id].js rename to functions/api/manage/delete/[[path]].js index b02e930f..e4249b72 100644 --- a/functions/api/manage/delete/[id].js +++ b/functions/api/manage/delete/[[path]].js @@ -13,18 +13,18 @@ export async function onRequest(context) { } = context; // 组装 CDN URL const url = new URL(request.url); - const cdnUrl = `https://${url.hostname}/file/${params.id}`; + const cdnUrl = `https://${url.hostname}/file/${params.path}`; - // 解码params.id - params.id = decodeURIComponent(params.id); + // 解码params.path + params.path = decodeURIComponent(params.path); try { // 读取图片信息 - const img = await env.img_url.getWithMetadata(params.id); + const img = await env.img_url.getWithMetadata(params.path); // 如果是R2渠道的图片,删除R2中对应的图片 if (img.metadata?.Channel === 'CloudflareR2') { - await env.img_r2.delete(params.id); + await env.img_r2.delete(params.path); } // S3 渠道的图片,删除S3中对应的图片 @@ -55,8 +55,8 @@ export async function onRequest(context) { } // 删除KV中的图片信息 - await env.img_url.delete(params.id); - const info = JSON.stringify(params.id); + await env.img_url.delete(params.path); + const info = JSON.stringify(params.path); // 清除CDN缓存 await purgeCFCache(env, cdnUrl); diff --git a/functions/api/manage/white/[id].js b/functions/api/manage/white/[[path]].js similarity index 67% rename from functions/api/manage/white/[id].js rename to functions/api/manage/white/[[path]].js index 794e17c4..4512cb8d 100644 --- a/functions/api/manage/white/[id].js +++ b/functions/api/manage/white/[[path]].js @@ -12,17 +12,20 @@ export async function onRequest(context) { } = context; // 组装 CDN URL const url = new URL(request.url); - const cdnUrl = `https://${url.hostname}/file/${params.id}`; - - // 解码params.id - params.id = decodeURIComponent(params.id); + + if (params.path) { + params.path = String(params.path).split(',').join('/'); + } + const cdnUrl = `https://${url.hostname}/file/${params.path}`; + // 解码params.path + params.path = decodeURIComponent(params.path); //read the metadata - const value = await env.img_url.getWithMetadata(params.id); + const value = await env.img_url.getWithMetadata(params.path); //change the metadata value.metadata.ListType = "White" - await env.img_url.put(params.id,"",{metadata: value.metadata}); + await env.img_url.put(params.path,"",{metadata: value.metadata}); const info = JSON.stringify(value.metadata); // 清除CDN缓存 diff --git a/functions/file/[id].js b/functions/file/[[path]].js similarity index 93% rename from functions/file/[id].js rename to functions/file/[[path]].js index ca66d8b2..ede53c7d 100644 --- a/functions/file/[id].js +++ b/functions/file/[[path]].js @@ -15,10 +15,13 @@ export async function onRequest(context) { // Contents of context object next, // used for middleware or to fetch assets data, // arbitrary space for passing data between middlewares } = context; - + let fileId; try { - // 解码params.id - params.id = decodeURIComponent(params.id); + // 解码params.path + params.path = decodeURIComponent(params.path); + // 从path中提取文件ID + fileId = params.path.split(',').join('/'); + } catch (e) { return new Response('Error: Decode Image ID Failed', { status: 400 }); } @@ -29,13 +32,13 @@ export async function onRequest(context) { // Contents of context object whiteListMode = securityConfig.access.whiteListMode; const url = new URL(request.url); - let Referer = request.headers.get('Referer'); + let Referer = request.headers.get('Referer') if (Referer) { try { let refererUrl = new URL(Referer); if (allowedDomains && allowedDomains.trim() !== '') { - let allowedDomainsList = allowedDomains.split(','); - let isAllowed = allowedDomainsList.some(domain => { + const domains = allowedDomains.split(','); + let isAllowed = domains.some(domain => { let domainPattern = new RegExp(`(^|\\.)${domain.replace('.', '\\.')}$`); // Escape dot in domain return domainPattern.test(refererUrl.hostname); }); @@ -51,10 +54,16 @@ export async function onRequest(context) { // Contents of context object if (typeof env.img_url == "undefined" || env.img_url == null || env.img_url == "") { return new Response('Error: Please configure KV database', { status: 500 }); } - const imgRecord = await env.img_url.getWithMetadata(params.id); - // 如果meatdata不存在,只可能是之前未设置KV,且存储在Telegraph上的图片,那么在后面获取时会返回404错误,此处不用处理 + const imgRecord = await env.img_url.getWithMetadata(fileId); + if (!imgRecord) { + return new Response('Error: Image Not Found', { status: 404 }); + } + // 如果metadata不存在,只可能是之前未设置KV,且存储在Telegraph上的图片 + if (!imgRecord.metadata) { + imgRecord.metadata = {}; + } - const fileName = imgRecord.metadata?.FileName || params.id; + const fileName = imgRecord.metadata?.FileName || fileId; const encodedFileName = encodeURIComponent(fileName); const fileType = imgRecord.metadata?.FileType || null; @@ -63,9 +72,7 @@ export async function onRequest(context) { // Contents of context object if (accessRes.status !== 200) { return accessRes; // 如果不可访问,直接返回 } - - - + // Cloudflare R2渠道 if (imgRecord.metadata?.Channel === 'CloudflareR2') { // 检查是否配置了R2 @@ -74,7 +81,7 @@ export async function onRequest(context) { // Contents of context object } const R2DataBase = env.img_r2; - const object = await R2DataBase.get(params.id); + const object = await R2DataBase.get(fileId); if (object === null) { return new Response('Error: Failed to fetch image', { status: 500 }); @@ -103,7 +110,6 @@ export async function onRequest(context) { // Contents of context object return newRes; } - // S3渠道 if (imgRecord.metadata?.Channel === "S3") { const s3Client = new S3Client({ @@ -150,15 +156,6 @@ export async function onRequest(context) { // Contents of context object return new Response(`Error: Failed to fetch from S3 - ${error.message}`, { status: 500 }); } } - - - - // 外链渠道 - if (imgRecord.metadata?.Channel === 'External') { - // 直接重定向到外链 - return Response.redirect(imgRecord.metadata?.ExternalLink, 302); - } - // Telegram及Telegraph渠道 let TgFileID = ''; // Tg的file_id @@ -174,7 +171,6 @@ export async function onRequest(context) { // Contents of context object } else { // 旧版telegraph } - // 构建目标 URL if (isTgChannel(imgRecord)) { // 获取TG图片真实地址 @@ -187,14 +183,12 @@ export async function onRequest(context) { // Contents of context object } else { targetUrl = 'https://telegra.ph/' + url.pathname + url.search; } - const response = await getFileContent(request); if (response === null) { return new Response('Error: Failed to fetch image', { status: 500 }); } else if (response.status === 404) { return await return404(url); } - try { const headers = new Headers(response.headers); headers.set('Content-Disposition', `inline; filename="${encodedFileName}"; filename*=UTF-8''${encodedFileName}`); @@ -208,13 +202,11 @@ export async function onRequest(context) { // Contents of context object } else { headers.set('Cache-Control', 'public, max-age=604800'); } - const newRes = new Response(response.body, { status: response.status, statusText: response.statusText, headers, }); - if (response.ok) { return newRes; } diff --git a/functions/upload.js b/functions/upload.js index 17c26a35..ea864c34 100644 --- a/functions/upload.js +++ b/functions/upload.js @@ -97,6 +97,15 @@ export async function onRequestPost(context) { // Contents of context object // 获得上传渠道 const urlParamUploadChannel = url.searchParams.get('uploadChannel'); + // 获取上传文件夹路径 + const uploadFolder = url.searchParams.get('uploadFolder') || ''; + // 处理文件夹路径格式,确保没有开头的/ + const normalizedFolder = uploadFolder + ? uploadFolder.replace(/^\/+/, '') // 移除开头的/ + .replace(/\/{2,}/g, '/') // 替换多个连续的/为单个/ + .replace(/\/$/, '') // 移除末尾的/ + : ''; + let uploadChannel = 'TelegramNew'; switch (urlParamUploadChannel) { case 'telegram': @@ -108,9 +117,6 @@ export async function onRequestPost(context) { // Contents of context object case 's3': uploadChannel = 'S3'; break; - case 'external': - uploadChannel = 'External'; - break; default: uploadChannel = 'TelegramNew'; break; @@ -141,6 +147,7 @@ export async function onRequestPost(context) { // Contents of context object ListType: "None", TimeStamp: time, Label: "None", + Folder: normalizedFolder || 'root', } @@ -164,24 +171,21 @@ export async function onRequestPost(context) { // Contents of context object const unique_index = time + Math.floor(Math.random() * 10000); let fullId = ''; if (nameType === 'index') { - // 仅前缀 - fullId = unique_index + '.' + fileExt; + // 只在 normalizedFolder 非空时添加路径 + fullId = normalizedFolder ? `${normalizedFolder}/${unique_index}.${fileExt}` : `${unique_index}.${fileExt}`; } else if (nameType === 'origin') { - // 仅文件名 - fullId = fileName? fileName : unique_index + '.' + fileExt; + fullId = normalizedFolder ? `${normalizedFolder}/${fileName}` : fileName; } else if (nameType === 'short') { - // 短链接,8位大小写字母+数字的随机字符 while (true) { const shortId = generateShortId(8); - const testFullId = shortId + '.' + fileExt; + const testFullId = normalizedFolder ? `${normalizedFolder}/${shortId}.${fileExt}` : `${shortId}.${fileExt}`; if (await env.img_url.get(testFullId) === null) { - fullId = shortId + '.' + fileExt; + fullId = testFullId; break; } } } else { - // 默认方式:前缀+文件名 - fullId = fileName? unique_index + '_' + fileName : unique_index + '.' + fileExt; + fullId = normalizedFolder ? `${normalizedFolder}/${unique_index}_${fileName}` : `${unique_index}_${fileName}`; } // 获得返回链接格式, default为返回/file/id, full为返回完整链接 @@ -220,10 +224,6 @@ export async function onRequestPost(context) { // Contents of context object } else { err = await res.text(); } - } else if (uploadChannel === 'External') { - // -------------外链渠道--------------- - const res = await uploadFileToExternal(env, formdata, fullId, metadata, returnLink, url); - return res; } else { // ----------------Telegram New 渠道------------------- const res = await uploadFileToTelegram(env, formdata, fullId, metadata, fileExt, fileName, fileType, url, clonedRequest, returnLink); @@ -530,36 +530,6 @@ async function uploadFileToTelegram(env, formdata, fullId, metadata, fileExt, fi } -// 外链渠道 -async function uploadFileToExternal(env, formdata, fullId, metadata, returnLink, originUrl) { - // 直接将外链写入metadata - metadata.Channel = "External"; - metadata.ChannelName = "External"; - // 从 formdata 中获取外链 - const extUrl = formdata.get('url'); - if (extUrl === null || extUrl === undefined) { - return new Response('Error: No url provided', { status: 400 }); - } - metadata.ExternalLink = extUrl; - // 写入KV数据库 - try { - await env.img_url.put(fullId, "", { - metadata: metadata, - }); - } catch (error) { - return new Response('Error: Failed to write to KV database', { status: 500 }); - } - - // 返回结果 - return new Response( - JSON.stringify([{ 'src': `${returnLink}` }]), - { - status: 200, - headers: { 'Content-Type': 'application/json' } - } - ); -} - // 图像审查 async function moderateContent(env, url, metadata) { const apikey = moderateContentApiKey; @@ -705,4 +675,4 @@ function generateShortId(length = 8) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; -} \ No newline at end of file +}