fix: unique keyHash in download URL path + 2s reload delay for save callback
This commit is contained in:
Binary file not shown.
@@ -6,6 +6,6 @@
|
|||||||
"originalName": "master_cv_template.docx",
|
"originalName": "master_cv_template.docx",
|
||||||
"path": "/root/workspace/cv-app/renderer/carbone-templates/433a0593-d3cf-414e-8763-ab5f87cbc75c.docx",
|
"path": "/root/workspace/cv-app/renderer/carbone-templates/433a0593-d3cf-414e-8763-ab5f87cbc75c.docx",
|
||||||
"uploadedAt": "2026-07-16T09:03:29.601Z",
|
"uploadedAt": "2026-07-16T09:03:29.601Z",
|
||||||
"updatedAt": "2026-07-16T12:06:39.375Z"
|
"updatedAt": "2026-07-16T12:09:36.709Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -147,11 +147,11 @@ router.get('/:templateId/config', (req, res) => {
|
|||||||
const docKey = generateDocKey(templateId, Date.now());
|
const docKey = generateDocKey(templateId, Date.now());
|
||||||
const fileName = template.originalName || template.name + '.docx';
|
const fileName = template.originalName || template.name + '.docx';
|
||||||
|
|
||||||
// Add file mtime as cache-buster to the document URL so ONLYOFFICE
|
// Add the doc key to the download URL path so ONLYOFFICE treats
|
||||||
// always fetches the latest version from our backend.
|
// every edit session as a completely fresh document URL.
|
||||||
// The mtime changes every time the file is saved, so the URL is
|
// Query params alone aren't enough — ONLYOFFICE may ignore them for caching.
|
||||||
// different after each save, forcing ONLYOFFICE to re-download.
|
const keyHash = docKey.split('_').pop();
|
||||||
const documentUrl = `${BACKEND_PUBLIC_URL}/api/onlyoffice/${templateId}/download?mtime=${fileMtime}&cb=${Date.now()}`;
|
const documentUrl = `${BACKEND_PUBLIC_URL}/api/onlyoffice/${templateId}/download/${keyHash}?mtime=${fileMtime}&cb=${Date.now()}`;
|
||||||
const callbackUrl = `${BACKEND_PUBLIC_URL}/api/onlyoffice/${templateId}/callback`;
|
const callbackUrl = `${BACKEND_PUBLIC_URL}/api/onlyoffice/${templateId}/callback`;
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
@@ -223,9 +223,35 @@ router.get('/:templateId/config', (req, res) => {
|
|||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// ENDPOINT: GET /api/onlyoffice/:templateId/download
|
// ENDPOINT: GET /api/onlyoffice/:templateId/download
|
||||||
// ONLYOFFICE fetches the .docx file from this URL
|
// ONLYOFFICE fetches the .docx file from this URL.
|
||||||
|
// The optional :keyHash path param provides per-session cache-busting.
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
|
router.get('/:templateId/download/:keyHash', (req, res) => {
|
||||||
|
const templateId = req.params.templateId;
|
||||||
|
const registry = loadRegistry();
|
||||||
|
const template = registry.find(t => t.id === templateId);
|
||||||
|
|
||||||
|
if (!template) {
|
||||||
|
return res.status(404).json({ error: 'Template not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(template.path)) {
|
||||||
|
return res.status(404).json({ error: 'Template file missing' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = template.originalName || template.name + '.docx';
|
||||||
|
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
|
||||||
|
res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
|
||||||
|
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||||
|
res.setHeader('Pragma', 'no-cache');
|
||||||
|
res.setHeader('Expires', '0');
|
||||||
|
|
||||||
|
const fileStream = fs.createReadStream(template.path);
|
||||||
|
fileStream.pipe(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fallback route without keyHash (backward compatibility)
|
||||||
router.get('/:templateId/download', (req, res) => {
|
router.get('/:templateId/download', (req, res) => {
|
||||||
const templateId = req.params.templateId;
|
const templateId = req.params.templateId;
|
||||||
const registry = loadRegistry();
|
const registry = loadRegistry();
|
||||||
|
|||||||
@@ -283,9 +283,11 @@ function closeOnlyofficeEditor() {
|
|||||||
// Reload the page to clear ONLYOFFICE's in-memory state.
|
// Reload the page to clear ONLYOFFICE's in-memory state.
|
||||||
// Save the active page so the user returns to Templates automatically
|
// Save the active page so the user returns to Templates automatically
|
||||||
sessionStorage.setItem('returnPage', 'templates');
|
sessionStorage.setItem('returnPage', 'templates');
|
||||||
|
// Wait 2 seconds for the save callback (status=2) to complete
|
||||||
|
// on our backend before reloading the page
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
location.reload();
|
location.reload();
|
||||||
}, 300);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open ONLYOFFICE editor in a modal
|
// Open ONLYOFFICE editor in a modal
|
||||||
|
|||||||
@@ -139,7 +139,7 @@
|
|||||||
<!-- Modals -->
|
<!-- Modals -->
|
||||||
<div class="modal-overlay" id="modal-overlay"></div>
|
<div class="modal-overlay" id="modal-overlay"></div>
|
||||||
|
|
||||||
<script src="/static/app.js?v=8"></script>
|
<script src="/static/app.js?v=9"></script>
|
||||||
<script src="/static/carbone.js?v=8"></script>
|
<script src="/static/carbone.js?v=9"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user