feat: add workflow/config to auto generate openapi json

This commit is contained in:
LukeGus
2026-01-12 19:28:25 -06:00
parent 8ae8520c44
commit 868ac39b71
7 changed files with 443 additions and 2742 deletions

32
.github/workflows/openapi.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Generate OpenAPI Specification
on:
workflow_dispatch:
jobs:
generate-openapi:
name: Generate OpenAPI JSON
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Generate OpenAPI specification
run: npm run generate:openapi
- name: Upload OpenAPI artifact
uses: actions/upload-artifact@v4
with:
name: openapi-spec
path: openapi.json
retention-days: 90

View File

@@ -1,437 +0,0 @@
name: Auto Translate
on:
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
translate-zh:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t zh --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-zh
path: src/locales/zh.json
continue-on-error: true
translate-ru:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t ru --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-ru
path: src/locales/ru.json
continue-on-error: true
translate-pt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t pt --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-pt
path: src/locales/pt.json
continue-on-error: true
translate-fr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t fr --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-fr
path: src/locales/fr.json
continue-on-error: true
translate-es:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t es --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-es
path: src/locales/es.json
continue-on-error: true
translate-de:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t de --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-de
path: src/locales/de.json
continue-on-error: true
translate-hi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t hi --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-hi
path: src/locales/hi.json
continue-on-error: true
translate-bn:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t bn --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-bn
path: src/locales/bn.json
continue-on-error: true
translate-ja:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t ja --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-ja
path: src/locales/ja.json
continue-on-error: true
translate-vi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t vi --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-vi
path: src/locales/vi.json
continue-on-error: true
translate-tr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t tr --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-tr
path: src/locales/tr.json
continue-on-error: true
translate-ko:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t ko --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-ko
path: src/locales/ko.json
continue-on-error: true
translate-it:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t it --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-it
path: src/locales/it.json
continue-on-error: true
translate-he:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t he --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-he
path: src/locales/he.json
continue-on-error: true
translate-ar:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t ar --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-ar
path: src/locales/ar.json
continue-on-error: true
translate-pl:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t pl --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-pl
path: src/locales/pl.json
continue-on-error: true
translate-nl:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t nl --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-nl
path: src/locales/nl.json
continue-on-error: true
translate-sv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t sv --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-sv
path: src/locales/sv.json
continue-on-error: true
translate-id:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t id --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-id
path: src/locales/id.json
continue-on-error: true
translate-th:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t th --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-th
path: src/locales/th.json
continue-on-error: true
translate-uk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t uk --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-uk
path: src/locales/uk.json
continue-on-error: true
translate-cs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t cs --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-cs
path: src/locales/cs.json
continue-on-error: true
translate-ro:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t ro --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-ro
path: src/locales/ro.json
continue-on-error: true
translate-el:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t el --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-el
path: src/locales/el.json
continue-on-error: true
translate-nb:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npx i18n-auto-translation -k ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} -d "src/locales" -f en -t nb --maxLinesPerRequest 1
- uses: actions/upload-artifact@v4
with:
name: translations-nb
path: src/locales/nb.json
continue-on-error: true
create-pr:
needs:
[
translate-zh,
translate-ru,
translate-pt,
translate-fr,
translate-es,
translate-de,
translate-hi,
translate-bn,
translate-ja,
translate-vi,
translate-tr,
translate-ko,
translate-it,
translate-he,
translate-ar,
translate-pl,
translate-nl,
translate-sv,
translate-id,
translate-th,
translate-uk,
translate-cs,
translate-ro,
translate-el,
translate-nb,
]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GHCR_TOKEN }}
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: translations-temp
- name: Move translations to src/locales
run: |
cp translations-temp/translations-zh/zh.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-ru/ru.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-pt/pt.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-fr/fr.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-es/es.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-de/de.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-hi/hi.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-bn/bn.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-ja/ja.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-vi/vi.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-tr/tr.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-ko/ko.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-it/it.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-he/he.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-ar/ar.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-pl/pl.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-nl/nl.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-sv/sv.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-id/id.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-th/th.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-uk/uk.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-cs/cs.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-ro/ro.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-el/el.json src/locales/ 2>/dev/null || true
cp translations-temp/translations-nb/nb.json src/locales/ 2>/dev/null || true
rm -rf translations-temp
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GHCR_TOKEN }}
commit-message: "chore: auto-translate to multiple languages"
branch: translations-update
delete-branch: true
title: "chore: Update translations for all languages"

File diff suppressed because it is too large Load Diff

262
package-lock.json generated
View File

@@ -34,6 +34,7 @@
"@tailwindcss/vite": "^4.1.14",
"@types/bcryptjs": "^2.4.6",
"@types/cookie-parser": "^1.4.9",
"@types/cytoscape": "^3.21.9",
"@types/jszip": "^3.4.0",
"@types/multer": "^2.0.0",
"@types/qrcode": "^1.5.5",
@@ -56,6 +57,7 @@
"cmdk": "^1.1.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"cytoscape": "^3.33.1",
"dotenv": "^17.2.0",
"drizzle-orm": "^0.44.3",
"express": "^5.1.0",
@@ -72,6 +74,7 @@
"node-fetch": "^3.3.2",
"qrcode": "^1.5.4",
"react": "^19.1.0",
"react-cytoscapejs": "^2.0.0",
"react-dom": "^19.1.0",
"react-h5-audio-player": "^3.10.1",
"react-hook-form": "^7.60.0",
@@ -122,11 +125,60 @@
"husky": "^9.1.7",
"lint-staged": "^16.2.3",
"prettier": "3.6.2",
"swagger-jsdoc": "^6.2.8",
"typescript": "~5.9.2",
"typescript-eslint": "^8.40.0",
"vite": "^7.1.5"
}
},
"node_modules/@apidevtools/json-schema-ref-parser": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
"integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jsdevtools/ono": "^7.1.3",
"@types/json-schema": "^7.0.6",
"call-me-maybe": "^1.0.1",
"js-yaml": "^4.1.0"
}
},
"node_modules/@apidevtools/openapi-schemas": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@apidevtools/swagger-methods": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
"dev": true,
"license": "MIT"
},
"node_modules/@apidevtools/swagger-parser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
"integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.6",
"@apidevtools/openapi-schemas": "^2.0.4",
"@apidevtools/swagger-methods": "^3.0.2",
"@jsdevtools/ono": "^7.1.3",
"call-me-maybe": "^1.0.1",
"z-schema": "^5.0.1"
},
"peerDependencies": {
"openapi-types": ">=7"
}
},
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -2619,6 +2671,13 @@
"url": "https://opencollective.com/js-sdsl"
}
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
"dev": true,
"license": "MIT"
},
"node_modules/@lezer/common": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.3.0.tgz",
@@ -5228,6 +5287,12 @@
"@types/node": "*"
}
},
"node_modules/@types/cytoscape": {
"version": "3.21.9",
"resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.21.9.tgz",
"integrity": "sha512-JyrG4tllI6jvuISPjHK9j2Xv/LTbnLekLke5otGStjFluIyA9JjgnvgZrSBsp8cEDpiTjwgZUZwpPv8TSBcoLw==",
"license": "MIT"
},
"node_modules/@types/d3-array": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
@@ -6958,6 +7023,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-me-maybe": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
"dev": true,
"license": "MIT"
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -7776,6 +7848,15 @@
"integrity": "sha512-cjrsQufETwxjvwZbYbKBCJNvmQ2++G9AvT45zDi7NXL9k2PdVcs2h0jQz96J6G4TMKRCcEsoJ+QTgQD00Igtjw==",
"license": "MIT"
},
"node_modules/cytoscape": {
"version": "3.33.1",
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz",
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
"license": "MIT",
"engines": {
"node": ">=0.10"
}
},
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
@@ -8304,6 +8385,19 @@
"license": "MIT",
"optional": true
},
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"esutils": "^2.0.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/dompurify": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
@@ -12195,6 +12289,14 @@
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
"license": "MIT"
},
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
"deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -12207,6 +12309,14 @@
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
@@ -14068,6 +14178,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/openapi-types": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -15109,6 +15227,19 @@
"node": ">=0.10.0"
}
},
"node_modules/react-cytoscapejs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-cytoscapejs/-/react-cytoscapejs-2.0.0.tgz",
"integrity": "sha512-t3SSl1DQy7+JQjN+8QHi1anEJlM3i3aAeydHTsJwmjo/isyKK7Rs7oCvU6kZsB9NwZidzZQR21Vm2PcBLG/Tjg==",
"license": "MIT",
"dependencies": {
"prop-types": "^15.8.1"
},
"peerDependencies": {
"cytoscape": "^3.2.19",
"react": ">=15.0.0"
}
},
"node_modules/react-dom": {
"version": "19.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
@@ -16717,6 +16848,95 @@
"node": ">=8"
}
},
"node_modules/swagger-jsdoc": {
"version": "6.2.8",
"resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
"integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"commander": "6.2.0",
"doctrine": "3.0.0",
"glob": "7.1.6",
"lodash.mergewith": "^4.6.2",
"swagger-parser": "^10.0.3",
"yaml": "2.0.0-1"
},
"bin": {
"swagger-jsdoc": "bin/swagger-jsdoc.js"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/swagger-jsdoc/node_modules/commander": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/swagger-jsdoc/node_modules/glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/swagger-jsdoc/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/swagger-jsdoc/node_modules/yaml": {
"version": "2.0.0-1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">= 6"
}
},
"node_modules/swagger-parser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz",
"integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tailwind-merge": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
@@ -17566,6 +17786,16 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/validator": {
"version": "13.15.26",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz",
"integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -18120,6 +18350,38 @@
"integrity": "sha512-YHDIOAqgRpfl1Ois9HcB8UFtWOxK8KJrV5TXpImj4BKYP1rWT04f/fMM9tQ9SYZlBKukT7NR+9wcI3UpB5BMDQ==",
"license": "MIT"
},
"node_modules/z-schema": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz",
"integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"validator": "^13.7.0"
},
"bin": {
"z-schema": "bin/z-schema"
},
"engines": {
"node": ">=8.0.0"
},
"optionalDependencies": {
"commander": "^9.4.1"
}
},
"node_modules/z-schema/node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/zod": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",

View File

@@ -17,6 +17,7 @@
"build": "vite build && tsc -p tsconfig.node.json",
"build:backend": "tsc -p tsconfig.node.json",
"dev:backend": "tsc -p tsconfig.node.json && node ./dist/backend/backend/starter.js",
"generate:openapi": "tsc -p tsconfig.node.json && node ./dist/backend/backend/swagger.js",
"preview": "vite preview",
"electron:dev": "concurrently \"npm run dev\" \"powershell -c \\\"Start-Sleep -Seconds 5\\\" && electron .\"",
"build:win-portable": "npm run build && electron-builder --win --dir",
@@ -144,6 +145,7 @@
"husky": "^9.1.7",
"lint-staged": "^16.2.3",
"prettier": "3.6.2",
"swagger-jsdoc": "^6.2.8",
"typescript": "~5.9.2",
"typescript-eslint": "^8.40.0",
"vite": "^7.1.5"

View File

@@ -2656,3 +2656,5 @@ router.post("/totp/verify-login", async (req, res) => {
return res.status(500).json({ error: "TOTP verification failed" });
}
});
export default router;

145
src/backend/swagger.ts Normal file
View File

@@ -0,0 +1,145 @@
import swaggerJSDoc from "swagger-jsdoc";
import path from "path";
import { fileURLToPath } from "url";
import { promises as fs } from "fs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const projectRoot = path.join(__dirname, "..", "..", "..");
const swaggerOptions: swaggerJSDoc.Options = {
definition: {
openapi: "3.0.3",
info: {
title: "Termix API",
version: "0.0.0",
description: "Termix Backend API Reference",
},
servers: [
{
url: "http://localhost:30001",
description: "Main database and authentication server",
},
{
url: "http://localhost:30003",
description: "SSH tunnel management server",
},
{
url: "http://localhost:30004",
description: "SSH file manager server",
},
{
url: "http://localhost:30005",
description: "Server statistics and monitoring server",
},
{
url: "http://localhost:30006",
description: "Dashboard server",
},
{
url: "http://localhost:30007",
description: "Docker management server",
},
],
components: {
securitySchemes: {
bearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
},
schemas: {
Error: {
type: "object",
properties: {
error: { type: "string" },
details: { type: "string" },
},
},
},
},
security: [
{
bearerAuth: [],
},
],
tags: [
{
name: "Alerts",
description: "System alerts and notifications management",
},
{
name: "Credentials",
description: "SSH credential management",
},
{
name: "Network Topology",
description: "Network topology visualization and management",
},
{
name: "RBAC",
description: "Role-based access control for host sharing",
},
{
name: "Snippets",
description: "Command snippet management",
},
{
name: "Terminal",
description: "Terminal command history",
},
{
name: "Users",
description: "User management and authentication",
},
{
name: "Dashboard",
description: "Dashboard statistics and activity",
},
{
name: "Docker",
description: "Docker container management",
},
{
name: "SSH Tunnels",
description: "SSH tunnel connection management",
},
{
name: "Server Stats",
description: "Server status monitoring and metrics collection",
},
{
name: "File Manager",
description: "SSH file management operations",
},
],
},
apis: [
path.join(projectRoot, "src", "backend", "database", "routes", "*.ts"),
path.join(projectRoot, "src", "backend", "dashboard.ts"),
path.join(projectRoot, "src", "backend", "ssh", "*.ts"),
],
};
async function generateOpenAPISpec() {
try {
const swaggerSpec = swaggerJSDoc(swaggerOptions);
const outputPath = path.join(projectRoot, "openapi.json");
await fs.writeFile(
outputPath,
JSON.stringify(swaggerSpec, null, 2),
"utf-8",
);
} catch (error) {
console.error("Failed to generate OpenAPI specification:", error);
process.exit(1);
}
}
generateOpenAPISpec();
export { swaggerOptions, generateOpenAPISpec };