mirror of
https://github.com/ZSCGR/CloudFlare-ImgBed.git
synced 2026-08-20 07:33:43 +08:00
205 lines
5.6 KiB
JavaScript
205 lines
5.6 KiB
JavaScript
/**
|
|
* 渠道配置匹配工具
|
|
* 负责从当前上传配置中定位历史文件 metadata 对应的渠道配置
|
|
*/
|
|
import { getUploadConfig } from '../../api/manage/sysConfig/upload.js';
|
|
|
|
// 旧 metadata 中可能保存过渠道身份字段,按强匹配到弱匹配依次尝试
|
|
const CHANNEL_IDENTITY_RULES = {
|
|
telegram: [
|
|
[
|
|
['botToken', 'TgBotToken'],
|
|
['chatId', 'TgChatId'],
|
|
],
|
|
],
|
|
s3: [
|
|
[
|
|
['accessKeyId', 'S3AccessKeyId'],
|
|
['endpoint', 'S3Endpoint'],
|
|
['bucketName', 'S3BucketName'],
|
|
],
|
|
[
|
|
['endpoint', 'S3Endpoint'],
|
|
['bucketName', 'S3BucketName'],
|
|
['pathStyle', 'S3PathStyle'],
|
|
],
|
|
[
|
|
['endpoint', 'S3Endpoint'],
|
|
['bucketName', 'S3BucketName'],
|
|
],
|
|
],
|
|
discord: [
|
|
[
|
|
['botToken', 'DiscordBotToken'],
|
|
['channelId', 'DiscordChannelId'],
|
|
],
|
|
],
|
|
huggingface: [
|
|
[
|
|
['repo', 'HfRepo'],
|
|
['isPrivate', 'HfIsPrivate'],
|
|
],
|
|
[
|
|
['repo', 'HfRepo'],
|
|
],
|
|
],
|
|
webdav: [
|
|
[
|
|
['baseUrl', 'WebDAVBaseUrl'],
|
|
['username', 'WebDAVUsername'],
|
|
],
|
|
[
|
|
['baseUrl', 'WebDAVBaseUrl'],
|
|
],
|
|
],
|
|
};
|
|
|
|
const URL_IDENTITY_FIELDS = new Set([
|
|
'endpoint',
|
|
'S3Endpoint',
|
|
'baseUrl',
|
|
'WebDAVBaseUrl',
|
|
]);
|
|
|
|
const BOOLEAN_IDENTITY_FIELDS = new Set([
|
|
'pathStyle',
|
|
'S3PathStyle',
|
|
'isPrivate',
|
|
'HfIsPrivate',
|
|
]);
|
|
|
|
/* ========== 主要函数 ========== */
|
|
|
|
// 加载上传渠道配置,失败时返回 null 并记录上下文
|
|
export async function loadChannelConfig(db, env, logContext = 'channel config') {
|
|
try {
|
|
return await getUploadConfig(db, env);
|
|
} catch (error) {
|
|
console.warn(`Failed to load upload config for ${logContext}:`, error.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// 根据 ChannelName、历史身份字段和旧 Telegram 默认名查找匹配渠道
|
|
export function findConfiguredChannel(uploadConfig, groupName, metadata = {}) {
|
|
if (!uploadConfig) return null;
|
|
|
|
const channels = uploadConfig[groupName]?.channels || [];
|
|
// 优先使用当前版本写入的渠道名,避免多渠道配置相似时误匹配
|
|
if (metadata?.ChannelName) {
|
|
const namedChannel = channels.find((channel) => channel.name === metadata.ChannelName);
|
|
if (namedChannel) return namedChannel;
|
|
}
|
|
|
|
const identityChannel = findChannelByPersistedIdentity(channels, groupName, metadata);
|
|
if (identityChannel) return identityChannel;
|
|
|
|
return findLegacyTelegramEnvChannel(channels, groupName, metadata);
|
|
}
|
|
|
|
/* ========== 关键函数 ========== */
|
|
|
|
// 兼容旧版默认 Telegram 环境变量渠道
|
|
function findLegacyTelegramEnvChannel(channels, groupName, metadata = {}) {
|
|
// 兼容未写入 ChannelName 的旧 Telegram 记录
|
|
if (groupName === 'telegram' && (metadata.Channel === 'Telegram' || metadata.Channel === 'TelegramNew')) {
|
|
return channels.find((channel) => channel.name === 'Telegram_env') || null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// 使用 metadata 中残留的身份字段反向匹配当前渠道配置
|
|
function findChannelByPersistedIdentity(channels, groupName, metadata = {}) {
|
|
const ruleGroups = CHANNEL_IDENTITY_RULES[groupName] || [];
|
|
|
|
for (const fields of ruleGroups) {
|
|
if (!hasAllMetadataFields(metadata, fields)) continue;
|
|
|
|
const matches = channels.filter((channel) => fieldsMatch(channel, metadata, fields));
|
|
if (matches.length === 1) {
|
|
return matches[0];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// 确保某组匹配规则所需的 metadata 字段都存在
|
|
function hasAllMetadataFields(metadata, fields) {
|
|
return fields.every(([, metadataKey]) => hasIdentityValue(metadata?.[metadataKey]));
|
|
}
|
|
|
|
// 比较渠道配置字段与 metadata 字段是否规范化后一致
|
|
function fieldsMatch(channel, metadata, fields) {
|
|
return fields.every(([channelKey, metadataKey]) => {
|
|
const channelValue = channel?.[channelKey];
|
|
const metadataValue = metadata?.[metadataKey];
|
|
|
|
if (!hasIdentityValue(channelValue) || !hasIdentityValue(metadataValue)) {
|
|
return false;
|
|
}
|
|
|
|
return normalizeIdentityValue(channelValue, channelKey) === normalizeIdentityValue(metadataValue, metadataKey);
|
|
});
|
|
}
|
|
|
|
/* ========== 工具函数 ========== */
|
|
|
|
// 判断身份字段是否可参与匹配
|
|
function hasIdentityValue(value) {
|
|
return value !== undefined && value !== null && value !== '';
|
|
}
|
|
|
|
// 根据字段类型规范化比较值
|
|
function normalizeIdentityValue(value, fieldName = '') {
|
|
const text = String(value).trim();
|
|
|
|
// URL 与布尔值需要规范化后再比较,避免协议、斜杠或类型差异导致匹配失败
|
|
if (URL_IDENTITY_FIELDS.has(fieldName)) {
|
|
return normalizeUrlIdentityValue(text);
|
|
}
|
|
|
|
if (BOOLEAN_IDENTITY_FIELDS.has(fieldName)) {
|
|
return normalizeBooleanIdentityValue(value);
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
// 将布尔身份字段统一成字符串形式
|
|
function normalizeBooleanIdentityValue(value) {
|
|
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
|
return String(value).trim().toLowerCase() === 'true' ? 'true' : 'false';
|
|
}
|
|
|
|
// 规范化 URL 身份字段,忽略协议、userinfo、查询、hash 和尾部斜杠差异
|
|
function normalizeUrlIdentityValue(value) {
|
|
try {
|
|
const url = new URL(ensureUrlProtocol(value));
|
|
url.username = '';
|
|
url.password = '';
|
|
url.hash = '';
|
|
url.search = '';
|
|
if (url.pathname.endsWith('/')) {
|
|
url.pathname = url.pathname.replace(/\/+$/, '/');
|
|
}
|
|
return url.toString()
|
|
.replace(/^https?:\/\//i, '')
|
|
.replace(/\/+$/, '');
|
|
} catch {
|
|
return value
|
|
.replace(/^https?:\/\//i, '')
|
|
.replace(/\/+$/, '');
|
|
}
|
|
}
|
|
|
|
// 为缺少协议的地址补充默认协议,便于 URL 解析
|
|
function ensureUrlProtocol(value) {
|
|
if (/^[a-z][a-z\d+.-]*:\/\//i.test(value)) {
|
|
return value;
|
|
}
|
|
|
|
return `https://${value}`;
|
|
}
|