diff --git a/app/office_proxy.py b/app/office_proxy.py new file mode 100644 index 0000000..5d6bfe8 --- /dev/null +++ b/app/office_proxy.py @@ -0,0 +1,51 @@ +"""office_proxy - 把 /office/* 请求转发到本机 univer-office Node 服务(9091)。 + +univer-office 承担 markdown<->IDocumentData 转换与 docx/xlsx/ppt 导出, +这些能力是 Node 生态(Univer + docx/exceljs/pptxgenjs),Python 侧无法直接实现, +故通过 ahserver 的 startswiths 机制做反向代理。 + +路由映射: + /office/render -> http://127.0.0.1:9091/render (markdown -> IDocumentData) + /office/extract -> http://127.0.0.1:9091/extract (IDocumentData -> 纯文本) + /office/export/docx -> http://127.0.0.1:9091/export/docx (markdown -> docx 二进制) +""" +import aiohttp +from aiohttp import web +from appPublic.registerfunction import registerFunction +from appPublic.log import error, debug + +OFFICE_BACKEND = 'http://127.0.0.1:9091' + +# hop-by-hop 头,转发时需剔除 +_HOP_BY_HOP = { + 'host', 'content-length', 'transfer-encoding', 'connection', + 'keep-alive', 'upgrade', 'proxy-authenticate', 'proxy-authorization', + 'te', 'trailers', +} + + +async def office_proxy(request, params_kw, *args, **env): + """把 /office/* 转发到 9091。args = leading 之后的路径段,如 ('render',) 或 ('export','docx')。""" + path = '/'.join(args) + url = f'{OFFICE_BACKEND}/{path}' + debug(f'office_proxy: {request.method} {url}') + + body = await request.read() + headers = {k: v for k, v in request.headers.items() if k.lower() not in _HOP_BY_HOP} + + try: + timeout = aiohttp.ClientTimeout(total=180) + async with aiohttp.ClientSession(timeout=timeout) as session: + async with session.request(request.method, url, headers=headers, data=body) as resp: + resp_body = await resp.read() + resp_headers = { + k: v for k, v in resp.headers.items() + if k.lower() not in _HOP_BY_HOP and k.lower() != 'content-encoding' + } + return web.Response(body=resp_body, status=resp.status, headers=resp_headers) + except Exception as e: + error(f'office_proxy failed: {e}') + return web.Response(body=f'office backend error: {e}'.encode(), status=502) + + +registerFunction('office_proxy', office_proxy) diff --git a/app/pipeline_app.py b/app/pipeline_app.py index d7058f9..2fa2484 100644 --- a/app/pipeline_app.py +++ b/app/pipeline_app.py @@ -39,6 +39,7 @@ def init(): from tenant.init import load_tenant from ktv_adapter import load_ktv_adapter from global_func import set_globalvariable + import office_proxy # 注册 /office/* 转发函数(univer-office Node 服务代理) try: from smssend import load_smssend except ImportError: diff --git a/conf/config.json b/conf/config.json index 4dab9f3..d897e3a 100644 --- a/conf/config.json +++ b/conf/config.json @@ -51,6 +51,10 @@ { "leading": "/i18n_getmsgs", "registerfunction": "i18n" + }, + { + "leading": "/office/", + "registerfunction": "office_proxy" } ] }, diff --git a/restart-pipeline.sh b/restart-pipeline.sh new file mode 100644 index 0000000..8d25a5c --- /dev/null +++ b/restart-pipeline.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# 重启 pipeline-app(避免 pkill -f pipeline_app 自杀:精确匹配 python 主进程路径) +cd /d/pipeline/pipeline-app + +echo "=== 杀旧进程 ===" +if [ -f pipeline.pid ]; then + kill $(cat pipeline.pid) 2>/dev/null +fi +pkill -f "app/pipeline_app.py" 2>/dev/null +sleep 3 + +if pgrep -f "app/pipeline_app.py" > /dev/null; then + echo "still running, force kill" + pkill -9 -f "app/pipeline_app.py" 2>/dev/null + sleep 2 +fi +rm -f pipeline.pid pipeline-web.pid pipeline-worker-*.pid + +echo "=== 启动 ===" +bash start.sh +sleep 6 + +echo "=== 进程 ===" +ps aux | grep "app/pipeline_app.py" | grep -v grep | head -2 + +echo "=== 日志 tail ===" +tail -15 logs/pipeline.log 2>/dev/null