mirror of
https://github.com/ZSCGR/CloudFlare-ImgBed.git
synced 2026-08-13 04:03:42 +08:00
Cloudflare Workers and Pages already provide the Fetch/Web APIs needed for WebDAV verbs, so the storage integration uses a small local helper instead of adding a Node-oriented WebDAV client. The channel now participates in upload, read, delete, move, rename, channel listing, and runtime config, with chunked uploads explicitly guarded because WebDAV has no portable server-side compose primitive. Constraint: Preserve Pages Functions and generated Worker deployment paths Constraint: No new npm dependency for WebDAV client behavior Rejected: Add a WebDAV npm client | likely Node API/compatibility and package-lock churn Rejected: Treat WebDAV as External URL only | not a complete storage channel lifecycle Confidence: high Scope-risk: moderate Directive: WebDAV here is third-party storage; keep it distinct from the built-in /dav server settings Tested: npm test; node worker/generate-routes.js; npx wrangler deploy --dry-run --config worker/wrangler.toml; git diff --cached --check Not-tested: Live third-party WebDAV provider credentials; Digest-only WebDAV authentication
137 lines
5.2 KiB
JavaScript
137 lines
5.2 KiB
JavaScript
import { getUploadConfig } from '../api/manage/sysConfig/upload.js';
|
||
import { getSecurityConfig } from '../api/manage/sysConfig/security.js';
|
||
import { getPageConfig } from '../api/manage/sysConfig/page.js';
|
||
import { getOthersConfig } from '../api/manage/sysConfig/others.js';
|
||
import { getDatabase } from './databaseAdapter.js';
|
||
import { getIndexMeta } from './indexManager.js';
|
||
|
||
/**
|
||
* 根据容量限制过滤渠道
|
||
* @param {Object} context - 上下文对象(包含 env)
|
||
* @param {Array} channels - 渠道列表
|
||
* @returns {Array} 过滤后的渠道列表
|
||
*/
|
||
async function filterChannelsByQuota(context, channels) {
|
||
// 先检查是否有任何渠道启用了容量限制,如果都没启用则跳过 KV 读取
|
||
const hasQuotaEnabled = channels.some(ch => ch.quota?.enabled && ch.quota?.limitGB);
|
||
if (!hasQuotaEnabled) {
|
||
return channels; // 无需读取 KV,直接返回所有渠道
|
||
}
|
||
|
||
// 获取索引元数据(只需 1 次读取)
|
||
const indexMeta = await getIndexMeta(context);
|
||
const channelStats = indexMeta.channelStats || {};
|
||
|
||
const result = [];
|
||
for (const channel of channels) {
|
||
// 未启用容量限制,直接通过
|
||
if (!channel.quota?.enabled || !channel.quota?.limitGB) {
|
||
result.push(channel);
|
||
continue;
|
||
}
|
||
|
||
try {
|
||
// 从索引元数据中获取该渠道的容量统计
|
||
const stats = channelStats[channel.name] || { usedMB: 0, fileCount: 0 };
|
||
|
||
const usedGB = stats.usedMB / 1024;
|
||
const limitGB = channel.quota.limitGB;
|
||
const threshold = channel.quota.threshold || 95;
|
||
|
||
// 未超过阈值,渠道可用
|
||
if ((usedGB / limitGB) * 100 < threshold) {
|
||
result.push(channel);
|
||
} else {
|
||
console.log(`Channel ${channel.name} quota exceeded: ${usedGB.toFixed(2)}GB / ${limitGB}GB (${threshold}% threshold)`);
|
||
}
|
||
} catch (error) {
|
||
console.error(`Failed to check quota for channel ${channel.name}:`, error);
|
||
// 检查失败时保守处理,允许使用该渠道
|
||
result.push(channel);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
export async function fetchUploadConfig(env, context = null) {
|
||
try {
|
||
const db = getDatabase(env);
|
||
const settings = await getUploadConfig(db, env);
|
||
// 去除 已禁用 的渠道
|
||
settings.telegram.channels = settings.telegram.channels.filter((channel) => channel.enabled);
|
||
settings.cfr2.channels = settings.cfr2.channels.filter((channel) => channel.enabled);
|
||
settings.s3.channels = settings.s3.channels.filter((channel) => channel.enabled);
|
||
settings.discord.channels = settings.discord.channels.filter((channel) => channel.enabled);
|
||
settings.huggingface.channels = settings.huggingface.channels.filter((channel) => channel.enabled);
|
||
settings.webdav.channels = settings.webdav.channels.filter((channel) => channel.enabled);
|
||
|
||
// 根据容量限制过滤渠道(可用于 R2、S3、WebDAV)
|
||
// 需要 context 来调用 getIndexMeta
|
||
if (context) {
|
||
settings.cfr2.channels = await filterChannelsByQuota(context, settings.cfr2.channels);
|
||
settings.s3.channels = await filterChannelsByQuota(context, settings.s3.channels);
|
||
settings.webdav.channels = await filterChannelsByQuota(context, settings.webdav.channels);
|
||
}
|
||
|
||
return settings;
|
||
} catch (error) {
|
||
console.error('Failed to fetch upload config:', error);
|
||
// 返回默认配置
|
||
return {
|
||
telegram: { channels: [] },
|
||
cfr2: { channels: [] },
|
||
s3: { channels: [] },
|
||
discord: { channels: [] },
|
||
huggingface: { channels: [] },
|
||
webdav: { channels: [] }
|
||
};
|
||
}
|
||
}
|
||
|
||
export async function fetchSecurityConfig(env) {
|
||
try {
|
||
const db = getDatabase(env);
|
||
const settings = await getSecurityConfig(db, env);
|
||
return settings;
|
||
} catch (error) {
|
||
console.error('Failed to fetch security config:', error);
|
||
// 返回默认配置
|
||
return {
|
||
auth: {
|
||
user: { authCode: "" },
|
||
admin: { adminUsername: "", adminPassword: "" }
|
||
},
|
||
upload: {
|
||
moderate: { enabled: false, channel: "default", moderateContentApiKey: "", nsfwApiPath: "" }
|
||
},
|
||
access: { allowedDomains: "", whiteListMode: false, sessionSecure: false, userSessionMaxAge: 14, adminSessionMaxAge: 14 }
|
||
};
|
||
}
|
||
}
|
||
|
||
export async function fetchPageConfig(env) {
|
||
try {
|
||
const db = getDatabase(env);
|
||
const settings = await getPageConfig(db, env);
|
||
return settings;
|
||
} catch (error) {
|
||
console.error('Failed to fetch page config:', error);
|
||
// 返回默认配置
|
||
return { config: [] };
|
||
}
|
||
}
|
||
|
||
export async function fetchOthersConfig(env) {
|
||
try {
|
||
const db = getDatabase(env);
|
||
const settings = await getOthersConfig(db, env);
|
||
return settings;
|
||
} catch (error) {
|
||
console.error('Failed to fetch others config:', error);
|
||
// 返回默认配置
|
||
return {
|
||
telemetry: { enabled: false }
|
||
};
|
||
}
|
||
}
|