Fix OnlyOffice: use internal IP for server-side URLs, create valid docx/pptx files
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m24s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m24s
This commit is contained in:
parent
382d78d492
commit
a0353445d9
2 changed files with 139 additions and 14 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
|
@ -117,12 +118,10 @@ func (h *OnlyOfficeHandler) CreateBlank(c *fiber.Ctx) error {
|
|||
counter++
|
||||
}
|
||||
|
||||
// Create an empty file. OnlyOffice will initialize it when opened.
|
||||
f, err := os.Create(finalPath)
|
||||
if err != nil {
|
||||
// Create a valid Office Open XML file (docx/pptx are ZIP archives)
|
||||
if err := createBlankOfficeFile(finalPath, ext); err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": "failed to create file"})
|
||||
}
|
||||
f.Close()
|
||||
|
||||
// Return the relative path
|
||||
relativePath, _ := filepath.Rel(h.Config.StorageDir, finalPath)
|
||||
|
|
@ -133,6 +132,135 @@ func (h *OnlyOfficeHandler) CreateBlank(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
// createBlankOfficeFile creates a minimal valid .docx or .pptx file.
|
||||
// These formats are ZIP archives containing XML files following the
|
||||
// Office Open XML (OOXML) standard.
|
||||
func createBlankOfficeFile(path string, ext string) error {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w := zip.NewWriter(f)
|
||||
defer w.Close()
|
||||
|
||||
if ext == ".pptx" {
|
||||
return writePptxContents(w)
|
||||
}
|
||||
return writeDocxContents(w)
|
||||
}
|
||||
|
||||
func writeDocxContents(w *zip.Writer) error {
|
||||
files := map[string]string{
|
||||
"[Content_Types].xml": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
||||
<Default Extension="xml" ContentType="application/xml"/>
|
||||
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
||||
</Types>`,
|
||||
"_rels/.rels": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
||||
</Relationships>`,
|
||||
"word/document.xml": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:body>
|
||||
<w:p><w:r><w:t></w:t></w:r></w:p>
|
||||
</w:body>
|
||||
</w:document>`,
|
||||
}
|
||||
for name, content := range files {
|
||||
fw, err := w.Create(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fw.Write([]byte(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writePptxContents(w *zip.Writer) error {
|
||||
files := map[string]string{
|
||||
"[Content_Types].xml": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
||||
<Default Extension="xml" ContentType="application/xml"/>
|
||||
<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
|
||||
<Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>
|
||||
<Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/>
|
||||
<Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>
|
||||
</Types>`,
|
||||
"_rels/.rels": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
|
||||
</Relationships>`,
|
||||
"ppt/presentation.xml": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<p:presentation xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
|
||||
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
|
||||
<p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rId1"/></p:sldMasterIdLst>
|
||||
<p:sldIdLst><p:sldId id="256" r:id="rId2"/></p:sldIdLst>
|
||||
<p:sldSz cx="9144000" cy="6858000" type="screen4x3"/>
|
||||
<p:notesSz cx="6858000" cy="9144000"/>
|
||||
</p:presentation>`,
|
||||
"ppt/_rels/presentation.xml.rels": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>
|
||||
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide1.xml"/>
|
||||
</Relationships>`,
|
||||
"ppt/slideMasters/slideMaster1.xml": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<p:sldMaster xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
|
||||
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
|
||||
<p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
|
||||
<p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr>
|
||||
</p:spTree></p:cSld>
|
||||
<p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst>
|
||||
</p:sldMaster>`,
|
||||
"ppt/slideMasters/_rels/slideMaster1.xml.rels": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
|
||||
</Relationships>`,
|
||||
"ppt/slideLayouts/slideLayout1.xml": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<p:sldLayout xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
|
||||
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" type="blank">
|
||||
<p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
|
||||
<p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr>
|
||||
</p:spTree></p:cSld>
|
||||
</p:sldLayout>`,
|
||||
"ppt/slideLayouts/_rels/slideLayout1.xml.rels": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/>
|
||||
</Relationships>`,
|
||||
"ppt/slides/slide1.xml": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<p:sld xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
|
||||
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
|
||||
<p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
|
||||
<p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr>
|
||||
</p:spTree></p:cSld>
|
||||
</p:sld>`,
|
||||
"ppt/slides/_rels/slide1.xml.rels": `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
|
||||
</Relationships>`,
|
||||
}
|
||||
for name, content := range files {
|
||||
fw, err := w.Create(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fw.Write([]byte(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignConfig takes the OnlyOffice configuration payload and signs it with the JWT secret
|
||||
func (h *OnlyOfficeHandler) SignConfig(c *fiber.Ctx) error {
|
||||
if h.Config.OnlyOfficeJWT == "" {
|
||||
|
|
|
|||
|
|
@ -37,17 +37,14 @@ export default function OnlyOfficeEditor({ file, type, onClose }: OnlyOfficeEdit
|
|||
useEffect(() => {
|
||||
if (!downloadToken) return;
|
||||
|
||||
const getApiBase = () => {
|
||||
if (process.env.NEXT_PUBLIC_API_URL) return process.env.NEXT_PUBLIC_API_URL;
|
||||
if (typeof window !== 'undefined') return `${window.location.origin}/api`;
|
||||
return 'http://localhost:8080';
|
||||
};
|
||||
// The OnlyOffice Document Server fetches these URLs server-side (not from the browser).
|
||||
// It's on the default bridge network and can reach the Unraid host IP, but NOT
|
||||
// the public domain (hairpin NAT) or Docker DNS names (different network).
|
||||
// Port 5827 is the frontend container (which proxies /api/* to the backend).
|
||||
const INTERNAL_URL = 'http://192.168.50.81:5827/api';
|
||||
|
||||
const API_BASE = getApiBase();
|
||||
|
||||
// Use the public API_BASE which correctly routes through the Nginx proxy to the Next.js frontend
|
||||
const fileUrl = `${API_BASE}/files/download/${encodeURIComponent(file.path)}?token=${downloadToken}`;
|
||||
const callbackUrl = `${API_BASE}/public/onlyoffice/callback?path=${encodeURIComponent(file.path)}&token=${downloadToken}`;
|
||||
const fileUrl = `${INTERNAL_URL}/files/download/${encodeURIComponent(file.path)}?token=${downloadToken}`;
|
||||
const callbackUrl = `${INTERNAL_URL}/public/onlyoffice/callback?path=${encodeURIComponent(file.path)}&token=${downloadToken}`;
|
||||
|
||||
const baseConfig: any = {
|
||||
document: {
|
||||
|
|
|
|||
Reference in a new issue