Update cloudflare-worker.js

This commit is contained in:
牡丹凤凰 2024-12-07 15:28:57 +08:00 committed by GitHub
parent f66adcd217
commit 423fdb6992
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,309 +1,174 @@
// 配置信息 // 配置信息
const config = { const config = {
R2_CUSTOM_DOMAIN: 'cherrystudio.ocool.online', R2_CUSTOM_DOMAIN: 'cherrystudio.ocool.online',
R2_BUCKET_NAME: 'cherrystudio', R2_BUCKET_NAME: 'cherrystudio',
// 缓存键名 // 缓存键名
CACHE_KEY: 'cherry-studio-latest-release', CACHE_KEY: 'cherry-studio-latest-release',
VERSION_DB: 'versions.json', VERSION_DB: 'versions.json',
LOG_FILE: 'logs.json', LOG_FILE: 'logs.json',
MAX_LOGS: 1000 // 最多保存多少条日志 MAX_LOGS: 1000 // 最多保存多少条日志
}; };
// Worker 入口函数 // Worker 入口函数
const worker = { const worker = {
// 定时器触发配置 // 定时器触发配置
scheduled: { scheduled: {
cron: '*/1 * * * *' // 每分钟执行一次 cron: '*/1 * * * *' // 每分钟执行一次
}, },
// 定时器执行函数 - 只负责检查和更新 // 定时器执行函数 - 只负责检查和更新
async scheduled(event, env, ctx) { async scheduled(event, env, ctx) {
try { try {
await initDataFiles(env); await initDataFiles(env);
console.log('开始定时检查新版本...'); console.log('开始定时检查新版本...');
// 注意这里使用新的函数 // 使用新的 checkNewRelease 函数
await checkNewRelease(env); await checkNewRelease(env);
} catch (error) { } catch (error) {
console.error('定时任务执行失败:', error); console.error('定时任务执行失败:', error);
} }
}, },
// HTTP 请求处理函数 - 只负责返回数据 // HTTP 请求处理函数 - 只负责返回数据
async fetch(request, env, ctx) { async fetch(request, env, ctx) {
if (!env || !env.R2_BUCKET) { if (!env || !env.R2_BUCKET) {
return new Response(JSON.stringify({ return new Response(JSON.stringify({
error: 'R2 存储桶未正确配置' error: 'R2 存储桶未正确配置'
}), { }), {
status: 500, status: 500,
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }
}); });
} }
const url = new URL(request.url); const url = new URL(request.url);
const filename = url.pathname.slice(1); const filename = url.pathname.slice(1);
try { try {
// 处理文件下载请求 // 处理文件下载请求
if (filename) { if (filename) {
return await handleDownload(env, filename); return await handleDownload(env, filename);
} }
// 只返回缓存的版本信息 // 只返回缓存的版本信息
return await getCachedRelease(env); return await getCachedRelease(env);
} catch (error) { } catch (error) {
return new Response(JSON.stringify({ return new Response(JSON.stringify({
error: error.message, error: error.message,
stack: error.stack stack: error.stack
}), { }), {
status: 500, status: 500,
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }
}); });
} }
} }
}; };
export default worker; export default worker;
/** /**
* 添加日志记录函数 * 添加日志记录函数
*/ */
async function addLog(env, type, event, details = null) { async function addLog(env, type, event, details = null) {
try { try {
const logFile = await env.R2_BUCKET.get(config.LOG_FILE); const logFile = await env.R2_BUCKET.get(config.LOG_FILE);
let logs = { logs: [] }; let logs = { logs: [] };
if (logFile) { if (logFile) {
logs = JSON.parse(await logFile.text()); logs = JSON.parse(await logFile.text());
} }
logs.logs.unshift({ logs.logs.unshift({
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
type, type,
event, event,
details details
}); });
// 保持日志数量在限制内 // 保持日志数量在限制内
if (logs.logs.length > config.MAX_LOGS) { if (logs.logs.length > config.MAX_LOGS) {
logs.logs = logs.logs.slice(0, config.MAX_LOGS); logs.logs = logs.logs.slice(0, config.MAX_LOGS);
} }
await env.R2_BUCKET.put(config.LOG_FILE, JSON.stringify(logs, null, 2)); await env.R2_BUCKET.put(config.LOG_FILE, JSON.stringify(logs, null, 2));
} catch (error) { } catch (error) {
console.error('写入日志失败:', error); console.error('写入日志失败:', error);
}
} }
}
/** /**
* 检查并更新发布版本 * 获取最新版本信息
* 由定时器触发检查新版本并更新 R2 存储 */
*/ async function getLatestRelease(env) {
async function checkAndUpdateRelease(env) { try {
try {
// 获取版本数据库
const versionDB = await env.R2_BUCKET.get(config.VERSION_DB);
let versions = { versions: {}, latestVersion: null, lastChecked: null };
if (versionDB) {
versions = JSON.parse(await versionDB.text());
}
// 获取 GitHub 最新版本
const githubResponse = await fetch('https://api.github.com/repos/kangfenmao/cherry-studio/releases/latest', {
headers: { 'User-Agent': 'CloudflareWorker' },
});
if (!githubResponse.ok) {
throw new Error('GitHub API 请求失败');
}
const releaseData = await githubResponse.json();
const version = releaseData.tag_name;
// 更新最后检查时间
versions.lastChecked = new Date().toISOString();
// 检查是否需要更新
if (versions.latestVersion !== version) {
await addLog(env, 'INFO', `发现新版本: ${version}`);
// 准备新版本记录
const versionRecord = {
version,
publishedAt: releaseData.published_at,
uploadedAt: null,
files: releaseData.assets.map(asset => ({
name: asset.name,
size: asset.size,
uploaded: false
})),
changelog: releaseData.body
};
// 上传文件
for (const asset of releaseData.assets) {
try {
const existingFile = await env.R2_BUCKET.get(asset.name);
if (existingFile) {
// 更新文件状态
const fileIndex = versionRecord.files.findIndex(f => f.name === asset.name);
if (fileIndex !== -1) {
versionRecord.files[fileIndex].uploaded = true;
}
continue;
}
const response = await fetch(asset.browser_download_url);
if (!response.ok) {
throw new Error(`下载失败: HTTP ${response.status}`);
}
const file = await response.arrayBuffer();
await env.R2_BUCKET.put(asset.name, file, {
httpMetadata: { contentType: getContentType(asset.name) }
});
// 更新文件状态
const fileIndex = versionRecord.files.findIndex(f => f.name === asset.name);
if (fileIndex !== -1) {
versionRecord.files[fileIndex].uploaded = true;
}
await addLog(env, 'INFO', `文件上传成功: ${asset.name}`);
} catch (error) {
await addLog(env, 'ERROR', `文件上传失败: ${asset.name}`, error.message);
}
}
// 更新版本记录
versionRecord.uploadedAt = new Date().toISOString();
versions.versions[version] = versionRecord;
versions.latestVersion = version;
// 保存版本数据库
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(versions, null, 2));
// 更新缓存
const cacheData = {
version,
publishedAt: releaseData.published_at,
changelog: releaseData.body,
downloads: versionRecord.files
.filter(file => file.uploaded)
.map(file => ({
name: file.name,
url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`,
size: formatFileSize(file.size)
}))
};
await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData));
// 清理旧版本
const versionList = Object.keys(versions.versions).sort((a, b) => compareVersions(b, a));
if (versionList.length > 2) {
const oldVersions = versionList.slice(2);
for (const oldVersion of oldVersions) {
const oldFiles = versions.versions[oldVersion].files;
for (const file of oldFiles) {
if (file.uploaded) {
await env.R2_BUCKET.delete(file.name);
await addLog(env, 'INFO', `删除旧文件: ${file.name}`);
}
}
delete versions.versions[oldVersion];
}
// 保存更新后的版本数据库
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(versions, null, 2));
}
return cacheData;
} else {
// 没有新版本,返回缓存数据
const cached = await env.R2_BUCKET.get(config.CACHE_KEY);
return cached ? JSON.parse(await cached.text()) : null;
}
} catch (error) {
await addLog(env, 'ERROR', '检查更新失败', error.message);
throw error;
}
}
/**
* 获取最新版本信息
*/
async function getLatestRelease(env) {
try {
const cached = await env.R2_BUCKET.get(config.CACHE_KEY); const cached = await env.R2_BUCKET.get(config.CACHE_KEY);
if (!cached) { if (!cached) {
// 如果缓存不存在,先检查版本数据库 // 如果缓存不存在,先检查版本数据库
const versionDB = await env.R2_BUCKET.get(config.VERSION_DB); const versionDB = await env.R2_BUCKET.get(config.VERSION_DB);
if (versionDB) { if (versionDB) {
const versions = JSON.parse(await versionDB.text()); const versions = JSON.parse(await versionDB.text());
if (versions.latestVersion) { if (versions.latestVersion) {
// 从版本数据库重建缓存 // 从版本数据库重建缓存
const latestVersion = versions.versions[versions.latestVersion]; const latestVersion = versions.versions[versions.latestVersion];
const cacheData = { const cacheData = {
version: latestVersion.version, version: latestVersion.version,
publishedAt: latestVersion.publishedAt, publishedAt: latestVersion.publishedAt,
changelog: latestVersion.changelog, changelog: latestVersion.changelog,
downloads: latestVersion.files downloads: latestVersion.files
.filter(file => file.uploaded) .filter(file => file.uploaded)
.map(file => ({ .map(file => ({
name: file.name, name: file.name,
url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`, url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`,
size: formatFileSize(file.size) size: formatFileSize(file.size)
})) }))
}; };
// 更新缓存 // 更新缓存
await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData)); await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData));
return new Response(JSON.stringify(cacheData), { return new Response(JSON.stringify(cacheData), {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' 'Access-Control-Allow-Origin': '*'
}
});
} }
});
} }
} // 如果版本数据库也没有数据,才执行检查更新
// 如果版本数据库也没有数据,才执行检查更新 const data = await checkNewRelease(env);
const data = await checkAndUpdateRelease(env); return new Response(JSON.stringify(data), {
return new Response(JSON.stringify(data), { headers: {
headers: { 'Content-Type': 'application/json',
'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'
'Access-Control-Allow-Origin': '*' }
} });
});
} }
const data = await cached.text(); const data = await cached.text();
return new Response(data, { return new Response(data, {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' 'Access-Control-Allow-Origin': '*'
} }
}); });
} catch (error) { } catch (error) {
await addLog(env, 'ERROR', '获取版本信息失败', error.message); await addLog(env, 'ERROR', '获取版本信息失败', error.message);
return new Response(JSON.stringify({ return new Response(JSON.stringify({
error: '获取版本信息失败: ' + error.message, error: '获取版本信息失败: ' + error.message,
detail: '请稍后再试' detail: '请稍<E8AFB7><E7A88D><EFBFBD>再试'
}), { }), {
status: 500, status: 500,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' 'Access-Control-Allow-Origin': '*'
} }
}); });
}
} }
}
// 修改下载处理函数,直接接收 env // 修改下载处理函数,直接接收 env
async function handleDownload(env, filename) { async function handleDownload(env, filename) {
try { try {
const object = await env.R2_BUCKET.get(filename); const object = await env.R2_BUCKET.get(filename);
if (!object) { if (!object) {
return new Response('文件未找到', { status: 404 }); return new Response('文件未找到', { status: 404 });
} }
// 设置响应头 // 设置响应头
@ -313,169 +178,169 @@ const config = {
headers.set('Content-Disposition', `attachment; filename="${filename}"`); headers.set('Content-Disposition', `attachment; filename="${filename}"`);
return new Response(object.body, { return new Response(object.body, {
headers headers
}); });
} catch (error) { } catch (error) {
console.error('下载文件时发生错误:', error); console.error('下载文件时发生错误:', error);
return new Response('获取文件失败', { status: 500 }); return new Response('获取文件失败', { status: 500 });
}
} }
}
/** /**
* 根据文件扩展名获取对应的 Content-Type * 根据文件扩展名获取对应的 Content-Type
*/ */
function getContentType(filename) { function getContentType(filename) {
const ext = filename.split('.').pop().toLowerCase(); const ext = filename.split('.').pop().toLowerCase();
const types = { const types = {
'exe': 'application/x-msdownload', // Windows 可执行文件 'exe': 'application/x-msdownload', // Windows 可执行文件
'dmg': 'application/x-apple-diskimage', // macOS 安装包 'dmg': 'application/x-apple-diskimage', // macOS 安装包
'zip': 'application/zip', // 压缩包 'zip': 'application/zip', // 压缩包
'AppImage': 'application/x-executable', // Linux 可执行文件 'AppImage': 'application/x-executable', // Linux 可执行文件
'blockmap': 'application/octet-stream' // 更新文件 'blockmap': 'application/octet-stream' // 更新文件
}; };
return types[ext] || 'application/octet-stream'; return types[ext] || 'application/octet-stream';
} }
/** /**
* 格式化文件大小 * 格式化文件大小
* 将字节转换为人类可读的格式B, KB, MB, GB * 将字节转换为人类可读的格式B, KB, MB, GB
*/ */
function formatFileSize(bytes) { function formatFileSize(bytes) {
const units = ['B', 'KB', 'MB', 'GB']; const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes; let size = bytes;
let unitIndex = 0; let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) { while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024; size /= 1024;
unitIndex++; unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
} }
/** return `${size.toFixed(2)} ${units[unitIndex]}`;
* 版本号比较函数 }
* 用于对版本号进行排序
*/
function compareVersions(a, b) {
const partsA = a.replace('v', '').split('.');
const partsB = b.replace('v', '').split('.');
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) { /**
* 版本号比较函数
* 用于对版本号进行排序
*/
function compareVersions(a, b) {
const partsA = a.replace('v', '').split('.');
const partsB = b.replace('v', '').split('.');
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
const numA = parseInt(partsA[i] || 0); const numA = parseInt(partsA[i] || 0);
const numB = parseInt(partsB[i] || 0); const numB = parseInt(partsB[i] || 0);
if (numA !== numB) { if (numA !== numB) {
return numA - numB; return numA - numB;
} }
}
return 0;
} }
/** return 0;
* 初始化数据文件 }
*/
async function initDataFiles(env) { /**
try { * 初始化数据文件
*/
async function initDataFiles(env) {
try {
// 检查并初始化版本数据库 // 检查并初始化版本数据库
const versionDB = await env.R2_BUCKET.get(config.VERSION_DB); const versionDB = await env.R2_BUCKET.get(config.VERSION_DB);
if (!versionDB) { if (!versionDB) {
const initialVersions = { const initialVersions = {
versions: {}, versions: {},
latestVersion: null, latestVersion: null,
lastChecked: new Date().toISOString() lastChecked: new Date().toISOString()
}; };
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(initialVersions, null, 2)); await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(initialVersions, null, 2));
await addLog(env, 'INFO', 'versions.json 初始化成功'); await addLog(env, 'INFO', 'versions.json 初始化成功');
} }
// 检查并初始化日志文件 // 检查并初始化日志文件
const logFile = await env.R2_BUCKET.get(config.LOG_FILE); const logFile = await env.R2_BUCKET.get(config.LOG_FILE);
if (!logFile) { if (!logFile) {
const initialLogs = { const initialLogs = {
logs: [{ logs: [{
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
type: 'INFO', type: 'INFO',
event: '系统初始化' event: '系统初始化'
}] }]
}; };
await env.R2_BUCKET.put(config.LOG_FILE, JSON.stringify(initialLogs, null, 2)); await env.R2_BUCKET.put(config.LOG_FILE, JSON.stringify(initialLogs, null, 2));
console.log('logs.json 初始化成功'); console.log('logs.json 初始化成功');
} }
} catch (error) { } catch (error) {
console.error('初始化数据文件失败:', error); console.error('初始化数据文件失败:', error);
}
} }
}
// 新增:只获取缓存的版本信息 // 新增:只获取缓存的版本信息
async function getCachedRelease(env) { async function getCachedRelease(env) {
try { try {
const cached = await env.R2_BUCKET.get(config.CACHE_KEY); const cached = await env.R2_BUCKET.get(config.CACHE_KEY);
if (!cached) { if (!cached) {
// 如果缓存不存在,从版本数据库获取 // 如果缓存不存在,从版本数据库获取
const versionDB = await env.R2_BUCKET.get(config.VERSION_DB); const versionDB = await env.R2_BUCKET.get(config.VERSION_DB);
if (versionDB) { if (versionDB) {
const versions = JSON.parse(await versionDB.text()); const versions = JSON.parse(await versionDB.text());
if (versions.latestVersion) { if (versions.latestVersion) {
const latestVersion = versions.versions[versions.latestVersion]; const latestVersion = versions.versions[versions.latestVersion];
const cacheData = { const cacheData = {
version: latestVersion.version, version: latestVersion.version,
publishedAt: latestVersion.publishedAt, publishedAt: latestVersion.publishedAt,
changelog: latestVersion.changelog, changelog: latestVersion.changelog,
downloads: latestVersion.files downloads: latestVersion.files
.filter(file => file.uploaded) .filter(file => file.uploaded)
.map(file => ({ .map(file => ({
name: file.name, name: file.name,
url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`, url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`,
size: formatFileSize(file.size) size: formatFileSize(file.size)
})) }))
}; };
// 重建缓存 // 重建缓存
await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData)); await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData));
return new Response(JSON.stringify(cacheData), { return new Response(JSON.stringify(cacheData), {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' 'Access-Control-Allow-Origin': '*'
}
});
} }
});
} }
} // 如果没有任何数据,返回错误
// 如果没有任何数据,返回错误 return new Response(JSON.stringify({
return new Response(JSON.stringify({ error: '没有可用的版本信息'
error: '没有可用的版本信息' }), {
}), { status: 404,
status: 404, headers: {
headers: { 'Content-Type': 'application/json',
'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'
'Access-Control-Allow-Origin': '*' }
} });
});
} }
// 返回缓存数据 // 返回缓存数据
return new Response(await cached.text(), { return new Response(await cached.text(), {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' 'Access-Control-Allow-Origin': '*'
} }
}); });
} catch (error) { } catch (error) {
await addLog(env, 'ERROR', '获取缓存版本信息失败', error.message); await addLog(env, 'ERROR', '获取缓存版本信息失败', error.message);
throw error; throw error;
}
} }
}
// 新增:只检查新版本并更新 // 新增:只检查新版本并更新
async function checkNewRelease(env) { async function checkNewRelease(env) {
try { try {
// 获取 GitHub 最新版本 // 获取 GitHub 最新版本
const githubResponse = await fetch('https://api.github.com/repos/kangfenmao/cherry-studio/releases/latest', { const githubResponse = await fetch('https://api.github.com/repos/kangfenmao/cherry-studio/releases/latest', {
headers: { 'User-Agent': 'CloudflareWorker' }, headers: { 'User-Agent': 'CloudflareWorker' },
}); });
if (!githubResponse.ok) { if (!githubResponse.ok) {
throw new Error('GitHub API 请求失败'); throw new Error('GitHub API 请求失败');
} }
const releaseData = await githubResponse.json(); const releaseData = await githubResponse.json();
@ -486,110 +351,165 @@ const config = {
let versions = { versions: {}, latestVersion: null, lastChecked: new Date().toISOString() }; let versions = { versions: {}, latestVersion: null, lastChecked: new Date().toISOString() };
if (versionDB) { if (versionDB) {
versions = JSON.parse(await versionDB.text()); versions = JSON.parse(await versionDB.text());
} }
// 如果版本相同,不需要更新 // 移除版本检查,改为记录是否有文件更新的标志
if (versions.latestVersion === version) { let hasUpdates = false;
console.log('当前已是最新版本'); if (versions.latestVersion !== version) {
return; await addLog(env, 'INFO', `发现新版本: ${version}`);
hasUpdates = true;
} else {
await addLog(env, 'INFO', `版本 ${version} 文件完整性检查开始`);
} }
await addLog(env, 'INFO', `发现新版本: ${version}`);
// 准备新版本记录 // 准备新版本记录
const versionRecord = { const versionRecord = {
version, version,
publishedAt: releaseData.published_at, publishedAt: releaseData.published_at,
uploadedAt: null, uploadedAt: null,
files: releaseData.assets.map(asset => ({ files: releaseData.assets.map(asset => ({
name: asset.name, name: asset.name,
size: asset.size, size: asset.size,
uploaded: false uploaded: false
})), })),
changelog: releaseData.body changelog: releaseData.body
}; };
// 上传文件 // 检查并上传文件
for (const asset of releaseData.assets) { for (const asset of releaseData.assets) {
try { try {
const existingFile = await env.R2_BUCKET.get(asset.name); const existingFile = await env.R2_BUCKET.get(asset.name);
if (existingFile) { // 检查文件是否存在且大小是否一致
// 更新文件状态 if (!existingFile || existingFile.size !== asset.size) {
const fileIndex = versionRecord.files.findIndex(f => f.name === asset.name); hasUpdates = true;
if (fileIndex !== -1) { const response = await fetch(asset.browser_download_url);
versionRecord.files[fileIndex].uploaded = true; if (!response.ok) {
} throw new Error(`下载失败: HTTP ${response.status}`);
continue; }
const file = await response.arrayBuffer();
await env.R2_BUCKET.put(asset.name, file, {
httpMetadata: { contentType: getContentType(asset.name) }
});
// 更新文件状态
const fileIndex = versionRecord.files.findIndex(f => f.name === asset.name);
if (fileIndex !== -1) {
versionRecord.files[fileIndex].uploaded = true;
}
await addLog(env, 'INFO', `文件${existingFile ? '更新' : '上传'}成功: ${asset.name}`);
} else {
// 文件存在且大小相同,标记为已上传
const fileIndex = versionRecord.files.findIndex(f => f.name === asset.name);
if (fileIndex !== -1) {
versionRecord.files[fileIndex].uploaded = true;
}
await addLog(env, 'INFO', `文件完整性验证通过: ${asset.name}`);
}
} catch (error) {
await addLog(env, 'ERROR', `文件处理失败: ${asset.name}`, error.message);
} }
const response = await fetch(asset.browser_download_url);
if (!response.ok) {
throw new Error(`下载失败: HTTP ${response.status}`);
}
const file = await response.arrayBuffer();
await env.R2_BUCKET.put(asset.name, file, {
httpMetadata: { contentType: getContentType(asset.name) }
});
// 更新文件状态
const fileIndex = versionRecord.files.findIndex(f => f.name === asset.name);
if (fileIndex !== -1) {
versionRecord.files[fileIndex].uploaded = true;
}
await addLog(env, 'INFO', `文件上传成功: ${asset.name}`);
} catch (error) {
await addLog(env, 'ERROR', `文件上传失败: ${asset.name}`, error.message);
}
} }
// 更新版本记录 // 只有在有更新或是新版本时才更新数据库和缓存
versionRecord.uploadedAt = new Date().toISOString(); if (hasUpdates) {
versions.versions[version] = versionRecord; // 更新版本记录
versions.latestVersion = version; versionRecord.uploadedAt = new Date().toISOString();
versions.versions[version] = versionRecord;
versions.latestVersion = version;
// 保存版本数据库 // 保存版本数据库
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(versions, null, 2)); await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(versions, null, 2));
// 更新缓存 // 更新缓存
const cacheData = { const cacheData = {
version, version,
publishedAt: releaseData.published_at, publishedAt: releaseData.published_at,
changelog: releaseData.body, changelog: releaseData.body,
downloads: versionRecord.files downloads: versionRecord.files
.filter(file => file.uploaded) .filter(file => file.uploaded)
.map(file => ({ .map(file => ({
name: file.name, name: file.name,
url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`, url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`,
size: formatFileSize(file.size) size: formatFileSize(file.size)
})) }))
}; };
await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData)); await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData));
await addLog(env, 'INFO', hasUpdates ? '更新完成' : '文件完整性检查完成');
// 清理旧版本 // 清理旧版本
const versionList = Object.keys(versions.versions).sort((a, b) => compareVersions(b, a)); const versionList = Object.keys(versions.versions).sort((a, b) => compareVersions(b, a));
if (versionList.length > 2) { if (versionList.length > 2) {
const oldVersions = versionList.slice(2); // 获取需要保留的两个最新版本
for (const oldVersion of oldVersions) { const keepVersions = versionList.slice(0, 2);
const oldFiles = versions.versions[oldVersion].files; // 获取所有需要删除的版本
for (const file of oldFiles) { const oldVersions = versionList.slice(2);
if (file.uploaded) {
await env.R2_BUCKET.delete(file.name); // 先获取 R2 桶中的所有文件列表
await addLog(env, 'INFO', `删除旧文件: ${file.name}`); const allFiles = await listAllFiles(env);
}
// 获取需要保留的文件名列表
const keepFiles = new Set();
for (const keepVersion of keepVersions) {
const versionFiles = versions.versions[keepVersion].files;
versionFiles.forEach(file => keepFiles.add(file.name));
}
// 删除所有旧版本文件
for (const oldVersion of oldVersions) {
const oldFiles = versions.versions[oldVersion].files;
for (const file of oldFiles) {
try {
if (file.uploaded) {
await env.R2_BUCKET.delete(file.name);
await addLog(env, 'INFO', `删除旧文件: ${file.name}`);
}
} catch (error) {
await addLog(env, 'ERROR', `删除旧文件失败: ${file.name}`, error.message);
}
}
delete versions.versions[oldVersion];
}
// 清理可能遗留的旧文件
for (const file of allFiles) {
if (!keepFiles.has(file.name)) {
try {
await env.R2_BUCKET.delete(file.name);
await addLog(env, 'INFO', `删除遗留文件: ${file.name}`);
} catch (error) {
await addLog(env, 'ERROR', `删除遗留文件失败: ${file.name}`, error.message);
}
}
}
// 保存更新后的版本数据库
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(versions, null, 2));
} }
delete versions.versions[oldVersion]; } else {
} await addLog(env, 'INFO', '所有文件完整性检查通过,无需更新');
// 保存更新后的版本数据库
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(versions, null, 2));
} }
return cacheData; return hasUpdates ? cacheData : null;
} catch (error) { } catch (error) {
await addLog(env, 'ERROR', '检查新版本失败', error.message); await addLog(env, 'ERROR', '检查新版本失败', error.message);
throw error; throw error;
}
} }
}
// 新增:获取 R2 桶中的所有文件列表
async function listAllFiles(env) {
const files = [];
let cursor;
do {
const listed = await env.R2_BUCKET.list({ cursor, include: ['customMetadata'] });
files.push(...listed.objects);
cursor = listed.cursor;
} while (cursor);
return files;
}