Add formatting functionality for document templates
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good
This update introduces a new route for formatting document templates, utilizing the `formatTemplateContent` function to beautify HTML and JavaScript within templates. The `formatDocumentTemplateRouteHandler` is added to handle formatting requests, and the content is processed to maintain EJS blocks. Additionally, a new `templateformatter.js` file is created, containing the logic for formatting templates, along with corresponding tests to ensure functionality. This enhancement improves the usability and presentation of document templates in the application.
This commit is contained in:
parent
8af8c3364f
commit
f467b1be33
@ -23,6 +23,7 @@
|
|||||||
"express-session": "^1.18.2",
|
"express-session": "^1.18.2",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"ioredis": "^6.0.0",
|
"ioredis": "^6.0.0",
|
||||||
|
"js-beautify": "^2.0.3",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"keycloak-connect": "^26.1.1",
|
"keycloak-connect": "^26.1.1",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
|
|||||||
117
pnpm-lock.yaml
generated
117
pnpm-lock.yaml
generated
@ -65,6 +65,9 @@ importers:
|
|||||||
ioredis:
|
ioredis:
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
|
js-beautify:
|
||||||
|
specifier: ^2.0.3
|
||||||
|
version: 2.0.3
|
||||||
jsonwebtoken:
|
jsonwebtoken:
|
||||||
specifier: ^9.0.2
|
specifier: ^9.0.2
|
||||||
version: 9.0.3
|
version: 9.0.3
|
||||||
@ -1425,6 +1428,9 @@ packages:
|
|||||||
'@one-ini/wasm@0.1.1':
|
'@one-ini/wasm@0.1.1':
|
||||||
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
|
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
|
||||||
|
|
||||||
|
'@one-ini/wasm@0.2.1':
|
||||||
|
resolution: {integrity: sha512-TUqERXGNTifZ9y2g3wPxQrw3HpHv/02DsW3D90T9x0hhonrL1ZqpSmNrU2XkoIq0fP1N6gZfVQzy2Fw1ZvGBNg==}
|
||||||
|
|
||||||
'@paralleldrive/cuid2@2.3.1':
|
'@paralleldrive/cuid2@2.3.1':
|
||||||
resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
|
resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
|
||||||
|
|
||||||
@ -1849,6 +1855,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
|
resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
|
||||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||||
|
|
||||||
|
abbrev@5.0.0:
|
||||||
|
resolution: {integrity: sha512-/XrFJgzQQQHpti1raDJC6m4ws6aNktmjBlhk8Fdlk7LwCEuDoieEJJY9OFHjfiFJFFRM2tK+Ky/IsfbbmlMu1w==}
|
||||||
|
engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
|
||||||
|
|
||||||
accepts@2.0.0:
|
accepts@2.0.0:
|
||||||
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
|
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@ -2057,6 +2067,10 @@ packages:
|
|||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
|
balanced-match@4.0.4:
|
||||||
|
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
bare-events@2.8.2:
|
bare-events@2.8.2:
|
||||||
resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==}
|
resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2154,6 +2168,10 @@ packages:
|
|||||||
brace-expansion@2.0.2:
|
brace-expansion@2.0.2:
|
||||||
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
||||||
|
|
||||||
|
brace-expansion@5.0.9:
|
||||||
|
resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==}
|
||||||
|
engines: {node: 20 || >=22}
|
||||||
|
|
||||||
braces@3.0.3:
|
braces@3.0.3:
|
||||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -2312,6 +2330,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
|
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
commander@14.0.3:
|
||||||
|
resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
commander@6.2.1:
|
commander@6.2.1:
|
||||||
resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
|
resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@ -2574,6 +2596,11 @@ packages:
|
|||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
editorconfig@3.0.2:
|
||||||
|
resolution: {integrity: sha512-T0ix8GhtxyKVfUFEcvdNDt3YGqlwkFHbD4/5bgFUDgFmxhI/cSRAeJ87/Sz//Cq8Eam6JX/e23RkoFO71P7aAA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
ee-first@1.1.1:
|
ee-first@1.1.1:
|
||||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||||
|
|
||||||
@ -3107,8 +3134,13 @@ packages:
|
|||||||
|
|
||||||
glob@10.5.0:
|
glob@10.5.0:
|
||||||
resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
|
resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
|
||||||
|
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
glob@13.0.6:
|
||||||
|
resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
glob@7.2.3:
|
glob@7.2.3:
|
||||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||||
deprecated: Glob versions prior to v9 are no longer supported
|
deprecated: Glob versions prior to v9 are no longer supported
|
||||||
@ -3608,10 +3640,18 @@ packages:
|
|||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
js-beautify@2.0.3:
|
||||||
|
resolution: {integrity: sha512-cyFbh3tkPhknnTD/0bLf0T0yy2ZIbqL05mttzbt4y1Zfr7NxqXQZ62dkBLKs3oHH/lpjmDRAnciJiSUyOy8XwQ==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
js-cookie@3.0.5:
|
js-cookie@3.0.5:
|
||||||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
js-cookie@3.0.8:
|
||||||
|
resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==}
|
||||||
|
|
||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
@ -3805,6 +3845,10 @@ packages:
|
|||||||
lru-cache@10.4.3:
|
lru-cache@10.4.3:
|
||||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||||
|
|
||||||
|
lru-cache@11.5.2:
|
||||||
|
resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==}
|
||||||
|
engines: {node: 20 || >=22}
|
||||||
|
|
||||||
lru-cache@5.1.1:
|
lru-cache@5.1.1:
|
||||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||||
|
|
||||||
@ -3884,6 +3928,10 @@ packages:
|
|||||||
minimalistic-crypto-utils@1.0.1:
|
minimalistic-crypto-utils@1.0.1:
|
||||||
resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
|
resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
|
||||||
|
|
||||||
|
minimatch@10.2.6:
|
||||||
|
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
minimatch@3.1.2:
|
minimatch@3.1.2:
|
||||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
|
|
||||||
@ -3906,6 +3954,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||||
engines: {node: '>=16 || 14 >=14.17'}
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
|
|
||||||
|
minipass@7.1.3:
|
||||||
|
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
|
||||||
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
|
|
||||||
mitt@3.0.1:
|
mitt@3.0.1:
|
||||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||||
|
|
||||||
@ -4068,6 +4120,11 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
nopt@10.0.1:
|
||||||
|
resolution: {integrity: sha512-df3sBr/6ax9hSGuC3CspvLlbnX8cP5L5nZwXF8cGN8l0zSWR6BvzmQ6jPUKjvo6+/xdpkNvEcucBNUdBeeV13g==}
|
||||||
|
engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
nopt@7.2.1:
|
nopt@7.2.1:
|
||||||
resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
|
resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
|
||||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||||
@ -4231,6 +4288,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
||||||
engines: {node: '>=16 || 14 >=14.18'}
|
engines: {node: '>=16 || 14 >=14.18'}
|
||||||
|
|
||||||
|
path-scurry@2.0.2:
|
||||||
|
resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
path-to-regexp@8.3.0:
|
path-to-regexp@8.3.0:
|
||||||
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
|
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
|
||||||
|
|
||||||
@ -7054,6 +7115,8 @@ snapshots:
|
|||||||
|
|
||||||
'@one-ini/wasm@0.1.1': {}
|
'@one-ini/wasm@0.1.1': {}
|
||||||
|
|
||||||
|
'@one-ini/wasm@0.2.1': {}
|
||||||
|
|
||||||
'@paralleldrive/cuid2@2.3.1':
|
'@paralleldrive/cuid2@2.3.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.8.0
|
'@noble/hashes': 1.8.0
|
||||||
@ -7577,6 +7640,8 @@ snapshots:
|
|||||||
|
|
||||||
abbrev@2.0.0: {}
|
abbrev@2.0.0: {}
|
||||||
|
|
||||||
|
abbrev@5.0.0: {}
|
||||||
|
|
||||||
accepts@2.0.0:
|
accepts@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-types: 3.0.2
|
mime-types: 3.0.2
|
||||||
@ -7883,6 +7948,8 @@ snapshots:
|
|||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
|
balanced-match@4.0.4: {}
|
||||||
|
|
||||||
bare-events@2.8.2: {}
|
bare-events@2.8.2: {}
|
||||||
|
|
||||||
bare-fs@4.5.5:
|
bare-fs@4.5.5:
|
||||||
@ -7978,6 +8045,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
|
||||||
|
brace-expansion@5.0.9:
|
||||||
|
dependencies:
|
||||||
|
balanced-match: 4.0.4
|
||||||
|
|
||||||
braces@3.0.3:
|
braces@3.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
fill-range: 7.1.1
|
fill-range: 7.1.1
|
||||||
@ -8138,6 +8209,8 @@ snapshots:
|
|||||||
|
|
||||||
commander@10.0.1: {}
|
commander@10.0.1: {}
|
||||||
|
|
||||||
|
commander@14.0.3: {}
|
||||||
|
|
||||||
commander@6.2.1: {}
|
commander@6.2.1: {}
|
||||||
|
|
||||||
commondir@1.0.1: {}
|
commondir@1.0.1: {}
|
||||||
@ -8374,6 +8447,13 @@ snapshots:
|
|||||||
minimatch: 9.0.1
|
minimatch: 9.0.1
|
||||||
semver: 7.7.4
|
semver: 7.7.4
|
||||||
|
|
||||||
|
editorconfig@3.0.2:
|
||||||
|
dependencies:
|
||||||
|
'@one-ini/wasm': 0.2.1
|
||||||
|
commander: 14.0.3
|
||||||
|
minimatch: 10.2.6
|
||||||
|
semver: 7.7.4
|
||||||
|
|
||||||
ee-first@1.1.1: {}
|
ee-first@1.1.1: {}
|
||||||
|
|
||||||
ejs@3.1.10:
|
ejs@3.1.10:
|
||||||
@ -8542,7 +8622,7 @@ snapshots:
|
|||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-plugin-react: 7.37.5(eslint@8.57.1)
|
eslint-plugin-react: 7.37.5(eslint@8.57.1)
|
||||||
|
|
||||||
eslint-config-standard@17.1.0(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1):
|
eslint-config-standard@17.1.0(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@9.39.2))(eslint-plugin-promise@6.6.0(eslint@9.39.2))(eslint@8.57.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-plugin-import: 2.32.0(eslint@8.57.1)
|
eslint-plugin-import: 2.32.0(eslint@8.57.1)
|
||||||
@ -9132,6 +9212,12 @@ snapshots:
|
|||||||
package-json-from-dist: 1.0.1
|
package-json-from-dist: 1.0.1
|
||||||
path-scurry: 1.11.1
|
path-scurry: 1.11.1
|
||||||
|
|
||||||
|
glob@13.0.6:
|
||||||
|
dependencies:
|
||||||
|
minimatch: 10.2.6
|
||||||
|
minipass: 7.1.3
|
||||||
|
path-scurry: 2.0.2
|
||||||
|
|
||||||
glob@7.2.3:
|
glob@7.2.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
fs.realpath: 1.0.0
|
fs.realpath: 1.0.0
|
||||||
@ -9862,8 +9948,18 @@ snapshots:
|
|||||||
js-cookie: 3.0.5
|
js-cookie: 3.0.5
|
||||||
nopt: 7.2.1
|
nopt: 7.2.1
|
||||||
|
|
||||||
|
js-beautify@2.0.3:
|
||||||
|
dependencies:
|
||||||
|
config-chain: 1.1.13
|
||||||
|
editorconfig: 3.0.2
|
||||||
|
glob: 13.0.6
|
||||||
|
js-cookie: 3.0.8
|
||||||
|
nopt: 10.0.1
|
||||||
|
|
||||||
js-cookie@3.0.5: {}
|
js-cookie@3.0.5: {}
|
||||||
|
|
||||||
|
js-cookie@3.0.8: {}
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
js-yaml@3.14.2:
|
js-yaml@3.14.2:
|
||||||
@ -10063,6 +10159,8 @@ snapshots:
|
|||||||
|
|
||||||
lru-cache@10.4.3: {}
|
lru-cache@10.4.3: {}
|
||||||
|
|
||||||
|
lru-cache@11.5.2: {}
|
||||||
|
|
||||||
lru-cache@5.1.1:
|
lru-cache@5.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
yallist: 3.1.1
|
yallist: 3.1.1
|
||||||
@ -10121,6 +10219,10 @@ snapshots:
|
|||||||
|
|
||||||
minimalistic-crypto-utils@1.0.1: {}
|
minimalistic-crypto-utils@1.0.1: {}
|
||||||
|
|
||||||
|
minimatch@10.2.6:
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 5.0.9
|
||||||
|
|
||||||
minimatch@3.1.2:
|
minimatch@3.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 1.1.12
|
brace-expansion: 1.1.12
|
||||||
@ -10141,6 +10243,8 @@ snapshots:
|
|||||||
|
|
||||||
minipass@7.1.2: {}
|
minipass@7.1.2: {}
|
||||||
|
|
||||||
|
minipass@7.1.3: {}
|
||||||
|
|
||||||
mitt@3.0.1: {}
|
mitt@3.0.1: {}
|
||||||
|
|
||||||
mkdirp@0.5.6:
|
mkdirp@0.5.6:
|
||||||
@ -10267,6 +10371,10 @@ snapshots:
|
|||||||
touch: 3.1.1
|
touch: 3.1.1
|
||||||
undefsafe: 2.0.5
|
undefsafe: 2.0.5
|
||||||
|
|
||||||
|
nopt@10.0.1:
|
||||||
|
dependencies:
|
||||||
|
abbrev: 5.0.0
|
||||||
|
|
||||||
nopt@7.2.1:
|
nopt@7.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
abbrev: 2.0.0
|
abbrev: 2.0.0
|
||||||
@ -10459,6 +10567,11 @@ snapshots:
|
|||||||
lru-cache: 10.4.3
|
lru-cache: 10.4.3
|
||||||
minipass: 7.1.2
|
minipass: 7.1.2
|
||||||
|
|
||||||
|
path-scurry@2.0.2:
|
||||||
|
dependencies:
|
||||||
|
lru-cache: 11.5.2
|
||||||
|
minipass: 7.1.3
|
||||||
|
|
||||||
path-to-regexp@8.3.0: {}
|
path-to-regexp@8.3.0: {}
|
||||||
|
|
||||||
pdf-to-img@6.2.0:
|
pdf-to-img@6.2.0:
|
||||||
@ -11107,7 +11220,7 @@ snapshots:
|
|||||||
standard@17.1.2:
|
standard@17.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-config-standard: 17.1.0(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)
|
eslint-config-standard: 17.1.0(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@9.39.2))(eslint-plugin-promise@6.6.0(eslint@9.39.2))(eslint@8.57.1)
|
||||||
eslint-config-standard-jsx: 11.0.0(eslint-plugin-react@7.37.5(eslint@8.57.1))(eslint@8.57.1)
|
eslint-config-standard-jsx: 11.0.0(eslint-plugin-react@7.37.5(eslint@8.57.1))(eslint@8.57.1)
|
||||||
eslint-plugin-import: 2.32.0(eslint@8.57.1)
|
eslint-plugin-import: 2.32.0(eslint@8.57.1)
|
||||||
eslint-plugin-n: 15.7.0(eslint@8.57.1)
|
eslint-plugin-n: 15.7.0(eslint@8.57.1)
|
||||||
|
|||||||
@ -43,6 +43,7 @@ import {
|
|||||||
getDocumentTemplateNeighborsRouteHandler,
|
getDocumentTemplateNeighborsRouteHandler,
|
||||||
previewDocumentTemplateRouteHandler,
|
previewDocumentTemplateRouteHandler,
|
||||||
downloadDocumentTemplateRouteHandler,
|
downloadDocumentTemplateRouteHandler,
|
||||||
|
formatDocumentTemplateRouteHandler,
|
||||||
} from '../../services/management/documenttemplates.js';
|
} from '../../services/management/documenttemplates.js';
|
||||||
|
|
||||||
// list of document templates
|
// list of document templates
|
||||||
@ -76,6 +77,10 @@ router.post('/', isAuthenticated, async (req, res) => {
|
|||||||
newDocumentTemplateRouteHandler(req, res);
|
newDocumentTemplateRouteHandler(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/format', isAuthenticated, async (req, res) => {
|
||||||
|
formatDocumentTemplateRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
// get document template stats
|
// get document template stats
|
||||||
router.get('/stats', isAuthenticated, async (req, res) => {
|
router.get('/stats', isAuthenticated, async (req, res) => {
|
||||||
getDocumentTemplateStatsRouteHandler(req, res);
|
getDocumentTemplateStatsRouteHandler(req, res);
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
getObjectNeighbors,
|
getObjectNeighbors,
|
||||||
} from '../../database/database.js';
|
} from '../../database/database.js';
|
||||||
import { templateManager } from '../../templates/templatemanager.js';
|
import { templateManager } from '../../templates/templatemanager.js';
|
||||||
|
import { formatTemplateContent } from '../../templates/templateformatter.js';
|
||||||
const logger = log4js.getLogger('Document Templates');
|
const logger = log4js.getLogger('Document Templates');
|
||||||
logger.level = config.server.logLevel;
|
logger.level = config.server.logLevel;
|
||||||
|
|
||||||
@ -124,6 +125,16 @@ export const editDocumentTemplateRouteHandler = async (req, res) => {
|
|||||||
|
|
||||||
logger.trace(`Document Template with ID: ${id}`);
|
logger.trace(`Document Template with ID: ${id}`);
|
||||||
|
|
||||||
|
let formattedContent = req.body.content;
|
||||||
|
if (req.body.content != null && typeof req.body.content === 'string') {
|
||||||
|
const formatResult = formatTemplateContent(req.body.content);
|
||||||
|
if (formatResult?.error) {
|
||||||
|
logger.warn('Error formatting document template content:', formatResult.error);
|
||||||
|
return res.status(formatResult.code || 400).send(formatResult);
|
||||||
|
}
|
||||||
|
formattedContent = formatResult.content;
|
||||||
|
}
|
||||||
|
|
||||||
const updateData = {
|
const updateData = {
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
name: req.body.name,
|
name: req.body.name,
|
||||||
@ -134,7 +145,7 @@ export const editDocumentTemplateRouteHandler = async (req, res) => {
|
|||||||
objectType: req.body.objectType,
|
objectType: req.body.objectType,
|
||||||
documentSize: req.body.documentSize,
|
documentSize: req.body.documentSize,
|
||||||
documentPrinters: req.body.documentPrinters,
|
documentPrinters: req.body.documentPrinters,
|
||||||
content: req.body.content,
|
content: formattedContent,
|
||||||
testObject: req.body.testObject,
|
testObject: req.body.testObject,
|
||||||
};
|
};
|
||||||
// Create audit log before updating
|
// Create audit log before updating
|
||||||
@ -263,6 +274,20 @@ export const getDocumentTemplateNeighborsRouteHandler = async (
|
|||||||
res.send(result);
|
res.send(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatDocumentTemplateRouteHandler = async (req, res) => {
|
||||||
|
const content = req.body?.content;
|
||||||
|
|
||||||
|
logger.debug('Formatting document template content');
|
||||||
|
|
||||||
|
const result = formatTemplateContent(content);
|
||||||
|
if (result?.error) {
|
||||||
|
logger.warn('Error formatting document template content:', result.error);
|
||||||
|
return res.status(result.code || 400).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send({ content: result.content });
|
||||||
|
};
|
||||||
|
|
||||||
export const previewDocumentTemplateRouteHandler = async (req, res) => {
|
export const previewDocumentTemplateRouteHandler = async (req, res) => {
|
||||||
const id = req.params.id;
|
const id = req.params.id;
|
||||||
const content = req.body?.content;
|
const content = req.body?.content;
|
||||||
|
|||||||
52
src/templates/__tests__/templateformatter.test.js
Normal file
52
src/templates/__tests__/templateformatter.test.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { formatTemplateContent } from '../templateformatter.js';
|
||||||
|
|
||||||
|
describe('formatTemplateContent', () => {
|
||||||
|
it('should return an error when content is missing', () => {
|
||||||
|
expect(formatTemplateContent()).toEqual({
|
||||||
|
error: 'Content is required and must be a string.',
|
||||||
|
code: 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when content is not a string', () => {
|
||||||
|
expect(formatTemplateContent({ foo: 'bar' })).toEqual({
|
||||||
|
error: 'Content is required and must be a string.',
|
||||||
|
code: 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty content unchanged', () => {
|
||||||
|
expect(formatTemplateContent('')).toEqual({ content: '' });
|
||||||
|
expect(formatTemplateContent(' ')).toEqual({ content: '' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format custom XML tags', () => {
|
||||||
|
const input = '<Container><Flex vertical="true"><Title1>Hello</Title1></Flex></Container>';
|
||||||
|
const result = formatTemplateContent(input);
|
||||||
|
|
||||||
|
expect(result.content).toBe(`<Container>
|
||||||
|
<Flex vertical="true">
|
||||||
|
<Title1>Hello</Title1>
|
||||||
|
</Flex>
|
||||||
|
</Container>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format XML while preserving EJS blocks', () => {
|
||||||
|
const input =
|
||||||
|
'<Container><Flex vertical="true"><Title1>Hello</Title1><% if (name) { %><Text><%= name %></Text><% } %></Flex></Container>';
|
||||||
|
const result = formatTemplateContent(input);
|
||||||
|
|
||||||
|
expect(result.content).toContain('<Container>');
|
||||||
|
expect(result.content).toContain('<% if (name) { %>');
|
||||||
|
expect(result.content).toContain('<%= name %>');
|
||||||
|
expect(result.content).toContain('<% } %>');
|
||||||
|
expect(result.content).toContain('<Text>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should leave EJS comments unchanged', () => {
|
||||||
|
const input = '<Container><%# comment %></Container>';
|
||||||
|
const result = formatTemplateContent(input);
|
||||||
|
|
||||||
|
expect(result.content).toContain('<%# comment %>');
|
||||||
|
});
|
||||||
|
});
|
||||||
85
src/templates/templateformatter.js
Normal file
85
src/templates/templateformatter.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import beautify from 'js-beautify';
|
||||||
|
|
||||||
|
const { html: beautifyHtml, js: beautifyJs } = beautify;
|
||||||
|
|
||||||
|
const EJS_BLOCK_REGEX = /<%[\s\S]*?%>/g;
|
||||||
|
const EJS_TAG_REGEX = /^<%([=#-]?)([\s\S]*?)%>/;
|
||||||
|
const EJS_PLACEHOLDER_REGEX = /<!--EJS_PH_(\d+)-->/g;
|
||||||
|
|
||||||
|
const HTML_BEAUTIFY_OPTIONS = {
|
||||||
|
indent_size: 2,
|
||||||
|
wrap_line_length: 0,
|
||||||
|
preserve_newlines: false,
|
||||||
|
max_preserve_newlines: 1,
|
||||||
|
indent_inner_html: true,
|
||||||
|
extra_liners: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const JS_BEAUTIFY_OPTIONS = {
|
||||||
|
indent_size: 2,
|
||||||
|
preserve_newlines: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatEjsTag(tag) {
|
||||||
|
const match = tag.match(EJS_TAG_REGEX);
|
||||||
|
if (!match) {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [, modifier, body] = match;
|
||||||
|
if (modifier === '#') {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmedBody = body.trim();
|
||||||
|
if (!trimmedBody) {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formattedBody = beautifyJs(trimmedBody, JS_BEAUTIFY_OPTIONS);
|
||||||
|
if (modifier) {
|
||||||
|
return `<%${modifier} ${formattedBody} %>`;
|
||||||
|
}
|
||||||
|
return `<% ${formattedBody} %>`;
|
||||||
|
} catch {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function maskEjsBlocks(content) {
|
||||||
|
const ejsBlocks = [];
|
||||||
|
|
||||||
|
const masked = content.replace(EJS_BLOCK_REGEX, (match) => {
|
||||||
|
const index = ejsBlocks.length;
|
||||||
|
ejsBlocks.push(match);
|
||||||
|
return `\n<!--EJS_PH_${index}-->\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { masked, ejsBlocks };
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreEjsBlocks(content, ejsBlocks) {
|
||||||
|
return content.replace(EJS_PLACEHOLDER_REGEX, (_, index) =>
|
||||||
|
formatEjsTag(ejsBlocks[Number(index)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTemplateContent(content) {
|
||||||
|
if (content == null || typeof content !== 'string') {
|
||||||
|
return { error: 'Content is required and must be a string.', code: 400 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.trim() === '') {
|
||||||
|
return { content: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { masked, ejsBlocks } = maskEjsBlocks(content);
|
||||||
|
let formatted = beautifyHtml(masked, HTML_BEAUTIFY_OPTIONS);
|
||||||
|
formatted = restoreEjsBlocks(formatted, ejsBlocks);
|
||||||
|
return { content: formatted.trimEnd() };
|
||||||
|
} catch (error) {
|
||||||
|
return { error: error.message || 'Failed to format template content.', code: 400 };
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user