Files
CloudFlare-ImgBed/functions/utils/sysConfig.js
T
htazq 9c7a2b4336 Enable third-party WebDAV storage without new runtime dependencies
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
2026-04-28 00:05:12 +08:00

137 lines
5.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 }
};
}
}