feat(office): 补提交 univer-office 办公代理(已部署上线)
/office/* 反向代理到本机 univer-office Node 服务(9091),承接 markdown<->IDocumentData 转换 + docx/xlsx/ppt 导出。含 office_proxy.py + config.json 路由 + pipeline_app 注册 + restart-pipeline.sh
This commit is contained in:
parent
c973a20fd1
commit
b849eb3e92
51
app/office_proxy.py
Normal file
51
app/office_proxy.py
Normal file
@ -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)
|
||||||
@ -39,6 +39,7 @@ def init():
|
|||||||
from tenant.init import load_tenant
|
from tenant.init import load_tenant
|
||||||
from ktv_adapter import load_ktv_adapter
|
from ktv_adapter import load_ktv_adapter
|
||||||
from global_func import set_globalvariable
|
from global_func import set_globalvariable
|
||||||
|
import office_proxy # 注册 /office/* 转发函数(univer-office Node 服务代理)
|
||||||
try:
|
try:
|
||||||
from smssend import load_smssend
|
from smssend import load_smssend
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|||||||
@ -51,6 +51,10 @@
|
|||||||
{
|
{
|
||||||
"leading": "/i18n_getmsgs",
|
"leading": "/i18n_getmsgs",
|
||||||
"registerfunction": "i18n"
|
"registerfunction": "i18n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"leading": "/office/",
|
||||||
|
"registerfunction": "office_proxy"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
27
restart-pipeline.sh
Normal file
27
restart-pipeline.sh
Normal file
@ -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
|
||||||
Loading…
x
Reference in New Issue
Block a user