refactor(api): 移除外部提交接口仅保留种子文件方式

移除 POST /api/upload-txt 和 POST /api/cache 接口,改为仅通过 seed.txt 文件管理URL抓取
更新README文档以反映接口变更,增强安全性
This commit is contained in:
2025-11-22 16:18:07 +08:00
parent 061998de0c
commit 7d023fc954
2 changed files with 5 additions and 83 deletions

View File

@@ -32,26 +32,9 @@
## 接口说明 ## 接口说明
- `POST /api/upload-txt`
- 上传 TXT字段名 `file`),每行一个 URL支持 `#` 注释与空行
- 响应项示例:
- `{
"url": "https://cdn.jsdmirror.com/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css",
"saved": "/css/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css",
"accessUrl": "https://<你的域名>/css/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css",
"filename": "bootstrap.min.css",
"type": "css",
"size": 232914,
"skipped": false
}`
- `POST /api/cache`
- 提交单条 `url` 或数组 `urls`
- 单条示例:`{"url":"https://.../bootstrap.min.css"}`
- 多条示例:`{"urls":["https://.../bootstrap.min.css","https://.../bootstrap.bundle.min.js"]}`
- `GET /api/seed` - `GET /api/seed`
- 从项目内置 `seed.txt` 读取并批量抓取,无需重启 - 从项目内置 `seed.txt` 读取并批量抓取,无需重启
- 管理抓取仅通过修改服务器上的 `seed.txt` 实现,服务端不接受外部提交 URL
## 静态访问 ## 静态访问
@@ -75,7 +58,7 @@
## 安全与白名单建议 ## 安全与白名单建议
- 推荐在 CDN/WAF 层配置防盗链白名单(如 `*.aaa.com``www.bbb.com` - 推荐在 CDN/WAF 层配置防盗链白名单(如 `*.aaa.com``www.bbb.com`
- 如需更强控制可扩展签名 URL 校验(服务端或边缘验证令牌) - 管理接口仅保留 `GET /api/seed`,不提供外部 POST如需更强控制可扩展签名 URL 校验(服务端或边缘验证令牌)
## 部署建议 ## 部署建议
@@ -98,4 +81,5 @@
- 去重逻辑(已存在则跳过,返回 `skipped: true` - 去重逻辑(已存在则跳过,返回 `skipped: true`
- `.env` 支持(`PORT`),适配多端口部署 - `.env` 支持(`PORT`),适配多端口部署
- 接口响应统一返回公共路径 `saved` 与完整 URL `accessUrl` - 接口响应统一返回公共路径 `saved` 与完整 URL `accessUrl`
- 依赖升级:`express@^5.1.0`、`multer@^2.0.2`、`axios@^1.13.2`、`morgan@^1.10.1` - 依赖升级:`express@^5.1.0``multer@^2.0.2``axios@^1.13.2``morgan@^1.10.1`
- 移除外部提交接口:`POST /api/upload-txt``POST /api/cache`,仅支持通过 `seed.txt` 批量抓取

View File

@@ -2,7 +2,6 @@ import 'dotenv/config'
import express from 'express' import express from 'express'
import axios from 'axios' import axios from 'axios'
import morgan from 'morgan' import morgan from 'morgan'
import multer from 'multer'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
@@ -11,7 +10,6 @@ const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename) const __dirname = path.dirname(__filename)
const app = express() const app = express()
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 5 * 1024 * 1024 } })
const PORT = Number(process.env.PORT || 3000) const PORT = Number(process.env.PORT || 3000)
const CACHE_ROOT = path.join(__dirname, 'cache') const CACHE_ROOT = path.join(__dirname, 'cache')
const CSS_DIR = path.join(CACHE_ROOT, 'css') const CSS_DIR = path.join(CACHE_ROOT, 'css')
@@ -197,67 +195,7 @@ app.get('/health', (req, res) => {
res.json({ ok: true }) res.json({ ok: true })
}) })
// 上传TXTmultipart/form-data字段名为 file // 已移除外部提交接口:/api/upload-txt 与 /api/cache
app.post('/api/upload-txt', upload.single('file'), async (req, res) => {
try {
if (!req.file) return res.status(400).json({ error: '缺少TXT文件字段: file' })
const txt = req.file.buffer.toString('utf8')
const urls = parseTxtToUrls(txt)
if (urls.length === 0) return res.status(400).json({ error: 'TXT未包含有效URL' })
const results = await batchFetch(urls)
const mapped = results.map(r => ({
url: r.url,
saved: r.saved ? getPublicPath(r.url, r.type, r.saved) : '',
accessUrl: r.saved ? buildAccessUrl(req, r.url, r.type, r.saved) : '',
size: r.size,
type: r.type,
skipped: r.skipped,
error: r.error,
filename: r.saved
}))
res.json({ count: mapped.length, results: mapped })
} catch (e) {
res.status(500).json({ error: e.message })
}
})
// 直接提交URL或URL列表
app.post('/api/cache', async (req, res) => {
try {
const { url, urls } = req.body || {}
if (url) {
const r = await fetchAndStore(url)
const publicPath = r.saved ? getPublicPath(r.url, r.type, r.saved) : ''
const accessUrl = r.saved ? buildAccessUrl(req, r.url, r.type, r.saved) : ''
return res.json({
url: r.url,
saved: publicPath,
accessUrl,
size: r.size,
type: r.type,
skipped: r.skipped,
filename: r.saved
})
}
if (Array.isArray(urls) && urls.length > 0) {
const results = await batchFetch(urls)
const mapped = results.map(r => ({
url: r.url,
saved: r.saved ? getPublicPath(r.url, r.type, r.saved) : '',
accessUrl: r.saved ? buildAccessUrl(req, r.url, r.type, r.saved) : '',
size: r.size,
type: r.type,
skipped: r.skipped,
error: r.error,
filename: r.saved
}))
return res.json({ count: mapped.length, results: mapped })
}
res.status(400).json({ error: '请提供 url 或 urls' })
} catch (e) {
res.status(500).json({ error: e.message })
}
})
/** /**
* 从项目内置 seed.txt 加载URL并执行批量缓存 * 从项目内置 seed.txt 加载URL并执行批量缓存