From 7a241af09fe81d221e7e900d40b10dc4a63dd292 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Thu, 15 Jan 2026 17:53:23 +0300 Subject: [PATCH] [feature] Add discovery handler "formats.json" --- .gitignore | 3 ++- Common/config/default.json | 4 ++++ DocService/sources/server.js | 38 ++++++++++++++++++++++++++++++++++++ package.json | 4 +++- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5727494d..910b081a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ node_modules local-development-*.json *.pyc run-develop-local.py -runtime.json \ No newline at end of file +runtime.json +document-formats/ \ No newline at end of file diff --git a/Common/config/default.json b/Common/config/default.json index 35f66798..66cf5f70 100644 --- a/Common/config/default.json +++ b/Common/config/default.json @@ -507,6 +507,10 @@ "themes": { "uri": "/web-apps/apps/common/main/resources/themes" }, + "documentFormats": { + "path": "./document-formats/formats.json", + "cacheTTL": "5m" + }, "editor": { "spellcheckerUrl": "", "reconnection": { diff --git a/DocService/sources/server.js b/DocService/sources/server.js index 3dc84c28..9eb3998d 100644 --- a/DocService/sources/server.js +++ b/DocService/sources/server.js @@ -453,6 +453,44 @@ docsCoServer.install(server, app, () => { } }); }); + app.get('/formats.json', apicache.middleware('5 minutes'), (req, res) => { + return co(function* () { + let formats = null; + const ctx = new operationContext.Context(); + try { + ctx.initFromRequest(req); + yield ctx.initTenantCache(); + ctx.logger.info('formats.json start'); + + if (!config.has('services.CoAuthoring.documentFormats.path')) { + ctx.logger.warn('formats.json: documentFormats.path not configured'); + res.sendStatus(404); + return; + } + + const formatsPath = config.get('services.CoAuthoring.documentFormats.path'); + const resolvedPath = path.resolve(__dirname, '../../..', formatsPath); + + ctx.logger.debug('formats.json path:%s', resolvedPath); + + const data = yield utils.readFile(resolvedPath, true); + const text = new TextDecoder('utf-8', {ignoreBOM: false}).decode(data); + formats = JSON.parse(text); + + ctx.logger.debug('formats.json loaded successfully'); + } catch (err) { + ctx.logger.error('formats.json error:%s', err.stack); + } finally { + if (formats) { + res.setHeader('Content-Type', 'application/json'); + res.send(formats); + } else { + res.sendStatus(404); + } + ctx.logger.info('formats.json end'); + } + }); + }); app.get('/document_editor_service_worker.js', apicache.middleware('5 min'), async (req, res) => { const staticContent = config.get('services.CoAuthoring.server.static_content'); if (staticContent['/sdkjs']) { diff --git a/package.json b/package.json index 841e8e07..4775add7 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@eslint/js": "9.16.0", "@jest/globals": "29.7.0", "cross-env": "7.0.3", + "degit": "2.8.4", "eslint": "9.16.0", "eslint-config-prettier": "10.1.8", "eslint-plugin-react": "^7.37.2", @@ -31,6 +32,7 @@ "format:check": "prettier . --check", "code:check": "run-s lint:check format:check", "code:fix": "run-s lint:fix format:fix", + "download:document-formats": "degit ONLYOFFICE/document-formats document-formats --force", "perf-expired": "cd ./DocService&& cross-env NODE_ENV=development-windows NODE_CONFIG_DIR=../Common/config node ../tests/perf/checkFileExpire.js", "unit tests": "cd ./DocService && jest unit --inject-globals=false --config=../tests/jest.config.js", "integration tests with server instance": "cd ./DocService && jest integration/withServerInstance --inject-globals=false --config=../tests/jest.config.js", @@ -56,6 +58,6 @@ "3d-party-lic-report:FileConverter": "run-s 3d-party-lic-json:FileConverter 3d-party-lic-downloader 3d-party-lic-md", "3d-party-lic-report:Metrics": "run-s 3d-party-lic-json:Metrics 3d-party-lic-downloader 3d-party-lic-md", "3d-party-lic-report": "run-s 3d-party-lic-md-header 3d-party-lic-report:*", - "build": "run-p install:*" + "build": "run-s download:document-formats && run-p install:*" } }