Skip to content

Commit a03766a

Browse files
committed
update v2.5.7
1 parent 62357b5 commit a03766a

33 files changed

+314
-107
lines changed
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

css/157.3f84c0cf.css.gz

13.7 KB
Binary file not shown.

css/739.00786d88.css.gz

-13.7 KB
Binary file not shown.

functions/api/manage/block/[[path]].js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { purgeCFCache } from "../../../utils/purgeCache";
1+
import { purgeCFCache, purgeRandomFileListCache, purgePublicFileListCache } from "../../../utils/purgeCache";
22
import { addFileToIndex } from "../../../utils/indexManager.js";
33
import { getDatabase } from "../../../utils/databaseAdapter.js";
44

@@ -15,12 +15,12 @@ export async function onRequest(context) {
1515

1616
// 组装 CDN URL
1717
const url = new URL(request.url);
18-
18+
1919
if (params.path) {
2020
params.path = String(params.path).split(',').join('/');
2121
}
2222
const cdnUrl = `https://${url.hostname}/file/${params.path}`;
23-
23+
2424
// 解码params.path
2525
params.path = decodeURIComponent(params.path);
2626

@@ -36,9 +36,13 @@ export async function onRequest(context) {
3636
// 清除CDN缓存
3737
await purgeCFCache(env, cdnUrl);
3838

39+
// 清除 randomFileList 等API缓存
40+
const normalizedFolder = params.path.split('/').slice(0, -1).join('/');
41+
await purgeRandomFileListCache(url.origin, normalizedFolder);
42+
await purgePublicFileListCache(url.origin, normalizedFolder);
43+
3944
// 更新索引
4045
waitUntil(addFileToIndex(context, params.path, value.metadata));
4146

4247
return new Response(info);
43-
44-
}
48+
}

functions/api/manage/delete/[[path]].js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { S3Client, DeleteObjectCommand } from "@aws-sdk/client-s3";
2-
import { purgeCFCache } from "../../../utils/purgeCache";
2+
import { purgeCFCache, purgeRandomFileListCache, purgePublicFileListCache } from "../../../utils/purgeCache";
33
import { removeFileFromIndex, batchRemoveFilesFromIndex } from "../../../utils/indexManager.js";
44
import { getDatabase } from '../../../utils/databaseAdapter.js';
55
import { DiscordAPI } from '../../../utils/discordAPI.js';
@@ -161,18 +161,10 @@ async function deleteFile(env, fileId, cdnUrl, url) {
161161
// 清除CDN缓存
162162
await purgeCFCache(env, cdnUrl);
163163

164-
// 清除randomFileList API缓存
165-
try {
166-
const cache = caches.default;
167-
const nullResponse = new Response(null, {
168-
headers: { 'Cache-Control': 'max-age=0' },
169-
});
170-
171-
const normalizedFolder = fileId.split('/').slice(0, -1).join('/');
172-
await cache.put(`${url.origin}/api/randomFileList?dir=${normalizedFolder}`, nullResponse);
173-
} catch (error) {
174-
console.error('Failed to clear cache:', error);
175-
}
164+
// 清除 api/randomFileList 等API缓存
165+
const normalizedFolder = fileId.split('/').slice(0, -1).join('/');
166+
await purgeRandomFileListCache(url.origin, normalizedFolder);
167+
await purgePublicFileListCache(url.origin, normalizedFolder);
176168

177169
return true;
178170
} catch (e) {

functions/api/manage/move/[[path]].js

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { S3Client, CopyObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
2-
import { purgeCFCache } from "../../../utils/purgeCache";
2+
import { purgeCFCache, purgeRandomFileListCache, purgePublicFileListCache } from "../../../utils/purgeCache";
33
import { moveFileInIndex, batchMoveFilesInIndex } from "../../../utils/indexManager.js";
44
import { getDatabase } from '../../../utils/databaseAdapter.js';
55

@@ -177,20 +177,11 @@ async function moveFile(env, fileId, newFileId, cdnUrl, url) {
177177
// 清除CDN缓存
178178
await purgeCFCache(env, cdnUrl);
179179

180-
// 清除randomFileList API缓存
181-
try {
182-
const cache = caches.default;
183-
const nullResponse = new Response(null, {
184-
headers: { 'Cache-Control': 'max-age=0' },
185-
});
186-
187-
const normalizedFolder = fileId.split('/').slice(0, -1).join('/');
188-
const normalizedDist = newFileId.split('/').slice(0, -1).join('/');
189-
await cache.put(`${url.origin}/api/randomFileList?dir=${normalizedFolder}`, nullResponse);
190-
await cache.put(`${url.origin}/api/randomFileList?dir=${normalizedDist}`, nullResponse);
191-
} catch (error) {
192-
console.error('Failed to clear cache:', error);
193-
}
180+
// 清除 api/randomFileList 等API缓存
181+
const normalizedFolder = fileId.split('/').slice(0, -1).join('/');
182+
const normalizedDist = newFileId.split('/').slice(0, -1).join('/');
183+
await purgeRandomFileListCache(url.origin, normalizedFolder, normalizedDist);
184+
await purgePublicFileListCache(url.origin, normalizedFolder, normalizedDist);
194185

195186
return true;
196187
} catch (e) {

functions/api/manage/white/[[path]].js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { purgeCFCache } from "../../../utils/purgeCache";
1+
import { purgeCFCache, purgeRandomFileListCache, purgePublicFileListCache } from "../../../utils/purgeCache";
22
import { addFileToIndex } from "../../../utils/indexManager.js";
33
import { getDatabase } from "../../../utils/databaseAdapter.js";
44

@@ -15,12 +15,12 @@ export async function onRequest(context) {
1515

1616
// 组装 CDN URL
1717
const url = new URL(request.url);
18-
18+
1919
if (params.path) {
2020
params.path = String(params.path).split(',').join('/');
2121
}
2222
const cdnUrl = `https://${url.hostname}/file/${params.path}`;
23-
23+
2424
// 解码params.path
2525
params.path = decodeURIComponent(params.path);
2626

@@ -36,9 +36,13 @@ export async function onRequest(context) {
3636
// 清除CDN缓存
3737
await purgeCFCache(env, cdnUrl);
3838

39+
// 清除 randomFileList 等API缓存
40+
const normalizedFolder = params.path.split('/').slice(0, -1).join('/');
41+
await purgeRandomFileListCache(url.origin, normalizedFolder);
42+
await purgePublicFileListCache(url.origin, normalizedFolder);
43+
3944
// 更新索引
4045
waitUntil(addFileToIndex(context, params.path, value.metadata));
4146

4247
return new Response(info);
43-
44-
}
48+
}

functions/api/public/list.js

Lines changed: 83 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,70 @@ function isAllowedDirectory(dir, allowedDirs) {
4747
return false;
4848
}
4949

50+
/**
51+
* 获取公开浏览文件列表(带缓存)
52+
* @param {Object} context - 上下文对象
53+
* @param {URL} url - 请求URL
54+
* @param {string} dir - 目录
55+
* @param {boolean} recursive - 是否递归
56+
* @returns {Promise<Object>} 文件列表和目录列表,包含 fromCache 字段
57+
*/
58+
async function getPublicFileList(context, url, dir, recursive) {
59+
// 构建缓存键(目录格式去掉末尾的/,与清除缓存时的格式一致)
60+
const cacheDir = dir.replace(/\/$/, '');
61+
const cacheKey = `${url.origin}/api/publicFileList?dir=${cacheDir}&recursive=${recursive}`;
62+
63+
// 检查缓存中是否有记录
64+
const cache = caches.default;
65+
const cacheRes = await cache.match(cacheKey);
66+
if (cacheRes) {
67+
const data = JSON.parse(await cacheRes.text());
68+
data.fromCache = true;
69+
return data;
70+
}
71+
72+
// 读取文件列表
73+
const result = await readIndex(context, {
74+
directory: dir,
75+
start: 0,
76+
count: -1,
77+
includeSubdirFiles: recursive,
78+
accessStatus: 'normal', // 只返回正常可访问的内容
79+
});
80+
81+
if (!result.success) {
82+
return { files: [], directories: [], totalCount: 0, fromCache: false };
83+
}
84+
85+
// 转换文件格式(只保留必要信息)
86+
const files = result.files.map(file => ({
87+
id: file.id,
88+
metadata: {
89+
FileType: file.metadata?.FileType,
90+
TimeStamp: file.metadata?.TimeStamp,
91+
FileSize: file.metadata?.FileSize,
92+
}
93+
}));
94+
95+
const cacheData = {
96+
files,
97+
directories: result.directories,
98+
totalCount: result.totalCount,
99+
};
100+
101+
// 缓存结果,缓存时间为24小时
102+
await cache.put(cacheKey, new Response(JSON.stringify(cacheData), {
103+
headers: {
104+
"Content-Type": "application/json",
105+
}
106+
}), {
107+
expirationTtl: 24 * 60 * 60
108+
});
109+
110+
cacheData.fromCache = false;
111+
return cacheData;
112+
}
113+
50114
export async function onRequest(context) {
51115
const { request, env } = context;
52116
const url = new URL(request.url);
@@ -88,13 +152,13 @@ export async function onRequest(context) {
88152
let dir = url.searchParams.get('dir') || '';
89153
let search = url.searchParams.get('search') || '';
90154
if (search) {
91-
search = decodeURIComponent(search).trim();
155+
search = decodeURIComponent(search).trim().toLowerCase();
92156
}
93-
157+
94158
// 获取高级搜索参数
95159
const recursive = url.searchParams.get('recursive') === 'true';
96160
const fileType = url.searchParams.get('type') || ''; // image, video, audio, other
97-
161+
98162
// 检查目录权限
99163
if (!isAllowedDirectory(dir, allowedDirs)) {
100164
return new Response(JSON.stringify({ error: 'Directory not allowed' }), {
@@ -115,47 +179,32 @@ export async function onRequest(context) {
115179
const start = parseInt(url.searchParams.get('start'), 10) || 0;
116180
const count = parseInt(url.searchParams.get('count'), 10) || 50;
117181

118-
// 读取文件列表(获取全部,因为需要先过滤 block/adult)
119-
const result = await readIndex(context, {
120-
directory: dir,
121-
search,
122-
start: 0,
123-
count: -1, // 获取全部
124-
includeSubdirFiles: recursive,
125-
});
126-
127-
if (!result.success) {
128-
return new Response(JSON.stringify({ error: 'Failed to read file list' }), {
129-
status: 500,
130-
headers: { 'Content-Type': 'application/json', ...corsHeaders }
131-
});
132-
}
182+
// 获取文件列表(带缓存)
183+
const cachedData = await getPublicFileList(context, url, dir, recursive);
133184

134185
// 过滤子目录,只返回允许的目录
135-
const filteredDirectories = result.directories.filter(subDir => {
186+
const filteredDirectories = cachedData.directories.filter(subDir => {
136187
return isAllowedDirectory(subDir, allowedDirs);
137188
});
138189

139190
// 文件类型过滤辅助函数
140191
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'avif'];
141192
const videoExts = ['mp4', 'webm', 'ogg', 'mov', 'm4v', 'mkv', 'avi', '3gp', 'mpeg', 'mpg', 'flv', 'wmv', 'ts', 'rmvb'];
142193
const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma', 'ape', 'opus'];
143-
194+
144195
const getFileExt = (name) => (name.split('.').pop() || '').toLowerCase();
145196
const isImageFile = (name) => imageExts.includes(getFileExt(name));
146197
const isVideoFile = (name) => videoExts.includes(getFileExt(name));
147198
const isAudioFile = (name) => audioExts.includes(getFileExt(name));
148199

149-
// 过滤掉 block 和 adult 图片(公开浏览不应显示这些内容)
150-
let filteredFiles = result.files.filter(file => {
151-
const listType = file.metadata?.ListType;
152-
const label = file.metadata?.Label;
153-
// 排除被屏蔽的和成人内容
154-
if (listType === 'Block' || label === 'adult') {
155-
return false;
156-
}
157-
return true;
158-
});
200+
let filteredFiles = cachedData.files;
201+
202+
// 搜索过滤
203+
if (search) {
204+
filteredFiles = filteredFiles.filter(file => {
205+
return file.id.toLowerCase().includes(search);
206+
});
207+
}
159208

160209
// 按文件类型过滤
161210
if (fileType) {
@@ -176,23 +225,19 @@ export async function onRequest(context) {
176225
// 过滤后再分页
177226
filteredFiles = filteredFiles.slice(start, start + count);
178227

179-
// 转换文件格式(只返回必要信息,隐藏敏感元数据)
228+
// 转换文件格式
180229
const safeFiles = filteredFiles.map(file => ({
181230
name: file.id,
182-
metadata: {
183-
FileType: file.metadata?.FileType,
184-
TimeStamp: file.metadata?.TimeStamp,
185-
FileSize: file.metadata?.FileSize,
186-
// 不返回 Channel、IP、Label 等敏感信息
187-
}
231+
metadata: file.metadata
188232
}));
189233

190234
return new Response(JSON.stringify({
191235
files: safeFiles,
192236
directories: filteredDirectories,
193-
totalCount: fileType ? filteredTotalCount : result.totalCount,
237+
totalCount: (search || fileType) ? filteredTotalCount : cachedData.totalCount,
194238
returnedCount: safeFiles.length,
195239
allowedDirs: allowedDirs, // 返回允许的目录列表供前端使用
240+
fromCache: cachedData.fromCache,
196241
}), {
197242
headers: { 'Content-Type': 'application/json', ...corsHeaders }
198243
});

functions/random/index.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export async function onRequest(context) {
4040
fileType = fileType.split(',');
4141
}
4242

43+
// 读取图片方向参数:landscape(横图), portrait(竖图), square(方图)
44+
const orientation = requestUrl.searchParams.get('orientation') || '';
45+
4346
// 读取指定文件夹
4447
const paramDir = requestUrl.searchParams.get('dir') || '';
4548
const dir = paramDir.replace(/^\/+/, '').replace(/\/{2,}/g, '/').replace(/\/$/, '');
@@ -62,6 +65,27 @@ export async function onRequest(context) {
6265
// 筛选出符合fileType要求的记录
6366
allRecords = allRecords.filter(item => { return fileType.some(type => item.FileType?.includes(type)) });
6467

68+
// 根据图片方向筛选
69+
if (orientation && allRecords.length > 0) {
70+
const SQUARE_THRESHOLD = 0.1; // 宽高比差异小于10%视为方图
71+
allRecords = allRecords.filter(item => {
72+
// 如果没有尺寸信息,跳过该记录
73+
if (!item.Width || !item.Height) return false;
74+
75+
const ratio = item.Width / item.Height;
76+
switch (orientation) {
77+
case 'landscape': // 横图:宽 > 高
78+
return ratio > (1 + SQUARE_THRESHOLD);
79+
case 'portrait': // 竖图:高 > 宽
80+
return ratio < (1 - SQUARE_THRESHOLD);
81+
case 'square': // 方图:宽 ≈ 高
82+
return ratio >= (1 - SQUARE_THRESHOLD) && ratio <= (1 + SQUARE_THRESHOLD);
83+
default:
84+
return true;
85+
}
86+
});
87+
}
88+
6589

6690
if (allRecords.length == 0) {
6791
return new Response(JSON.stringify({}), { status: 200 });
@@ -109,13 +133,15 @@ async function getRandomFileList(context, url, dir) {
109133
return JSON.parse(await cacheRes.text());
110134
}
111135

112-
let allRecords = await readIndex(context, { directory: dir, count: -1, includeSubdirFiles: true });
136+
let allRecords = await readIndex(context, { directory: dir, count: -1, includeSubdirFiles: true, accessStatus: 'normal' });
113137

114-
// 仅保留记录的name和metadata中的FileType字段
138+
// 仅保留记录的name和metadata中的必要字段
115139
allRecords = allRecords.files?.map(item => {
116140
return {
117141
name: item.id,
118-
FileType: item.metadata?.FileType
142+
FileType: item.metadata?.FileType,
143+
Width: item.metadata?.Width,
144+
Height: item.metadata?.Height
119145
}
120146
});
121147

0 commit comments

Comments
 (0)