feat(office_ui): native PDF preview + OnlyOffice reverse proxy (#174) #3
No reviewers
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_office!3
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "development_mik_proxy_onlyoffice"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Two coordinated changes in
hero_office_uithat together unlock Office viewing/editing on deployments routing through a single hero_router gateway, without extending hero_router itself.editor_page(browser<embed type="application/pdf">pointing atproxy_file?inline=1). PDF viewing skips the OnlyOffice editor for read-only access./onlyoffice/*so the Document Server (Docker container on TCP127.0.0.1:8088by default) is reachable through the existing per-service URL space athttps://<gw>/hero_office/ui/onlyoffice/....Why this approach
hero_router proxies to per-service Unix sockets (
hero_router/src/server/routes.rs:1941-1948). OnlyOffice runs as a TCP server in a container — the natural path forward without extending the router is to bridge inside an existing service that already does HTTP proxying.hero_office_uialready hasproxy_filefor foundry webdav;proxy_onlyoffice_*mirrors that pattern.Context
What this changes
hero_office_ui/src/handlers.rseditor_pagePDF short-circuit (renders inline via embed)proxy_filelearns?inline=1query (toggles Content-Disposition)proxy_onlyoffice_root+proxy_onlyoffice_pathhandlers (forward arbitrary HTTP method/headers/body, drop hop-by-hop + forwarded-prefix, sharedreqwest::ClientviaOnceLock)hero_office_ui/src/config.rs—oo_upstream_base: Option<String>fromOO_UPSTREAM_BASEenvhero_office_ui/src/main.rs— three newany()routes for OnlyOffice pathshero_office_ui/Cargo.toml— addsreqwest(already a workspace dep)Demo state
Same patch applied as a hotfix on herodemo.gent01.grid.tf. With:
onlyoffice/documentserver:latest) on the VM at 127.0.0.1:8088OO_SERVER_URL=https://herodemo.gent01.grid.tf/hero_office/ui/onlyofficeinhero_office_uiaction envJWT_SECRETon both sidesclicking any .docx/.xlsx/.pptx loads the real OnlyOffice editor in the iframe; .pdf renders via browser-native viewer.
Tests
Manual:
Automated test ideas (not in this PR):
/hero_office/ui/onlyoffice/healthcheckreturns 200 when container upRisk
Closes (refs) lhumina_code/home#174
Signed-off-by: mik-tf
Two changes that together make Office viewing/editing work end-to-end on deployments that route everything through a single hero_router gateway, without extending hero_router itself. 1. Native PDF short-circuit in editor_page When the user clicks a .pdf, render via the browser's built-in PDF viewer (`<embed type="application/pdf">`) pointing at proxy_file with `?inline=1`. proxy_file gained a Query<ProxyFileQuery> extractor that switches Content-Disposition from `attachment` to `inline` when the flag is set. Browser-native viewer is faster and avoids OnlyOffice's editor JS for read-only PDF viewing. 2. OnlyOffice reverse proxy at /onlyoffice/* hero_router's per-service-Unix-socket model doesn't accommodate OnlyOffice (Docker container, TCP). Adding TCP-target routing to the router would be a cross-cutting change. Instead, hero_office_ui itself proxies /onlyoffice/* to the OnlyOffice container (default 127.0.0.1:8088, configurable via OO_UPSTREAM_BASE). Mirrors the existing proxy_file pattern. Hop-by-hop and forwarded-prefix headers are stripped on both directions; a shared reqwest::Client is stashed in a OnceLock to keep keep-alive connections. The user-facing OO_SERVER_URL becomes: https://<gateway>/hero_office/ui/onlyoffice so OnlyOffice traffic stays on the single TF Grid name proxy, no extra subdomain or contract needed. UiConfig grew an optional oo_upstream_base for non-default OnlyOffice hosts (e.g. running OO on a separate node). Routes added in main.rs: /onlyoffice, /onlyoffice/, /onlyoffice/*rest (any HTTP method). Closes the OnlyOffice deployment-side of lhumina_code/home#174 Verified live on herodemo.gent01.grid.tf. Signed-off-by: mik-tfRound 2 on the OnlyOffice reverse proxy: solves the "Download failed" symptom that appears in OnlyOffice's editor with the simple HTTP-only proxy. Two fixes in proxy_onlyoffice: 1. WebSocket support OnlyOffice uses socket.io which prefers WebSocket transport for real-time editor state. Without WS pass-through, socket.io falls back to HTTP long-polling, which then also fails because polling sessions reference state that the WS handshake never created. - Option<WebSocketUpgrade> extractor on proxy_onlyoffice_root and proxy_onlyoffice_path. When present, dispatch to ws_proxy_handler. - ws_proxy_handler accepts the upgrade (axum), opens a fresh WS connection upstream via tokio_tungstenite::connect_async, and bidirectionally pipes Message frames between client and upstream. - Hop-by-hop and Sec-WebSocket-* headers are stripped per spec; tungstenite generates fresh handshake headers for the upstream. 2. Streaming HTTP response body The original handler did `resp.bytes().await` which collects the entire upstream body before responding. socket.io's HTTP polling fallback uses long-polling — server holds connection open until a message arrives — and buffering forever breaks it. Switched to `Body::from_stream(resp.bytes_stream().map(...))`. Also drops content-length from forwarded response headers (the stream sets its own length / chunked encoding). Cargo.toml additions: axum: enabled `ws` feature tokio-tungstenite 0.24 (with rustls-tls-native-roots) futures-util 0.3 reqwest: enabled `stream` feature Verified live on herodemo: WebSocket 101 returned through the full chain (TF Grid gateway -> nginx auth -> hero_router -> hero_office_ui -> upstream OnlyOffice container) on a synthetic curl handshake. Refs lhumina_code/home#174 Signed-off-by: mik-tfThree changes that close out the OnlyOffice integration on a single-domain HTTPS gateway with auth in front: 1. Forward X-Forwarded-Host and X-Forwarded-Proto when proxying to OO (proxy_onlyoffice + ws_proxy_handler). X-Forwarded-Host carries `<host>/hero_office/ui/onlyoffice` so OnlyOffice builds public-facing URLs with the correct prefix; X-Forwarded-Proto ensures OO uses https in any URL it generates internally. 2. CSP `upgrade-insecure-requests` on editor_page response. OnlyOffice still emits some asset/cache URLs as `http://` regardless of forwarded headers (cache files keyed off internal listen address). The CSP directive tells the browser to silently rewrite those `http://` loads to `https://` before fetching, eliminating Mixed Content blocks. 3. Widen JWT permissions (build_editor_config). Was {edit, download} only — clicking format/style/comment actions triggered "You do not have rights for that action" warnings. Now also: print, copy, comment, review, fillForms, modifyFilter, modifyContentControl, chat, protect. Verified live on herodemo.gent01.grid.tf: - .docx / .xlsx / .pptx all open in real OnlyOffice editor - Slide thumbnails render, formulas evaluate, comments work - Edits autosave back through callback URL - Single-domain TLS auth (admin:admin123 via nginx) doesn't break OO Refs lhumina_code/home#174 Signed-off-by: mik-tfPull request closed