1. 创建脚本文件 rss.sh
nano /root/rss.sh
rss.sh 脚本内容:
#!/bin/bash
# ============================================================
# rss.sh — xBlog RSS 订阅管理脚本
# 使用方式:bash /root/rss.sh
# ============================================================
# ============================
# 配置区域(按需修改)
# ============================
SITE_DIR="/opt/1panel/www/sites/xblog/index" # 网站根目录(宿主机路径)
SITE_DIR_CONTAINER="/www/sites/xblog/index" # 网站根目录(容器内路径)
SITE_URL="https://xblog.itxgo.com" # 网站域名,末尾不加斜杠
BLOG_TITLE="iTxGo🍃 - xBlog™️" # 网站标题
BLOG_DESC="PHP + SQLite 简约博客" # 网站描述
RSS_LIMIT=20 # RSS 收录文章数量
RSS_SECRET="xblog_rss" # 手动触发密钥
BLOG_EMAIL="163@163.com" # 作者邮箱(用于 RSS author 字段)
FILE_OWNER="1000:1000" # 网站文件所有者(与其他网站文件保持一致)
PHP_BIN="docker exec php php" # PHP 执行命令(容器环境)
SCRIPT_PATH="$(realpath "$0")" # 本脚本自身路径(卸载时使用)
# ============================
# 派生路径(无需修改)
PHP_SCRIPT="$SITE_DIR/generate_rss.php"
RSS_FILE="$SITE_DIR/rss.xml"
DB_FILE="$SITE_DIR/blog.db"
# ============================================================
# 颜色输出
# ============================================================
GREEN="\033[0;32m"
RED="\033[0;31m"
YELLOW="\033[1;33m"
CYAN="\033[0;36m"
RESET="\033[0m"
ok() { echo -e "${GREEN}✅ $1${RESET}"; }
err() { echo -e "${RED}❌ $1${RESET}"; }
info() { echo -e "${CYAN}ℹ️ $1${RESET}"; }
warn() { echo -e "${YELLOW}⚠️ $1${RESET}"; }
# ============================================================
# 功能函数
# ============================================================
# 写入 generate_rss.php
write_php() {
cat > "$PHP_SCRIPT" << PHPEOF
<?php
/**
* generate_rss.php — RSS 订阅生成脚本
* 由 rss.sh 自动生成,请勿手动修改配置项
*/
define('RSS_BASE_URL', '${SITE_URL}');
define('RSS_OUTPUT', __DIR__ . '/rss.xml');
define('RSS_SECRET', '${RSS_SECRET}');
define('RSS_LIMIT', ${RSS_LIMIT});
define('BLOG_TITLE', '${BLOG_TITLE}');
define('BLOG_DESC', '${BLOG_DESC}');
define('BLOG_EMAIL', '${BLOG_EMAIL}');
function generateRss(): int|false
{
\$dbPath = __DIR__ . '/blog.db';
if (!file_exists(\$dbPath)) {
error_log('[RSS] 数据库文件不存在: ' . \$dbPath);
return false;
}
try {
\$db = new PDO('sqlite:' . \$dbPath);
\$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
\$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException \$e) {
error_log('[RSS] 数据库连接失败: ' . \$e->getMessage());
return false;
}
try {
\$stmt = \$db->prepare(
"SELECT a.id, a.title, a.excerpt, a.created_at, a.updated_at, u.username
FROM articles a
LEFT JOIN users u ON u.id = a.author_id
ORDER BY a.created_at DESC
LIMIT :limit"
);
\$stmt->bindValue(':limit', RSS_LIMIT, PDO::PARAM_INT);
\$stmt->execute();
\$articles = \$stmt->fetchAll();
} catch (PDOException \$e) {
error_log('[RSS] 文章查询失败: ' . \$e->getMessage());
return false;
}
\$buildDate = date(DATE_RSS);
\$channelLink = RSS_BASE_URL . '/';
\$feedLink = RSS_BASE_URL . '/rss.xml';
\$xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
\$xml .= '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">' . "\n";
\$xml .= " <channel>\n";
\$xml .= " <title>" . htmlspecialchars(BLOG_TITLE, ENT_XML1 | ENT_QUOTES, 'UTF-8') . "</title>\n";
\$xml .= " <link>" . htmlspecialchars(\$channelLink, ENT_XML1 | ENT_QUOTES, 'UTF-8') . "</link>\n";
\$xml .= " <description>" . htmlspecialchars(BLOG_DESC, ENT_XML1 | ENT_QUOTES, 'UTF-8') . "</description>\n";
\$xml .= " <language>zh-CN</language>\n";
\$xml .= " <lastBuildDate>" . \$buildDate . "</lastBuildDate>\n";
\$xml .= ' <atom:link href="' . htmlspecialchars(\$feedLink, ENT_XML1 | ENT_QUOTES, 'UTF-8') . '" rel="self" type="application/rss+xml"/>' . "\n";
foreach (\$articles as \$article) {
\$link = RSS_BASE_URL . '/article.php?id=' . (int)\$article['id'];
\$title = htmlspecialchars(\$article['title'] ?? '', ENT_XML1 | ENT_QUOTES, 'UTF-8');
\$author = htmlspecialchars(\$article['username'] ?? 'unknown', ENT_XML1 | ENT_QUOTES, 'UTF-8');
\$desc = htmlspecialchars(trim(\$article['excerpt'] ?? ''), ENT_XML1 | ENT_QUOTES, 'UTF-8');
\$pubDate = formatRssDate(\$article['created_at']);
\$guid = htmlspecialchars(\$link, ENT_XML1 | ENT_QUOTES, 'UTF-8');
\$xml .= " <item>\n";
\$xml .= " <title>{\$title}</title>\n";
\$xml .= " <link>{\$guid}</link>\n";
\$xml .= " <author>" . BLOG_EMAIL . " ({\$author})</author>\n";
\$xml .= " <description>{\$desc}</description>\n";
\$xml .= " <pubDate>{\$pubDate}</pubDate>\n";
\$xml .= " <guid isPermaLink=\"true\">{\$guid}</guid>\n";
\$xml .= " </item>\n";
}
\$xml .= " </channel>\n";
\$xml .= "</rss>\n";
\$result = file_put_contents(RSS_OUTPUT, \$xml, LOCK_EX);
if (\$result === false) {
error_log('[RSS] 写入 rss.xml 失败,请检查目录权限: ' . RSS_OUTPUT);
return false;
}
return count(\$articles);
}
function formatRssDate(?string \$datetime): string
{
if (empty(\$datetime)) return date(DATE_RSS);
\$ts = strtotime(\$datetime);
return \$ts ? date(DATE_RSS, \$ts) : date(DATE_RSS);
}
if (basename(__FILE__) === basename(\$_SERVER['SCRIPT_FILENAME'] ?? '') || php_sapi_name() === 'cli') {
if (php_sapi_name() === 'cli') {
\$ok = generateRss();
echo \$ok !== false
? "[RSS] 生成成功: " . RSS_OUTPUT . "(共 {\$ok} 篇文章)" . PHP_EOL
: "[RSS] 生成失败,请检查错误日志。" . PHP_EOL;
exit(\$ok !== false ? 0 : 1);
}
\$key = \$_GET['key'] ?? '';
if (!hash_equals(RSS_SECRET, \$key)) {
http_response_code(403);
exit('403 Forbidden: 密钥错误');
}
\$ok = generateRss();
header('Content-Type: text/plain; charset=utf-8');
echo \$ok !== false
? "✅ rss.xml 已生成:" . RSS_OUTPUT . "\n📄 共收录 {\$ok} 篇文章"
: "❌ 生成失败,请查看 PHP 错误日志。";
exit();
}
PHPEOF
}
# 检查是否已安装
is_installed() {
[ -f "$PHP_SCRIPT" ]
}
# ============================================================
# 菜单功能:1 安装 RSS
# ============================================================
do_install() {
echo ""
if is_installed; then
warn "检测到已安装,请使用「2 更新 RSS」手动刷新,或直接重新安装(将覆盖现有文件)。"
read -rp "是否继续重新安装?[y/N] " confirm
[[ "$confirm" =~ ^[Yy]$ ]] || { info "已取消。"; return; }
fi
# 检查网站目录
if [ ! -d "$SITE_DIR" ]; then
err "网站目录不存在:$SITE_DIR"
info "请检查脚本顶部 SITE_DIR 配置项。"
return 1
fi
# 检查数据库
if [ ! -f "$DB_FILE" ]; then
err "未找到数据库文件:$DB_FILE"
return 1
fi
info "正在写入 generate_rss.php ..."
write_php && chown "$FILE_OWNER" "$PHP_SCRIPT" && ok "generate_rss.php 已写入" || { err "写入失败,请检查目录权限。"; return 1; }
info "正在生成初始 rss.xml ..."
$PHP_BIN "$SITE_DIR_CONTAINER/generate_rss.php"
if [ -f "$RSS_FILE" ]; then
chown "$FILE_OWNER" "$RSS_FILE"
ok "rss.xml 已生成:$RSS_FILE"
else
err "rss.xml 生成失败,请检查 PHP 错误日志。"
fi
echo ""
ok "安装完成!"
info "请在 1panel 面板「计划任务」中添加定时更新任务"
info "RSS 订阅地址:${SITE_URL}/rss.xml"
}
# ============================================================
# 菜单功能:2 更新 RSS
# ============================================================
do_update() {
echo ""
if ! is_installed; then
err "尚未安装,请先选择「1 安装 RSS」。"
return 1
fi
info "正在更新 rss.xml ..."
$PHP_BIN "$SITE_DIR_CONTAINER/generate_rss.php"
if [ -f "$RSS_FILE" ]; then
ok "rss.xml 更新成功"
info "文件路径:$RSS_FILE"
else
err "更新失败,请检查 PHP 错误日志。"
fi
}
# ============================================================
# 菜单功能:3 卸载 RSS
# ============================================================
do_uninstall_rss() {
echo ""
if ! is_installed; then
warn "未检测到已安装的 RSS 文件。"
return
fi
warn "将删除以下文件:"
echo " - $PHP_SCRIPT"
echo " - $RSS_FILE"
read -rp "确认卸载?[y/N] " confirm
[[ "$confirm" =~ ^[Yy]$ ]] || { info "已取消。"; return; }
[ -f "$PHP_SCRIPT" ] && rm -f "$PHP_SCRIPT" && ok "已删除 generate_rss.php"
[ -f "$RSS_FILE" ] && rm -f "$RSS_FILE" && ok "已删除 rss.xml"
echo ""
ok "卸载完成。"
warn "请记得在 1panel 面板「计划任务」中手动删除对应任务。"
}
# ============================================================
# 菜单功能:4 卸载脚本
# ============================================================
do_uninstall_script() {
echo ""
warn "将删除本脚本自身:$SCRIPT_PATH"
if is_installed; then
warn "检测到 RSS 仍已安装,建议先执行「3 卸载 RSS」。"
read -rp "是否同时卸载 RSS?[y/N] " also_uninstall
[[ "$also_uninstall" =~ ^[Yy]$ ]] && do_uninstall_rss
fi
read -rp "确认删除脚本?[y/N] " confirm
[[ "$confirm" =~ ^[Yy]$ ]] || { info "已取消。"; return; }
rm -f "$SCRIPT_PATH"
ok "脚本已删除:$SCRIPT_PATH"
exit 0
}
# ============================================================
# 菜单功能:5 使用说明
# ============================================================
do_help() {
echo ""
echo -e "${CYAN}=============================${RESET}"
echo -e "${CYAN} 使用说明 ${RESET}"
echo -e "${CYAN}=============================${RESET}"
echo ""
echo -e "${YELLOW}【脚本配置项】${RESET}(位于 rss.sh 顶部)"
echo " SITE_DIR 网站根目录路径(宿主机)"
echo " SITE_DIR_CONTAINER 网站根目录路径(容器内)"
echo " SITE_URL 博客域名,末尾不加斜杠"
echo " BLOG_TITLE 博客标题"
echo " BLOG_DESC 博客描述"
echo " RSS_LIMIT RSS 收录文章数量(默认 20)"
echo " RSS_SECRET 手动触发更新的密钥"
echo " PHP_BIN PHP 执行命令"
echo ""
echo -e "${YELLOW}【菜单功能】${RESET}"
echo " 1. 安装 RSS 写入 generate_rss.php 并立即生成 rss.xml"
echo " 2. 更新 RSS 立即手动刷新一次 rss.xml"
echo " 3. 卸载 RSS 删除相关文件(需手动删除 1panel 计划任务)"
echo " 4. 卸载脚本 删除本脚本,可选同时卸载 RSS"
echo ""
echo -e "${YELLOW}【生成的文件】${RESET}(均位于网站根目录)"
echo " generate_rss.php RSS 生成脚本"
echo " rss.xml RSS 订阅文件(自动生成)"
echo ""
echo -e "${YELLOW}【1panel 计划任务】${RESET}"
echo " 在 1panel 面板新建计划任务,类型选「Shell 脚本」,"
echo " 内容填写:"
echo ""
echo -e " ${GREEN}bash $(realpath "$0") update${RESET}"
echo ""
echo -e "${YELLOW}【手动触发更新】${RESET}(浏览器访问)"
echo " ${SITE_URL}/generate_rss.php?key=${RSS_SECRET}"
echo ""
echo -e "${YELLOW}【RSS 订阅地址】${RESET}"
echo " ${SITE_URL}/rss.xml"
echo ""
}
# ============================================================
# 主菜单
# ============================================================
show_menu() {
echo ""
echo -e "${CYAN}=============================${RESET}"
echo -e "${CYAN} xBlog RSS 订阅 ${RESET}"
echo -e "${CYAN}=============================${RESET}"
echo " 1. 安装 RSS"
echo " 2. 更新 RSS"
echo " 3. 卸载 RSS"
echo " 4. 卸载脚本"
echo " 5. 使用说明"
echo -e "${CYAN}=============================${RESET}"
echo -n "请选择操作 [1-5],按 q 退出:"
}
# ============================================================
# 入口
# ============================================================
# 支持直接传参,方便 1panel 计划任务非交互式调用
# 用法:bash /root/rss.sh update
if [ "$1" = "update" ]; then
do_update
exit $?
fi
while true; do
show_menu
read -r choice
case "$choice" in
1) do_install ;;
2) do_update ;;
3) do_uninstall_rss ;;
4) do_uninstall_script ;;
5) do_help ;;
q|Q) echo ""; info "已退出。"; exit 0 ;;
*) warn "无效输入,请输入 1-5 或 q。" ;;
esac
echo ""
read -rp "按 Enter 返回菜单..." _
done
2. 保存文件
按 Ctrl + X → 确认保存按 Y → 确认文件名按 Enter
3. 赋予执行权限并运行脚本
chmod +x /root/rss.sh && bash /root/rss.sh
💡 脚本使用说明
配置项
配置文件位于
rss.sh顶部,请根据实际环境修改
| 配置项 | 说明 | 示例值 |
|---|---|---|
SITE_DIR |
网站根目录路径(宿主机) | /opt/1panel/www/sites/xblog/index |
SITE_DIR_CONTAINER |
网站根目录路径(容器内) | /www/sites/xblog/index |
SITE_URL |
博客域名(末尾不加斜杠) | https://xblog.itxgo.com |
BLOG_TITLE |
博客标题 | iTxGo🍃 - xBlog™️ |
BLOG_DESC |
博客描述 | PHP + SQLite 简约博客 |
RSS_LIMIT |
RSS 收录文章数量 | 20 |
RSS_SECRET |
手动触发密钥 | xblog_rss |
BLOG_EMAIL |
作者邮箱 | itdd@163.com |
FILE_OWNER |
网站文件所有者 | 1000:1000 |
PHP_BIN |
PHP 执行命令 | docker exec php php |
SCRIPT_PATH |
本脚本自身路径 | $(realpath "$0") |
菜单功能
| 选项 | 功能 | 说明 |
|---|---|---|
| 1 | 安装 RSS | 自动写入generate_rss.php,并立即生成rss.xml |
| 2 | 更新 RSS | 立即手动刷新一次rss.xml |
| 3 | 卸载 RSS | 删除相关文件(需手动删除 1Panel 计划任务) |
| 4 | 卸载脚本 | 删除本脚本rss.sh,可选同时卸载 RSS |
| 5 | 使用说明 | 显示当前这份帮助文档 |
生成的文件
所有文件均位于网站根目录下:
| 文件 | 用途 |
|---|---|
generate_rss.php |
RSS 订阅生成脚本(核心) |
rss.xml |
RSS 订阅文件(自动生成) |
1Panel 计划任务配置
步骤说明
- 登录 1Panel 管理面板
- 进入「计划任务」功能
- 点击「新建计划任务」
- 任务类型选择「Shell 脚本」
- 执行周期按需选择(推荐:每天执行一次)
- 脚本内容填写:
bash
bash /root/rss.sh update
🌐 手动触发更新
浏览器访问
https://xblog.itxgo.com/generate_rss.php?key=xblog_rss
⚠️ 安全提示:
- 请将密钥
xblog_rss替换为您实际设置的RSS_SECRET- 建议使用复杂密钥,防止未授权访问
返回结果示例
成功时:
✅ rss.xml 已生成:/www/sites/xblog/index/rss.xml
📄 共收录 20 篇文章
失败时:
❌ 生成失败,请查看 PHP 错误日志。
🔗 RSS 订阅地址
https://xblog.itxgo.com/rss.xml
📌 可将此地址提交至 RSS 阅读器(Feedly、RSSHub Radar 等)订阅博客更新