feat(seed): 支持从远程URL加载seed.txt并添加认证配置
新增环境变量配置支持从远程Gitea等URL加载seed.txt文件,包括: - SEED_SOURCE: 指定来源(file/url) - SEED_URL: 远程文件地址 - SEED_AUTH_HEADER/SEED_TOKEN: 认证配置 添加远程加载失败时自动回退到本地文件的功能 更新README文档说明配置方法
This commit is contained in:
17
.env.example
Normal file
17
.env.example
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# 服务器端口
|
||||||
|
PORT=3000
|
||||||
|
|
||||||
|
# Seed 来源:file 或 url
|
||||||
|
SEED_SOURCE=file
|
||||||
|
|
||||||
|
# 当 SEED_SOURCE=url 时,配置远程原始TXT地址
|
||||||
|
# 示例:Gitea Raw 文件直链或 API Raw 接口
|
||||||
|
# SEED_URL=https://gitea.example.com/<owner>/<repo>/raw/seed.txt
|
||||||
|
SEED_URL=
|
||||||
|
|
||||||
|
# 认证(任选其一)
|
||||||
|
# 1) 自定义 Authorization 头(例如 Bearer 或 Basic)
|
||||||
|
# SEED_AUTH_HEADER=Bearer xxxxx
|
||||||
|
SEED_AUTH_HEADER=
|
||||||
|
# 2) Token 形式(将自动使用 Authorization: token <TOKEN>)
|
||||||
|
SEED_TOKEN=
|
||||||
31
README.md
31
README.md
@@ -166,6 +166,20 @@
|
|||||||
- `npm install && npm start`
|
- `npm install && npm start`
|
||||||
- 建议使用 systemd/pm2 守护进程,并在 Nginx 反向代理到 `127.0.0.1:<PORT>`
|
- 建议使用 systemd/pm2 守护进程,并在 Nginx 反向代理到 `127.0.0.1:<PORT>`
|
||||||
|
|
||||||
|
## 环境变量示例(.env.example)
|
||||||
|
|
||||||
|
- 复制示例文件并按需修改:
|
||||||
|
- Windows PowerShell:`Copy-Item .env.example .env`
|
||||||
|
- Linux/Mac:`cp .env.example .env`
|
||||||
|
- 可用变量:
|
||||||
|
- `PORT`:服务器端口(默认 `3000`)
|
||||||
|
- `SEED_SOURCE`:`file` 或 `url`(默认 `file`)
|
||||||
|
- `SEED_URL`:当 `SEED_SOURCE=url` 时的远程TXT地址(Gitea Raw直链或API)
|
||||||
|
- `SEED_AUTH_HEADER`:自定义认证头(如 `Bearer <token>`)
|
||||||
|
- `SEED_TOKEN`:令牌(将自动以 `Authorization: token <TOKEN>` 发送)
|
||||||
|
- 生效方式:修改 `.env` 后重启服务
|
||||||
|
- 相关代码位置:`server.js:19`(环境变量读取)、`server.js:419`(远程/本地加载逻辑)
|
||||||
|
|
||||||
## 常见问题(FAQ)
|
## 常见问题(FAQ)
|
||||||
|
|
||||||
- 修改 `seed.txt` 是否需要重启?不需要,`GET /api/seed` 会重新读取。
|
- 修改 `seed.txt` 是否需要重启?不需要,`GET /api/seed` 会重新读取。
|
||||||
@@ -190,6 +204,7 @@
|
|||||||
- 页面视觉细节:为 `header` 与 `main` 增加间距(≥30px),背景设置 `background-attachment: fixed` 并覆盖视窗(居中、等比、无重复)
|
- 页面视觉细节:为 `header` 与 `main` 增加间距(≥30px),背景设置 `background-attachment: fixed` 并覆盖视窗(居中、等比、无重复)
|
||||||
- 修复:CSS 依赖路径对 `..` 上级目录的正确解析与落盘(兼容 Font Awesome 的 `../webfonts`);增强 CDN 回退匹配支持 `@scope` 包名(jsDelivr / unpkg)
|
- 修复:CSS 依赖路径对 `..` 上级目录的正确解析与落盘(兼容 Font Awesome 的 `../webfonts`);增强 CDN 回退匹配支持 `@scope` 包名(jsDelivr / unpkg)
|
||||||
- 新增:开源许可文件 `LICENSE`(GPL-3.0),并在 README 增加许可说明与仓库地址
|
- 新增:开源许可文件 `LICENSE`(GPL-3.0),并在 README 增加许可说明与仓库地址
|
||||||
|
- 新增:`seed.txt` 支持远程来源(Gitea 原始文件),通过环境变量配置
|
||||||
|
|
||||||
## 开源许可与仓库地址
|
## 开源许可与仓库地址
|
||||||
|
|
||||||
@@ -200,4 +215,18 @@
|
|||||||
- 分发时需保留版权声明与本许可证文本,并开放源代码
|
- 分发时需保留版权声明与本许可证文本,并开放源代码
|
||||||
- 不提供任何形式的担保,详见 `LICENSE` 的免责声明章节
|
- 不提供任何形式的担保,详见 `LICENSE` 的免责声明章节
|
||||||
|
|
||||||
Copyright © 2025 Asset Cache Server Developer By [SnowZ](https://ckk.photo8.site/Photo8/Asset-cache)
|
## Seed 配置(本地与远程)
|
||||||
|
|
||||||
|
- 默认行为:`GET /api/seed` 读取仓库根目录的本地 `seed.txt`
|
||||||
|
- 远程来源:将环境变量设置为以下值以从 Gitea 原始文件拉取
|
||||||
|
- `SEED_SOURCE=url`
|
||||||
|
- `SEED_URL=<你的Gitea原始TXT地址>`(例如:`https://gitea.example.com/api/v1/repos/<owner>/<repo>/raw/seed.txt?ref=main` 或 Raw 文件直链)
|
||||||
|
- 可选认证:
|
||||||
|
- `SEED_AUTH_HEADER="Bearer <token>"`(自定义 Authorization 头)
|
||||||
|
- 或 `SEED_TOKEN="<token>"`(自动使用 `Authorization: token <token>` 头)
|
||||||
|
- 回退机制:远程拉取失败时,若本地 `seed.txt` 存在则自动使用本地文件
|
||||||
|
- 相关代码:
|
||||||
|
- 远程/本地加载逻辑:`server.js:419`(`loadSeedTxt`)
|
||||||
|
- 执行批量抓取:`server.js:440`(`runSeed`)
|
||||||
|
|
||||||
|
Copyright © 2025 Asset Cache Server Developer By [SnowZ](https://ckk.photo8.site/Photo8/Asset-cache)
|
||||||
|
|||||||
77
seed.txt
77
seed.txt
@@ -1,18 +1,83 @@
|
|||||||
# 示例:本文件内置一些常用的CSS/JS地址用于测试
|
# 示例:本文件内置一些常用的CSS/JS地址用于测试
|
||||||
# 注释行以 # 开头;空行将被忽略
|
# 注释行以 # 开头;空行将被忽略
|
||||||
|
|
||||||
# Bootstrap 5.3.0 样式与脚本
|
# Bootstrap
|
||||||
https://cdn.jsdmirror.com/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css
|
https://cdn.jsdmirror.com/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css
|
||||||
https://cdn.jsdmirror.com/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js
|
https://cdn.jsdmirror.com/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js
|
||||||
https://cdn.tailwindcss.com
|
|
||||||
|
|
||||||
# Element UI 2.15.13 样式(用于验证字体依赖自动抓取)
|
# font-awesome
|
||||||
https://dist.jicelue.com/css/npm/element-ui@2.15.13/lib/theme-chalk/index.css
|
|
||||||
|
|
||||||
# Font Awesome Free 6(验证 webfonts 自动抓取)
|
|
||||||
https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/all.min.css
|
https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/all.min.css
|
||||||
https://s4.zstatic.net/ajax/libs/font-awesome/5.15.4/css/all.min.css
|
https://s4.zstatic.net/ajax/libs/font-awesome/5.15.4/css/all.min.css
|
||||||
https://s4.zstatic.net/ajax/libs/font-awesome/5.15.4/css/v4-shims.min.css
|
https://s4.zstatic.net/ajax/libs/font-awesome/5.15.4/css/v4-shims.min.css
|
||||||
https://s4.zstatic.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css
|
https://s4.zstatic.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css
|
||||||
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css
|
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css
|
||||||
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css
|
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css
|
||||||
|
|
||||||
|
# layer
|
||||||
|
https://s4.zstatic.net/ajax/libs/layer/3.1.1/layer.js
|
||||||
|
https://s4.zstatic.net/ajax/libs/layer/3.1.1/theme/default/layer.css
|
||||||
|
|
||||||
|
# iconify
|
||||||
|
https://code.iconify.design/3/3.1.1/iconify.min.js
|
||||||
|
|
||||||
|
# JQUERY
|
||||||
|
https://upcdn.b0.upaiyun.com/libs/jquery/jquery-1.9.1.min.js
|
||||||
|
https://upcdn.b0.upaiyun.com/libs/jquery/jquery-2.0.3.min.js
|
||||||
|
https://upcdn.b0.upaiyun.com/libs/jqueryui/jquery.ui-1.9.1.min.js
|
||||||
|
https://cdn.staticfile.net/jquery/3.7.1/jquery.js
|
||||||
|
https://cdn.staticfile.net/jquery/3.7.1/jquery.min.js
|
||||||
|
https://cdn.staticfile.net/jquery/3.7.1/jquery.min.map
|
||||||
|
https://cdn.staticfile.net/jquery/3.7.1/jquery.slim.js
|
||||||
|
https://cdn.staticfile.net/jquery/3.7.1/jquery.slim.min.js
|
||||||
|
https://cdn.staticfile.net/jquery/3.7.1/jquery.slim.min.map
|
||||||
|
|
||||||
|
# xgplayer
|
||||||
|
https://unpkg.byted-static.com/xgplayer/3.0.10/dist/index.min.js
|
||||||
|
https://unpkg.byted-static.com/xgplayer-hls/3.0.10/dist/index.min.js
|
||||||
|
https://unpkg.byted-static.com/xgplayer-flv/3.0.10/dist/index.min.js
|
||||||
|
https://unpkg.byted-static.com/xgplayer-dash/3.0.0-alpha.2/dist/index.min.js
|
||||||
|
https://unpkg.byted-static.com/xgplayer-mp4/3.0.10/dist/index.min.js
|
||||||
|
https://unpkg.byted-static.com/xgplayer/3.0.10/dist/index.min.css
|
||||||
|
https://cdn.jsdelivr.net/npm/xgplayer-hls.js@3.0.20/dist/index.min.js
|
||||||
|
|
||||||
|
# swarmcloud
|
||||||
|
https://cdn.jsdelivr.net/npm/@swarmcloud/hls/p2p-engine.min.js
|
||||||
|
https://cdn.jsdelivr.net/npm/@swarmcloud/hls/hls.min.js
|
||||||
|
|
||||||
|
# tailwindcss/browser
|
||||||
|
https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.1.17/dist/index.global.min.js
|
||||||
|
https://cdn.tailwindcss.com
|
||||||
|
|
||||||
|
# iconify
|
||||||
|
https://code.iconify.design/3/3.1.1/iconify.min.js
|
||||||
|
|
||||||
|
# notyf
|
||||||
|
https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css
|
||||||
|
https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js
|
||||||
|
|
||||||
|
# Vue.js
|
||||||
|
https://unpkg.com/vue@3.5.7/dist/vue.global.prod.js
|
||||||
|
https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js
|
||||||
|
|
||||||
|
# Axios
|
||||||
|
https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
|
||||||
|
https://cdn.jsdelivr.net/npm/axios@0.26.0/dist/axios.min.js
|
||||||
|
https://cdn.jsdmirror.com/npm/axios@0.21.1/dist/axios.min.js
|
||||||
|
|
||||||
|
# Swiper
|
||||||
|
https://cdnjs.cloudflare.com/ajax/libs/Swiper/7.4.1/swiper-bundle.min.js
|
||||||
|
https://cdnjs.cloudflare.com/ajax/libs/Swiper/7.4.1/swiper-bundle.min.css
|
||||||
|
|
||||||
|
# npm
|
||||||
|
https://cdn.jsdelivr.net/npm/element-ui@2.15.7/lib/theme-chalk/index.css
|
||||||
|
https://cdn.jsdelivr.net/npm/element-ui@2.15.7/lib/index.js
|
||||||
|
https://cdn.jsdelivr.net/npm/element-ui/lib/index.js
|
||||||
|
https://cdn.jsdelivr.net/npm/element-ui/lib/theme-chalk/index.css
|
||||||
|
https://cdn.jsdmirror.com/npm/element-ui@2.15.13/lib/theme-chalk/index.css
|
||||||
|
https://cdn.jsdmirror.com/npm/element-ui@2.15.13/lib/index.js
|
||||||
|
https://cdn.jsdmirror.com/npm/element-ui@2.15.6/lib/theme-chalk/index.css
|
||||||
|
https://cdn.jsdmirror.com/npm/element-ui@2.15.6/lib/index.js
|
||||||
|
|
||||||
|
# Emoji
|
||||||
|
https://cdn.staticfile.net/emoji-js/3.8.0/emoji.js
|
||||||
|
https://cdn.staticfile.net/emoji-js/3.8.0/emoji.css
|
||||||
40
server.js
40
server.js
@@ -16,6 +16,11 @@ const CSS_DIR = path.join(CACHE_ROOT, 'css')
|
|||||||
const JS_DIR = path.join(CACHE_ROOT, 'js')
|
const JS_DIR = path.join(CACHE_ROOT, 'js')
|
||||||
const SEED_FILE = path.join(__dirname, 'seed.txt')
|
const SEED_FILE = path.join(__dirname, 'seed.txt')
|
||||||
const PUBLIC_DIR = path.join(__dirname, 'public')
|
const PUBLIC_DIR = path.join(__dirname, 'public')
|
||||||
|
// Seed 源设置(默认读取本地 seed.txt,可选从远程URL读取原始TXT)
|
||||||
|
const SEED_SOURCE = (process.env.SEED_SOURCE || 'file').toLowerCase()
|
||||||
|
const SEED_URL = process.env.SEED_URL || ''
|
||||||
|
const SEED_TOKEN = process.env.SEED_TOKEN || ''
|
||||||
|
const SEED_AUTH_HEADER = process.env.SEED_AUTH_HEADER || ''
|
||||||
|
|
||||||
// 中间件
|
// 中间件
|
||||||
app.use(express.json({ limit: '2mb' }))
|
app.use(express.json({ limit: '2mb' }))
|
||||||
@@ -405,14 +410,35 @@ app.get('/health', (req, res) => {
|
|||||||
// 已移除外部提交接口:/api/upload-txt 与 /api/cache
|
// 已移除外部提交接口:/api/upload-txt 与 /api/cache
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从项目内置 seed.txt 加载URL并执行批量缓存
|
* 加载 Seed 文本(本地或远程)
|
||||||
|
* - 当 SEED_SOURCE = 'url' 且配置了 SEED_URL 时,优先从远程拉取
|
||||||
|
* - 支持可选认证头(SEED_AUTH_HEADER),或 SEED_TOKEN 以 Authorization: token <TOKEN> 形式
|
||||||
|
* - 远程拉取失败时回退到本地文件(若存在)
|
||||||
|
* @returns {Promise<string>} 返回原始TXT文本(可能为空字符串)
|
||||||
|
*/
|
||||||
|
async function loadSeedTxt() {
|
||||||
|
if (SEED_SOURCE === 'url' && SEED_URL) {
|
||||||
|
try {
|
||||||
|
const headers = { 'User-Agent': 'AssetCache/1.0', 'Accept': 'text/plain, */*' }
|
||||||
|
if (SEED_AUTH_HEADER) headers['Authorization'] = SEED_AUTH_HEADER
|
||||||
|
else if (SEED_TOKEN) headers['Authorization'] = `token ${SEED_TOKEN}`
|
||||||
|
const resp = await axios.get(SEED_URL, { responseType: 'text', timeout: 20000, headers })
|
||||||
|
const txt = typeof resp.data === 'string' ? resp.data : (resp.data?.toString?.() || '')
|
||||||
|
if (txt && txt.trim().length > 0) return txt
|
||||||
|
} catch (e) {
|
||||||
|
// 回退到本地
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fs.existsSync(SEED_FILE)) return fs.readFileSync(SEED_FILE, 'utf8')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载Seed(本地/远程)并执行批量缓存
|
||||||
* @returns {Promise<{count:number, results:any[]}>}
|
* @returns {Promise<{count:number, results:any[]}>}
|
||||||
*/
|
*/
|
||||||
async function runSeedFromFile() {
|
async function runSeed() {
|
||||||
if (!fs.existsSync(SEED_FILE)) {
|
const txt = await loadSeedTxt()
|
||||||
return { count: 0, results: [] }
|
|
||||||
}
|
|
||||||
const txt = fs.readFileSync(SEED_FILE, 'utf8')
|
|
||||||
const urls = parseTxtToUrls(txt)
|
const urls = parseTxtToUrls(txt)
|
||||||
if (urls.length === 0) return { count: 0, results: [] }
|
if (urls.length === 0) return { count: 0, results: [] }
|
||||||
const results = await batchFetch(urls)
|
const results = await batchFetch(urls)
|
||||||
@@ -422,7 +448,7 @@ async function runSeedFromFile() {
|
|||||||
// 触发内置seed.txt抓取
|
// 触发内置seed.txt抓取
|
||||||
app.get('/api/seed', async (req, res) => {
|
app.get('/api/seed', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const r = await runSeedFromFile()
|
const r = await runSeed()
|
||||||
const mapped = (r.results || []).map(x => ({
|
const mapped = (r.results || []).map(x => ({
|
||||||
url: x.url,
|
url: x.url,
|
||||||
saved: x.saved ? getPublicPath(x.url, x.type, x.saved) : '',
|
saved: x.saved ? getPublicPath(x.url, x.type, x.saved) : '',
|
||||||
|
|||||||
Reference in New Issue
Block a user