added Swagger documentation
This commit is contained in:
parent
a3dfdccd0d
commit
6085443549
168
backend/package-lock.json
generated
168
backend/package-lock.json
generated
@ -35,7 +35,6 @@
|
||||
"pino": "^9.0.0",
|
||||
"pino-pretty": "^11.0.0",
|
||||
"sequelize": "^6.37.0",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"uuid": "^14.0.1",
|
||||
"validator": "^13.15.35"
|
||||
@ -58,7 +57,6 @@
|
||||
"@types/passport-google-oauth2": "^0.1.10",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/passport-microsoft": "^2.1.1",
|
||||
"@types/swagger-jsdoc": "^6.0.4",
|
||||
"@types/swagger-ui-express": "^4.1.8",
|
||||
"@types/validator": "^13.15.10",
|
||||
"@typescript-eslint/eslint-plugin": "^8.62.1",
|
||||
@ -76,50 +74,6 @@
|
||||
"node": ">=24 <25"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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/@aws-crypto/crc32": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
|
||||
@ -1255,12 +1209,6 @@
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"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==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
|
||||
@ -2369,12 +2317,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json2csv": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/json2csv/-/json2csv-5.0.7.tgz",
|
||||
@ -2566,13 +2508,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/swagger-jsdoc": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz",
|
||||
"integrity": "sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/swagger-ui-express": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz",
|
||||
@ -3321,6 +3256,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/array-buffer-byte-length": {
|
||||
@ -3640,6 +3576,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
@ -3806,6 +3743,7 @@
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@ -3930,12 +3868,6 @@
|
||||
"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==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@ -4039,6 +3971,7 @@
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
@ -4049,6 +3982,7 @@
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
@ -4353,6 +4287,7 @@
|
||||
"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"
|
||||
@ -5254,6 +5189,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -5664,6 +5600,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
@ -5895,26 +5832,6 @@
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"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/glob-parent": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
@ -6292,6 +6209,7 @@
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
@ -6784,6 +6702,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
|
||||
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -6992,6 +6911,7 @@
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
@ -7025,12 +6945,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.mergewith": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
|
||||
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
@ -7123,6 +7037,7 @@
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
@ -7632,13 +7547,6 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||
@ -7838,6 +7746,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -9251,47 +9160,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "10.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-ui-dist": {
|
||||
"version": "5.17.14",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz",
|
||||
@ -10199,15 +10067,6 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
@ -10224,6 +10083,7 @@
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.6.tgz",
|
||||
"integrity": "sha512-+XR1GhnWklYdfr8YaZv/iu+vY+ux7V5DS5zH1DQf6bO5ufrt/5cgNhVO5qyhsjFXvsqQb/f08DWE9b6uPscyAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.get": "^4.4.2",
|
||||
|
||||
@ -55,7 +55,6 @@
|
||||
"pino": "^9.0.0",
|
||||
"pino-pretty": "^11.0.0",
|
||||
"sequelize": "^6.37.0",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"uuid": "^14.0.1",
|
||||
"validator": "^13.15.35"
|
||||
@ -93,7 +92,6 @@
|
||||
"@types/passport-google-oauth2": "^0.1.10",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/passport-microsoft": "^2.1.1",
|
||||
"@types/swagger-jsdoc": "^6.0.4",
|
||||
"@types/swagger-ui-express": "^4.1.8",
|
||||
"@types/validator": "^13.15.10",
|
||||
"@typescript-eslint/eslint-plugin": "^8.62.1",
|
||||
|
||||
@ -4,7 +4,6 @@ import express from 'express';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import helmet from 'helmet';
|
||||
import swaggerJsDoc from 'swagger-jsdoc';
|
||||
import * as swaggerUI from 'swagger-ui-express';
|
||||
|
||||
import './auth/auth.ts';
|
||||
@ -23,6 +22,7 @@ import {
|
||||
downloadLimiter,
|
||||
searchLimiter,
|
||||
} from './middlewares/rateLimiter.ts';
|
||||
import { createOpenApiDocument } from './openapi/document.ts';
|
||||
import accessLogsRoutesModule from './routes/access_logs.ts';
|
||||
import assetVariantsRoutesModule from './routes/asset_variants.ts';
|
||||
import assetsRoutesModule from './routes/assets.ts';
|
||||
@ -55,9 +55,6 @@ import type {
|
||||
HealthResponse,
|
||||
MountRuntimeEntityRoute,
|
||||
RuntimeReadOrAuthMiddleware,
|
||||
SwaggerDocumentOptions,
|
||||
SwaggerHostMiddleware,
|
||||
SwaggerUiModuleWithHost,
|
||||
} from './types/index.ts';
|
||||
import {
|
||||
exitAfterLogging,
|
||||
@ -73,7 +70,6 @@ import {
|
||||
} from './utils/request-context.ts';
|
||||
|
||||
const app = express();
|
||||
const swaggerUiWithHost: SwaggerUiModuleWithHost = swaggerUI;
|
||||
|
||||
registerProcessErrorHandlers();
|
||||
initializePermissionsMiddleware();
|
||||
@ -154,54 +150,12 @@ const getBaseUrl = (url: string | undefined): string => {
|
||||
return url.endsWith('/api') ? url.slice(0, -4) : url;
|
||||
};
|
||||
|
||||
const options: SwaggerDocumentOptions = {
|
||||
definition: {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
version: '1.0.0',
|
||||
title: 'Tour Builder Platform',
|
||||
description:
|
||||
'Tour Builder Platform Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.',
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || config.swaggerUrl,
|
||||
description: 'Development server',
|
||||
},
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
bearerAuth: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
UnauthorizedError: {
|
||||
description: 'Access token is missing or invalid',
|
||||
},
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{
|
||||
bearerAuth: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
apis: ['./src/routes/*.{js,ts}'],
|
||||
};
|
||||
|
||||
const specs = swaggerJsDoc(options);
|
||||
const swaggerHostMiddleware: SwaggerHostMiddleware = (req, _res, next) => {
|
||||
swaggerUiWithHost.host =
|
||||
getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || req.get('host') || '';
|
||||
next();
|
||||
};
|
||||
const specs = createOpenApiDocument({
|
||||
serverUrl: getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || config.swaggerUrl,
|
||||
});
|
||||
|
||||
app.use(
|
||||
'/api-docs',
|
||||
swaggerHostMiddleware,
|
||||
swaggerUI.serve,
|
||||
swaggerUI.setup(specs),
|
||||
);
|
||||
|
||||
1959
backend/src/openapi/document.ts
Normal file
1959
backend/src/openapi/document.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,7 @@
|
||||
import type { ErrorRequestHandler, NextFunction, Request, RequestHandler, Response, Router } from 'express';
|
||||
import type swaggerJSDoc from 'swagger-jsdoc';
|
||||
import type * as swaggerUiExpress from 'swagger-ui-express';
|
||||
import type { ErrorRequestHandler, RequestHandler, Router } from 'express';
|
||||
|
||||
import type { CurrentUser } from './auth.ts';
|
||||
|
||||
export type SwaggerDocumentOptions = swaggerJSDoc.Options;
|
||||
|
||||
export type SwaggerUiModuleWithHost = typeof swaggerUiExpress & {
|
||||
host?: string;
|
||||
};
|
||||
|
||||
export type ExpressRouter = Router;
|
||||
|
||||
export interface HealthResponse {
|
||||
@ -33,10 +25,6 @@ export interface MountRuntimeEntityRoute {
|
||||
|
||||
export type AppErrorHandler = ErrorRequestHandler;
|
||||
|
||||
export interface SwaggerHostMiddleware {
|
||||
(req: Request, res: Response, next: NextFunction): void;
|
||||
}
|
||||
|
||||
export interface PublicAccessHardeningSummary {
|
||||
publicRolePermissions: number;
|
||||
publicUsersWithCustomPermissions: number;
|
||||
|
||||
@ -131,9 +131,6 @@ export type {
|
||||
PublicAccessHardeningSummary,
|
||||
RuntimeJwtVerifyHandler,
|
||||
RuntimeReadOrAuthMiddleware,
|
||||
SwaggerDocumentOptions,
|
||||
SwaggerHostMiddleware,
|
||||
SwaggerUiModuleWithHost,
|
||||
} from './app.ts';
|
||||
export type {
|
||||
EmailSendResult,
|
||||
|
||||
114
backend/tests/openapi-document.test.ts
Normal file
114
backend/tests/openapi-document.test.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import type { NextFunction } from 'express';
|
||||
import { createRequest, createResponse } from 'node-mocks-http';
|
||||
import * as swaggerUI from 'swagger-ui-express';
|
||||
|
||||
import { createOpenApiDocument } from '../src/openapi/document.ts';
|
||||
import type { OpenApiDocument } from '../src/openapi/document.ts';
|
||||
|
||||
function createTestDocument(): OpenApiDocument {
|
||||
return createOpenApiDocument({
|
||||
serverUrl: 'http://localhost:3000',
|
||||
});
|
||||
}
|
||||
|
||||
function collectRefs(value: unknown): Set<string> {
|
||||
const refs = new Set<string>();
|
||||
const serialized = JSON.stringify(value);
|
||||
const refPattern = /"\$ref"\s*:\s*"([^"]+)"/g;
|
||||
|
||||
for (const match of serialized.matchAll(refPattern)) {
|
||||
const ref = match[1];
|
||||
if (ref) {
|
||||
refs.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
function hasComponentRef(document: OpenApiDocument, ref: string): boolean {
|
||||
const match = /^#\/components\/(schemas|responses|parameters|securitySchemes)\/(.+)$/.exec(ref);
|
||||
if (!match) return false;
|
||||
|
||||
const section = match[1];
|
||||
const name = match[2];
|
||||
if (!section || !name) return false;
|
||||
|
||||
switch (section) {
|
||||
case 'schemas':
|
||||
return name in document.components.schemas;
|
||||
case 'responses':
|
||||
return name in document.components.responses;
|
||||
case 'parameters':
|
||||
return name in document.components.parameters;
|
||||
case 'securitySchemes':
|
||||
return name in document.components.securitySchemes;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void test('OpenAPI document exposes comprehensive route coverage', () => {
|
||||
const document = createTestDocument();
|
||||
const requiredPaths = [
|
||||
'/api/health',
|
||||
'/api/auth/signin/local',
|
||||
'/api/users',
|
||||
'/api/users/count',
|
||||
'/api/users/autocomplete',
|
||||
'/api/projects/{id}/clone',
|
||||
'/api/file/presign',
|
||||
'/api/file/upload-sessions/{sessionId}/chunks/{chunkIndex}',
|
||||
'/api/runtime-access/me',
|
||||
'/api/project-ui-control-settings/project/{projectId}/env/{environment}',
|
||||
'/api/tour_pages/reverse-video-status',
|
||||
];
|
||||
|
||||
assert.equal(document.openapi, '3.0.0');
|
||||
|
||||
for (const path of requiredPaths) {
|
||||
assert.ok(document.paths[path], `Missing OpenAPI path: ${path}`);
|
||||
}
|
||||
});
|
||||
|
||||
void test('OpenAPI document resolves all internal refs', () => {
|
||||
const document = createTestDocument();
|
||||
const refs = collectRefs(document);
|
||||
const missingRefs = [...refs].filter((ref) => !hasComponentRef(document, ref));
|
||||
|
||||
assert.equal(missingRefs.length, 0, `Missing refs: ${missingRefs.join(', ')}`);
|
||||
});
|
||||
|
||||
void test('OpenAPI factory CRUD paths are generated consistently', () => {
|
||||
const document = createTestDocument();
|
||||
const resourcePath = '/api/assets';
|
||||
|
||||
assert.ok(document.paths[resourcePath]?.post);
|
||||
assert.ok(document.paths[resourcePath]?.get);
|
||||
assert.ok(document.paths[`${resourcePath}/bulk-import`]?.post);
|
||||
assert.ok(document.paths[`${resourcePath}/deleteByIds`]?.post);
|
||||
assert.ok(document.paths[`${resourcePath}/count`]?.get);
|
||||
assert.ok(document.paths[`${resourcePath}/autocomplete`]?.get);
|
||||
assert.ok(document.paths[`${resourcePath}/{id}`]?.get);
|
||||
assert.ok(document.paths[`${resourcePath}/{id}`]?.put);
|
||||
assert.ok(document.paths[`${resourcePath}/{id}`]?.delete);
|
||||
});
|
||||
|
||||
void test('Swagger UI setup serves documentation HTML without throwing', () => {
|
||||
const document = createTestDocument();
|
||||
const req = createRequest({ url: '/api-docs/' });
|
||||
const res = createResponse();
|
||||
const handler = swaggerUI.setup(document);
|
||||
let nextError: unknown = null;
|
||||
const next: NextFunction = (error?: unknown) => {
|
||||
nextError = error;
|
||||
};
|
||||
|
||||
handler(req, res, next);
|
||||
|
||||
assert.equal(nextError, null);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.match(String(res._getData()), /swagger-ui/);
|
||||
});
|
||||
@ -434,9 +434,7 @@ const ConstructorToolbar = forwardRef<HTMLDivElement, ConstructorToolbarProps>(
|
||||
className='h-10 w-[86px]'
|
||||
label={isSaving ? 'Saving...' : 'Save'}
|
||||
subtitle={
|
||||
lastSavedAt
|
||||
? dataFormatter.relativeTimestamp(lastSavedAt)
|
||||
: ' '
|
||||
lastSavedAt ? dataFormatter.relativeTimestamp(lastSavedAt) : ' '
|
||||
}
|
||||
onClick={onSave}
|
||||
disabled={isSaving}
|
||||
|
||||
@ -642,8 +642,7 @@ export function ElementEditorPanel({
|
||||
<NavigationSettingsSectionCompact
|
||||
type={
|
||||
selectedElement.type as
|
||||
| 'navigation_next'
|
||||
| 'navigation_prev'
|
||||
'navigation_next' | 'navigation_prev'
|
||||
}
|
||||
navType={selectedElement.navType}
|
||||
navLabel={selectedElement.navLabel || ''}
|
||||
@ -1310,10 +1309,7 @@ export function ElementEditorPanel({
|
||||
}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value as
|
||||
| 'left'
|
||||
| 'center'
|
||||
| 'right'
|
||||
| '';
|
||||
'left' | 'center' | 'right' | '';
|
||||
updateSelectedElement({
|
||||
infoPanelTitleTextAlign: val || undefined,
|
||||
});
|
||||
@ -1465,10 +1461,7 @@ export function ElementEditorPanel({
|
||||
}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value as
|
||||
| 'left'
|
||||
| 'center'
|
||||
| 'right'
|
||||
| '';
|
||||
'left' | 'center' | 'right' | '';
|
||||
updateSelectedElement({
|
||||
infoPanelTextTextAlign: val || undefined,
|
||||
});
|
||||
@ -1639,10 +1632,7 @@ export function ElementEditorPanel({
|
||||
}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value as
|
||||
| 'left'
|
||||
| 'center'
|
||||
| 'right'
|
||||
| '';
|
||||
'left' | 'center' | 'right' | '';
|
||||
updateSelectedElement({
|
||||
infoPanelSpanTextAlign: val || undefined,
|
||||
});
|
||||
@ -2093,10 +2083,7 @@ export function ElementEditorPanel({
|
||||
// Handle slide transition properties with proper prefixes
|
||||
if (prop === 'slideTransitionType') {
|
||||
const typedValue = (value || undefined) as
|
||||
| 'fade'
|
||||
| 'none'
|
||||
| ''
|
||||
| undefined;
|
||||
'fade' | 'none' | '' | undefined;
|
||||
if (selectedElement.type === 'gallery') {
|
||||
updateSelectedElement({
|
||||
gallerySlideTransitionType: typedValue,
|
||||
|
||||
@ -148,7 +148,10 @@ const StyleSettingsSection: React.FC<StyleSettingsSectionProps> = ({
|
||||
max='100'
|
||||
value={opacityToPercentInput(values.opacity)}
|
||||
onChange={(event) =>
|
||||
onChange('opacity', percentInputToOpacityValue(event.target.value))
|
||||
onChange(
|
||||
'opacity',
|
||||
percentInputToOpacityValue(event.target.value),
|
||||
)
|
||||
}
|
||||
placeholder='50'
|
||||
/>
|
||||
|
||||
@ -8,6 +8,8 @@ export const portApi =
|
||||
: '';
|
||||
export const baseURLApi = `${hostApi}${portApi ? `:${portApi}` : ``}/api`;
|
||||
|
||||
export const swaggerDocsUrl = baseURLApi.replace(/\/api\/?$/, '/api-docs');
|
||||
|
||||
export const localStorageDarkModeKey = 'darkMode';
|
||||
|
||||
export const localStorageStyleKey = 'style';
|
||||
|
||||
@ -36,8 +36,7 @@ const PageNavigationContext =
|
||||
// Provider
|
||||
// ============================================================================
|
||||
|
||||
export interface PageNavigationProviderProps
|
||||
extends UsePageNavigationStateOptions {
|
||||
export interface PageNavigationProviderProps extends UsePageNavigationStateOptions {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@ -48,10 +48,7 @@ interface PreloadQueueItem {
|
||||
}
|
||||
|
||||
export type PreloadPhase =
|
||||
| 'idle'
|
||||
| 'phase1_current_page'
|
||||
| 'phase2_transitions'
|
||||
| 'complete';
|
||||
'idle' | 'phase1_current_page' | 'phase2_transitions' | 'complete';
|
||||
|
||||
interface UsePreloadOrchestratorResult {
|
||||
isPreloading: boolean;
|
||||
|
||||
@ -47,8 +47,7 @@ export interface UseTransitionCreationOptions {
|
||||
}
|
||||
|
||||
export interface UseTransitionCreationResult
|
||||
extends TransitionCreationState,
|
||||
TransitionCreationActions {}
|
||||
extends TransitionCreationState, TransitionCreationActions {}
|
||||
|
||||
/**
|
||||
* Hook to manage transition creation form state.
|
||||
|
||||
@ -60,11 +60,7 @@ export interface UseTransitionPlaybackOptions {
|
||||
}
|
||||
|
||||
export type PlaybackPhase =
|
||||
| 'idle'
|
||||
| 'preparing'
|
||||
| 'playing'
|
||||
| 'finishing'
|
||||
| 'completed';
|
||||
'idle' | 'preparing' | 'playing' | 'finishing' | 'completed';
|
||||
|
||||
export interface UseTransitionPlaybackResult {
|
||||
phase: PlaybackPhase;
|
||||
|
||||
@ -404,11 +404,7 @@ export function toPreloadAssetInfo(asset: AssetToCache): PreloadAssetInfo {
|
||||
url: asset.originalUrl,
|
||||
pageId: asset.pageId,
|
||||
assetType: assetType as
|
||||
| 'image'
|
||||
| 'video'
|
||||
| 'audio'
|
||||
| 'transition'
|
||||
| 'other',
|
||||
'image' | 'video' | 'audio' | 'transition' | 'other',
|
||||
priority: asset.priority,
|
||||
};
|
||||
}
|
||||
|
||||
@ -131,12 +131,7 @@ export function remToDesignPx(value: number): number {
|
||||
export function normalizeToCanvasUnits(
|
||||
value: string | number | undefined,
|
||||
property:
|
||||
| 'width'
|
||||
| 'height'
|
||||
| 'fontSize'
|
||||
| 'padding'
|
||||
| 'borderRadius'
|
||||
| 'gap',
|
||||
'width' | 'height' | 'fontSize' | 'padding' | 'borderRadius' | 'gap',
|
||||
designWidth: number = CANVAS_CONFIG.defaults.width,
|
||||
designHeight: number = CANVAS_CONFIG.defaults.height,
|
||||
): string {
|
||||
|
||||
@ -266,10 +266,10 @@ export function hasHoverEffects(
|
||||
): boolean {
|
||||
return Boolean(
|
||||
effects.hoverScale ||
|
||||
effects.hoverOpacity ||
|
||||
effects.hoverBackgroundColor ||
|
||||
effects.hoverColor ||
|
||||
effects.hoverBoxShadow,
|
||||
effects.hoverOpacity ||
|
||||
effects.hoverBackgroundColor ||
|
||||
effects.hoverColor ||
|
||||
effects.hoverBoxShadow,
|
||||
);
|
||||
}
|
||||
|
||||
@ -281,9 +281,9 @@ export function hasFocusEffects(
|
||||
): boolean {
|
||||
return Boolean(
|
||||
effects.focusScale ||
|
||||
effects.focusOpacity ||
|
||||
effects.focusOutline ||
|
||||
effects.focusBoxShadow,
|
||||
effects.focusOpacity ||
|
||||
effects.focusOutline ||
|
||||
effects.focusBoxShadow,
|
||||
);
|
||||
}
|
||||
|
||||
@ -295,8 +295,8 @@ export function hasActiveEffects(
|
||||
): boolean {
|
||||
return Boolean(
|
||||
effects.activeScale ||
|
||||
effects.activeOpacity ||
|
||||
effects.activeBackgroundColor,
|
||||
effects.activeOpacity ||
|
||||
effects.activeBackgroundColor,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -18,11 +18,7 @@ import { toCU } from './canvasScale';
|
||||
* Gallery section names for styling
|
||||
*/
|
||||
export type GallerySectionName =
|
||||
| 'header'
|
||||
| 'title'
|
||||
| 'span'
|
||||
| 'card'
|
||||
| 'wrapper';
|
||||
'header' | 'title' | 'span' | 'card' | 'wrapper';
|
||||
|
||||
/**
|
||||
* Default values for gallery sections using canvas units.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import * as icon from '@mdi/js';
|
||||
import { swaggerDocsUrl } from './config';
|
||||
import { MenuAsideItem } from './types/menu';
|
||||
|
||||
const menuAside: MenuAsideItem[] = [
|
||||
@ -31,7 +32,7 @@ const menuAside: MenuAsideItem[] = [
|
||||
icon: icon.mdiAccountCircle,
|
||||
},
|
||||
{
|
||||
href: '/api-docs',
|
||||
href: swaggerDocsUrl,
|
||||
target: '_blank',
|
||||
label: 'Swagger',
|
||||
icon: icon.mdiFileCode,
|
||||
|
||||
@ -23,9 +23,7 @@ const ProjectsListPage = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const projectsRaw = useAppSelector((state) => state.projects.data) as
|
||||
| Project[]
|
||||
| Project
|
||||
| undefined;
|
||||
Project[] | Project | undefined;
|
||||
// Handle both array (from list fetch) and single object (after edit fetch)
|
||||
const projects: Project[] = Array.isArray(projectsRaw)
|
||||
? projectsRaw
|
||||
|
||||
@ -114,10 +114,7 @@ export interface CarouselSlide {
|
||||
export type InfoPanelItemType = 'image' | 'video' | '360';
|
||||
export type InfoPanelMediaOpenMode = 'panel' | 'fullscreen';
|
||||
export type InfoPanelImageClickAction =
|
||||
| 'panel'
|
||||
| 'fullscreen'
|
||||
| 'target_page'
|
||||
| 'external_url';
|
||||
'panel' | 'fullscreen' | 'target_page' | 'external_url';
|
||||
export type InfoPanelLinkClickAction = 'target_page' | 'external_url';
|
||||
|
||||
/**
|
||||
@ -165,12 +162,7 @@ export interface InfoPanelInfoSpan {
|
||||
* - 'images': Media viewer with preview + thumbnail strip
|
||||
*/
|
||||
export type InfoPanelSectionType =
|
||||
| 'header'
|
||||
| 'title'
|
||||
| 'text'
|
||||
| 'spans'
|
||||
| 'cards'
|
||||
| 'images';
|
||||
'header' | 'title' | 'text' | 'spans' | 'cards' | 'images';
|
||||
|
||||
/**
|
||||
* Section instance with unique ID, settings, AND data.
|
||||
@ -269,8 +261,7 @@ export function generateItemId(): string {
|
||||
* Extends ElementStyleProperties for CSS styling and ElementEffectProperties for effects.
|
||||
*/
|
||||
export interface BaseCanvasElement
|
||||
extends ElementStyleProperties,
|
||||
ElementEffectProperties {
|
||||
extends ElementStyleProperties, ElementEffectProperties {
|
||||
id: string;
|
||||
type: CanvasElementType | string;
|
||||
label?: string;
|
||||
|
||||
@ -50,13 +50,7 @@ export interface TableColumnConfig {
|
||||
valueOptions?: string[];
|
||||
// Custom render function name
|
||||
renderType?:
|
||||
| 'link'
|
||||
| 'boolean'
|
||||
| 'date'
|
||||
| 'datetime'
|
||||
| 'image'
|
||||
| 'actions'
|
||||
| 'relation';
|
||||
'link' | 'boolean' | 'date' | 'datetime' | 'image' | 'actions' | 'relation';
|
||||
// For relation columns
|
||||
relationField?: string;
|
||||
}
|
||||
|
||||
@ -6,22 +6,13 @@
|
||||
|
||||
// Asset variant types matching backend model
|
||||
export type AssetVariantType =
|
||||
| 'thumbnail'
|
||||
| 'preview'
|
||||
| 'webp'
|
||||
| 'mp4_low'
|
||||
| 'mp4_high'
|
||||
| 'original';
|
||||
'thumbnail' | 'preview' | 'webp' | 'mp4_low' | 'mp4_high' | 'original';
|
||||
|
||||
export type AssetType = 'image' | 'video' | 'audio' | 'transition' | 'other';
|
||||
|
||||
// Preload job status
|
||||
export type PreloadJobStatus =
|
||||
| 'queued'
|
||||
| 'downloading'
|
||||
| 'completed'
|
||||
| 'error'
|
||||
| 'paused';
|
||||
'queued' | 'downloading' | 'completed' | 'error' | 'paused';
|
||||
|
||||
// Download job for tracking individual asset downloads
|
||||
export interface PreloadJob {
|
||||
@ -44,11 +35,7 @@ export interface PreloadJob {
|
||||
|
||||
// Project offline status
|
||||
export type ProjectOfflineStatus =
|
||||
| 'not_downloaded'
|
||||
| 'downloading'
|
||||
| 'downloaded'
|
||||
| 'outdated'
|
||||
| 'error';
|
||||
'not_downloaded' | 'downloading' | 'downloaded' | 'outdated' | 'error';
|
||||
|
||||
// Offline project metadata
|
||||
export interface OfflineProject {
|
||||
|
||||
@ -92,11 +92,7 @@ export interface NavigableElement {
|
||||
* Transition phase from useTransitionPlayback
|
||||
*/
|
||||
export type TransitionPhase =
|
||||
| 'idle'
|
||||
| 'preparing'
|
||||
| 'playing'
|
||||
| 'finishing'
|
||||
| 'completed';
|
||||
'idle' | 'preparing' | 'playing' | 'finishing' | 'completed';
|
||||
|
||||
/**
|
||||
* Background transition options
|
||||
|
||||
@ -8,13 +8,7 @@
|
||||
* Color keys for general UI elements
|
||||
*/
|
||||
export type ColorKey =
|
||||
| 'white'
|
||||
| 'light'
|
||||
| 'contrast'
|
||||
| 'success'
|
||||
| 'danger'
|
||||
| 'warning'
|
||||
| 'info';
|
||||
'white' | 'light' | 'contrast' | 'success' | 'danger' | 'warning' | 'info';
|
||||
|
||||
/**
|
||||
* Color keys for buttons
|
||||
|
||||
@ -2,11 +2,7 @@ import type { BaseEntity } from './entities';
|
||||
|
||||
export type SystemUiControlType = 'fullscreen' | 'sound' | 'offline';
|
||||
export type SystemUiControlAnchor =
|
||||
| 'center'
|
||||
| 'top-left'
|
||||
| 'top-right'
|
||||
| 'bottom-left'
|
||||
| 'bottom-right';
|
||||
'center' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
||||
|
||||
export interface SystemUiControlSettings {
|
||||
enabled?: boolean;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user