使用 Cloudflare Worker 处理下载分流
- 支持 GitHub Release 和 Cloudflare R2 分流
- 支持 Swiflow_latest_xxxx.exe 下载最新版(下载链接)
- 支持 Release.json 直接 从GitHub 获取最新发布信息
js
const GITHUB_REPO = "OptLTD/swiflow-app";
const R2_DOMAIN = "https://r2.swiflow.cc";
function extractVersionFromFilename(filename) {
const match = filename.match(/Swiflow_(\d+\.\d+\.\d+)_/);
return match ? match[1] : null;
}
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const pathname = url.pathname;
if (pathname === "/release.json") {
return handleReleaseJson(ctx);
}
const filename = pathname.slice(1);
if (filename.startsWith("Swiflow_latest_")) {
return handleLatestRedirect(filename, ctx, request);
}
const version = extractVersionFromFilename(filename);
if (!filename || !version) {
return new Response("Invalid filename or missing version", { status: 400 });
}
const isCN = request.headers.get("cf-ipcountry") === "CN";
const githubURL = `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${filename}`;
const r2URL = `${R2_DOMAIN}/${filename}`;
const target = isCN ? r2URL : githubURL;
return Response.redirect(target, 302);
}
};
async function handleReleaseJson(ctx) {
const cache = caches.default;
const cacheKey = new Request("https://dl.swiflow.cc/release.json");
const cached = await cache.match(cacheKey);
if (cached) return cached;
const githubApi = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
const res = await fetch(githubApi, {
headers: {
"User-Agent": "Swiflow-Worker",
"Accept": "application/vnd.github+json"
}
});
if (!res.ok) {
return new Response("Failed to fetch release info", { status: 502 });
}
const data = await res.json();
const result = {
version: data.tag_name.replace(/^v/, ""),
published_at: data.published_at,
assets: data.assets.map(a => ({
name: a.name,
size: a.size,
url: `https://dl.swiflow.cc/${a.name}`
}))
};
const response = new Response(JSON.stringify(result), {
status: 200,
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600"
}
});
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
}
async function handleLatestRedirect(filename, ctx, request) {
const cache = caches.default;
const cacheKey = new Request("https://dl.swiflow.cc/release.json");
let json;
// 尝试从缓存读取 release.json
const cached = await cache.match(cacheKey);
if (cached) {
json = await cached.json();
} else {
const response = await handleReleaseJson(ctx);
json = await response.json();
}
const matchedAsset = json.assets.find(a => a.name.endsWith(filename.replace("latest", json.version)));
if (!matchedAsset) {
return new Response("No matching latest asset", { status: 404 });
}
const realFilename = matchedAsset.name;
const isCN = request.headers.get("cf-ipcountry") === "CN";
const version = json.version;
const targetURL = isCN
? `${R2_DOMAIN}/release-assets/${realFilename}`
: `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${realFilename}`;
return Response.redirect(targetURL, 302);
}
GitHub Action:上传 release 文件到 R2 根目录
yaml
name: Sync Assets to Cloudflare R2
on:
release:
types: [published, edited]
jobs:
sync-release-to-r2:
runs-on: ubuntu-latest
permissions:
contents: write
# 明确设置仓库上下文变量
env:
R2_BUCKET_NAME: "swiflow"
R2_ENDPOINT_URL: "https://2d002fceba7555e1c72fd486005708ea.r2.cloudflarestorage.com"
steps:
# ----------- 第一步:验证上下文 -----------
- name: Debug context
run: |
echo "Release ID: ${{ github.event.release.id }}"
echo "Tag 名称: ${{ github.event.release.tag_name }}"
# ----------- 第二步:下载资源 -----------
- name: Download release assets
env:
GH_TOKEN: ${{ secrets.MY_GH_TOKEN }}
run: |
set -euo pipefail
mkdir -p release-assets
echo "🔄 从 OptLTD/swiflow-app 获取 Release 资源..."
api_url="https://api.github.com/repos/OptLTD/swiflow-app/releases/${{ github.event.release.id }}"
# 带错误处理的请求
release_info=$(curl -fLsS \
-H "Authorization: token $GH_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"$api_url") || {
echo "❌ 无法获取 Release 信息"
curl -v "$api_url" > debug.log # 保存调试信息
exit 1
}
# 安全解析资源URL
asset_urls=$(echo "$release_info" | jq -r '.assets[]?.browser_download_url // empty')
[ -z "$asset_urls" ] && echo "ℹ️ 无附件资源" && exit 0
echo "📦 需要下载的资源:"
for url in $asset_urls; do
filename="${url##*/}" # 更安全的文件名提取
echo "⬇️ 下载: $filename"
curl -fL \
-H "Authorization: token $GH_TOKEN" \
-o "release-assets/${filename}" \
"$url" || echo "⚠️ 下载失败: $url"
done
echo "✅ 下载完成!"
ls -lh release-assets/
# ----------- 第三步:配置R2连接 -----------
- name: Setup R2 credentials
run: |
pip install --quiet awscli
mkdir -p ~/.aws
# 安全写入凭证(避免命令注入)
cat <<EOF > ~/.aws/credentials
[default]
aws_access_key_id = $(printf '%s' "${{ secrets.R2_ACCESS_KEY_ID }}" | sed 's/[\/&]/\\&/g')
aws_secret_access_key = $(printf '%s' "${{ secrets.R2_SECRET_ACCESS_KEY }}" | sed 's/[\/&]/\\&/g')
EOF
cat <<EOF > ~/.aws/config
[default]
region = auto
s3 =
endpoint_url = $R2_ENDPOINT_URL
EOF
# ----------- 第四步:上传到R2 -----------
- name: Upload to R2
run: |
set -e
echo "🚀 上传到: s3://$R2_BUCKET_NAME/"
# 检查文件是否存在
[ ! -d "release-assets" ] && echo "❌ release-assets 目录不存在" && exit 1
[ -z "$(ls -A release-assets)" ] && echo "ℹ️ 无文件可上传" && exit 0
# 使用awscli并行上传
find release-assets -type f -print0 | xargs -0 -P 4 -I file \
aws s3 cp file "s3://$R2_BUCKET_NAME/$(basename file)" \
--endpoint-url "$R2_ENDPOINT_URL" \
--no-progress
echo "✅ 上传完成"
# ----------- 第五步:清理 -----------
- name: Cleanup
if: always()
run: rm -rf release-assets