From edc9334521d1f536345ce6c2452fa8e11e3bbe36 Mon Sep 17 00:00:00 2001 From: Kyattsukuro Date: Mon, 4 Aug 2025 15:17:06 +0200 Subject: [PATCH] improved ui --- .../__pycache__/db_handler.cpython-312.pyc | Bin 4424 -> 4424 bytes .../__pycache__/auth.cpython-312.pyc | Bin 0 -> 6380 bytes .../__pycache__/messages.cpython-312.pyc | Bin 0 -> 2513 bytes backend_py/{ => endpoints}/auth.py | 0 backend_py/{ => endpoints}/messages.py | 2 +- backend_py/main.py | 4 +- data/db.sqlite | Bin 16384 -> 16384 bytes frontend/package-lock.json | 206 ++++++++++++++++++ frontend/package.json | 1 + frontend/src/App.vue | 3 +- frontend/src/assets/icons/error.svg | 12 + frontend/src/assets/icons/info.svg | 5 + frontend/src/assets/icons/success.svg | 12 + frontend/src/assets/main.css | 35 +++ frontend/src/components/Header.vue | 48 ++++ frontend/src/components/UserInfo.vue | 57 +++++ frontend/src/composable/auth.ts | 94 +++++--- frontend/src/composable/message.ts | 13 +- frontend/src/router/index.ts | 4 +- frontend/src/views/Chat.vue | 105 ++++++--- frontend/src/views/Login.vue | 40 +++- frontend/src/views/User.vue | 58 ++--- frontend/vite.config.ts | 3 +- 23 files changed, 603 insertions(+), 99 deletions(-) create mode 100644 backend_py/endpoints/__pycache__/auth.cpython-312.pyc create mode 100644 backend_py/endpoints/__pycache__/messages.cpython-312.pyc rename backend_py/{ => endpoints}/auth.py (100%) rename backend_py/{ => endpoints}/messages.py (97%) create mode 100644 frontend/src/assets/icons/error.svg create mode 100644 frontend/src/assets/icons/info.svg create mode 100644 frontend/src/assets/icons/success.svg create mode 100644 frontend/src/components/Header.vue create mode 100644 frontend/src/components/UserInfo.vue diff --git a/backend_py/__pycache__/db_handler.cpython-312.pyc b/backend_py/__pycache__/db_handler.cpython-312.pyc index 5f795fe21612d4fa4ed0fb7ac9b59a6de2c8540f..7e95b6c446532020e95d583ce4ffc2f63b27a02f 100644 GIT binary patch delta 20 acmX@1bV7;yG%qg~0}upuP1wk7D+mBQs|7>= delta 20 acmX@1bV7;yG%qg~0}$wJ?%&95D+mBQnFU7x diff --git a/backend_py/endpoints/__pycache__/auth.cpython-312.pyc b/backend_py/endpoints/__pycache__/auth.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e8db6fcdf1eb7cf5ef640716f5545df9da630fb GIT binary patch literal 6380 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 S=oI>JoEKToBN<-#C;vaJrb}-C literal 0 HcmV?d00001 diff --git a/backend_py/endpoints/__pycache__/messages.cpython-312.pyc b/backend_py/endpoints/__pycache__/messages.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50a0ebb98c478d7bf7ccd9b5238cb40a58f54fce GIT binary patch literal 2513 zcma)7O>7fK6rS1H^?&UAK}f*VAtYfDA_!5nM1Vvog;okwN+@7aZH;Ge?0CKI?3y&l zky3$F+D0W3hq_Xwx>7GydZ1jI-k>L{9-1^rNk*U|^?d-M!U%ndD55xvOdQ9i5?4$TCs_KFtZHg; z&Gf`Qu1&ID(--%d{?&K0pfAkZ6ecXoHIozM|H=wK?AMpNNtFf!O#jh^=Ey zdh;WB5S<9m8@Ns76t(?ttMQF5O=53*6tpv5fR!`ZC3-184?L>;{ zSOf~-Fj9pCr(kX5EJ2MCHf9?axMdo3NOLW~79P%=0?#EOHFb%AM&P&d0ERD-Q(r5h zH_;y$7M%wF0t|R9xIGfU6W0s<^2n zBd;OEo59Ae)=*TQ|7KCCee=39xmAU8jVqv?1dWU<<9NDy`axL1?&|28=B|z+T|FU` zH06>90vo!@C0~U%#hirWaN#oynoc@~Ayj#fI^iL?PE0!`i1f(+AB)AF9_kymOx9<^ zVC7if5sQM6HuG7Q98NfW=M%{hmZL^~tPiHVmCiYKUo8#0^JAh35|>V7)0dc0b@MiW zmUU%+0mB5k7i@g&L9rdHUec_)T8XTBQAGM6-2b1PnH#l_hf~D+ujIA`h3^noq?Y^4|g};-lTzgyIYQzynP3_WtoSB z`tHfWA)ukhWX`Zfg(}G@RJoX})*YZAq`}W6+)YrPCmh94QA#a^k`#7?5~m(()HSM7 zEsY0}Q|mS;9?_x4}g=fi^&$Lx58%r3JjOp-_Bix>Zh?@ccnL$PjJYs!?+$;qk;y{0Ur|tI+GyK zK1ATXM7F?=N{+eA;W3HU34_=On`&*O*871kFEd4#A(Hj$SQ1=&7hL}EK3JFJvgKX6 zG-+tPHt^}dLMS>Hik4DkXFjxR;^>k$R8dra|Gn0>l6L)_t4CLTsIhG!(lr<9T8Q+| zMfzv<-P$l8IW}=za^%c>uxD27`Fqum8atMe;_qMF+BtcA^5kOc=Go}$vn~7o2<@+E z;D2h%EPWR|+<|U)Y&!fZxxEj=ctYPGJm=lll1nnV*R)ncYR0y5ZYSrG6e0j+G@QuM zEL5b-8~00ZxZa6(CkoCmm-!P~!AWOrw;lyahc{hEn3L3L%bh+l7zN=;VJ}U{1EM$L%aS&(T8aJebjv)?O5`JE)P$TimKwB zWwm_z+lC4P^+wlnWoeCri4z7F zClf>5O-JLx;>64-JAZ(y_gbvcyvuv{{O0CejN2>^QWn( z6=8Hef7KY`L70Z?4WVyKR)@CZy#FeS>3h_)n-Ko|*zLa(Mcy;H= zuC$A@^%-XG%dNZhQk_rT!#f_mMYEFWDkMxwyKOX=Y~>f@6SwNqb@~9O<>Jcns+e2Y z@RKZ-tD9TT9?78tS0fJk&>K((?+9XfA-FdlPDxvBvQ z9)=cKH~`4Km70M=8Hd9St|(`mdF8-65Tp|L0bQFLL6VvxKm_0Qqy!rdP;nW^LGt8h zFD!{1gI&)pC~ckb3~(O}<&HS66?h?tq`J+VfVky}(r-#0Hv$@v1nV-m((MRA%avd; z;UwN6aN~oic3Q5$U~F(36+;uEhmAcgd>r&J_z777two?*h(8^LL>{WX#)$)!ID(j% zu<)i)f~9?mvBS`r<)H;lDM1>5)W@8WKpTQ7h>hk9RZ?WqRZyi~#3V@$x?1Iw6JQvg zNHUdLBtA$|5f6pf%VxbSjr_gc{OFbL`BY)FbGgeXgoaIR1nZ!V-`_}w7c%r8s55{2 F;TOljDy#qi delta 96 zcmZo@U~Fh$oFL68I#I@%QFLR%5`GRQ{!|A3&HSmG1r;LsC#TBK2a3FA;Q!A58Yps% ff8qqm$*=V#Wf%k)82BWx!_5i?i+DFquww!M(3lq} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e1d093d..b4c2253 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -35,6 +35,7 @@ "typescript": "~5.8.0", "vite": "^6.2.4", "vite-plugin-vue-devtools": "^7.7.2", + "vite-svg-loader": "^5.1.0", "vitest": "^3.1.1", "vue-tsc": "^2.2.8" } @@ -2162,6 +2163,16 @@ "vite": "^5.2.0 || ^6" } }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@tsconfig/node22": { "version": "22.0.2", "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.2.tgz", @@ -3415,6 +3426,50 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3428,6 +3483,42 @@ "node": ">=4" } }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/cssstyle": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.4.0.tgz", @@ -3563,6 +3654,65 @@ "node": ">=8" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -5169,6 +5319,13 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -6297,6 +6454,42 @@ "node": ">=8" } }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -6842,6 +7035,19 @@ "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0" } }, + "node_modules/vite-svg-loader": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.0.tgz", + "integrity": "sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "svgo": "^3.0.2" + }, + "peerDependencies": { + "vue": ">=3.2.13" + } + }, "node_modules/vite/node_modules/fdir": { "version": "6.4.6", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", diff --git a/frontend/package.json b/frontend/package.json index 37135d5..c5e690a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -43,6 +43,7 @@ "typescript": "~5.8.0", "vite": "^6.2.4", "vite-plugin-vue-devtools": "^7.7.2", + "vite-svg-loader": "^5.1.0", "vitest": "^3.1.1", "vue-tsc": "^2.2.8" } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b07d388..dcdc4c2 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,8 +1,9 @@ diff --git a/frontend/src/assets/icons/error.svg b/frontend/src/assets/icons/error.svg new file mode 100644 index 0000000..34c5a0c --- /dev/null +++ b/frontend/src/assets/icons/error.svg @@ -0,0 +1,12 @@ + + + + error + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/icons/info.svg b/frontend/src/assets/icons/info.svg new file mode 100644 index 0000000..1be5d55 --- /dev/null +++ b/frontend/src/assets/icons/info.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/assets/icons/success.svg b/frontend/src/assets/icons/success.svg new file mode 100644 index 0000000..cd99734 --- /dev/null +++ b/frontend/src/assets/icons/success.svg @@ -0,0 +1,12 @@ + + + + success + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index d4b5078..32f4994 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -1 +1,36 @@ @import 'tailwindcss'; + +h1 { + @apply text-3xl font-bold; +} + +h2 { + @apply text-2xl font-semibold; +} + +h3 { + @apply text-xl font-medium; +} +p { + @apply text-base; +} + +input, +textarea { + @apply w-full p-2 border border-gray-500 bg-teal-200 rounded hover:bg-teal-300 focus:bg-teal-300 focus:outline-none focus:ring-2 focus:ring-teal-500; +} +input::placeholder { + @apply text-gray-500 hover:text-gray-700; +} + +button { + @apply w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600; +} + +main { + @apply w-full h-screen flex items-center justify-center; +} + +.boxed { + @apply space-y-2 bg-blue-200 p-4 rounded-2xl shadow-lg; +} diff --git a/frontend/src/components/Header.vue b/frontend/src/components/Header.vue new file mode 100644 index 0000000..62fd3b0 --- /dev/null +++ b/frontend/src/components/Header.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/frontend/src/components/UserInfo.vue b/frontend/src/components/UserInfo.vue new file mode 100644 index 0000000..f614e85 --- /dev/null +++ b/frontend/src/components/UserInfo.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/frontend/src/composable/auth.ts b/frontend/src/composable/auth.ts index 9b0a69c..224d80c 100644 --- a/frontend/src/composable/auth.ts +++ b/frontend/src/composable/auth.ts @@ -1,5 +1,6 @@ import { API_URL } from '@/main' import { getJsonOrError } from '@/composable/utils' +import { computed, ref, type Ref } from 'vue' export interface User { user: string @@ -14,38 +15,69 @@ const readToken = () => { return null } -export const getSessionFromJWT = (): User => { - let coockie = readToken() - if (!coockie) { - throw new Error('No token found in cookies') +const userHandler = () => { + let curentUser: Ref = ref(null) + + const removeToken = () => { + document.cookie = 'oauth2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;' + curentUser.value = null } - let token = coockie.split(' ')[coockie.split(' ').length - 1] // Get the last part of the token - try { - // Phrase JWT - const base64Url = token.split('.')[1] // Get the payload part - const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/') // Base64 adjustments - return JSON.parse(atob(base64)).sub // Decode and parse JSON - } catch (_) { - throw new Error('Invalid token format') + + const getSessionFromJWT = (): User => { + let coockie = readToken() + if (!coockie) { + throw new Error('No token found in cookies') + } + let token = coockie.split(' ')[coockie.split(' ').length - 1] // Get the last part of the token + try { + // Phrase JWT + const base64Url = token.split('.')[1] // Get the payload part + const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/') // Base64 adjustments + return JSON.parse(atob(base64)).sub // Decode and parse JSON + } catch (_) { + throw new Error('Invalid token format') + } + } + + const requestToken = async (user: string, password: string): Promise => { + return fetch(`${API_URL}/user/token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', // set coockies from responce + body: JSON.stringify({ + user: user, + password: password, + }), + }).then(async (response) => { + let data = await getJsonOrError(response) + curentUser.value = { + user: data.sub.user as string, + role: data.sub.role as string, + } + return curentUser.value + }) + } + + const currentUser = (): User | null => { + if (curentUser.value === null) { + try { + curentUser.value = getSessionFromJWT() + } catch (e) { + console.error('Error getting session from JWT:', e) + curentUser.value = null + } + } + return curentUser.value + } + + return { + getSessionFromJWT, + requestToken, + removeToken, + currentUser: computed(() => currentUser()), } } -export const requestToken = async (user: string, password: string): Promise => { - return fetch(`${API_URL}/user/token`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', // set coockies from responce - body: JSON.stringify({ - user: user, - password: password, - }), - }).then(async (response) => { - let data = await getJsonOrError(response) - return { - user: data.sub.user as string, - role: data.sub.role as string, - } as User - }) -} +export const primaryUser = userHandler() diff --git a/frontend/src/composable/message.ts b/frontend/src/composable/message.ts index 942b9f3..b859dac 100644 --- a/frontend/src/composable/message.ts +++ b/frontend/src/composable/message.ts @@ -13,7 +13,9 @@ interface Message { export const messageHandler = (room: string = 'general') => { let messages: Ref> = ref({}) - function requestMessages(since: string | undefined): Promise> { + function requestMessages( + since: string | undefined = undefined, + ): Promise> { let query = since ? `?since=${since}` : '' return fetch(`${API_URL}/messages/${room}${query}`, { @@ -53,5 +55,14 @@ export const messageHandler = (room: string = 'general') => { } return messages.value[highestKey] }, + previousMessage: (id: number): Message | undefined => { + const keys = Object.keys(messages.value).map(Number) + const index = keys.indexOf(Number(id)) + if (index > 0) { + console.log('Previous message found:', messages.value[keys[index - 1]]) + return messages.value[keys[index - 1]] + } + return undefined + }, } } diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 4126e12..e132129 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -16,8 +16,8 @@ const router = createRouter({ meta: { requiresAuth: true }, }, { - path: '/user', - name: 'user', + path: '/admin', + name: 'admin', component: () => import('../views/User.vue'), }, { diff --git a/frontend/src/views/Chat.vue b/frontend/src/views/Chat.vue index 6c92695..e9abeb4 100644 --- a/frontend/src/views/Chat.vue +++ b/frontend/src/views/Chat.vue @@ -1,18 +1,46 @@