Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 11 additions & 39 deletions docker-compose.yml
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not modify the docker compose file.

Original file line number Diff line number Diff line change
@@ -1,50 +1,22 @@
version: '3.8'
services:
rsshub:
# two ways to enable Playwright:
# * comment out marked lines, then use this image instead: diygod/rsshub:chromium-bundled
# * (consumes more disk space and memory) leave everything unchanged
image: diygod/rsshub # or ghcr.io/diygod/rsshub
image: franksun123/rsshub:latest
container_name: rsshubfrank
restart: always
ports:
- '1200:1200'
network_mode: host
environment:
NODE_ENV: production
CACHE_TYPE: redis
REDIS_URL: 'redis://redis:6379/'
PLAYWRIGHT_WS_ENDPOINT: 'ws://browserless:3000' # marked
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:1200/healthz']
interval: 30s
timeout: 10s
retries: 3
- NODE_ENV=production
- CACHE_TYPE=redis
- REDIS_URL=redis://127.0.0.1:6379/0
- FLARESOLVERR_URL=http://127.0.0.1:8191/v1
depends_on:
- redis
- browserless # marked

browserless: # marked
image: browserless/chrome # marked
restart: always # marked
ulimits: # marked
core: # marked
hard: 0 # marked
soft: 0 # marked
healthcheck: # marked
test: ['CMD', 'curl', '-f', 'http://localhost:3000/pressure'] # marked
interval: 30s # marked
timeout: 10s # marked
retries: 3 # marked

redis:
image: redis:alpine
container_name: rsshub-redis
restart: always
network_mode: host
volumes:
- redis-data:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 30s
timeout: 10s
retries: 5
start_period: 5s

volumes:
redis-data:
- ./redis-data:/data
123 changes: 123 additions & 0 deletions lib/routes/gxnas/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { load } from 'cheerio';
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

import type { DataItem, Route } from '@/types';
import logger from '@/utils/logger';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';

const FLARESOLVERR_URL = process.env.FLARESOLVERR_URL || 'http://flaresolverr:8191/v1';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not read process.env in your own route.

const SESSION_ID = 'gxnas_session';

// Session singleton: create once, reuse across requests
let sessionCreated = false;

async function ensureSession() {
if (sessionCreated) {
return;
}
try {
logger.debug(`Creating FlareSolverr session: ${SESSION_ID}`);
await ofetch(FLARESOLVERR_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { cmd: 'sessions.create', session: SESSION_ID },
});
sessionCreated = true;
logger.debug(`FlareSolverr session created: ${SESSION_ID}`);
} catch (e) {

Check failure

Code scanning / oxlint

eslint-plugin-unicorn(catch-error-name) Error

The catch parameter "e" should be named "error"
Rename e to error
// Session might already exist from a previous run
logger.debug(`Session creation failed (may already exist): ${(e as Error).message}`);
sessionCreated = true;
}
}

export const route: Route = {
path: '/',
categories: ['blog'],
example: '/gxnas',
radar: [
{
source: ['wp.gxnas.com/'],
},
],
url: 'wp.gxnas.com/',
name: '最新文章',
maintainers: ['Franklittleboy'],
handler,
description: `:::warning
该网站受 Cloudflare 保护,需要配置 FlareSolverr 服务([文档](https://github.com/FlareSolverr/FlareSolverr))。
环境变量 \`FLARESOLVERR_URL\` 默认为 \`http://flaresolverr:8191/v1\`。
:::`,
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: true,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
};

async function handler() {
const rootUrl = 'https://wp.gxnas.com';

await ensureSession();

logger.debug(`Fetching ${rootUrl} via FlareSolverr session ${SESSION_ID}`);
const result = await ofetch(FLARESOLVERR_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: {
cmd: 'request.get',
url: rootUrl,
session: SESSION_ID,
maxTimeout: 180000,
},
});

const html = result?.solution?.response;
if (!html) {
throw new Error('FlareSolverr 返回内容为空');
}

const $ = load(html);

const items = $('.article-panel')
.toArray()
.map((el) => {
const $el = $(el);
const titleEl = $el.find('.header .title a');
const title = titleEl.text().trim();
const link = titleEl.attr('href');
const categoryEl = $el.find('.header .label');
const category = categoryEl.text().trim();
const contentEl = $el.find('.content');
const description = contentEl.html() || '';
const thumbEl = $el.find('.a-thumb img');
const image = thumbEl.attr('src');

// Date format: 2023年11月17日
const dateText = $el.find('.a-meta span').first().text().trim();
let pubDate: Date | undefined;
const m = dateText.match(/(\d{4})年(\d{1,2})月(\d{1,2})日/);
if (m) {
pubDate = new Date(Number.parseInt(m[1]), Number.parseInt(m[2]) - 1, Number.parseInt(m[3]));
}

return {
title,
link,
description: image ? `<img src="${image}"><br>${description}` : description,
category,
pubDate: pubDate && parseDate(pubDate),
} as DataItem;
})
.filter((item) => item.title && item.link);

return {
title: 'GXNAS博客',
link: rootUrl,
item: items,
description: 'GXNAS博客 - NAS博客|NAS社区|NAS交流|NAS技术|群晖教程|软路由',
};
}
8 changes: 8 additions & 0 deletions lib/routes/gxnas/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Namespace } from '@/types';

export const namespace: Namespace = {
name: 'GXNAS博客',
url: 'wp.gxnas.com',
description: 'GXNAS博客 - NAS技术交流',
lang: 'zh-CN',
};