mirror of
https://github.com/MarSeventh/CloudFlare-ImgBed.git
synced 2026-05-03 10:26:58 +00:00
253 lines
8.4 KiB
JavaScript
253 lines
8.4 KiB
JavaScript
import {
|
|
readIndex, mergeOperationsToIndex, deleteAllOperations, rebuildIndex,
|
|
getIndexInfo, getIndexStorageStats
|
|
} from '../../utils/indexManager.js';
|
|
import { getDatabase } from '../../utils/databaseAdapter.js';
|
|
|
|
// CORS 跨域响应头
|
|
const corsHeaders = {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
|
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
'Access-Control-Max-Age': '86400',
|
|
};
|
|
|
|
export async function onRequest(context) {
|
|
const { request, waitUntil } = context;
|
|
const url = new URL(request.url);
|
|
|
|
// 解析查询参数
|
|
let start = parseInt(url.searchParams.get('start'), 10) || 0;
|
|
let count = parseInt(url.searchParams.get('count'), 10) || 50;
|
|
let sum = url.searchParams.get('sum') === 'true';
|
|
let recursive = url.searchParams.get('recursive') === 'true';
|
|
let dir = url.searchParams.get('dir') || '';
|
|
let search = url.searchParams.get('search') || '';
|
|
let channel = url.searchParams.get('channel') || '';
|
|
let listType = url.searchParams.get('listType') || '';
|
|
let action = url.searchParams.get('action') || '';
|
|
let includeTags = url.searchParams.get('includeTags') || '';
|
|
let excludeTags = url.searchParams.get('excludeTags') || '';
|
|
|
|
// 处理搜索关键字
|
|
if (search) {
|
|
search = decodeURIComponent(search).trim();
|
|
}
|
|
|
|
// 处理标签参数
|
|
const includeTagsArray = includeTags ? includeTags.split(',').map(t => t.trim()).filter(t => t) : [];
|
|
const excludeTagsArray = excludeTags ? excludeTags.split(',').map(t => t.trim()).filter(t => t) : [];
|
|
|
|
// 处理目录参数
|
|
if (dir.startsWith('/')) {
|
|
dir = dir.substring(1);
|
|
}
|
|
if (dir && !dir.endsWith('/')) {
|
|
dir += '/';
|
|
}
|
|
|
|
try {
|
|
// 特殊操作:重建索引
|
|
if (action === 'rebuild') {
|
|
waitUntil(rebuildIndex(context, (processed) => {
|
|
console.log(`Rebuilt ${processed} files...`);
|
|
}));
|
|
|
|
return new Response('Index rebuilt asynchronously', {
|
|
headers: { "Content-Type": "text/plain", ...corsHeaders }
|
|
});
|
|
}
|
|
|
|
// 特殊操作:合并挂起的原子操作到索引
|
|
if (action === 'merge-operations') {
|
|
waitUntil(mergeOperationsToIndex(context));
|
|
|
|
return new Response('Operations merged into index asynchronously', {
|
|
headers: { "Content-Type": "text/plain", ...corsHeaders }
|
|
});
|
|
}
|
|
|
|
// 特殊操作:清除所有原子操作
|
|
if (action === 'delete-operations') {
|
|
waitUntil(deleteAllOperations(context));
|
|
|
|
return new Response('All operations deleted asynchronously', {
|
|
headers: { "Content-Type": "text/plain", ...corsHeaders }
|
|
});
|
|
}
|
|
|
|
// 特殊操作:获取索引存储信息
|
|
if (action === 'index-storage-stats') {
|
|
const stats = await getIndexStorageStats(context);
|
|
return new Response(JSON.stringify(stats), {
|
|
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
});
|
|
}
|
|
|
|
// 特殊操作:获取索引信息
|
|
if (action === 'info') {
|
|
const info = await getIndexInfo(context);
|
|
return new Response(JSON.stringify(info), {
|
|
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
});
|
|
}
|
|
|
|
// 普通查询:只返回总数
|
|
if (count === -1 && sum) {
|
|
const result = await readIndex(context, {
|
|
search,
|
|
directory: dir,
|
|
channel,
|
|
listType,
|
|
includeTags: includeTagsArray,
|
|
excludeTags: excludeTagsArray,
|
|
countOnly: true
|
|
});
|
|
|
|
return new Response(JSON.stringify({
|
|
sum: result.totalCount,
|
|
indexLastUpdated: result.indexLastUpdated
|
|
}), {
|
|
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
});
|
|
}
|
|
|
|
// 普通查询:返回数据
|
|
const result = await readIndex(context, {
|
|
search,
|
|
directory: dir,
|
|
start,
|
|
count,
|
|
channel,
|
|
listType,
|
|
includeTags: includeTagsArray,
|
|
excludeTags: excludeTagsArray,
|
|
includeSubdirFiles: recursive,
|
|
});
|
|
|
|
// 索引读取失败,直接从 KV 中获取所有文件记录
|
|
if (!result.success) {
|
|
const dbRecords = await getAllFileRecords(context.env, dir);
|
|
|
|
return new Response(JSON.stringify({
|
|
files: dbRecords.files,
|
|
directories: dbRecords.directories,
|
|
totalCount: dbRecords.totalCount,
|
|
directFileCount: dbRecords.directFileCount,
|
|
directFolderCount: dbRecords.directFolderCount,
|
|
returnedCount: dbRecords.returnedCount,
|
|
indexLastUpdated: Date.now(),
|
|
isIndexedResponse: false // 标记这是来自 KV 的响应
|
|
}), {
|
|
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
});
|
|
}
|
|
|
|
// 转换文件格式
|
|
const compatibleFiles = result.files.map(file => ({
|
|
name: file.id,
|
|
metadata: file.metadata
|
|
}));
|
|
|
|
return new Response(JSON.stringify({
|
|
files: compatibleFiles,
|
|
directories: result.directories,
|
|
totalCount: result.totalCount,
|
|
directFileCount: result.directFileCount,
|
|
directFolderCount: result.directFolderCount,
|
|
returnedCount: result.returnedCount,
|
|
indexLastUpdated: result.indexLastUpdated,
|
|
isIndexedResponse: true // 标记这是来自索引的响应
|
|
}), {
|
|
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error in list-indexed API:', error);
|
|
return new Response(JSON.stringify({
|
|
error: 'Internal server error',
|
|
message: error.message
|
|
}), {
|
|
status: 500,
|
|
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
});
|
|
}
|
|
}
|
|
|
|
async function getAllFileRecords(env, dir) {
|
|
const allRecords = [];
|
|
let cursor = null;
|
|
|
|
try {
|
|
const db = getDatabase(env);
|
|
|
|
while (true) {
|
|
const response = await db.list({
|
|
prefix: dir,
|
|
limit: 1000,
|
|
cursor: cursor
|
|
});
|
|
|
|
// 检查响应格式
|
|
if (!response || !response.keys || !Array.isArray(response.keys)) {
|
|
console.error('Invalid response from database list:', response);
|
|
break;
|
|
}
|
|
|
|
cursor = response.cursor;
|
|
|
|
for (const item of response.keys) {
|
|
// 跳过管理相关的键
|
|
if (item.name.startsWith('manage@') || item.name.startsWith('chunk_')) {
|
|
continue;
|
|
}
|
|
|
|
// 跳过没有元数据的文件
|
|
if (!item.metadata || !item.metadata.TimeStamp) {
|
|
continue;
|
|
}
|
|
|
|
allRecords.push(item);
|
|
}
|
|
|
|
if (!cursor) break;
|
|
|
|
// 添加协作点
|
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
}
|
|
|
|
// 提取目录信息
|
|
const directories = new Set();
|
|
const filteredRecords = [];
|
|
allRecords.forEach(item => {
|
|
const subDir = item.name.substring(dir.length);
|
|
const firstSlashIndex = subDir.indexOf('/');
|
|
if (firstSlashIndex !== -1) {
|
|
directories.add(dir + subDir.substring(0, firstSlashIndex));
|
|
} else {
|
|
filteredRecords.push(item);
|
|
}
|
|
});
|
|
|
|
return {
|
|
files: filteredRecords,
|
|
directories: Array.from(directories),
|
|
totalCount: allRecords.length,
|
|
directFileCount: filteredRecords.length,
|
|
directFolderCount: directories.size,
|
|
returnedCount: filteredRecords.length
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('Error in getAllFileRecords:', error);
|
|
return {
|
|
files: [],
|
|
directories: [],
|
|
totalCount: 0,
|
|
directFileCount: 0,
|
|
directFolderCount: 0,
|
|
returnedCount: 0,
|
|
error: error.message
|
|
};
|
|
}
|
|
} |