diff --git a/package-lock.json b/package-lock.json index d13f698..55a5fa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,8 +26,10 @@ "redis": "^4.6.7", "sharp": "^0.32.3", "socket.io": "^4.7.2", + "swagger-ui-express": "^5.0.0", "validator": "^13.9.0", - "winston": "^3.10.0" + "winston": "^3.10.0", + "yaml": "^2.3.2" }, "devDependencies": { "@commitlint/cli": "^17.7.2", @@ -45,6 +47,7 @@ "@types/multer-s3": "^3.0.0", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", + "@types/swagger-ui-express": "^4.1.4", "@types/validator": "^13.7.17", "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", @@ -3414,6 +3417,16 @@ "@types/superagent": "*" } }, + "node_modules/@types/swagger-ui-express": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.4.tgz", + "integrity": "sha512-h6dfIPFveCJKpStDtjrB+4pig4DAf9Uu2Z51RB7Fj3s6AifexmqhZxBoG50K/k3Afz7wyXsIAY5ZIDTlC2VjrQ==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/serve-static": "*" + } + }, "node_modules/@types/triple-beam": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.3.tgz", @@ -11671,6 +11684,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.0.tgz", + "integrity": "sha512-NUHSYoe5XRTk/Are8jPJ6phzBh3l9l33nEyXosM17QInoV95/jng8+PuSGtbD407QoPf93MH3Bkh773OgesJpA==" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, "node_modules/synckit": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", @@ -12887,6 +12919,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", + "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", + "engines": { + "node": ">= 14" + } + }, "node_modules/yamljs": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", diff --git a/package.json b/package.json index 272dc9d..9c0ecd4 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@types/multer-s3": "^3.0.0", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", + "@types/swagger-ui-express": "^4.1.4", "@types/validator": "^13.7.17", "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", @@ -85,7 +86,9 @@ "redis": "^4.6.7", "sharp": "^0.32.3", "socket.io": "^4.7.2", + "swagger-ui-express": "^5.0.0", "validator": "^13.9.0", - "winston": "^3.10.0" + "winston": "^3.10.0", + "yaml": "^2.3.2" } } diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000..f2ce718 Binary files /dev/null and b/public/favicon.png differ diff --git a/public/swagger-ui.css b/public/swagger-ui.css new file mode 100644 index 0000000..01f6b95 --- /dev/null +++ b/public/swagger-ui.css @@ -0,0 +1,1826 @@ +/* Thanks ostranme for creating this amazing theme */ +/* https://github.com/ostranme/swagger-ui-themes */ + +@charset "UTF-8"; +.swagger-ui html { + box-sizing: border-box; +} + +.swagger-ui *, +.swagger-ui :after, +.swagger-ui :before { + box-sizing: inherit; +} + +.swagger-ui .topbar { + display: none; +} + +.swagger-ui body { + margin: 0; + background: #fafafa; +} + +.swagger-ui .wrapper { + width: 100%; + max-width: 1460px; + margin: 0 auto; + padding: 0 20px; +} + +.swagger-ui .opblock-tag-section { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; +} + +.swagger-ui .opblock-tag { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 10px 20px 10px 10px; + cursor: pointer; + -webkit-transition: all 0.2s; + transition: all 0.2s; + border-bottom: 1px solid rgba(59, 65, 81, 0.3); + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.swagger-ui .opblock-tag:hover { + background: rgba(0, 0, 0, 0.02); +} + +.swagger-ui .opblock-tag { + font-size: 24px; + margin: 0 0 5px; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .opblock-tag.no-desc span { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.swagger-ui .opblock-tag svg { + -webkit-transition: all 0.4s; + transition: all 0.4s; +} + +.swagger-ui .opblock-tag small { + font-size: 14px; + font-weight: 400; + padding: 0 10px; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .parŠ°meter__type { + font-size: 12px; + padding: 5px 0; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; +} + +.swagger-ui .view-line-link { + position: relative; + top: 3px; + width: 20px; + margin: 0 5px; + cursor: pointer; + -webkit-transition: all 0.5s; + transition: all 0.5s; +} + +.swagger-ui .opblock { + margin: 0 0 15px; + border: 1px solid #000; + border-radius: 4px; + box-shadow: 0 0 3px rgba(0, 0, 0, 0.19); +} + +.swagger-ui .opblock.is-open .opblock-summary { + border-bottom: 1px solid #000; +} + +.swagger-ui .opblock .opblock-section-header { + padding: 8px 20px; + background: hsla(0, 0%, 100%, 0.8); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.swagger-ui .opblock .opblock-section-header, +.swagger-ui .opblock .opblock-section-header label { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.swagger-ui .opblock .opblock-section-header label { + font-size: 12px; + font-weight: 700; + margin: 0; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .opblock .opblock-section-header label span { + padding: 0 10px 0 0; +} + +.swagger-ui .opblock .opblock-section-header h4 { + font-size: 14px; + margin: 0; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .opblock .opblock-summary-method { + font-size: 14px; + font-weight: 700; + min-width: 80px; + padding: 6px 15px; + text-align: center; + border-radius: 3px; + background: #000; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); + font-family: + Titillium Web, + sans-serif; + color: #fff; +} + +.swagger-ui .opblock .opblock-summary-path, +.swagger-ui .opblock .opblock-summary-path__deprecated { + font-size: 16px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 0 10px; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.swagger-ui .opblock .opblock-summary-path .view-line-link, +.swagger-ui .opblock .opblock-summary-path__deprecated .view-line-link { + position: relative; + top: 2px; + width: 0; + margin: 0; + cursor: pointer; + -webkit-transition: all 0.5s; + transition: all 0.5s; +} + +.swagger-ui .opblock .opblock-summary-path:hover .view-line-link, +.swagger-ui .opblock .opblock-summary-path__deprecated:hover .view-line-link { + width: 18px; + margin: 0 5px; +} + +.swagger-ui .opblock .opblock-summary-path__deprecated { + text-decoration: line-through; +} + +.swagger-ui .opblock .opblock-summary-description { + font-size: 13px; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .opblock .opblock-summary { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 5px; + cursor: pointer; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.swagger-ui .opblock.opblock-post { + border-color: #49cc90; + background: rgba(73, 204, 144, 0.1); +} + +.swagger-ui .opblock.opblock-post .opblock-summary-method { + background: #49cc90; +} + +.swagger-ui .opblock.opblock-post .opblock-summary { + border-color: #49cc90; +} + +.swagger-ui .opblock.opblock-put { + border-color: #fca130; + background: rgba(252, 161, 48, 0.1); +} + +.swagger-ui .opblock.opblock-put .opblock-summary-method { + background: #fca130; +} + +.swagger-ui .opblock.opblock-put .opblock-summary { + border-color: #fca130; +} + +.swagger-ui .opblock.opblock-delete { + border-color: #f93e3e; + background: rgba(249, 62, 62, 0.1); +} + +.swagger-ui .opblock.opblock-delete .opblock-summary-method { + background: #f93e3e; +} + +.swagger-ui .opblock.opblock-delete .opblock-summary { + border-color: #f93e3e; +} + +.swagger-ui .opblock.opblock-get { + border-color: #61affe; + background: rgba(97, 175, 254, 0.1); +} + +.swagger-ui .opblock.opblock-get .opblock-summary-method { + background: #61affe; +} + +.swagger-ui .opblock.opblock-get .opblock-summary { + border-color: #61affe; +} + +.swagger-ui .opblock.opblock-patch { + border-color: #50e3c2; + background: rgba(80, 227, 194, 0.1); +} + +.swagger-ui .opblock.opblock-patch .opblock-summary-method { + background: #50e3c2; +} + +.swagger-ui .opblock.opblock-patch .opblock-summary { + border-color: #50e3c2; +} + +.swagger-ui .opblock.opblock-head { + border-color: #9012fe; + background: rgba(144, 18, 254, 0.1); +} + +.swagger-ui .opblock.opblock-head .opblock-summary-method { + background: #9012fe; +} + +.swagger-ui .opblock.opblock-head .opblock-summary { + border-color: #9012fe; +} + +.swagger-ui .opblock.opblock-options { + border-color: #0d5aa7; + background: rgba(13, 90, 167, 0.1); +} + +.swagger-ui .opblock.opblock-options .opblock-summary-method { + background: #0d5aa7; +} + +.swagger-ui .opblock.opblock-options .opblock-summary { + border-color: #0d5aa7; +} + +.swagger-ui .opblock.opblock-deprecated { + opacity: 0.6; + border-color: #ebebeb; + background: hsla(0, 0%, 92%, 0.1); +} + +.swagger-ui .opblock.opblock-deprecated .opblock-summary-method { + background: #ebebeb; +} + +.swagger-ui .opblock.opblock-deprecated .opblock-summary { + border-color: #ebebeb; +} + +.swagger-ui .tab { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + margin: 20px 0 10px; + padding: 0; + list-style: none; +} + +.swagger-ui .tab li { + font-size: 12px; + min-width: 100px; + min-width: 90px; + padding: 0; + cursor: pointer; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .tab li:first-of-type { + position: relative; + padding-left: 0; +} + +.swagger-ui .tab li:first-of-type:after { + position: absolute; + top: 0; + right: 6px; + width: 1px; + height: 100%; + content: ''; + background: rgba(0, 0, 0, 0.2); +} + +.swagger-ui .tab li.active { + font-weight: 700; +} + +.swagger-ui .opblock-description-wrapper, +.swagger-ui .opblock-title_normal { + padding: 15px 20px; +} + +.swagger-ui .opblock-description-wrapper, +.swagger-ui .opblock-description-wrapper h4, +.swagger-ui .opblock-title_normal, +.swagger-ui .opblock-title_normal h4 { + font-size: 12px; + margin: 0 0 5px; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .opblock-description-wrapper p, +.swagger-ui .opblock-title_normal p { + font-size: 14px; + margin: 0; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .execute-wrapper { + padding: 20px; + text-align: right; +} + +.swagger-ui .execute-wrapper .btn { + width: 100%; + padding: 8px 40px; +} + +.swagger-ui .body-param-options { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; +} + +.swagger-ui .body-param-options .body-param-edit { + padding: 10px 0; +} + +.swagger-ui .body-param-options label { + padding: 8px 0; +} + +.swagger-ui .body-param-options label select { + margin: 3px 0 0; +} + +.swagger-ui .responses-inner { + padding: 20px; +} + +.swagger-ui .responses-inner h4, +.swagger-ui .responses-inner h5 { + font-size: 12px; + margin: 10px 0 5px; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .response-col_status { + font-size: 14px; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .response-col_status .response-undocumented { + font-size: 11px; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #999; +} + +.swagger-ui .response-col_description__inner span { + font-size: 12px; + font-style: italic; + display: block; + margin: 10px 0; + padding: 10px; + border-radius: 4px; + background: #41444e; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #fff; +} + +.swagger-ui .response-col_description__inner span p { + margin: 0; +} + +.swagger-ui .opblock-body pre { + font-size: 12px; + margin: 0; + padding: 10px; + white-space: pre-wrap; + border-radius: 4px; + background: #41444e; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #fff; +} + +.swagger-ui .opblock-body pre span { + color: #fff !important; +} + +.swagger-ui .scheme-container { + margin: 0 0 20px; + padding: 30px 0; + background: #fff; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15); +} + +.swagger-ui .scheme-container .schemes { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.swagger-ui .scheme-container .schemes > label { + font-size: 12px; + font-weight: 700; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + margin: -20px 15px 0 0; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .scheme-container .schemes > label select { + min-width: 130px; + text-transform: uppercase; +} + +.swagger-ui .loading-container { + padding: 40px 0 60px; +} + +.swagger-ui .loading-container .loading { + position: relative; +} + +.swagger-ui .loading-container .loading:after { + font-size: 10px; + font-weight: 700; + position: absolute; + top: 50%; + left: 50%; + content: 'loading'; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + text-transform: uppercase; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .loading-container .loading:before { + position: absolute; + top: 50%; + left: 50%; + display: block; + width: 60px; + height: 60px; + margin: -30px; + content: ''; + -webkit-animation: + rotation 1s infinite linear, + opacity 0.5s; + animation: + rotation 1s infinite linear, + opacity 0.5s; + opacity: 1; + border: 2px solid rgba(85, 85, 85, 0.1); + border-top-color: rgba(0, 0, 0, 0.6); + border-radius: 100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} + +@-webkit-keyframes rotation { + to { + -webkit-transform: rotate(1turn); + transform: rotate(1turn); + } +} + +@keyframes rotation { + to { + -webkit-transform: rotate(1turn); + transform: rotate(1turn); + } +} + +@-webkit-keyframes blinker { + 50% { + opacity: 0; + } +} + +@keyframes blinker { + 50% { + opacity: 0; + } +} + +.swagger-ui .btn { + font-size: 14px; + font-weight: 700; + padding: 5px 23px; + -webkit-transition: all 0.3s; + transition: all 0.3s; + border: 2px solid #888; + border-radius: 4px; + background: transparent; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .btn[disabled] { + cursor: not-allowed; + opacity: 0.3; +} + +.swagger-ui .btn:hover { + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); +} + +.swagger-ui .btn.cancel { + border-color: #ff6060; + font-family: + Titillium Web, + sans-serif; + color: #ff6060; +} + +.swagger-ui .btn.authorize { + line-height: 1; + display: inline; + color: #49cc90; + border-color: #49cc90; +} + +.swagger-ui .btn.authorize span { + float: left; + padding: 4px 20px 0 0; +} + +.swagger-ui .btn.authorize svg { + fill: #49cc90; +} + +.swagger-ui .btn.execute { + -webkit-animation: pulse 2s infinite; + animation: pulse 2s infinite; + color: #fff; + border-color: #4990e2; +} + +@-webkit-keyframes pulse { + 0% { + color: #fff; + background: #4990e2; + box-shadow: 0 0 0 0 rgba(73, 144, 226, 0.8); + } + 70% { + box-shadow: 0 0 0 5px rgba(73, 144, 226, 0); + } + to { + color: #fff; + background: #4990e2; + box-shadow: 0 0 0 0 rgba(73, 144, 226, 0); + } +} + +@keyframes pulse { + 0% { + color: #fff; + background: #4990e2; + box-shadow: 0 0 0 0 rgba(73, 144, 226, 0.8); + } + 70% { + box-shadow: 0 0 0 5px rgba(73, 144, 226, 0); + } + to { + color: #fff; + background: #4990e2; + box-shadow: 0 0 0 0 rgba(73, 144, 226, 0); + } +} + +.swagger-ui .btn-group { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 30px; +} + +.swagger-ui .btn-group .btn { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.swagger-ui .btn-group .btn:first-child { + border-radius: 4px 0 0 4px; +} + +.swagger-ui .btn-group .btn:last-child { + border-radius: 0 4px 4px 0; +} + +.swagger-ui .authorization__btn { + padding: 0 10px; + border: none; + background: none; +} + +.swagger-ui .authorization__btn.locked { + opacity: 1; +} + +.swagger-ui .authorization__btn.unlocked { + opacity: 0.4; +} + +.swagger-ui .expand-methods, +.swagger-ui .expand-operation { + border: none; + background: none; +} + +.swagger-ui .expand-methods svg, +.swagger-ui .expand-operation svg { + width: 20px; + height: 20px; +} + +.swagger-ui .expand-methods { + padding: 0 10px; +} + +.swagger-ui .expand-methods:hover svg { + fill: #444; +} + +.swagger-ui .expand-methods svg { + -webkit-transition: all 0.3s; + transition: all 0.3s; + fill: #777; +} + +.swagger-ui button { + cursor: pointer; + outline: none; +} + +.swagger-ui select { + font-size: 14px; + font-weight: 700; + padding: 5px 40px 5px 10px; + border: 2px solid #41444e; + border-radius: 4px; + background: #f7f7f7 + url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCI+ICAgIDxwYXRoIGQ9Ik0xMy40MTggNy44NTljLjI3MS0uMjY4LjcwOS0uMjY4Ljk3OCAwIC4yNy4yNjguMjcyLjcwMSAwIC45NjlsLTMuOTA4IDMuODNjLS4yNy4yNjgtLjcwNy4yNjgtLjk3OSAwbC0zLjkwOC0zLjgzYy0uMjctLjI2Ny0uMjctLjcwMSAwLS45NjkuMjcxLS4yNjguNzA5LS4yNjguOTc4IDBMMTAgMTFsMy40MTgtMy4xNDF6Ii8+PC9zdmc+) + right 10px center no-repeat; + background-size: 20px; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25); + font-family: + Titillium Web, + sans-serif; + color: #3b4151; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.swagger-ui select[multiple] { + margin: 5px 0; + padding: 5px; + background: #f7f7f7; +} + +.swagger-ui .opblock-body select { + min-width: 230px; +} + +.swagger-ui label { + font-size: 12px; + font-weight: 700; + margin: 0 0 5px; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui input[type='email'], +.swagger-ui input[type='password'], +.swagger-ui input[type='search'], +.swagger-ui input[type='text'] { + min-width: 100px; + margin: 5px 0; + padding: 8px 10px; + border: 1px solid #d9d9d9; + border-radius: 4px; + background: #fff; +} + +.swagger-ui input[type='email'].invalid, +.swagger-ui input[type='password'].invalid, +.swagger-ui input[type='search'].invalid, +.swagger-ui input[type='text'].invalid { + -webkit-animation: shake 0.4s 1; + animation: shake 0.4s 1; + border-color: #f93e3e; + background: #feebeb; +} + +@-webkit-keyframes shake { + 10%, + 90% { + -webkit-transform: translate3d(-1px, 0, 0); + transform: translate3d(-1px, 0, 0); + } + 20%, + 80% { + -webkit-transform: translate3d(2px, 0, 0); + transform: translate3d(2px, 0, 0); + } + 30%, + 50%, + 70% { + -webkit-transform: translate3d(-4px, 0, 0); + transform: translate3d(-4px, 0, 0); + } + 40%, + 60% { + -webkit-transform: translate3d(4px, 0, 0); + transform: translate3d(4px, 0, 0); + } +} + +@keyframes shake { + 10%, + 90% { + -webkit-transform: translate3d(-1px, 0, 0); + transform: translate3d(-1px, 0, 0); + } + 20%, + 80% { + -webkit-transform: translate3d(2px, 0, 0); + transform: translate3d(2px, 0, 0); + } + 30%, + 50%, + 70% { + -webkit-transform: translate3d(-4px, 0, 0); + transform: translate3d(-4px, 0, 0); + } + 40%, + 60% { + -webkit-transform: translate3d(4px, 0, 0); + transform: translate3d(4px, 0, 0); + } +} + +.swagger-ui textarea { + font-size: 12px; + width: 100%; + min-height: 280px; + padding: 10px; + border: none; + border-radius: 4px; + outline: none; + background: hsla(0, 0%, 100%, 0.8); + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; +} + +.swagger-ui textarea:focus { + border: 2px solid #61affe; +} + +.swagger-ui textarea.curl { + font-size: 12px; + min-height: 100px; + margin: 0; + padding: 10px; + resize: none; + border-radius: 4px; + background: #41444e; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #fff; +} + +.swagger-ui .checkbox { + padding: 5px 0 10px; + -webkit-transition: opacity 0.5s; + transition: opacity 0.5s; + color: #333; +} + +.swagger-ui .checkbox label { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +.swagger-ui .checkbox p { + font-weight: 400 !important; + font-style: italic; + margin: 0 !important; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; +} + +.swagger-ui .checkbox input[type='checkbox'] { + display: none; +} + +.swagger-ui .checkbox input[type='checkbox'] + label > .item { + position: relative; + top: 3px; + display: inline-block; + width: 16px; + height: 16px; + margin: 0 8px 0 0; + padding: 5px; + cursor: pointer; + border-radius: 1px; + background: #e8e8e8; + box-shadow: 0 0 0 2px #e8e8e8; + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; +} + +.swagger-ui .checkbox input[type='checkbox'] + label > .item:active { + -webkit-transform: scale(0.9); + transform: scale(0.9); +} + +.swagger-ui .checkbox input[type='checkbox']:checked + label > .item { + background: #e8e8e8 + url("data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='8' viewBox='3 7 10 8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%2341474E' fill-rule='evenodd' d='M6.333 15L3 11.667l1.333-1.334 2 2L11.667 7 13 8.333z'/%3E%3C/svg%3E") + 50% no-repeat; +} + +.swagger-ui .dialog-ux { + position: fixed; + z-index: 9999; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.swagger-ui .dialog-ux .backdrop-ux { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0, 0, 0, 0.8); +} + +.swagger-ui .dialog-ux .modal-ux { + position: absolute; + z-index: 9999; + top: 50%; + left: 50%; + width: 100%; + min-width: 300px; + max-width: 650px; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + border: 1px solid #ebebeb; + border-radius: 4px; + background: #fff; + box-shadow: 0 10px 30px 0 rgba(0, 0, 0, 0.2); +} + +.swagger-ui .dialog-ux .modal-ux-content { + overflow-y: auto; + max-height: 540px; + padding: 20px; +} + +.swagger-ui .dialog-ux .modal-ux-content p { + font-size: 12px; + margin: 0 0 5px; + color: #41444e; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .dialog-ux .modal-ux-content h4 { + font-size: 18px; + font-weight: 600; + margin: 15px 0 0; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .dialog-ux .modal-ux-header { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 12px 0; + border-bottom: 1px solid #ebebeb; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.swagger-ui .dialog-ux .modal-ux-header .close-modal { + padding: 0 10px; + border: none; + background: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.swagger-ui .dialog-ux .modal-ux-header h3 { + font-size: 20px; + font-weight: 600; + margin: 0; + padding: 0 20px; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .model { + font-size: 12px; + font-weight: 300; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; +} + +.swagger-ui .model-toggle { + font-size: 10px; + position: relative; + top: 6px; + display: inline-block; + margin: auto 0.3em; + cursor: pointer; + -webkit-transition: -webkit-transform 0.15s ease-in; + transition: -webkit-transform 0.15s ease-in; + transition: transform 0.15s ease-in; + transition: + transform 0.15s ease-in, + -webkit-transform 0.15s ease-in; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-transform-origin: 50% 50%; + transform-origin: 50% 50%; +} + +.swagger-ui .model-toggle.collapsed { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); +} + +.swagger-ui .model-toggle:after { + display: block; + width: 20px; + height: 20px; + content: ''; + background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z'/%3E%3C/svg%3E") + 50% no-repeat; + background-size: 100%; +} + +.swagger-ui .model-jump-to-path { + position: relative; + cursor: pointer; +} + +.swagger-ui .model-jump-to-path .view-line-link { + position: absolute; + top: -0.4em; + cursor: pointer; +} + +.swagger-ui .model-title { + position: relative; +} + +.swagger-ui .model-title:hover .model-hint { + visibility: visible; +} + +.swagger-ui .model-hint { + position: absolute; + top: -1.8em; + visibility: hidden; + padding: 0.1em 0.5em; + white-space: nowrap; + color: #ebebeb; + border-radius: 4px; + background: rgba(0, 0, 0, 0.7); +} + +.swagger-ui section.models { + margin: 30px 0; + border: 1px solid rgba(59, 65, 81, 0.3); + border-radius: 4px; +} + +.swagger-ui section.models.is-open { + padding: 0 0 20px; +} + +.swagger-ui section.models.is-open h4 { + margin: 0 0 5px; + border-bottom: 1px solid rgba(59, 65, 81, 0.3); +} + +.swagger-ui section.models.is-open h4 svg { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); +} + +.swagger-ui section.models h4 { + font-size: 16px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + margin: 0; + padding: 10px 20px 10px 10px; + cursor: pointer; + -webkit-transition: all 0.2s; + transition: all 0.2s; + font-family: + Titillium Web, + sans-serif; + color: #777; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.swagger-ui section.models h4 svg { + -webkit-transition: all 0.4s; + transition: all 0.4s; +} + +.swagger-ui section.models h4 span { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.swagger-ui section.models h4:hover { + background: rgba(0, 0, 0, 0.02); +} + +.swagger-ui section.models h5 { + font-size: 16px; + margin: 0 0 10px; + font-family: + Titillium Web, + sans-serif; + color: #777; +} + +.swagger-ui section.models .model-jump-to-path { + position: relative; + top: 5px; +} + +.swagger-ui section.models .model-container { + margin: 0 20px 15px; + -webkit-transition: all 0.5s; + transition: all 0.5s; + border-radius: 4px; + background: rgba(0, 0, 0, 0.05); +} + +.swagger-ui section.models .model-container:hover { + background: rgba(0, 0, 0, 0.07); +} + +.swagger-ui section.models .model-container:first-of-type { + margin: 20px; +} + +.swagger-ui section.models .model-container:last-of-type { + margin: 0 20px; +} + +.swagger-ui section.models .model-box { + background: none; +} + +.swagger-ui .model-box { + padding: 10px; + border-radius: 4px; + background: rgba(0, 0, 0, 0.1); +} + +.swagger-ui .model-box .model-jump-to-path { + position: relative; + top: 4px; +} + +.swagger-ui .model-title { + font-size: 16px; + font-family: + Titillium Web, + sans-serif; + color: #555; +} + +.swagger-ui span > span.model, +.swagger-ui span > span.model .brace-close { + padding: 0 0 0 10px; +} + +.swagger-ui .prop-type { + color: #55a; +} + +.swagger-ui .prop-enum { + display: block; +} + +.swagger-ui .prop-format { + color: #999; +} + +.swagger-ui table { + width: 100%; + padding: 0 10px; + border-collapse: collapse; +} + +.swagger-ui table.model tbody tr td { + padding: 0; + vertical-align: top; +} + +.swagger-ui table.model tbody tr td:first-of-type { + width: 100px; + padding: 0; +} + +.swagger-ui table.headers td { + font-size: 12px; + font-weight: 300; + vertical-align: middle; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; +} + +.swagger-ui table tbody tr td { + padding: 10px 0 0; + vertical-align: top; +} + +.swagger-ui table tbody tr td:first-of-type { + width: 20%; + padding: 10px 0; +} + +.swagger-ui table thead tr td, +.swagger-ui table thead tr th { + font-size: 12px; + font-weight: 700; + padding: 12px 0; + text-align: left; + border-bottom: 1px solid rgba(59, 65, 81, 0.2); + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .parameters-col_description p { + font-size: 14px; + margin: 0; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .parameters-col_description input[type='text'] { + width: 100%; + max-width: 340px; +} + +.swagger-ui .parameter__name { + font-size: 16px; + font-weight: 400; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .parameter__name.required { + font-weight: 700; +} + +.swagger-ui .parameter__name.required:after { + font-size: 10px; + position: relative; + top: -6px; + padding: 5px; + content: 'required'; + color: rgba(255, 0, 0, 0.6); +} + +.swagger-ui .parameter__in { + font-size: 12px; + font-style: italic; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #888; +} + +.swagger-ui .table-container { + padding: 20px; +} + +.swagger-ui .topbar { + padding: 8px 30px; + background-color: #89bf04; +} + +.swagger-ui .topbar .topbar-wrapper { + -ms-flex-align: center; +} + +.swagger-ui .topbar .topbar-wrapper, +.swagger-ui .topbar a { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + align-items: center; +} + +.swagger-ui .topbar a { + font-size: 1.5em; + font-weight: 700; + text-decoration: none; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + -ms-flex-align: center; + font-family: + Titillium Web, + sans-serif; + color: #fff; +} + +.swagger-ui .topbar a span { + margin: 0; + padding: 0 10px; +} + +.swagger-ui .topbar .download-url-wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +.swagger-ui .topbar .download-url-wrapper input[type='text'] { + min-width: 350px; + margin: 0; + border: 2px solid #547f00; + border-radius: 4px 0 0 4px; + outline: none; +} + +.swagger-ui .topbar .download-url-wrapper .download-url-button { + font-size: 16px; + font-weight: 700; + padding: 4px 40px; + border: none; + border-radius: 0 4px 4px 0; + background: #547f00; + font-family: + Titillium Web, + sans-serif; + color: #fff; +} + +.swagger-ui .info { + margin: 50px 0; +} + +.swagger-ui .info hgroup.main { + margin: 0 0 20px; +} + +.swagger-ui .info hgroup.main a { + font-size: 12px; +} + +.swagger-ui .info p { + font-size: 14px; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .info code { + padding: 3px 5px; + border-radius: 4px; + background: rgba(0, 0, 0, 0.05); + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #9012fe; +} + +.swagger-ui .info a { + font-size: 14px; + -webkit-transition: all 0.4s; + transition: all 0.4s; + font-family: + Open Sans, + sans-serif; + color: #4990e2; +} + +.swagger-ui .info a:hover { + color: #1f69c0; +} + +.swagger-ui .info > div { + margin: 0 0 5px; +} + +.swagger-ui .info .base-url { + font-size: 12px; + font-weight: 300 !important; + margin: 0; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; +} + +.swagger-ui .info .title { + font-size: 36px; + margin: 0; + font-family: + Open Sans, + sans-serif; + color: #3b4151; +} + +.swagger-ui .info .title small { + font-size: 10px; + position: relative; + top: -5px; + display: inline-block; + margin: 0 0 0 5px; + padding: 2px 4px; + vertical-align: super; + border-radius: 57px; + background: #7d8492; +} + +.swagger-ui .info .title small pre { + margin: 0; + font-family: + Titillium Web, + sans-serif; + color: #fff; +} + +.swagger-ui .auth-btn-wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 10px 0; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.swagger-ui .auth-wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + -webkit-box-pack: end; + -ms-flex-pack: end; + justify-content: flex-end; +} + +.swagger-ui .auth-wrapper .authorize { + padding-right: 20px; +} + +.swagger-ui .auth-container { + margin: 0 0 10px; + padding: 10px 20px; + border-bottom: 1px solid #ebebeb; +} + +.swagger-ui .auth-container:last-of-type { + margin: 0; + padding: 10px 20px; + border: 0; +} + +.swagger-ui .auth-container h4 { + margin: 5px 0 15px !important; +} + +.swagger-ui .auth-container .wrapper { + margin: 0; + padding: 0; +} + +.swagger-ui .auth-container input[type='password'], +.swagger-ui .auth-container input[type='text'] { + min-width: 230px; +} + +.swagger-ui .auth-container .errors { + font-size: 12px; + padding: 10px; + border-radius: 4px; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; +} + +.swagger-ui .scopes h2 { + font-size: 14px; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +.swagger-ui .scope-def { + padding: 0 0 20px; +} + +.swagger-ui .errors-wrapper { + margin: 20px; + padding: 10px 20px; + -webkit-animation: scaleUp 0.5s; + animation: scaleUp 0.5s; + border: 2px solid #f93e3e; + border-radius: 4px; + background: rgba(249, 62, 62, 0.1); +} + +.swagger-ui .errors-wrapper .error-wrapper { + margin: 0 0 10px; +} + +.swagger-ui .errors-wrapper .errors h4 { + font-size: 14px; + margin: 0; + font-family: + Source Code Pro, + monospace; + font-weight: 600; + color: #3b4151; +} + +.swagger-ui .errors-wrapper hgroup { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.swagger-ui .errors-wrapper hgroup h4 { + font-size: 20px; + margin: 0; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + font-family: + Titillium Web, + sans-serif; + color: #3b4151; +} + +@-webkit-keyframes scaleUp { + 0% { + -webkit-transform: scale(0.8); + transform: scale(0.8); + opacity: 0; + } + to { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } +} + +@keyframes scaleUp { + 0% { + -webkit-transform: scale(0.8); + transform: scale(0.8); + opacity: 0; + } + to { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } +} + +.swagger-ui .Resizer.vertical.disabled { + display: none; +} + +/*# sourceMappingURL=swagger-ui.css.map*/ + +/** + * Swagger UI Theme Overrides + * + * Theme: Flattop + * Author: Mark Ostrander + * Github: https://github.com/ostranme/swagger-ui-themes + */ + +.swagger-ui .opblock.opblock-post { + border-color: #dadfe1; + background: rgba(236, 240, 241, 0.1); +} + +.swagger-ui .opblock.opblock-post .opblock-summary-method { + background: #049372; +} + +.swagger-ui .opblock.opblock-post .opblock-summary { + border-color: #dadfe1; +} + +.swagger-ui .opblock.opblock-put { + border-color: #dadfe1; + background: rgba(236, 240, 241, 0.1); +} + +.swagger-ui .opblock.opblock-put .opblock-summary-method { + background: #eb9532; +} + +.swagger-ui .opblock.opblock-put .opblock-summary { + border-color: #dadfe1; +} + +.swagger-ui .opblock.opblock-delete { + border-color: #dadfe1; + background: rgba(236, 240, 241, 0.1); +} + +.swagger-ui .opblock.opblock-delete .opblock-summary-method { + background: #c0392b; +} + +.swagger-ui .opblock.opblock-delete .opblock-summary { + border-color: #dadfe1; +} + +.swagger-ui .opblock.opblock-get { + border-color: #dadfe1; + background: rgba(236, 240, 241, 0.1); +} + +.swagger-ui .opblock.opblock-get .opblock-summary-method { + background: #34495e; +} + +.swagger-ui .opblock.opblock-get .opblock-summary { + border-color: #dadfe1; +} + +.swagger-ui .opblock.opblock-patch { + border-color: #dadfe1; + background: rgba(236, 240, 241, 0.1); +} + +.swagger-ui .opblock.opblock-patch .opblock-summary-method { + background: #f57c00; +} + +.swagger-ui .opblock.opblock-patch .opblock-summary { + border-color: #dadfe1; +} + +.swagger-ui .opblock.opblock-head { + border-color: #dadfe1; + background: rgba(236, 240, 241, 0.1); +} + +.swagger-ui .opblock.opblock-head .opblock-summary-method { + background: #5c6bc0; +} + +.swagger-ui .opblock.opblock-head .opblock-summary { + border-color: #dadfe1; +} + +.swagger-ui .opblock.opblock-options { + border-color: #dadfe1; + background: rgba(236, 240, 241, 0.1); +} + +.swagger-ui .opblock.opblock-options .opblock-summary-method { + background: #3f51b5; +} + +.swagger-ui .opblock.opblock-options .opblock-summary { + border-color: #dadfe1; +} + +.swagger-ui .topbar { + padding: 8px 30px; + background-color: #2c3e50; +} + +.swagger-ui .topbar .download-url-wrapper input[type='text'] { + min-width: 350px; + margin: 0; + border: 2px solid #dadfe1; + border-radius: 4px 0 0 4px; + outline: none; +} + +.swagger-ui .topbar .download-url-wrapper .download-url-button { + font-size: 16px; + font-weight: 700; + padding: 4px 40px; + border: none; + border-radius: 0 4px 4px 0; + background: #dadfe1; + font-family: + Titillium Web, + sans-serif; + color: #2c3e50; +} + +.swagger-ui .info a { + font-size: 14px; + -webkit-transition: all 0.4s; + transition: all 0.4s; + font-family: + Open Sans, + sans-serif; + color: #336e7b; +} + +.swagger-ui .info a:hover { + color: #336e7b; +} + +.swagger-ui .btn.authorize { + line-height: 1; + display: inline; + color: #336e7b; + border-color: #336e7b; +} +.swagger-ui .btn.authorize svg { + fill: #336e7b; +} diff --git a/src/app.ts b/src/app.ts index 04c0b92..8f2ab0c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -6,11 +6,13 @@ import express from 'express' import limiter from 'middlewares/rate-limit' import morganMiddleware from 'middlewares/morgan' import router from './routes' +import swaggerUI from 'swagger-ui-express' +import swaggerDocument from 'helpers/parse-swagger' +import swaggerConfig from 'config/swagger' const app = express() // TODO: test socket io, emit notifications when create one. -// TODO: start to create the client, or a barebone to test socket io app.use(express.json()) app.use(express.urlencoded({ extended: true })) @@ -24,10 +26,20 @@ app.use( optionsSuccessStatus: 200, }), ) +app.use(express.static('public')) app.use(limiter) app.use(router) +app.use( + '/docs', + swaggerUI.serve, + swaggerUI.setup(swaggerDocument, swaggerConfig), +) app.use(compression({ level: 9 })) +app.get('/', function (_req, res) { + res.redirect('/docs') +}) + app.use((_req, res) => { res.status(404).json({ error: 'Endpoint not found', diff --git a/src/config/swagger.ts b/src/config/swagger.ts new file mode 100644 index 0000000..749f089 --- /dev/null +++ b/src/config/swagger.ts @@ -0,0 +1,9 @@ +import type { SwaggerUiOptions } from 'swagger-ui-express' + +const swaggerConfig: SwaggerUiOptions = { + customCssUrl: '/swagger-ui.css', + customSiteTitle: 'Project Knedita Docs', + customfavIcon: '/favicon.png', +} + +export default swaggerConfig diff --git a/src/helpers/parse-swagger.ts b/src/helpers/parse-swagger.ts new file mode 100644 index 0000000..26111e8 --- /dev/null +++ b/src/helpers/parse-swagger.ts @@ -0,0 +1,7 @@ +import { parse } from 'yaml' +import { readFileSync } from 'fs' + +const swaggerConfigFile = readFileSync('./swagger.yaml', 'utf-8') +const swaggerDocument = parse(swaggerConfigFile) + +export default swaggerDocument diff --git a/swagger.yaml b/swagger.yaml new file mode 100644 index 0000000..cf074ce --- /dev/null +++ b/swagger.yaml @@ -0,0 +1,521 @@ +openapi: 3.0.0 + +info: + version: 0.0.1 + title: Project Knedita - Documentation + description: An open source social media project. + license: + name: MIT + url: https://opensource.org/licenses/MIT + +tags: + - name: Users + description: User related endpoints + - name: Posts + description: Post related endpoints + - name: Comments + description: Comment related endpoints + +paths: + # Users - GET paths + /user/info: + get: + summary: Returns information about a specific user. + description: Returns information about a specific user (profile picture, display name, etc). + tags: + - Users + parameters: + - name: u + in: query + required: true + schema: + type: string + example: 'cookie' + responses: + '200': + description: An object containing all user information. + content: + application/json: + schema: + type: object + properties: + id: + type: string + profileImage: + type: string + displayName: + type: string + username: + type: string + createdAt: + type: string + followers: + type: integer + example: 0 + following: + type: integer + example: 0 + posts: + type: array + items: + type: object + properties: + id: + type: string + content: + type: string + createdAt: + type: string + updatedAt: + type: string + likedPosts: + type: array + items: + type: object + properties: + postId: + type: string + '400': + $ref: '#/components/responses/BadRequest' + + /user/fetch-posts: + get: + summary: Returns all posts made by specific user. + description: Returns all posts made by specific user. + tags: + - Users + parameters: + - name: u + in: query + required: true + schema: + type: string + example: 'cookie' + responses: + '200': + description: An array of objects containing post information (returns an empty array if no posts were found). + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: string + content: + type: string + createdAt: + type: string + updatedAt: + type: string + author: + type: object + properties: + displayName: + type: string + username: + type: string + profileImage: + type: string + _count: + type: object + properties: + likes: + type: integer + comments: + type: integer + + /user/search: + get: + summary: Returns user with the provided username. + description: Returns up to 10 users with the provided username. + tags: + - Users + parameters: + - name: u + in: query + required: true + schema: + type: string + example: 'cookie' + responses: + '200': + description: An array of objects containing basic user information (returns an empty array if no users were found). + content: + application/json: + schema: + type: array + items: + type: object + properties: + displayName: + type: string + username: + type: string + profileImage: + type: string + + /user/auth: + post: + summary: Authenticate user + description: Authenticate the provided user + tags: + - Users + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + email: + type: string + password: + type: string + responses: + '200': + description: Returns authentication token and user's username + content: + application/json: + schema: + type: object + properties: + token: + type: string + user: + type: string + example: 'cookie' + '400': + $ref: '#/components/responses/BadRequest' + + /user/signup: + post: + summary: Creates a new user + description: Creates a new user + tags: + - Users + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + email: + type: string + password: + type: string + responses: + '200': + description: An object containing basic user information + content: + application/json: + schema: + type: object + properties: + displayName: + type: string + username: + type: string + createdAt: + type: string + '400': + $ref: '#/components/responses/BadRequest' + + # Users - POST paths + /user/delete: + post: + summary: Deletes an authenticated user + description: Deletes an authenticated user + tags: + - Users + security: + - JWTAuth: [] + responses: + '200': + $ref: '#/components/responses/EmptyResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + + /user/follow-user: + post: + summary: Follows a user + description: Follows a user + security: + - JWTAuth: [] + tags: + - Users + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + userToFollow: + type: string + example: 'cookie1' + responses: + '200': + $ref: '#/components/responses/EmptyResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + + /user/like-comment: + post: + summary: Likes a post comment + description: Likes a post comment + security: + - JWTAuth: [] + tags: + - Users + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + commentId: + type: string + example: '7fb6e6e7-8e37-4814-bab6-36e5aa35796c' + responses: + '200': + $ref: '#/components/responses/EmptyResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + + /user/like-post: + post: + summary: Likes a post + description: Likes a post + security: + - JWTAuth: [] + tags: + - Users + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + postId: + type: string + example: f4e2b724-f226-4674-8e3b-0c0c512b09d4 + responses: + '200': + $ref: '#/components/responses/EmptyResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + + /user/me: + post: + summary: Returns basic information about the authenticated user + description: Returns basic information about the authenticated user + security: + - JWTAuth: [] + tags: + - Users + responses: + '200': + description: Authenticated user information + content: + application/json: + schema: + type: object + properties: + id: + type: string + profileImage: + type: string + displayName: + type: string + username: + type: string + createdAt: + type: string + followers: + type: integer + example: 0 + following: + type: integer + example: 0 + posts: + type: array + items: + type: object + properties: + id: + type: string + content: + type: string + createdAt: + type: string + updatedAt: + type: string + likedPosts: + type: array + items: + type: object + properties: + postId: + type: string + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + + # Users - PUT paths + /user/profile-picture/upload: + put: + summary: Uploads a profile picture for the authenticated user + description: Uploads a profile picture for the authenticated user + security: + - JWTAuth: [] + tags: + - Users + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + image: + type: string + format: binary + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + profileImage: + type: string + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + + /user/update-name: + put: + security: + - JWTAuth: [] + tags: + - Users + + /user/update-email: + put: + security: + - JWTAuth: [] + tags: + - Users + + /user/update-password: + put: + security: + - JWTAuth: [] + tags: + - Users + + # Posts - GET paths + /post/info: + get: + tags: + - Posts + + /post/fetch-likes: + get: + tags: + - Posts + + # Posts - POST paths + /post/create: + post: + tags: + - Posts + + /post/delete: + post: + tags: + - Posts + + # Posts - PUT paths + /post/update: + put: + tags: + - Posts + + # Comments - GET paths + /comment/info: + get: + tags: + - Comments + + /comment/fetch-likes: + get: + tags: + - Comments + + # Comments - POST paths + /comment/create: + post: + tags: + - Comments + + /comment/delete: + post: + tags: + - Comments + + # Comments - PUT paths + /comment/update: + put: + tags: + - Comments + +components: + securitySchemes: + JWTAuth: + type: http + scheme: bearer + description: JWT Authentication via Authorization header + responses: + BadRequest: + description: Bad Request + content: + application/json: + schema: + type: object + properties: + error: + type: string + EmptyResponse: + description: Successful Response (empty response) + content: + application/json: + schema: + type: object + Unauthorized: + description: Unauthorized + content: + application/json: + schema: + type: object + properties: + error: + type: string