mirror of
https://github.com/MarSeventh/CloudFlare-ImgBed.git
synced 2026-01-31 09:03:19 +08:00
Merge pull request #418 from axibayuit-a11y/pr-for-upstream
feat: 公开浏览功能增强 & Discord API 429重试
This commit is contained in:
@@ -84,9 +84,17 @@ export async function onRequest(context) {
|
||||
const allowedDirStr = publicBrowse.allowedDir || '';
|
||||
let allowedDirs = allowedDirStr.split(',').map(d => d.trim()).filter(d => d);
|
||||
|
||||
// 获取请求的目录
|
||||
// 获取请求的目录和搜索参数
|
||||
let dir = url.searchParams.get('dir') || '';
|
||||
|
||||
let search = url.searchParams.get('search') || '';
|
||||
if (search) {
|
||||
search = decodeURIComponent(search).trim();
|
||||
}
|
||||
|
||||
// 获取高级搜索参数
|
||||
const recursive = url.searchParams.get('recursive') === 'true';
|
||||
const fileType = url.searchParams.get('type') || ''; // image, video, audio, other
|
||||
|
||||
// 检查目录权限
|
||||
if (!isAllowedDirectory(dir, allowedDirs)) {
|
||||
return new Response(JSON.stringify({ error: 'Directory not allowed' }), {
|
||||
@@ -107,12 +115,13 @@ export async function onRequest(context) {
|
||||
const start = parseInt(url.searchParams.get('start'), 10) || 0;
|
||||
const count = parseInt(url.searchParams.get('count'), 10) || 50;
|
||||
|
||||
// 读取文件列表
|
||||
// 读取文件列表(获取全部,因为需要先过滤 block/adult)
|
||||
const result = await readIndex(context, {
|
||||
directory: dir,
|
||||
start,
|
||||
count,
|
||||
includeSubdirFiles: false,
|
||||
search,
|
||||
start: 0,
|
||||
count: -1, // 获取全部
|
||||
includeSubdirFiles: recursive,
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
@@ -127,8 +136,48 @@ export async function onRequest(context) {
|
||||
return isAllowedDirectory(subDir, allowedDirs);
|
||||
});
|
||||
|
||||
// 文件类型过滤辅助函数
|
||||
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'avif'];
|
||||
const videoExts = ['mp4', 'webm', 'ogg', 'mov', 'm4v', 'mkv', 'avi', '3gp', 'mpeg', 'mpg', 'flv', 'wmv', 'ts', 'rmvb'];
|
||||
const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma', 'ape', 'opus'];
|
||||
|
||||
const getFileExt = (name) => (name.split('.').pop() || '').toLowerCase();
|
||||
const isImageFile = (name) => imageExts.includes(getFileExt(name));
|
||||
const isVideoFile = (name) => videoExts.includes(getFileExt(name));
|
||||
const isAudioFile = (name) => audioExts.includes(getFileExt(name));
|
||||
|
||||
// 过滤掉 block 和 adult 图片(公开浏览不应显示这些内容)
|
||||
let filteredFiles = result.files.filter(file => {
|
||||
const listType = file.metadata?.ListType;
|
||||
const label = file.metadata?.Label;
|
||||
// 排除被屏蔽的和成人内容
|
||||
if (listType === 'Block' || label === 'adult') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// 按文件类型过滤
|
||||
if (fileType) {
|
||||
filteredFiles = filteredFiles.filter(file => {
|
||||
const name = file.id;
|
||||
switch (fileType) {
|
||||
case 'image': return isImageFile(name);
|
||||
case 'video': return isVideoFile(name);
|
||||
case 'audio': return isAudioFile(name);
|
||||
case 'other': return !isImageFile(name) && !isVideoFile(name) && !isAudioFile(name);
|
||||
default: return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 计算过滤后的总数和分页
|
||||
const filteredTotalCount = filteredFiles.length;
|
||||
// 过滤后再分页
|
||||
filteredFiles = filteredFiles.slice(start, start + count);
|
||||
|
||||
// 转换文件格式(只返回必要信息,隐藏敏感元数据)
|
||||
const safeFiles = result.files.map(file => ({
|
||||
const safeFiles = filteredFiles.map(file => ({
|
||||
name: file.id,
|
||||
metadata: {
|
||||
FileType: file.metadata?.FileType,
|
||||
@@ -141,8 +190,8 @@ export async function onRequest(context) {
|
||||
return new Response(JSON.stringify({
|
||||
files: safeFiles,
|
||||
directories: filteredDirectories,
|
||||
totalCount: result.totalCount,
|
||||
returnedCount: result.returnedCount,
|
||||
totalCount: fileType ? filteredTotalCount : result.totalCount,
|
||||
returnedCount: safeFiles.length,
|
||||
allowedDirs: allowedDirs, // 返回允许的目录列表供前端使用
|
||||
}), {
|
||||
headers: { 'Content-Type': 'application/json', ...corsHeaders }
|
||||
|
||||
@@ -83,26 +83,46 @@ export class DiscordAPI {
|
||||
* 获取消息信息(用于获取文件 URL)
|
||||
* @param {string} channelId - 频道 ID
|
||||
* @param {string} messageId - 消息 ID
|
||||
* @param {number} maxRetries - 最大重试次数(默认 3 次)
|
||||
* @returns {Promise<Object|null>} 消息数据或 null
|
||||
*/
|
||||
async getMessage(channelId, messageId) {
|
||||
try {
|
||||
const response = await fetch(`${this.baseURL}/channels/${channelId}/messages/${messageId}`, {
|
||||
method: 'GET',
|
||||
headers: this.defaultHeaders
|
||||
});
|
||||
async getMessage(channelId, messageId, maxRetries = 3) {
|
||||
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
const response = await fetch(`${this.baseURL}/channels/${channelId}/messages/${messageId}`, {
|
||||
method: 'GET',
|
||||
headers: this.defaultHeaders
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Discord getMessage error:', response.status, response.statusText);
|
||||
// 429 速率限制:等待后重试
|
||||
if (response.status === 429) {
|
||||
const retryAfter = response.headers.get('Retry-After');
|
||||
const waitTime = retryAfter ? parseFloat(retryAfter) * 1000 : 1000 * (attempt + 1);
|
||||
console.warn(`Discord 429 rate limit, waiting ${waitTime}ms before retry ${attempt + 1}/${maxRetries}`);
|
||||
|
||||
if (attempt < maxRetries) {
|
||||
await new Promise(resolve => setTimeout(resolve, waitTime));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Discord getMessage error:', response.status, response.statusText);
|
||||
return null;
|
||||
}
|
||||
|
||||
const messageData = await response.json();
|
||||
return messageData;
|
||||
} catch (error) {
|
||||
console.error('Error getting Discord message:', error.message);
|
||||
if (attempt < maxRetries) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500 * (attempt + 1)));
|
||||
continue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const messageData = await response.json();
|
||||
return messageData;
|
||||
} catch (error) {
|
||||
console.error('Error getting Discord message:', error.message);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user