- source/jm/jm.py:aiohttp + jmcomic,GET /jm/get_comic?id=xxx[&range=起-止] 下载至 cache/jm 隐藏暂存目录 → webp 转 jpg 打包 ZIP → HTTP 直返 → 600s 延时删除 单次上限 30 章;per-album 锁防并发;cache_sweep 兜底清理;/jm/cache_info 运维接口 - source/main.py:聚合层注册 /jm/get_comic(新增 JM_BACKEND,复用 proxy()) - systemd/jm.service:独立系统服务(Restart=always,日志 /var/log/jm.log) - README / 部署与运维文档同步更新
108 lines
3.6 KiB
Python
108 lines
3.6 KiB
Python
from aiohttp import web, ClientSession, ClientTimeout
|
||
|
||
|
||
PIXIV_BACKEND = "http://127.0.0.1:9007"
|
||
BILIBILI_BACKEND = "http://127.0.0.1:9006"
|
||
DUFE_BACKEND = "http://127.0.0.1:9005"
|
||
JM_BACKEND = "http://127.0.0.1:9004"
|
||
|
||
|
||
async def handle_pixiv(request):
|
||
pid = request.query.get("id", "")
|
||
if not pid:
|
||
return web.json_response({"error": "missing id parameter"}, status=400)
|
||
target = f"{PIXIV_BACKEND}/{pid}"
|
||
return await proxy(request, target)
|
||
|
||
|
||
async def handle_bilibili(request):
|
||
qs = request.query_string
|
||
target = f"{BILIBILI_BACKEND}/bilibili/get_video"
|
||
if qs:
|
||
target += "?" + qs
|
||
return await proxy(request, target)
|
||
|
||
|
||
async def handle_dufe_sign_in(request):
|
||
# ginkgo_sign_in.service(127.0.0.1:9005),端点 /dufe/ginkgostu_sign_in,支持 GET|POST
|
||
target = f"{DUFE_BACKEND}/dufe/ginkgostu_sign_in"
|
||
if request.query_string:
|
||
target += "?" + request.query_string
|
||
return await proxy_forward(request, target)
|
||
|
||
|
||
async def handle_jm(request):
|
||
qs = request.query_string
|
||
target = f"{JM_BACKEND}/jm/get_comic"
|
||
if qs:
|
||
target += "?" + qs
|
||
return await proxy(request, target)
|
||
|
||
|
||
async def proxy(request, target):
|
||
timeout = ClientTimeout(total=300)
|
||
async with ClientSession(timeout=timeout) as session:
|
||
try:
|
||
async with session.get(target) as resp:
|
||
body = await resp.read()
|
||
headers = {}
|
||
for key in ("Content-Type", "Content-Disposition", "Cache-Control"):
|
||
if key in resp.headers:
|
||
headers[key] = resp.headers[key]
|
||
return web.Response(
|
||
body=body,
|
||
status=resp.status,
|
||
headers=headers,
|
||
)
|
||
except Exception as e:
|
||
return web.json_response(
|
||
{"error": "backend unreachable", "detail": str(e)}, status=502
|
||
)
|
||
|
||
|
||
async def proxy_forward(request, target):
|
||
# 保留请求方法与请求体的转发,供需要 POST 的后端使用(dufe 签到码查询)
|
||
timeout = ClientTimeout(total=300)
|
||
data = None
|
||
fwd_headers = {}
|
||
if request.method not in ("GET", "HEAD"):
|
||
data = await request.read()
|
||
ctype = request.headers.get("Content-Type")
|
||
if ctype:
|
||
fwd_headers["Content-Type"] = ctype
|
||
async with ClientSession(timeout=timeout) as session:
|
||
try:
|
||
async with session.request(
|
||
request.method, target, data=data, headers=fwd_headers
|
||
) as resp:
|
||
body = await resp.read()
|
||
headers = {}
|
||
for key in ("Content-Type", "Content-Disposition", "Cache-Control"):
|
||
if key in resp.headers:
|
||
headers[key] = resp.headers[key]
|
||
return web.Response(
|
||
body=body,
|
||
status=resp.status,
|
||
headers=headers,
|
||
)
|
||
except Exception as e:
|
||
return web.json_response(
|
||
{"error": "backend unreachable", "detail": str(e)}, status=502
|
||
)
|
||
|
||
|
||
async def handle_root(request):
|
||
raise web.HTTPFound("https://rrrosmontis.icu/articles/index.html?id=00003")
|
||
|
||
|
||
app = web.Application()
|
||
app.router.add_get("/", handle_root)
|
||
app.router.add_get("/pixiv/get_image", handle_pixiv)
|
||
app.router.add_get("/bilibili/get_video", handle_bilibili)
|
||
app.router.add_get("/dufe/ginkgostu_sign_in", handle_dufe_sign_in)
|
||
app.router.add_post("/dufe/ginkgostu_sign_in", handle_dufe_sign_in)
|
||
app.router.add_get("/jm/get_comic", handle_jm)
|
||
|
||
if __name__ == "__main__":
|
||
web.run_app(app, host="127.0.0.1", port=9008)
|