code quality updates
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# Server port
|
||||
PORT=3000
|
||||
|
||||
# Log level: fatal | error | warn | info | debug | trace
|
||||
LOG_LEVEL=info
|
||||
|
||||
# Node environment: production | development | test
|
||||
NODE_ENV=development
|
||||
|
||||
# App version override (optional – falls back to VERSION file)
|
||||
# APP_VERSION=1.0.0
|
||||
Generated
+280
-1
@@ -10,11 +10,14 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.10.0",
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^5.1.0",
|
||||
"helmet": "^8.1.0"
|
||||
"helmet": "^8.1.0",
|
||||
"pino": "^10.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.7.0",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"supertest": "^7.1.3"
|
||||
}
|
||||
},
|
||||
@@ -909,6 +912,12 @@
|
||||
"@noble/hashes": "^1.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@pinojs/redact": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
|
||||
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.27.8",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||
@@ -1144,6 +1153,15 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/atomic-sleep": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
|
||||
@@ -1554,6 +1572,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@@ -1673,6 +1698,16 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/dateformat": {
|
||||
"version": "4.6.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
|
||||
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
@@ -1764,6 +1799,18 @@
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.4.2",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
|
||||
"integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@@ -1820,6 +1867,16 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
@@ -2016,6 +2073,13 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-copy": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.4.tgz",
|
||||
"integrity": "sha512-eVAiWVNPSEGIzDl5yPuLrx8fNMogScXvD9xp1Kzd41FjRIz2I3sSIcxsFeM5EzFfHAfobdvs8ZySffUopljvIA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
@@ -2387,6 +2451,13 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/help-me": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
|
||||
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
@@ -3257,6 +3328,16 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/joycon": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
|
||||
"integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -3518,6 +3599,16 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -3589,6 +3680,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/on-exit-leak-free": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
||||
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@@ -3775,6 +3875,81 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "10.3.1",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
|
||||
"integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pinojs/redact": "^0.4.0",
|
||||
"atomic-sleep": "^1.0.0",
|
||||
"on-exit-leak-free": "^2.1.0",
|
||||
"pino-abstract-transport": "^3.0.0",
|
||||
"pino-std-serializers": "^7.0.0",
|
||||
"process-warning": "^5.0.0",
|
||||
"quick-format-unescaped": "^4.0.3",
|
||||
"real-require": "^0.2.0",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"sonic-boom": "^4.0.1",
|
||||
"thread-stream": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"pino": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-abstract-transport": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz",
|
||||
"integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split2": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-pretty": {
|
||||
"version": "13.1.3",
|
||||
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz",
|
||||
"integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.7",
|
||||
"dateformat": "^4.6.3",
|
||||
"fast-copy": "^4.0.0",
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"help-me": "^5.0.0",
|
||||
"joycon": "^3.1.1",
|
||||
"minimist": "^1.2.6",
|
||||
"on-exit-leak-free": "^2.1.0",
|
||||
"pino-abstract-transport": "^3.0.0",
|
||||
"pump": "^3.0.0",
|
||||
"secure-json-parse": "^4.0.0",
|
||||
"sonic-boom": "^4.0.1",
|
||||
"strip-json-comments": "^5.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"pino-pretty": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-pretty/node_modules/strip-json-comments": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
|
||||
"integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-std-serializers": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
|
||||
"integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pirates": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
|
||||
@@ -3826,6 +4001,22 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/process-warning": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
||||
"integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prompts": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
|
||||
@@ -3859,6 +4050,17 @@
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
|
||||
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
|
||||
@@ -3891,6 +4093,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/quick-format-unescaped": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
||||
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@@ -3922,6 +4130,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/real-require": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
|
||||
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
@@ -4022,12 +4239,38 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safe-stable-stringify": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
||||
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
|
||||
"integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
@@ -4200,6 +4443,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sonic-boom": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
|
||||
"integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"atomic-sleep": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -4221,6 +4473,15 @@
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
@@ -4401,6 +4662,24 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/thread-stream": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz",
|
||||
"integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"real-require": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/thread-stream/node_modules/real-require": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz",
|
||||
"integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tmpl": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||
|
||||
+4
-1
@@ -19,11 +19,14 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.10.0",
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^5.1.0",
|
||||
"helmet": "^8.1.0"
|
||||
"helmet": "^8.1.0",
|
||||
"pino": "^10.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.7.0",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"supertest": "^7.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
+18
-21
@@ -1,19 +1,20 @@
|
||||
const router = require('express').Router();
|
||||
const axios = require('axios');
|
||||
const { escapeSvg, safeColor } = require('../lib/sanitize');
|
||||
const { parseSize, parseStyle, parseLabelpos, parseFontweight } = require('../lib/validate');
|
||||
const { FONT_FAMILY, calcFontSize, estimateTextWidth } = require('../lib/svg-utils');
|
||||
const logger = require('../lib/logger');
|
||||
|
||||
router.get('/badge', async (req, res) => {
|
||||
const {
|
||||
icon = 'simpleicons',
|
||||
label = 'Simple Icons',
|
||||
style = 'rect', // badge stílusa: rect (négyzetes), flat (lekerekített sarkok)
|
||||
bgicon = 'none', // ikon háttérszín, alap: világosszürke
|
||||
bglabel = 'none', // label háttérszín, alap: sötétszürke
|
||||
color = '#000000',
|
||||
size = 24,
|
||||
labelpos = 'right',
|
||||
fontweight = 'normal',
|
||||
} = req.query;
|
||||
const icon = req.query.icon || 'simpleicons';
|
||||
const label = req.query.label || 'Simple Icons';
|
||||
const style = parseStyle(req.query.style);
|
||||
const bgicon = req.query.bgicon || 'none';
|
||||
const bglabel = req.query.bglabel || 'none';
|
||||
const color = req.query.color || '#000000';
|
||||
const size = parseSize(req.query.size);
|
||||
const labelpos = parseLabelpos(req.query.labelpos);
|
||||
const fontweight = parseFontweight(req.query.fontweight);
|
||||
|
||||
try {
|
||||
// download icon SVG from SimpleIcons CDN
|
||||
@@ -41,15 +42,11 @@ router.get('/badge', async (req, res) => {
|
||||
const safeFontWeight = escapeSvg(fontweight);
|
||||
|
||||
const padding = 10;
|
||||
const iconSize = parseInt(size);
|
||||
const fontSize = Math.round(iconSize * 0.6);
|
||||
const iconSize = size;
|
||||
const fontSize = calcFontSize(iconSize);
|
||||
const textPadding = Math.round(iconSize * 0.4);
|
||||
const radius = 0;
|
||||
const approxCharWidth = fontSize * 0.6;
|
||||
const textWidth = effectiveLabel.length * approxCharWidth;
|
||||
|
||||
// Kinyerjük az ikon SVG-ből a width és height attribútumokat (ha vannak)
|
||||
let iconViewBoxWidth = iconSize;
|
||||
const radius = style === 'flat' ? 3 : 0;
|
||||
const textWidth = estimateTextWidth(effectiveLabel, fontSize);
|
||||
let iconViewBoxHeight = iconSize;
|
||||
const widthMatch = iconSVG.match(/width="(\d+\.?\d*)"/);
|
||||
const heightMatch = iconSVG.match(/height="(\d+\.?\d*)"/);
|
||||
@@ -78,7 +75,7 @@ router.get('/badge', async (req, res) => {
|
||||
|
||||
let width, height, iconGroup, textElem;
|
||||
|
||||
const fontFamily = "Verdana,Geneva,DejaVu Sans,sans-serif";
|
||||
const fontFamily = FONT_FAMILY;
|
||||
const fontWeight = safeFontWeight; // paraméterből
|
||||
|
||||
if (labelpos === 'left') {
|
||||
@@ -149,7 +146,7 @@ router.get('/badge', async (req, res) => {
|
||||
if (err.response && err.response.status === 404) {
|
||||
return res.status(404).send('Icon not found');
|
||||
} else {
|
||||
console.error(err);
|
||||
logger.error({ err, icon, url: req.url }, 'Badge generation failed');
|
||||
res.status(500).send('Internal Server Error');
|
||||
}
|
||||
}
|
||||
|
||||
+25
-24
@@ -1,21 +1,21 @@
|
||||
const router = require('express').Router();
|
||||
const { escapeSvg, safeColor } = require('../lib/sanitize');
|
||||
const { parseSize, parseStyle } = require('../lib/validate');
|
||||
const { FONT_FAMILY, calcFontSize } = require('../lib/svg-utils');
|
||||
|
||||
|
||||
router.get('/tag', (req, res) => {
|
||||
const {
|
||||
tag = 'badgedex',
|
||||
label = 'BadgeDex',
|
||||
style = 'rect', // badge stílusa: rect (négyzetes), flat (lekerekített sarkok)
|
||||
bgtag = 'none', // ikon háttérszín, alap: világosszürke
|
||||
bglabel = 'none', // label háttérszín, alap: sötétszürke
|
||||
color = '#000000',
|
||||
size = 24,
|
||||
labelpos = 'right',
|
||||
fontweight = 'normal',
|
||||
} = req.query;
|
||||
const tag = req.query.tag || 'badgedex';
|
||||
const label = req.query.label || 'BadgeDex';
|
||||
const style = parseStyle(req.query.style);
|
||||
const bgtag = req.query.bgtag || 'none';
|
||||
const bglabel = req.query.bglabel || 'none';
|
||||
const color = req.query.color || '#000000';
|
||||
const size = parseSize(req.query.size);
|
||||
const labelpos = req.query.labelpos || 'right';
|
||||
const fontweight = req.query.fontweight || 'normal';
|
||||
|
||||
const fontFamily = "Verdana,Geneva,DejaVu Sans,sans-serif";
|
||||
const fontFamily = FONT_FAMILY;
|
||||
const fontWeight = escapeSvg(fontweight);
|
||||
const safeTag = escapeSvg(tag);
|
||||
const safeLabel = escapeSvg(label);
|
||||
@@ -28,9 +28,9 @@ router.get('/tag', (req, res) => {
|
||||
//const tagX = labelpos === 'left' ? 455 : 165;
|
||||
|
||||
// A méretet pixelben adjuk meg, alapértelmezett 24px
|
||||
const textPadding = Number(5);
|
||||
const badgeSize = Number(size);
|
||||
const badgeFontSize = Math.round(badgeSize * 0.6); // A szöveg mérete a badge méretéhez igazítva
|
||||
const textPadding = 5;
|
||||
const badgeSize = size;
|
||||
const badgeFontSize = calcFontSize(badgeSize);
|
||||
const tagTextLength = tag.length * (badgeFontSize * 0.7); // A tag szöveg hossza
|
||||
const labelTextLength = label.length * (badgeFontSize * 0.8); // A label szöveg hossza
|
||||
const badgeWidth = tagTextLength + labelTextLength + 10; // A badge szélessége a szövegek hosszától függően
|
||||
@@ -48,24 +48,25 @@ router.get('/tag', (req, res) => {
|
||||
</g>`.trim();
|
||||
|
||||
|
||||
const rx = 3;
|
||||
const flat = `
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="r">
|
||||
<rect width="62" height="20" rx="3" fill="#fff"/>
|
||||
<rect width="${badgeWidth}" height="${badgeSize}" rx="${rx}" fill="#fff"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="31" height="20" fill="#555"/>
|
||||
<rect x="31" width="31" height="20" fill="#007ec6"/>
|
||||
<rect width="62" height="20" fill="url(#s)"/>
|
||||
<rect width="${tagTextLength + textPadding}" height="${badgeSize}" fill="${safeBgTag}"/>
|
||||
<rect x="${tagTextLength + textPadding}" width="${labelTextLength + textPadding}" height="${badgeSize}" fill="${safeBgLabel}"/>
|
||||
<rect width="${badgeWidth}" height="${badgeSize}" fill="url(#s)"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="${fontFamily}" text-rendering="geometricPrecision" font-size="110" font-weight="${fontWeight}">
|
||||
<text aria-hidden="true" x="165" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">${safeTag}</text>
|
||||
<text x="165" y="140" transform="scale(.1)" fill="#fff" textLength="210">${safeTag}</text>
|
||||
<text aria-hidden="true" x="455" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">${safeLabel}</text>
|
||||
<text x="455" y="140" transform="scale(.1)" fill="#fff" textLength="210">${safeLabel}</text>
|
||||
<g fill="#fff" text-anchor="middle" font-family="${fontFamily}" text-rendering="geometricPrecision" font-size="${badgeFontSize}" font-weight="${fontWeight}">
|
||||
<text aria-hidden="true" x="${(tagTextLength + textPadding) / 2}" y="${badgeFontSize + textPadding + 1}" fill="#010101" fill-opacity=".3">${safeTag}</text>
|
||||
<text x="${(tagTextLength + textPadding) / 2}" y="${badgeFontSize + textPadding}" fill="#fff">${safeTag}</text>
|
||||
<text aria-hidden="true" x="${tagTextLength + textPadding + (labelTextLength + textPadding) / 2}" y="${badgeFontSize + textPadding + 1}" fill="#010101" fill-opacity=".3">${safeLabel}</text>
|
||||
<text x="${tagTextLength + textPadding + (labelTextLength + textPadding) / 2}" y="${badgeFontSize + textPadding}" fill="#fff">${safeLabel}</text>
|
||||
</g>`.trim();
|
||||
|
||||
const svg = `
|
||||
|
||||
+14
-5
@@ -1,10 +1,13 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const express = require('express');
|
||||
const helmet = require('helmet');
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
const logger = require('./lib/logger');
|
||||
const api = require('./api');
|
||||
const version = require('./lib/version').version();
|
||||
|
||||
const api = require('./api');
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
app.use(helmet({
|
||||
crossOriginResourcePolicy: false,
|
||||
@@ -12,7 +15,13 @@ app.use(helmet({
|
||||
|
||||
app.use('/', api.router);
|
||||
|
||||
// Central error handler – catches all unhandled errors
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
app.use((err, req, res, next) => {
|
||||
logger.error({ err, url: req.url }, 'Unhandled error');
|
||||
res.status(err.status || 500).json({ error: err.message || 'Internal Server Error' });
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`[ ${new Date()} ] [ 🚀 BadgeDex version: ${version} ]`);
|
||||
console.log(`[ ${new Date()} ] [ 🚀 BadgeDex API running at http://localhost:${PORT}/ ]`);
|
||||
logger.info(`BadgeDex version ${version} listening on http://localhost:${PORT}/`);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const pino = require('pino');
|
||||
|
||||
const logger = pino({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
transport:
|
||||
process.env.NODE_ENV !== 'production'
|
||||
? { target: 'pino-pretty', options: { colorize: true } }
|
||||
: undefined,
|
||||
});
|
||||
|
||||
module.exports = logger;
|
||||
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
/** Standard font family used by both badge and tag endpoints. */
|
||||
const FONT_FAMILY = 'Verdana,Geneva,DejaVu Sans,sans-serif';
|
||||
|
||||
/** Font-size scales with the icon / badge size. */
|
||||
const calcFontSize = (size) => Math.round(size * 0.6);
|
||||
|
||||
/**
|
||||
* Rough text-width estimate based on font-size and character count.
|
||||
* Uses a 0.6 coefficient which works well for Verdana / sans-serif.
|
||||
*/
|
||||
const estimateTextWidth = (text, fontSize) =>
|
||||
text.length * fontSize * 0.6;
|
||||
|
||||
module.exports = { FONT_FAMILY, calcFontSize, estimateTextWidth };
|
||||
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
const ALLOWED_STYLES = ['rect', 'flat'];
|
||||
const ALLOWED_LABELPOS = ['right', 'left', 'above', 'below'];
|
||||
const SIZE_MIN = 8;
|
||||
const SIZE_MAX = 256;
|
||||
|
||||
/**
|
||||
* Parse and clamp the `size` query parameter.
|
||||
* Returns a positive integer in [SIZE_MIN, SIZE_MAX].
|
||||
*/
|
||||
const parseSize = (raw) => {
|
||||
const n = parseInt(raw, 10);
|
||||
if (Number.isNaN(n) || n < SIZE_MIN || n > SIZE_MAX) return 24;
|
||||
return n;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a valid style or the default.
|
||||
*/
|
||||
const parseStyle = (raw) =>
|
||||
ALLOWED_STYLES.includes(raw) ? raw : 'rect';
|
||||
|
||||
/**
|
||||
* Return a valid labelpos or the default.
|
||||
*/
|
||||
const parseLabelpos = (raw) =>
|
||||
ALLOWED_LABELPOS.includes(raw) ? raw : 'right';
|
||||
|
||||
/**
|
||||
* Return a valid font-weight string.
|
||||
* Accepts: normal, bold, lighter, bolder, 100–900.
|
||||
*/
|
||||
const parseFontweight = (raw) => {
|
||||
if (/^(normal|bold|lighter|bolder)$/.test(raw)) return raw;
|
||||
if (/^[1-9]00$/.test(raw)) return raw;
|
||||
return 'normal';
|
||||
};
|
||||
|
||||
module.exports = { parseSize, parseStyle, parseLabelpos, parseFontweight, SIZE_MIN, SIZE_MAX };
|
||||
Reference in New Issue
Block a user