Merge pull request #418 from axibayuit-a11y/pr-for-upstream

feat: 公开浏览功能增强 & Discord API 429重试
This commit is contained in:
叁月柒
2026-01-03 00:38:29 +08:00
committed by GitHub
2 changed files with 92 additions and 23 deletions

View File

@@ -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 }

View File

@@ -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;
}
/**