mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-08-28 17:43:25 +08:00
feat: add Cloudflare Containers deployment support (#20932)
- Create Container Worker entry point with load balancing (20 instances) - Add wrangler-container.toml configuration with KV binding for environment variables - Configure container to use standard-1 instance type with Chromium support - Implement dynamic environment variable loading from Cloudflare KV Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,3 +40,5 @@ package-lock.json
|
||||
# pnpm-lock.yaml
|
||||
yarn.lock
|
||||
yarn-error.log
|
||||
|
||||
dist-container
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Cloudflare Container Worker entry point
|
||||
// This Worker manages the RSSHub container lifecycle and proxies requests
|
||||
|
||||
import { Container } from '@cloudflare/containers';
|
||||
import type { KVNamespace } from '@cloudflare/workers-types';
|
||||
|
||||
const INSTANCE_COUNT = 20;
|
||||
|
||||
export class RSSHubContainer extends Container {
|
||||
defaultPort = 1200;
|
||||
sleepAfter = '10m';
|
||||
enableInternet = true;
|
||||
}
|
||||
|
||||
interface Env {
|
||||
RSSHUB_CONTAINER: DurableObjectNamespace<RSSHubContainer>;
|
||||
CONFIG: KVNamespace;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
// Load env vars from KV
|
||||
const envVars: Record<string, string> = {
|
||||
NODE_ENV: 'production',
|
||||
};
|
||||
|
||||
const keys = await env.CONFIG.list();
|
||||
await Promise.all(
|
||||
keys.keys.map(async ({ name }) => {
|
||||
const value = await env.CONFIG.get(name);
|
||||
if (value) {
|
||||
envVars[name] = value;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Randomly select an instance for load balancing
|
||||
const instanceIndex = Math.floor(Math.random() * INSTANCE_COUNT);
|
||||
const container = env.RSSHUB_CONTAINER.getByName(`rsshub-${instanceIndex}`);
|
||||
|
||||
// Start container with env vars and wait for port to be ready
|
||||
await container.startAndWaitForPorts({
|
||||
startOptions: { envVars },
|
||||
});
|
||||
|
||||
return container.fetch(request);
|
||||
},
|
||||
};
|
||||
@@ -36,6 +36,8 @@
|
||||
"worker-dev": "npm run worker-build && wrangler dev",
|
||||
"worker-deploy": "npm run worker-build && wrangler deploy",
|
||||
"worker-test": "npm run worker-build && vitest run lib/worker.worker.test.ts",
|
||||
"container-build": "tsdown --config ./tsdown-container.config.ts",
|
||||
"container-deploy": "npm run container-build && wrangler deploy --config wrangler-container.toml --containers-rollout=immediate",
|
||||
"dev": "cross-env NODE_ENV=dev NODE_OPTIONS='--max-http-header-size=32768' tsx watch --inspect --clear-screen=false lib/index.ts",
|
||||
"dev:cache": "cross-env NODE_ENV=production NODE_OPTIONS='--max-http-header-size=32768' tsx watch --clear-screen=false lib/index.ts",
|
||||
"format": "eslint --cache --fix \"**/*.{ts,tsx,js,yml}\" --concurrency auto && oxfmt .",
|
||||
@@ -144,6 +146,7 @@
|
||||
"@babel/preset-env": "7.28.6",
|
||||
"@babel/preset-typescript": "7.28.5",
|
||||
"@bbob/types": "4.3.1",
|
||||
"@cloudflare/containers": "0.1.0",
|
||||
"@cloudflare/puppeteer": "1.0.4",
|
||||
"@cloudflare/workers-types": "4.20260118.0",
|
||||
"@eslint/eslintrc": "3.3.3",
|
||||
|
||||
Generated
+9
-1
@@ -296,6 +296,9 @@ importers:
|
||||
'@bbob/types':
|
||||
specifier: 4.3.1
|
||||
version: 4.3.1
|
||||
'@cloudflare/containers':
|
||||
specifier: 0.1.0
|
||||
version: 0.1.0
|
||||
'@cloudflare/puppeteer':
|
||||
specifier: 1.0.4
|
||||
version: 1.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
@@ -1104,6 +1107,9 @@ packages:
|
||||
'@bundled-es-modules/tough-cookie@0.1.6':
|
||||
resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==}
|
||||
|
||||
'@cloudflare/containers@0.1.0':
|
||||
resolution: {integrity: sha512-nuXnmkpJZzbjYdguRI2hB0sw1QCBMWdNuGDNQwEiJSLebtKRFpBt/d6AStGjp+8wGD2plPbd2U/mQerYF9kzJg==}
|
||||
|
||||
'@cloudflare/kv-asset-handler@0.4.2':
|
||||
resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -7553,6 +7559,8 @@ snapshots:
|
||||
'@types/tough-cookie': 4.0.5
|
||||
tough-cookie: 4.1.4
|
||||
|
||||
'@cloudflare/containers@0.1.0': {}
|
||||
|
||||
'@cloudflare/kv-asset-handler@0.4.2': {}
|
||||
|
||||
'@cloudflare/puppeteer@1.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
@@ -12251,7 +12259,7 @@ snapshots:
|
||||
debug: 4.4.3
|
||||
devtools-protocol: 0.0.1367902
|
||||
typed-query-selector: 2.12.0
|
||||
ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
transitivePeerDependencies:
|
||||
- bare-buffer
|
||||
- bufferutil
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from 'tsdown';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['./lib/container.ts'],
|
||||
outDir: 'dist-container',
|
||||
format: 'esm',
|
||||
minify: true,
|
||||
clean: true,
|
||||
platform: 'node',
|
||||
target: 'esnext',
|
||||
treeshake: true,
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
},
|
||||
external: ['@cloudflare/containers'],
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
name = "rsshub-container"
|
||||
main = "dist-container/container.mjs"
|
||||
compatibility_date = "2026-01-16"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
|
||||
[build]
|
||||
command = "pnpm run container-build"
|
||||
|
||||
[observability]
|
||||
enabled = true
|
||||
|
||||
# Container configuration
|
||||
[[containers]]
|
||||
class_name = "RSSHubContainer"
|
||||
image = "./Dockerfile"
|
||||
max_instances = 20
|
||||
instance_type = "standard-1"
|
||||
image_vars = { PUPPETEER_SKIP_DOWNLOAD = "0" }
|
||||
|
||||
# Durable Object binding for the container
|
||||
[[durable_objects.bindings]]
|
||||
name = "RSSHUB_CONTAINER"
|
||||
class_name = "RSSHubContainer"
|
||||
|
||||
# Required migration for Durable Objects with SQLite
|
||||
[[migrations]]
|
||||
tag = "v1"
|
||||
new_sqlite_classes = ["RSSHubContainer"]
|
||||
|
||||
# KV namespace for container environment variables
|
||||
[[kv_namespaces]]
|
||||
binding = "CONFIG"
|
||||
|
||||
# Example: Set env vars in KV using wrangler CLI:
|
||||
# wrangler kv:key put --namespace-id=rsshub-container-config "GITHUB_ACCESS_TOKEN" "your-token"
|
||||
# wrangler kv:key put --namespace-id=rsshub-container-config "ACCESS_KEY" "your-key"
|
||||
Reference in New Issue
Block a user