mirror of
https://github.com/MarSeventh/CloudFlare-ImgBed.git
synced 2026-01-31 09:03:19 +08:00
feat: add HuggingFace storage channel support
- Add HuggingFace API wrapper class (huggingfaceAPI.js) - Support upload, download, delete operations via HuggingFace Hub API - Support public repos (unlimited storage) and private repos (100GB limit) - Private repos: server proxies requests with Authorization header - Auto-create repo if not exists (with write token) - Add HuggingFace to auto-retry channel list - Environment variables: HF_TOKEN, HF_REPO, HF_PRIVATE - Support load balancing for multiple HuggingFace channels
This commit is contained in:
@@ -3,6 +3,7 @@ import { purgeCFCache } from "../../../utils/purgeCache";
|
||||
import { removeFileFromIndex, batchRemoveFilesFromIndex } from "../../../utils/indexManager.js";
|
||||
import { getDatabase } from '../../../utils/databaseAdapter.js';
|
||||
import { DiscordAPI } from '../../../utils/discordAPI.js';
|
||||
import { HuggingFaceAPI } from '../../../utils/huggingfaceAPI.js';
|
||||
|
||||
// CORS 跨域响应头
|
||||
const corsHeaders = {
|
||||
@@ -148,6 +149,11 @@ async function deleteFile(env, fileId, cdnUrl, url) {
|
||||
await deleteDiscordFile(img);
|
||||
}
|
||||
|
||||
// HuggingFace 渠道的图片,需要删除 HuggingFace 中对应的文件
|
||||
if (img.metadata?.Channel === 'HuggingFace') {
|
||||
await deleteHuggingFaceFile(img);
|
||||
}
|
||||
|
||||
// 删除数据库中的记录
|
||||
// 注意:容量统计现在由索引自动维护,删除文件后索引更新时会自动重新计算
|
||||
await db.delete(fileId);
|
||||
@@ -224,4 +230,30 @@ async function deleteDiscordFile(img) {
|
||||
console.error("Discord Delete Failed:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 删除 HuggingFace 渠道的图片
|
||||
async function deleteHuggingFaceFile(img) {
|
||||
const token = img.metadata?.HfToken;
|
||||
const repo = img.metadata?.HfRepo;
|
||||
const filePath = img.metadata?.HfFilePath;
|
||||
const isPrivate = img.metadata?.HfIsPrivate || false;
|
||||
|
||||
if (!token || !repo || !filePath) {
|
||||
console.warn('HuggingFace file missing required metadata for deletion');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const huggingfaceAPI = new HuggingFaceAPI(token, repo, isPrivate);
|
||||
const success = await huggingfaceAPI.deleteFile(filePath, `Delete ${filePath}`);
|
||||
if (!success) {
|
||||
console.error('HuggingFace Delete Failed: API returned false');
|
||||
}
|
||||
return success;
|
||||
} catch (error) {
|
||||
console.error("HuggingFace Delete Failed:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,10 +217,54 @@ export async function getUploadConfig(db, env) {
|
||||
discord.loadBalance = discordLoadBalance
|
||||
|
||||
|
||||
// =====================读取 HuggingFace 渠道配置=====================
|
||||
const huggingface = {}
|
||||
const huggingfaceChannels = []
|
||||
huggingface.channels = huggingfaceChannels
|
||||
|
||||
// 从环境变量读取 HuggingFace 配置
|
||||
if (env.HF_TOKEN) {
|
||||
huggingfaceChannels.push({
|
||||
id: 1,
|
||||
name: 'HuggingFace_env',
|
||||
type: 'huggingface',
|
||||
savePath: 'environment variable',
|
||||
token: env.HF_TOKEN,
|
||||
repo: env.HF_REPO,
|
||||
isPrivate: env.HF_PRIVATE === 'true',
|
||||
enabled: true,
|
||||
fixed: true,
|
||||
})
|
||||
}
|
||||
|
||||
for (const hf of settingsKV.huggingface?.channels || []) {
|
||||
// 如果 savePath 是 environment variable,修改可变参数
|
||||
if (hf.savePath === 'environment variable') {
|
||||
// 如果环境变量未删除,进行覆盖操作
|
||||
if (huggingfaceChannels[0]) {
|
||||
huggingfaceChannels[0].enabled = hf.enabled
|
||||
huggingfaceChannels[0].isPrivate = hf.isPrivate
|
||||
}
|
||||
continue
|
||||
}
|
||||
// id 自增
|
||||
hf.id = huggingfaceChannels.length + 1
|
||||
huggingfaceChannels.push(hf)
|
||||
}
|
||||
|
||||
// 负载均衡
|
||||
const huggingfaceLoadBalance = settingsKV.huggingface?.loadBalance || {
|
||||
enabled: false,
|
||||
channels: [],
|
||||
}
|
||||
huggingface.loadBalance = huggingfaceLoadBalance
|
||||
|
||||
|
||||
settings.telegram = telegram
|
||||
settings.cfr2 = cfr2
|
||||
settings.s3 = s3
|
||||
settings.discord = discord
|
||||
settings.huggingface = huggingface
|
||||
|
||||
return settings;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
|
||||
import { fetchSecurityConfig } from "../utils/sysConfig";
|
||||
import { TelegramAPI } from "../utils/telegramAPI";
|
||||
import { DiscordAPI } from "../utils/discordAPI";
|
||||
import { HuggingFaceAPI } from "../utils/huggingfaceAPI";
|
||||
import { setCommonHeaders, setRangeHeaders, handleHeadRequest, getFileContent, isTgChannel,
|
||||
returnWithCheck, return404, isDomainAllowed } from './fileTools';
|
||||
import { getDatabase } from '../utils/databaseAdapter.js';
|
||||
@@ -78,6 +79,11 @@ export async function onRequest(context) { // Contents of context object
|
||||
return await handleDiscordFile(context, imgRecord.metadata, encodedFileName, fileType);
|
||||
}
|
||||
|
||||
/* HuggingFace 渠道 */
|
||||
if (imgRecord.metadata?.Channel === 'HuggingFace') {
|
||||
return await handleHuggingFaceFile(context, imgRecord.metadata, encodedFileName, fileType);
|
||||
}
|
||||
|
||||
/* 外链渠道 */
|
||||
if (imgRecord.metadata?.Channel === 'External') {
|
||||
// 直接重定向到外链
|
||||
@@ -539,3 +545,73 @@ async function handleDiscordFile(context, metadata, encodedFileName, fileType) {
|
||||
return new Response(`Error: Failed to fetch from Discord - ${error.message}`, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 处理 HuggingFace 文件读取
|
||||
async function handleHuggingFaceFile(context, metadata, encodedFileName, fileType) {
|
||||
const { request, url, Referer } = context;
|
||||
|
||||
try {
|
||||
const hfRepo = metadata.HfRepo;
|
||||
const hfFilePath = metadata.HfFilePath;
|
||||
const hfToken = metadata.HfToken;
|
||||
const hfIsPrivate = metadata.HfIsPrivate || false;
|
||||
|
||||
if (!hfRepo || !hfFilePath) {
|
||||
return new Response('Error: HuggingFace file info not found', { status: 500 });
|
||||
}
|
||||
|
||||
// 构建文件 URL
|
||||
const fileUrl = metadata.HfFileUrl || `https://huggingface.co/datasets/${hfRepo}/resolve/main/${hfFilePath}`;
|
||||
|
||||
// 处理 HEAD 请求
|
||||
if (request.method === 'HEAD') {
|
||||
const headers = new Headers();
|
||||
setCommonHeaders(headers, encodedFileName, fileType, Referer, url);
|
||||
return handleHeadRequest(headers);
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
const fetchHeaders = {};
|
||||
|
||||
// 私有仓库需要 Authorization
|
||||
if (hfIsPrivate && hfToken) {
|
||||
fetchHeaders['Authorization'] = `Bearer ${hfToken}`;
|
||||
}
|
||||
|
||||
// 支持 Range 请求
|
||||
const range = request.headers.get('Range');
|
||||
if (range) {
|
||||
fetchHeaders['Range'] = range;
|
||||
}
|
||||
|
||||
const response = await fetch(fileUrl, {
|
||||
method: 'GET',
|
||||
headers: fetchHeaders
|
||||
});
|
||||
|
||||
if (!response.ok && response.status !== 206) {
|
||||
return new Response(`Error: Failed to fetch from HuggingFace - ${response.status}`, { status: response.status });
|
||||
}
|
||||
|
||||
// 构建响应头
|
||||
const headers = new Headers();
|
||||
setCommonHeaders(headers, encodedFileName, fileType, Referer, url);
|
||||
|
||||
// 复制相关头部
|
||||
if (response.headers.get('Content-Length')) {
|
||||
headers.set('Content-Length', response.headers.get('Content-Length'));
|
||||
}
|
||||
if (response.headers.get('Content-Range')) {
|
||||
headers.set('Content-Range', response.headers.get('Content-Range'));
|
||||
}
|
||||
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
headers
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
return new Response(`Error: Failed to fetch from HuggingFace - ${error.message}`, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { initializeChunkedUpload, handleChunkUpload, uploadLargeFileToTelegram,
|
||||
import { handleChunkMerge } from "./chunkMerge";
|
||||
import { TelegramAPI } from "../utils/telegramAPI";
|
||||
import { DiscordAPI } from "../utils/discordAPI";
|
||||
import { HuggingFaceAPI } from "../utils/huggingfaceAPI";
|
||||
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
|
||||
import { getDatabase } from '../utils/databaseAdapter.js';
|
||||
|
||||
@@ -105,6 +106,9 @@ async function processFileUpload(context, formdata = null) {
|
||||
case 'discord':
|
||||
uploadChannel = 'Discord';
|
||||
break;
|
||||
case 'huggingface':
|
||||
uploadChannel = 'HuggingFace';
|
||||
break;
|
||||
case 'external':
|
||||
uploadChannel = 'External';
|
||||
break;
|
||||
@@ -200,6 +204,14 @@ async function processFileUpload(context, formdata = null) {
|
||||
} else {
|
||||
err = await res.text();
|
||||
}
|
||||
} else if (uploadChannel === 'HuggingFace') {
|
||||
// ---------------------HuggingFace 渠道------------------
|
||||
const res = await uploadFileToHuggingFace(context, fullId, metadata, returnLink);
|
||||
if (res.status === 200 || !autoRetry) {
|
||||
return res;
|
||||
} else {
|
||||
err = await res.text();
|
||||
}
|
||||
} else if (uploadChannel === 'External') {
|
||||
// --------------------外链渠道----------------------
|
||||
const res = await uploadFileToExternal(context, fullId, metadata, returnLink);
|
||||
@@ -633,12 +645,96 @@ async function uploadFileToDiscord(context, fullId, metadata, returnLink) {
|
||||
}
|
||||
|
||||
|
||||
// 上传到 HuggingFace
|
||||
async function uploadFileToHuggingFace(context, fullId, metadata, returnLink) {
|
||||
const { env, waitUntil, uploadConfig, formdata } = context;
|
||||
const db = getDatabase(env);
|
||||
|
||||
// 获取 HuggingFace 渠道配置
|
||||
const hfSettings = uploadConfig.huggingface;
|
||||
if (!hfSettings || !hfSettings.channels || hfSettings.channels.length === 0) {
|
||||
return createResponse('Error: No HuggingFace channel configured', { status: 400 });
|
||||
}
|
||||
|
||||
// 选择渠道(支持负载均衡)
|
||||
const hfChannels = hfSettings.channels;
|
||||
const hfChannel = hfSettings.loadBalance?.enabled
|
||||
? hfChannels[Math.floor(Math.random() * hfChannels.length)]
|
||||
: hfChannels[0];
|
||||
|
||||
if (!hfChannel || !hfChannel.token || !hfChannel.repo) {
|
||||
return createResponse('Error: HuggingFace channel not properly configured', { status: 400 });
|
||||
}
|
||||
|
||||
const file = formdata.get('file');
|
||||
const fileName = metadata.FileName;
|
||||
|
||||
// 构建文件路径:images/年月/文件名
|
||||
const now = new Date();
|
||||
const yearMonth = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}`;
|
||||
const hfFilePath = `images/${yearMonth}/${fullId}`;
|
||||
|
||||
const huggingfaceAPI = new HuggingFaceAPI(hfChannel.token, hfChannel.repo, hfChannel.isPrivate || false);
|
||||
|
||||
try {
|
||||
// 上传文件到 HuggingFace
|
||||
const result = await huggingfaceAPI.uploadFile(file, hfFilePath, `Upload ${fileName}`);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error('Failed to upload file to HuggingFace');
|
||||
}
|
||||
|
||||
// 更新 metadata
|
||||
metadata.Channel = "HuggingFace";
|
||||
metadata.ChannelName = hfChannel.name || "HuggingFace_env";
|
||||
metadata.FileSize = (file.size / 1024 / 1024).toFixed(2);
|
||||
metadata.HfRepo = hfChannel.repo;
|
||||
metadata.HfFilePath = hfFilePath;
|
||||
metadata.HfToken = hfChannel.token;
|
||||
metadata.HfIsPrivate = hfChannel.isPrivate || false;
|
||||
metadata.HfFileUrl = result.fileUrl;
|
||||
|
||||
// 图像审查(公开仓库直接访问,私有仓库需要代理)
|
||||
let moderateUrl = result.fileUrl;
|
||||
if (!hfChannel.isPrivate) {
|
||||
metadata.Label = await moderateContent(env, moderateUrl);
|
||||
} else {
|
||||
// 私有仓库暂不支持图像审查,标记为 None
|
||||
metadata.Label = "None";
|
||||
}
|
||||
|
||||
// 写入 KV 数据库
|
||||
try {
|
||||
await db.put(fullId, "", { metadata });
|
||||
} catch (error) {
|
||||
return createResponse('Error: Failed to write to KV database', { status: 500 });
|
||||
}
|
||||
|
||||
// 结束上传
|
||||
waitUntil(endUpload(context, fullId, metadata));
|
||||
|
||||
// 返回成功响应
|
||||
return createResponse(
|
||||
JSON.stringify([{ 'src': returnLink }]),
|
||||
{
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error('HuggingFace upload error:', error.message);
|
||||
return createResponse(`Error: HuggingFace upload failed - ${error.message}`, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 自动切换渠道重试
|
||||
async function tryRetry(err, context, uploadChannel, fullId, metadata, fileExt, fileName, fileType, returnLink) {
|
||||
const { env, url, formdata } = context;
|
||||
|
||||
// 渠道列表(Discord 因为有 10MB 限制,放在最后尝试)
|
||||
const channelList = ['CloudflareR2', 'TelegramNew', 'S3', 'Discord'];
|
||||
const channelList = ['CloudflareR2', 'TelegramNew', 'S3', 'HuggingFace', 'Discord'];
|
||||
const errMessages = {};
|
||||
errMessages[uploadChannel] = 'Error: ' + uploadChannel + err;
|
||||
|
||||
@@ -651,6 +747,8 @@ async function tryRetry(err, context, uploadChannel, fullId, metadata, fileExt,
|
||||
res = await uploadFileToTelegram(context, fullId, metadata, fileExt, fileName, fileType, returnLink);
|
||||
} else if (channelList[i] === 'S3') {
|
||||
res = await uploadFileToS3(context, fullId, metadata, returnLink);
|
||||
} else if (channelList[i] === 'HuggingFace') {
|
||||
res = await uploadFileToHuggingFace(context, fullId, metadata, returnLink);
|
||||
} else if (channelList[i] === 'Discord') {
|
||||
res = await uploadFileToDiscord(context, fullId, metadata, returnLink);
|
||||
}
|
||||
|
||||
219
functions/utils/huggingfaceAPI.js
Normal file
219
functions/utils/huggingfaceAPI.js
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Hugging Face Hub API 封装类
|
||||
* 用于上传文件到 Hugging Face 仓库并获取文件
|
||||
*/
|
||||
export class HuggingFaceAPI {
|
||||
constructor(token, repo, isPrivate = false) {
|
||||
this.token = token;
|
||||
this.repo = repo; // 格式: username/repo-name
|
||||
this.isPrivate = isPrivate;
|
||||
this.baseURL = 'https://huggingface.co';
|
||||
this.apiURL = 'https://huggingface.co/api';
|
||||
this.defaultHeaders = {
|
||||
'Authorization': `Bearer ${this.token}`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查仓库是否存在
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async repoExists() {
|
||||
try {
|
||||
const response = await fetch(`${this.apiURL}/datasets/${this.repo}`, {
|
||||
method: 'GET',
|
||||
headers: this.defaultHeaders
|
||||
});
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
console.error('Error checking repo existence:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建仓库(如果不存在)
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async createRepoIfNotExists() {
|
||||
try {
|
||||
const exists = await this.repoExists();
|
||||
if (exists) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.apiURL}/repos/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...this.defaultHeaders,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.repo.split('/')[1], // 仓库名(不含用户名)
|
||||
type: 'dataset',
|
||||
private: this.isPrivate
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(`Failed to create repo: ${response.status} - ${errorData.error || response.statusText}`);
|
||||
}
|
||||
|
||||
console.log(`Created HuggingFace repo: ${this.repo}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error creating repo:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到仓库
|
||||
* @param {File|Blob} file - 要上传的文件
|
||||
* @param {string} filePath - 存储路径(如 images/xxx.jpg)
|
||||
* @param {string} commitMessage - 提交信息
|
||||
* @returns {Promise<Object>} 上传结果
|
||||
*/
|
||||
async uploadFile(file, filePath, commitMessage = 'Upload file') {
|
||||
try {
|
||||
// 确保仓库存在
|
||||
await this.createRepoIfNotExists();
|
||||
|
||||
// 将文件转换为 base64
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const base64Content = this.arrayBufferToBase64(arrayBuffer);
|
||||
|
||||
// 使用 commit API 上传文件
|
||||
const response = await fetch(`${this.apiURL}/datasets/${this.repo}/commit/main`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...this.defaultHeaders,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
summary: commitMessage,
|
||||
operations: [{
|
||||
op: 'addOrUpdate',
|
||||
path: filePath,
|
||||
content: base64Content,
|
||||
encoding: 'base64'
|
||||
}]
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(`HuggingFace upload error: ${response.status} - ${errorData.error || response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
// 构建文件 URL
|
||||
const fileUrl = `${this.baseURL}/datasets/${this.repo}/resolve/main/${filePath}`;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
commitId: result.commitId || result.oid,
|
||||
filePath: filePath,
|
||||
fileUrl: fileUrl,
|
||||
fileSize: file.size
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('HuggingFace upload error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param {string} filePath - 文件路径
|
||||
* @param {string} commitMessage - 提交信息
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async deleteFile(filePath, commitMessage = 'Delete file') {
|
||||
try {
|
||||
const response = await fetch(`${this.apiURL}/datasets/${this.repo}/commit/main`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...this.defaultHeaders,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
summary: commitMessage,
|
||||
operations: [{
|
||||
op: 'delete',
|
||||
path: filePath
|
||||
}]
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.error('HuggingFace delete error:', response.status, errorData);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error deleting file from HuggingFace:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件内容(用于私有仓库)
|
||||
* @param {string} filePath - 文件路径
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
async getFileContent(filePath) {
|
||||
const fileUrl = `${this.baseURL}/datasets/${this.repo}/resolve/main/${filePath}`;
|
||||
|
||||
const response = await fetch(fileUrl, {
|
||||
headers: this.isPrivate ? this.defaultHeaders : {}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的公开访问 URL
|
||||
* @param {string} filePath - 文件路径
|
||||
* @returns {string}
|
||||
*/
|
||||
getFileURL(filePath) {
|
||||
return `${this.baseURL}/datasets/${this.repo}/resolve/main/${filePath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
* @param {string} filePath - 文件路径
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async fileExists(filePath) {
|
||||
try {
|
||||
const fileUrl = this.getFileURL(filePath);
|
||||
const response = await fetch(fileUrl, {
|
||||
method: 'HEAD',
|
||||
headers: this.isPrivate ? this.defaultHeaders : {}
|
||||
});
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayBuffer 转 Base64
|
||||
* @param {ArrayBuffer} buffer
|
||||
* @returns {string}
|
||||
*/
|
||||
arrayBufferToBase64(buffer) {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let binary = '';
|
||||
for (let i = 0; i < bytes.byteLength; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return btoa(binary);
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,7 @@ export async function fetchUploadConfig(env, context = null) {
|
||||
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);
|
||||
|
||||
// 根据容量限制过滤渠道(仅 R2 和 S3)
|
||||
// 需要 context 来调用 getIndexMeta
|
||||
@@ -78,7 +79,8 @@ export async function fetchUploadConfig(env, context = null) {
|
||||
telegram: { channels: [] },
|
||||
cfr2: { channels: [] },
|
||||
s3: { channels: [] },
|
||||
discord: { channels: [] }
|
||||
discord: { channels: [] },
|
||||
huggingface: { channels: [] }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user