22 Commits

Author SHA1 Message Date
Nikolai Petukhov
13f4d43761 chats and messages 2024-10-05 11:46:18 +03:00
Nikolai Petukhov
22a549e269 an 2024-10-04 16:06:48 +03:00
Nikolai Petukhov
25c3e16c74 changing fields in account is done and upd chats retrieval 2024-10-04 15:53:50 +03:00
Nikolai Petukhov
86db5df813 chat retrieval is done 2024-10-04 14:29:00 +03:00
Nikolai Petukhov
d1e824ab77 retrieving chats 2024-10-04 11:21:21 +03:00
Nikolai Petukhov
073c61977f init for chats and api 2024-10-04 00:06:44 +03:00
Nikolai Petukhov
1301c145e8 check json format 2024-10-03 23:20:40 +03:00
Nikolai Petukhov
ac6dffa129 check json format 2024-10-03 23:12:38 +03:00
Nikolai Petukhov
8e4cad4c85 check json format 2024-10-03 23:06:18 +03:00
Nikolai Petukhov
d1091e570b check json format 2024-10-03 23:01:38 +03:00
Nikolai Petukhov
4a5041a65e check json format 2024-10-03 22:48:57 +03:00
Nikolai Petukhov
8d0fadc906 change name action for api 2024-10-03 22:35:39 +03:00
Nikolai Petukhov
6bea0428f4 api link fix 2024-10-03 21:26:02 +03:00
Nikolai Petukhov
660f2e9d5c 0.3.0 2024-10-03 21:16:23 +03:00
Nikolai Petukhov
a9b683797b auth with api 2024-10-03 21:15:48 +03:00
Nikolai Petukhov
a3484f4525 backend init 2024-09-28 12:51:59 +03:00
Nikolai Petukhov
876ef28221 0.2.4 2024-09-28 10:37:42 +03:00
Nikolai Petukhov
e6231f86b4 account page is done 2024-09-28 10:34:06 +03:00
Nikolai Petukhov
9d13f571d9 0.2.3 2024-09-28 10:00:38 +03:00
Nikolai Petukhov
f654851e1a fix dependencies 2024-09-28 09:56:43 +03:00
Askar Akhmetkhanov
3fb107fd8b Commit 2024-09-28 00:12:00 +03:00
Nikolai Petukhov
2a881f3920 small fix 2024-09-21 17:25:03 +03:00
41 changed files with 2742 additions and 120 deletions

1
.env Normal file
View File

@@ -0,0 +1 @@
TOKEN_KEY=5frv12e4few3r

View File

@@ -23,5 +23,6 @@ module.exports = {
},
config: {
"enterfront.api": "/api",
// paste stand URL to config
},
};

606
package-lock.json generated
View File

@@ -1,25 +1,31 @@
{
"name": "enterfront",
"version": "0.2.2",
"version": "0.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "enterfront",
"version": "0.2.2",
"version": "0.3.0",
"dependencies": {
"@brojs/cli": "^1.0.0",
"@brojs/create": "^1.0.0",
"@ijl/cli": "^5.1.0",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"dotenv": "^16.4.5",
"emoji-mart": "^5.6.0",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-emoji-picker": "^1.0.13",
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5",
"styled-components": "^6.1.13",
"typescript": "^5.5.4"
"typescript": "^5.5.4",
"ws": "^8.18.0"
}
},
"node_modules/@ampproject/remapping": {
@@ -2686,9 +2692,10 @@
}
},
"node_modules/@ijl/cli/node_modules/@types/react": {
"version": "17.0.80",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz",
"integrity": "sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==",
"version": "17.0.83",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.83.tgz",
"integrity": "sha512-l0m4ArKJvmFtR4e8UmKrj1pB4tUgOhJITf+mADyF/p69Ts1YAR/E+G9XEM0mHXKVRa1dQNHseyyDNzeuAXfXQw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
@@ -2714,6 +2721,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
@@ -2727,6 +2735,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
@@ -2769,6 +2778,7 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
@@ -3132,6 +3142,7 @@
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
"license": "MIT",
"optional": true,
"peer": true
},
@@ -3458,6 +3469,15 @@
"ajv": "^8.8.2"
}
},
"node_modules/amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==",
"license": "BSD-3-Clause OR MIT",
"engines": {
"node": ">=0.4.2"
}
},
"node_modules/ansi-html-community": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
@@ -3559,6 +3579,12 @@
"node": ">=0.10.0"
}
},
"node_modules/asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"license": "MIT"
},
"node_modules/assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -3567,6 +3593,15 @@
"node": ">=0.10.0"
}
},
"node_modules/ast-types": {
"version": "0.9.6",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz",
"integrity": "sha512-qEdtR2UH78yyHX/AUNfXmJTlM48XoFZKBdwi1nzkI1mJL21cmbu0cvjxjpkXJ5NENMq42H+hNs8VLJcqXLerBQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -3687,6 +3722,15 @@
"node": ">=0.10.0"
}
},
"node_modules/base62": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/base62/-/base62-1.2.8.tgz",
"integrity": "sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
@@ -3802,6 +3846,12 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause"
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -4037,6 +4087,15 @@
"node": ">=6"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/collection-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -4094,6 +4153,64 @@
"resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
"integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="
},
"node_modules/commoner": {
"version": "0.10.8",
"resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz",
"integrity": "sha512-3/qHkNMM6o/KGXHITA14y78PcfmXh4+AOCJpSoF73h4VY1JpdGv3CHMS5+JW6SwLhfJt4RhNmLAa7+RRX/62EQ==",
"license": "MIT",
"dependencies": {
"commander": "^2.5.0",
"detective": "^4.3.1",
"glob": "^5.0.15",
"graceful-fs": "^4.1.2",
"iconv-lite": "^0.4.5",
"mkdirp": "^0.5.0",
"private": "^0.1.6",
"q": "^1.1.2",
"recast": "^0.11.17"
},
"bin": {
"commonize": "bin/commonize"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commoner/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
"node_modules/commoner/node_modules/glob": {
"version": "5.0.15",
"resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
"integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"license": "ISC",
"dependencies": {
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "2 || 3",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
}
},
"node_modules/commoner/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"license": "MIT",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/component-emitter": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
@@ -4224,6 +4341,13 @@
"webpack": "^5.1.0"
}
},
"node_modules/core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA==",
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"license": "MIT"
},
"node_modules/core-js-compat": {
"version": "3.38.1",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz",
@@ -4437,6 +4561,15 @@
"node": ">=0.10.0"
}
},
"node_modules/defined": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz",
"integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/del": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
@@ -4531,6 +4664,28 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/detective": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz",
"integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==",
"license": "MIT",
"dependencies": {
"acorn": "^5.2.1",
"defined": "^1.0.0"
}
},
"node_modules/detective/node_modules/acorn": {
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -4547,6 +4702,18 @@
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@@ -4557,6 +4724,15 @@
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -4567,6 +4743,24 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.18.tgz",
"integrity": "sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ=="
},
"node_modules/emoji-annotation-to-unicode": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/emoji-annotation-to-unicode/-/emoji-annotation-to-unicode-0.2.4.tgz",
"integrity": "sha512-0GAasay1KK/a+Vns0aK1EwRMdLTvvvHO5Jc0SFwfwVhOhroIyT1IqBT8igZFrn3aw0u2b1LRshNsofkiL3jCkA==",
"license": "MIT"
},
"node_modules/emoji-emoticon-to-unicode": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/emoji-emoticon-to-unicode/-/emoji-emoticon-to-unicode-0.2.2.tgz",
"integrity": "sha512-fZ/wYomR+zc1/Sp/uY85M06XaGr+P+mH/uzh4l3rPPnv3YXrF3W2Cm/IqWcXvMw8m65/vmVvigBVqvkUfPJi4A==",
"license": "MIT"
},
"node_modules/emoji-mart": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz",
"integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==",
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -4624,6 +4818,27 @@
"node": ">= 0.6"
}
},
"node_modules/engine.io/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/enhanced-resolve": {
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
@@ -4644,6 +4859,19 @@
"node": ">=6"
}
},
"node_modules/envify": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/envify/-/envify-3.4.1.tgz",
"integrity": "sha512-XLiBFsLtNF0MOZl+vWU59yPb3C2JtrQY2CNJn22KH75zPlHWY5ChcAQuf4knJeWT/lLkrx3sqvhP/J349bt4Bw==",
"license": "MIT",
"dependencies": {
"jstransform": "^11.0.3",
"through": "~2.3.4"
},
"bin": {
"envify": "bin/envify"
}
},
"node_modules/envinfo": {
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz",
@@ -4721,6 +4949,31 @@
"node": ">=8.0.0"
}
},
"node_modules/esprima": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
"integrity": "sha512-AWwVMNxwhN8+NIPQzAQZCm7RkLC4RbM3B1OobMuyp3i+w73X57KCKaVIxaRZb+DYCojq7rspo+fmuQfAboyhFg==",
"license": "BSD-2-Clause",
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/esprima-fb": {
"version": "15001.1.0-dev-harmony-fb",
"resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz",
"integrity": "sha512-59dDGQo2b3M/JfKIws0/z8dcXH2mnVHkfSPRhCYS91JNGfGNwr7GsSF6qzWZuOGvw5Ii0w9TtylrX07MGmlOoQ==",
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -5065,6 +5318,19 @@
"reusify": "^1.0.4"
}
},
"node_modules/fbjs": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.6.1.tgz",
"integrity": "sha512-4KW7tT33ytfazK3Ekvesbsa4A5J79hUrdXONQGZ0wM6i3PFc70YknF9kj1eyx3mDupgJ7Z+ifFhcMJ+ps2eZIw==",
"license": "BSD-3-Clause",
"dependencies": {
"core-js": "^1.0.0",
"loose-envify": "^1.0.0",
"promise": "^7.0.3",
"ua-parser-js": "^0.7.9",
"whatwg-fetch": "^0.9.0"
}
},
"node_modules/filesize": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
@@ -6468,6 +6734,101 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonwebtoken": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
"license": "MIT",
"dependencies": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^7.5.4"
},
"engines": {
"node": ">=12",
"npm": ">=6"
}
},
"node_modules/jsonwebtoken/node_modules/semver": {
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/jstransform": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.3.tgz",
"integrity": "sha512-LGm87w0A8E92RrcXt94PnNHkFqHmgDy3mKHvNZOG7QepKCTCH/VB6S+IEN+bT4uLN3gVpOT0vvOOVd96osG71g==",
"license": "BSD-3-Clause",
"dependencies": {
"base62": "^1.1.0",
"commoner": "^0.10.1",
"esprima-fb": "^15001.1.0-dev-harmony-fb",
"object-assign": "^2.0.0",
"source-map": "^0.4.2"
},
"bin": {
"jstransform": "bin/jstransform"
},
"engines": {
"node": ">=0.8.8"
}
},
"node_modules/jstransform/node_modules/object-assign": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz",
"integrity": "sha512-CdsOUYIh5wIiozhJ3rLQgmUTgcyzFwZZrqhkKhODMoGtPKM+wt0h0CNIoauJWMsS9822EdzPsF/6mb4nLvPN5g==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/jstransform/node_modules/source-map": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==",
"license": "BSD-3-Clause",
"dependencies": {
"amdefine": ">=0.0.4"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"license": "MIT",
"dependencies": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"node_modules/keygrip": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
@@ -6537,11 +6898,59 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash.compact": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash.compact/-/lodash.compact-3.0.1.tgz",
"integrity": "sha512-2ozeiPi+5eBXW1CLtzjk8XQFhQOEMwwfxblqeq6EGyTxZJ1bPATqilY0e6g2SLQpP4KuMeuioBhEnWz5Pr7ICQ==",
"license": "MIT"
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
"license": "MIT"
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
"license": "MIT"
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
"license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"license": "MIT"
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -7342,6 +7751,15 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/private": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
"integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@@ -7350,6 +7768,15 @@
"node": ">= 0.6.0"
}
},
"node_modules/promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"license": "MIT",
"dependencies": {
"asap": "~2.0.3"
}
},
"node_modules/prompts": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz",
@@ -7398,6 +7825,17 @@
"node": ">=6"
}
},
"node_modules/q": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
"deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)",
"license": "MIT",
"engines": {
"node": ">=0.6.0",
"teleport": ">=0.2.0"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -7717,6 +8155,60 @@
"react": "^18.3.1"
}
},
"node_modules/react-emoji": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/react-emoji/-/react-emoji-0.4.4.tgz",
"integrity": "sha512-NQUk+QgF3af2poGioW9Dv6kQg9nJCEZVxRsYRYLa1H2XTelEkLVMeeTNU8Y5cMjQRuVnAfl7TzJ4zSix/qxDiQ==",
"license": "MIT",
"dependencies": {
"emoji-annotation-to-unicode": "^0.2.4",
"emoji-emoticon-to-unicode": "^0.2.2",
"escape-string-regexp": "^1.0.3",
"lodash.compact": "^3.0.0",
"object-assign": "^2.0.0"
}
},
"node_modules/react-emoji-picker": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/react-emoji-picker/-/react-emoji-picker-1.0.13.tgz",
"integrity": "sha512-nLcVgNb3Iok9Jy5frAPJoflAbHKg9Df+UZuhYVZ3bK12A2k9nFzmQyI2apTvMPC2r4h/KpHepfK10siMdqKR4g==",
"license": "ISC",
"dependencies": {
"react": "^0.14.3",
"react-emoji": "^0.4.1"
}
},
"node_modules/react-emoji-picker/node_modules/react": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/react/-/react-0.14.10.tgz",
"integrity": "sha512-yxMw5aorZG4qsLVBfjae4wGFvd5708DhcxaXLJ3IOTgr1TCs8k9+ZheGgLGr5OfwWMhSahNbGvvoEDzrxVWouA==",
"license": "BSD-3-Clause",
"dependencies": {
"envify": "^3.0.0",
"fbjs": "^0.6.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-emoji/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/react-emoji/node_modules/object-assign": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz",
"integrity": "sha512-CdsOUYIh5wIiozhJ3rLQgmUTgcyzFwZZrqhkKhODMoGtPKM+wt0h0CNIoauJWMsS9822EdzPsF/6mb4nLvPN5g==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-error-overlay": {
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
@@ -7784,6 +8276,19 @@
"react-dom": ">=16.8"
}
},
"node_modules/react-toastify": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz",
"integrity": "sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==",
"license": "MIT",
"dependencies": {
"clsx": "^2.1.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -7796,6 +8301,30 @@
"node": ">=8.10.0"
}
},
"node_modules/recast": {
"version": "0.11.23",
"resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz",
"integrity": "sha512-+nixG+3NugceyR8O1bLU45qs84JgI3+8EauyRZafLgC9XbdAOIVgwV1Pe2da0YzGo62KzWoZwUpVEQf6qNAXWA==",
"license": "MIT",
"dependencies": {
"ast-types": "0.9.6",
"esprima": "~3.1.0",
"private": "~0.1.5",
"source-map": "~0.5.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/recast/node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/rechoir": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
@@ -8455,6 +8984,27 @@
"ws": "~8.17.1"
}
},
"node_modules/socket.io-adapter/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
@@ -8867,6 +9417,12 @@
"tslib": "^2"
}
},
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
"license": "MIT"
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -8996,6 +9552,32 @@
"node": ">=14.17"
}
},
"node_modules/ua-parser-js": {
"version": "0.7.39",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz",
"integrity": "sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/ua-parser-js"
},
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
},
{
"type": "github",
"url": "https://github.com/sponsors/faisalman"
}
],
"license": "MIT",
"bin": {
"ua-parser-js": "script/cli.js"
},
"engines": {
"node": "*"
}
},
"node_modules/uglify-js": {
"version": "3.19.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
@@ -9459,6 +10041,11 @@
"node": ">=6"
}
},
"node_modules/whatwg-fetch": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz",
"integrity": "sha512-DIuh7/cloHxHYwS/oRXGgkALYAntijL63nsgMQsNSnBj825AysosAqA2ZbYXGRqpPRiNH7335dTqV364euRpZw=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
@@ -9540,9 +10127,10 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},

View File

@@ -5,13 +5,19 @@
"@ijl/cli": "^5.1.0",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"dotenv": "^16.4.5",
"emoji-mart": "^5.6.0",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-emoji-picker": "^1.0.13",
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5",
"styled-components": "^6.1.13",
"typescript": "^5.5.4"
"typescript": "^5.5.4",
"ws": "^8.18.0"
},
"main": "./src/index.tsx",
"scripts": {
@@ -21,5 +27,5 @@
"clean": "rimraf dist"
},
"name": "enterfront",
"version": "0.2.2"
"version": "0.3.0"
}

View File

@@ -1,15 +1,39 @@
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import React, {useEffect} from 'react';
import { BrowserRouter } from 'react-router-dom';
import { Dashboard } from './dashboard';
import {ToastContainer} from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
import './index.css'
import {displayMessage} from "./backend/notifications/notifications.js";
import {MessageType} from "./backend/notifications/message";
const App = () => {
useEffect(() => {
document.title = 'Enterfront';
}, []);
useEffect(() => {
const msg = localStorage.getItem('message');
if (!msg) return;
displayMessage(msg, MessageType.SUCCESS);
localStorage.removeItem('message');
}, []);
return(
<div>
<BrowserRouter>
<Dashboard />
</BrowserRouter>
<ToastContainer/>
</div>
)
}

60
src/backend/api.js Normal file
View File

@@ -0,0 +1,60 @@
import {getConfigValue} from "@brojs/cli";
const LOCAL = "http://localhost:8099";
const DEV = "";
export const BASE_API_URL = LOCAL + getConfigValue("enterfront.api") + "/enterfront";
// fetch(`${BASE_API_URL}/books/list`)
export async function post(path, body) {
const token = localStorage.getItem('token');
const res = await fetch(`${BASE_API_URL}${path}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": token ? `Bearer ${token}` : undefined
},
body: JSON.stringify(body)
});
console.log("Initial data from API:", res)
const data = JSON.parse(await res.text());
console.log("Data from API:", data)
if (res.status === 200) {
console.log("Received post:", data);
return {ok: true, data: data};
} else {
console.log("Error during post:", data.message);
return {ok: false, data: data};
}
}
export async function get(path){
const token = localStorage.getItem('token');
const res = await fetch(`${BASE_API_URL}${path}`, {
method: "GET",
headers: {
"Authorization": token ? `Bearer ${token}` : undefined
}
});
console.log("Data from API:", res)
const data = await res.json();
if (res.status === 200) {
console.log("Received get:", data);
return {ok: true, data: data};
} else {
console.log("Error during get:", data.message);
return {ok: false, data: data};
}
}

View File

@@ -0,0 +1,43 @@
const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: 8080 });
const clients = new Map();
wss.on("connection", (ws, req) => {
console.log("New client connected");
ws.on("message", (message) => {
try {
const parsedMessage = JSON.parse(message);
if (parsedMessage.type === "register") {
clients.set(parsedMessage.userId, ws);
console.log(`User registered: ${parsedMessage.userId}`);
} else if (parsedMessage.type === "message") {
const recipientWs = clients.get(parsedMessage.recipientId);
if (recipientWs) {
recipientWs.send(
JSON.stringify({
senderId: parsedMessage.senderId,
message: parsedMessage.message,
timestamp: new Date().toISOString(),
})
);
} else {
console.error(`User ${parsedMessage.recipientId} is not connected.`);
}
}
} catch (err) {
console.error("Error processing message:", err.message);
}
});
ws.on("close", () => {
console.log("Client disconnected");
[...clients.entries()].forEach(([userId, clientWs]) => {
if (clientWs === ws) {
clients.delete(userId);
console.log(`User disconnected: ${userId}`);
}
});
});
});

View File

@@ -1,7 +1,7 @@
export default class Interlocutor {
constructor(id, name) {
this.name = name
this.id = id
this.name = name;
this.id = id;
}
static name;
static id;

View File

@@ -0,0 +1,6 @@
export enum MessageType {
ERROR,
SUCCESS,
INFO,
WARN
}

View File

@@ -0,0 +1,28 @@
import {toast} from "react-toastify";
import {MessageType} from "./message.tsx";
export const displayMessage = (message, type) => {
switch (type) {
default:
case MessageType.ERROR:
toast.error(message, {
position: 'bottom-right',
});
break;
case MessageType.INFO:
toast.info(message, {
position: 'bottom-right',
});
break;
case MessageType.SUCCESS:
toast.success(message, {
position: 'bottom-right',
});
break;
case MessageType.WARN:
toast.warn(message, {
position: 'bottom-right',
});
break;
}
}

View File

@@ -1,3 +1,7 @@
export default class User {
constructor(id, name) {
this.id = id;
this.name = name;
this.status = "online";
}
}

View File

@@ -0,0 +1,51 @@
import React, {useState} from 'react';
import { URLs } from "../../__data__/urls";
import ActionButton from "./ActionButton.jsx";
import InputField from "../reg/InputField.jsx";
const AccountButtons = (props) => {
const [chName, setChName] = useState(false);
const [chPassword, setChPassword] = useState(false);
const [nickname, setNickname] = useState("");
const [password, setPassword] = useState("");
return (
<div className="account-buttons">
{props.registered ? (
<>
<ActionButton title={"Exit"} action={props.exitHandler}/>
<ActionButton title={"Change Name"} action={() => setChName(true)}/>
{chName ? (
<InputField
title={""}
value={nickname}
setValue={setNickname}
placeholder='Enter your new nickname'
submit={nickname}
enter={props.changeNameHandler}
/>
) : null}
<ActionButton title={"Change Pass"} action={() => setChPassword(true)}/>
{chPassword ? (
<div>
<InputField
title={""}
value={password}
setValue={setPassword}
placeholder='Enter your new password'
submit={password}
enter={props.changePassHandler}
/>
</div>
) : null}
</>
) : null}
<a className="MyButton mclaren-regular" href={URLs.home.url}>Back</a>
</div>
);
};
export default AccountButtons;

View File

@@ -0,0 +1,11 @@
import React from 'react';
const ActionButton = (props) => {
return (
<a className="MyButton mclaren-regular" onClick={() => {
props.action()
}}>{props.title}</a>
);
};
export default ActionButton;

View File

@@ -0,0 +1,18 @@
import React from 'react';
const HelloItem = (props) => {
return (
<div className="hello-item-class">
{!!props.nickname ? (
<>
<h1 className="mclaren-regular">Hello, {props.nickname}!</h1>
<p className="mclaren-regular">Your ID: {props.id}</p>
</>
) : (
<p className="mclaren-regular">You don't have an account :(</p>
)}
</div>
);
};
export default HelloItem;

View File

@@ -0,0 +1,54 @@
.account-buttons {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 30px;
}
.account-buttons a {
display: flex;
font-size: 1.5vw;
transition: color 0.2s ease-in;
width: 20vw;
margin-top: 2vw;
}
.account-buttons a:hover {
color: black;
}
.hello-item-class {
display: flex;
flex-direction: column;
align-items: center;
}
.hello-item-class h1 {
font-size: 4vw;
margin-bottom: 0;
}
.hello-item-class p {
font-size: 1.5vw;
}
@media only screen and (max-width: 800px) {
.account-buttons a {
font-size: 2.5vh;
width: 60vw;
margin-top: 3vh;
}
.hello-item-class h1 {
font-size: 5vh;
}
.hello-item-class p {
font-size: 2vh;
}
}

View File

@@ -43,6 +43,10 @@ const Card = (props) => {
transition: color 0.3s ease-in;
}
h2 {
margin-top: 0;
}
&:hover {
box-shadow: 0 10px 40px rgb(${hexToRgb(props.color)});
cursor: pointer;
@@ -56,7 +60,7 @@ const Card = (props) => {
flex-direction: column;
border-radius: 2vh;
padding: 3vw;
padding: 5vw;
}
`

View File

@@ -1,9 +1,44 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
import Card from "./Card.jsx";
import {get} from "../../backend/api";
import {displayMessage} from "../../backend/notifications/notifications";
import {MessageType} from "../../backend/notifications/message";
const ChatsList = (props) => {
const { chats } = props;
const [customChats, setCustomChats] = useState([]);
const updateList = async () => {
const username = localStorage.getItem("username");
if (!username) {return null;}
const updatedChats = await Promise.all(
chats.map(async (chat) => {
const interlocutorId = chat.id1 === username ? chat.id2 : chat.id1
const {ok, data} = await get('/auth/' + interlocutorId);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return null;
}
const interlocutor = data.user;
return {
id: interlocutorId,
name: interlocutor.nickname,
lastMessage: chat.messages.length > 0 ? chat.messages[chat.messages.length - 1].data : "",
}
})
);
setCustomChats(updatedChats.filter(chat => chat !== null));
};
useEffect(() => {updateList().then();}, [chats])
const colorMap = {
orange: 'FFA500FF',
aqua: '00FFFFFF',
@@ -19,13 +54,14 @@ const ChatsList = (props) => {
function getColor(chatId) {
const keys = Object.keys(colorMap);
const index = chatId % keys.length;
const numericId = Array.from(chatId).reduce((sum, char) => sum + char.charCodeAt(0), 0);
const index = numericId % keys.length;
return colorMap[keys[index]];
}
return (
<div className="ChatsList">
{chats.map((item, index) => (
{customChats.map((item, index) => (
<Card
key={index}
name={item.name}

View File

@@ -0,0 +1,11 @@
import React from 'react';
const Search = (props) => {
return (
<a className="MyButton search-class mclaren-regular" onClick={() => {
props.search(props.item);
}}>Find</a>
);
};
export default Search;

View File

@@ -34,6 +34,8 @@
margin-left: 4vw;
margin-right: 4vw;
padding-top: 0;
}
.ChatsList {

View File

@@ -12,11 +12,30 @@
color: orange;
}
.search-class {
margin-top: 2vw;
margin-bottom: 4vw;
display: flex;
align-items: center;
}
.search-input div input {
background-color: white;
color: black;
border: 3px solid black;
}
@media only screen and (max-width: 800px) {
.homeTitle {
font-size: 8vh;
}
.search-class {
margin-top: 3vh;
}
}
.chatIcon {

View File

@@ -1,6 +1,7 @@
@import url("reg/index.css");
@import url("init/index.css");
@import url("home/index.css");
@import url("account/index.css");
.MyButton {
text-decoration: none;

View File

@@ -17,3 +17,16 @@
font-weight: bold;
}
@media only screen and (max-width: 800px) {
.MyButton.singleNavButton p {
font-size: 2.5vh;
}
.MyButton.singleNavButton {
min-width: 60vw;
border-radius: 30vh;
margin-top: 3vh;
}
}

View File

@@ -1,6 +1,7 @@
import React from 'react';
const InputField = (props) => {
console.log('class:', props.className)
return (
<div>
<p>{props.title}</p>
@@ -8,6 +9,14 @@ const InputField = (props) => {
onChange={(e) => props.setValue(e.target.value)}
value={props.value}
className="Input"
placeholder={(props.placeholder) ? props.placeholder : ''}
onKeyDown={(e) => {
if (e.key === 'Enter') {
if (props.submit) {
props.enter(props.submit);
}
}
}}
/>
</div>
);

View File

@@ -1,14 +1,14 @@
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import React from "react";
import { Routes, Route } from "react-router-dom";
import { URLs } from './__data__/urls';
import { URLs } from "./__data__/urls";
import Home from './pages/Home.jsx'
import Init from './pages/Init.jsx'
import Account from './pages/Account.jsx'
import Chat from './pages/Chat.jsx'
import SignIn from './pages/SignIn.jsx'
import SignUp from './pages/SignUp.jsx'
import Home from "./pages/Home.jsx";
import Init from "./pages/Init.jsx";
import Account from "./pages/Account.jsx";
import Chat from "./pages/Chat.jsx";
import SignIn from "./pages/SignIn.jsx";
import SignUp from "./pages/SignUp.jsx";
export const Dashboard = () => {
return (

View File

@@ -1,9 +1,81 @@
import React from "react";
import React, {useEffect, useState} from "react";
import AccountButtons from "../components/account/AccountButtons.jsx";
import userIcon from "../../images/user.svg";
import {get, post} from "../backend/api";
import {displayMessage} from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message";
import HelloItem from "../components/account/HelloItem.jsx";
import { URLs } from "../__data__/urls";
const Account = () => {
return (
<h1>Account</h1>
)
const exitHandler = () => {
localStorage.removeItem("username");
localStorage.removeItem("token");
localStorage.setItem("message", "Exited successfully!");
window.location.href = "/";
}
const [nickname, setNickname] = useState("");
const [id, setId] = useState("");
async function changeNameHandler (newNickname) {
if (!newNickname) return;
const {ok, data} = await post('/change/nickname', {id: id, newNickname: newNickname});
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
} else {
localStorage.setItem("message", "Name was changed");
window.location.href = URLs.account.url;
}
}
async function changePassHandler (newPass){
if (!newPass) return;
const {ok, data} = await post('/change/password', {id: id, newPassword: newPass});
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
} else {
localStorage.setItem("message", "Password was changed");
window.location.href = URLs.account.url;
}
}
async function getUser() {
const username = localStorage.getItem("username");
if (!username) {
displayMessage("You're not logged in!", MessageType.WARN);
return;
}
const {ok, data} = await get('/auth/' + username);
if (!ok) {
displayMessage("Some error with auth:" + data.message, MessageType.ERROR);
return;
}
setNickname(data.user.nickname);
setId(username);
}
useEffect(() => {getUser().then()}, [])
return (
<div className="account-items">
<img src={userIcon} alt="user" />
<HelloItem nickname={nickname} id={id} />
<AccountButtons
exitHandler={exitHandler}
changeNameHandler={changeNameHandler}
changePassHandler={changePassHandler}
registered={!!nickname}
/>
</div>
);
};
export default Account;

View File

@@ -1,20 +1,256 @@
import React, {useEffect, useState} from 'react';
import React, { useEffect, useState, useRef } from "react";
import { useNavigate } from "react-router-dom";
import "./css/Chat.css";
import { FaPaperPlane, FaSmile } from "react-icons/fa";
import {get, post} from "../backend/api";
import {displayMessage} from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message";
const emojis = [
"😀",
"😁",
"😂",
"🤣",
"😃",
"😄",
"😅",
"😆",
"😉",
"😊",
"😋",
"😎",
"😍",
"😘",
"🥰",
"😗",
"😙",
"😚",
"🙂",
"🤗",
"🤩",
"🤔",
"😐",
"😑",
"😶",
"🙄",
"😏",
"😣",
"😥",
"😮",
"🤐",
"😯",
"😪",
"😫",
"😴",
"😌",
"😛",
"😜",
"🤪",
"🤨",
"😝",
"🤑",
"😒",
"😓",
"😔",
"😕",
"😖",
"😞",
"😟",
"😠",
"😡",
"🤬",
"😱",
"😨",
"😧",
"😇",
"🥳",
"🥺",
"😻",
"😼",
"😽",
"🙈",
"🙉",
"🙊",
"💀",
"👻",
"👽",
];
const Chat = () => {
const [interlocutorId, setInterlocutorId] = useState(0); // State to hold the interlocutorId
const [interlocutorId, setInterlocutorId] = useState("");
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState("");
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const socket = useRef(null);
const chatRef = useRef(null);
const navigate = useNavigate();
function getInterlocutorId() {
const id = localStorage.getItem('interlocutorId');
return id ? id : 0;
}
const [myId, setMyId] = useState("");
useEffect(() => {
const id = getInterlocutorId();
// const id = parseInt(localStorage.getItem("interlocutorId"), 10) || 0;
const id = localStorage.getItem("interlocutorId")
setInterlocutorId(id);
const username = localStorage.getItem("username");
setMyId(username);
if (!id || !username) {
displayMessage("You are not logged in!", MessageType.WARN);
return () => {};
}
socket.current = new WebSocket("ws://localhost:8080");
socket.current.onopen = () => {
console.log("WebSocket connected");
socket.current.send(
JSON.stringify({ type: "register", userId: "yourUserId" })
);
};
socket.current.onmessage = (event) => {
const receivedData = JSON.parse(event.data);
setMessages((prev) => [...prev, receivedData]);
};
socket.current.onerror = (event) => {
console.error("WebSocket error observed:", event);
};
socket.current.onclose = () => {
console.log("WebSocket closed");
};
return () => {
socket.current.close();
};
}, []);
useEffect(() => {
retrieveMessages().then();
}, [myId, interlocutorId])
useEffect(() => {
if (chatRef.current) {
chatRef.current.scrollTop = chatRef.current.scrollHeight;
}
}, [messages]);
// The function for sending message to the DB
async function sendMessageToDB (messageData) {
const { ok, data } = post('/chat/message/' + myId + '/' + interlocutorId, { message: messageData });
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
}
}
// The function retrieves messages from the DB for the current chat
async function retrieveMessages () {
if (!myId || !interlocutorId) {return;}
const { ok, data } = await get('/chat/item/' + myId + '/' + interlocutorId);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return;
}
setMessages(data.chat.messages);
}
const sendMessage = () => {
if (newMessage.trim()) {
const messageData = {
senderId: myId,
recipientId: interlocutorId,
data: newMessage,
timestamp: new Date().toLocaleTimeString(),
};
socket.current.send(JSON.stringify(messageData));
setMessages((prev) => [...prev, messageData]);
sendMessageToDB(messageData).then();
console.log('format:', messageData);
setNewMessage("");
}
};
const handleKeyPress = (e) => {
if (e.key === "Enter") {
sendMessage();
}
};
const handleEmojiSelect = (emoji) => {
setNewMessage((prev) => prev + emoji);
setShowEmojiPicker(false);
};
return (
<div className="chat-container">
<div className="chat-header">
<h2>Chat with ... (id = {interlocutorId})</h2>
<button
onClick={() => navigate("/enterfront/home")}
className="home-button"
>
Home
</button>{" "}
{}
</div>
<div className="chat-messages" ref={chatRef}>
{messages.map((msg, index) => (
<div
key={index}
className={`message-bubble ${
msg.senderId === myId ? "sent" : "received"
}`}
>
<div className="message-content">
<b>{msg.senderId === myId ? "You" : "They"}:</b>{" "}
{msg.data}
</div>
<span className="message-timestamp">{msg.timestamp}</span>
</div>
))}
</div>
<div className="chat-input-container">
<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="Type a message..."
className="chat-input"
onKeyDown={handleKeyPress}
/>
<button
className="emoji-button"
onClick={() => setShowEmojiPicker((prev) => !prev)}
>
<FaSmile />
</button>
<button onClick={sendMessage} className="send-button">
<FaPaperPlane />
</button>
{showEmojiPicker && (
<div className="emoji-picker">
{emojis.map((emoji, index) => (
<span
key={index}
className="emoji"
onClick={() => handleEmojiSelect(emoji)}
style={{ cursor: "pointer", padding: "5px" }}
>
{emoji}
</span>
))}
</div>
)}
</div>
</div>
);
};

View File

@@ -1,64 +1,53 @@
import React from "react";
import React, {useEffect, useState} from "react";
import HomeTitle from "../components/home/HomeTitle.jsx";
import ChatsList from "../components/home/ChatsList.jsx";
import Header from "../components/home/Header.jsx";
import {displayMessage} from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message";
import {get, post} from "../backend/api";
import InputField from "../components/reg/InputField.jsx";
import Search from "../components/home/Search.jsx";
import {URLs} from "../__data__/urls";
const Home = () => {
const [chats, setChats] = useState([])
const [interlocutor, setInterlocutor] = useState("")
// temp for testing
const chats = [
{
name: "Alice Johnson",
id: 123456,
lastMessage: "See you later!"
},
{
name: "Bob Smith",
id: 654321,
lastMessage: "Got it, thanks!"
},
{
name: "Charlie Brown",
id: 234567,
lastMessage: "How's the project going? How's the project going? How's the project going?" +
"How's the project going? How's the project going?"
},
{
name: "David Clark",
id: 765432,
lastMessage: "I'll send the files."
},
{
name: "Eve Adams",
id: 345678,
lastMessage: "Let's meet tomorrow."
},
{
name: "Frank Wright",
id: 876543,
lastMessage: "Can you review this?"
},
{
name: "Grace Lee",
id: 456789,
lastMessage: "Thanks for your help!"
},
{
name: "Hannah Scott",
id: 987654,
lastMessage: "See you at the meeting."
},
{
name: "Ian Davis",
id: 567890,
lastMessage: "Let me know when you're free."
},
{
name: "Jill Thompson",
id: 678901,
lastMessage: "I'll catch up with you later."
async function retrieveChats() {
const username = localStorage.getItem("username");
if (!username) {
displayMessage("You're not logged in!", MessageType.WARN);
return;
}
];
const {ok, data} = await get('/chat/list/' + username);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return;
}
setChats(data.chats);
}
async function createChat(alias) {
const username = localStorage.getItem("username");
if (!username) {
displayMessage("You're not logged in!", MessageType.WARN);
return;
}
displayMessage("Sent", MessageType.INFO);
const {ok, data} = await post('/chat/item/' + username + '/' + alias);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
} else {
localStorage.setItem('message', 'Successfully opened chat!');
localStorage.setItem('interlocutorId', alias);
window.location.href = URLs.chat.url;
}
}
useEffect(() => {retrieveChats().then()}, [])
return (
<div className="homeWrapper">
@@ -67,6 +56,18 @@ const Home = () => {
</div>
<HomeTitle/>
<div className="search-input">
<InputField
title="Create new chat"
value={interlocutor}
setValue={setInterlocutor}
placeholder="Enter the username (id)"
/>
</div>
<Search search={createChat} item={interlocutor}/>
<p>Your chats</p>
<ChatsList chats={chats} />
</div>

View File

@@ -3,12 +3,41 @@ import InputField from "../components/reg/InputField.jsx";
import LoginButtons from "../components/reg/LoginButtons.jsx";
import LoginTitle from "../components/reg/loginTitle.jsx";
import {MessageType} from "../backend/notifications/message.tsx";
import {displayMessage} from "../backend/notifications/notifications.js";
import {post} from "../backend/api.js";
const SignIn = () => {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const submit = (e) => {
const [nameErrorsCounter, setNameErrorsCounter] = useState(0);
async function submit() {
console.log('Sign In!')
const {ok, data} = await post('/auth/login', {name: name, password: password});
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
if (nameErrorsCounter >= 1) {
displayMessage("Note that you need to enter your ID name", MessageType.INFO);
setNameErrorsCounter(0);
} else {
setNameErrorsCounter(nameErrorsCounter + 1);
}
return;
}
localStorage.setItem('token', data.token);
localStorage.setItem('username', name);
setNameErrorsCounter(0);
localStorage.setItem('message', 'Successfully logged in!');
window.location.href = "/";
}
return (

View File

@@ -2,20 +2,59 @@ import React, {useState} from 'react';
import InputField from "../components/reg/InputField.jsx";
import LoginButtons from "../components/reg/LoginButtons.jsx";
import LoginTitle from "../components/reg/loginTitle.jsx";
import {post} from "../backend/api";
import {displayMessage} from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message";
const SignUp = () => {
const [name, setName] = useState("");
const [nickname, setNickname] = useState("");
const [password, setPassword] = useState("");
const [repeatPassword, setRepeatPassword] = useState("");
const submit = (e) => {
console.log('Sign Up!')
async function login(name, password) {
const {ok, data} = await post('/auth/login', {name: name, password: password});
return {loginStatus: ok, loginData: data};
}
async function submit () {
console.log('Sign Up!');
if (password !== repeatPassword) {
displayMessage("Passwords don't match", MessageType.WARN);
return;
}
const {ok, data} = await post('/auth/reg',
{name: name, password: password, nickname: nickname});
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return;
}
const { loginStatus, loginData } = await login(name, password);
console.log(loginStatus, loginData)
if (!loginStatus) {
displayMessage(loginData.message, MessageType.ERROR);
return;
}
localStorage.setItem('token', loginData.token);
localStorage.setItem('username', name);
localStorage.setItem('message', 'Successfully signed up!');
window.location.href = "/";
}
return (
<div className="LoginList">
<LoginTitle/>
<InputField title="Name" setValue={setName} value={name}/>
<InputField title="Nickname (for others)" setValue={setNickname} value={nickname}/>
<InputField title="Password" setValue={setPassword} value={password}/>
<InputField title="Repeat Password" setValue={setRepeatPassword} value={repeatPassword}/>

145
src/pages/css/Chat.css Normal file
View File

@@ -0,0 +1,145 @@
.chat-container {
display: flex;
flex-direction: column;
width: 100%;
max-width: 600px;
margin: auto;
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
background-color: #f9f9f9;
}
.chat-header {
padding: 10px;
background-color: #007bff;
color: white;
text-align: center;
}
.chat-messages {
display: flex;
flex-direction: column;
flex-grow: 1;
padding: 10px;
overflow-y: auto;
background-color: #fff;
}
.message-bubble {
margin: 5px;
padding: 10px 15px;
border-radius: 10px;
max-width: 70%;
word-wrap: break-word;
display: inline-block;
}
.sent {
background-color: #007bff;
color: white;
align-self: flex-end;
text-align: right;
margin-left: auto;
}
.received {
background-color: #f1f0f0;
align-self: flex-start;
text-align: left;
margin-right: auto;
}
.message-content {
font-size: 14px;
margin: 0;
padding: 0;
}
.message-timestamp {
font-size: 10px;
color: #999;
margin-top: 5px;
display: block;
}
.chat-input-container {
display: flex;
padding: 10px;
border-top: 1px solid #ddd;
background-color: #f0f0f0;
}
.chat-input {
flex-grow: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 20px;
font-size: 14px;
outline: none;
}
.send-button,
.emoji-button {
background-color: transparent;
border: none;
font-size: 20px;
margin-left: 10px;
cursor: pointer;
transition: color 0.3s;
}
.send-button:hover,
.emoji-button:hover {
color: #0056b3;
}
.send-button {
color: #007bff;
}
.emoji-button {
color: #f0c040;
}
.chat-input:focus {
border-color: #007bff;
}
.emoji-picker {
position: absolute;
bottom: 60px;
right: 10px;
z-index: 10;
background-color: white;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
display: flex;
flex-wrap: wrap;
max-width: 200px;
}
.emoji {
font-size: 24px;
cursor: pointer;
transition: transform 0.2s;
}
.emoji:hover {
transform: scale(1.2);
}
.home-button {
background-color: #4caf50;
color: white;
padding: 10px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
margin-left: auto;
}
.home-button:hover {
background-color: #45a049;
}

12
src/pages/css/account.css Normal file
View File

@@ -0,0 +1,12 @@
.account-items {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 5vw;
}
.account-items img {
margin-bottom: 1vw;
}

View File

@@ -1,3 +1,4 @@
@import url("css/init.css");
@import url("css/home.css");
@import url("css/input.css");
@import url("css/account.css");

77
stubs/api/auth/index.js Normal file
View File

@@ -0,0 +1,77 @@
const authRouter = require('express').Router();
// For creating tokens
const jwt = require('jsonwebtoken');
require('dotenv').config();
const TOKEN_KEY = process.env.TOKEN_KEY;
module.exports = authRouter;
const { addUserToDB, getUserFromDB } = require('../db');
// Get a user by its id
authRouter.get('/:id', (req, res) => {
const user = getUserFromDB(req.params.id);
console.log("Request get in /auth:", req.params.id);
if (user) {
res.status(200).send({user});
} else {
res.status(404).send({message: 'User was not found'});
}
})
// For login (authorization)
authRouter.post('/login', (req, res) => {
const { name, password } = req.body;
console.log("Request login in /auth:", name);
const user = getUserFromDB(name);
// Invalid identification
if (!user) {
res.status(401).send({message: 'Invalid credentials (id)'});
return;
}
// Invalid authentication
if (!password || password !== user.password) {
res.status(401).send({message: 'Invalid credentials (password)'});
return;
}
// Now, authorization
const token = jwt.sign({id: name}, TOKEN_KEY, {
expiresIn: '1h'
})
res.status(200).send({token});
})
authRouter.post('/reg', (req, res) => {
const { name, password, nickname } = req.body;
console.log("Request reg in /auth:", name);
const user = getUserFromDB(name);
// Invalid identification
if (user) {
res.status(409).send({message: 'Such id already exists'});
return;
}
if (!name || !password || !nickname) {
res.status(401).send({message: 'Empty or invalid fields'});
return;
}
// Add to 'DB'
const newUser = {id: name, password: password, nickname: nickname};
addUserToDB(newUser)
res.status(200).send({user: newUser});
})

52
stubs/api/auth/users.json Normal file
View File

@@ -0,0 +1,52 @@
[
{
"nickname": "Alice Johnson",
"password": "1234",
"id": "alice"
},
{
"nickname": "Bob Smith",
"password": "1234",
"id": "bobsm"
},
{
"nickname": "Charlie Brown",
"password": "1234",
"id": "charl"
},
{
"nickname": "David Clark",
"password": "1234",
"id": "david"
},
{
"nickname": "Eve Adams",
"password": "1234",
"id": "evead"
},
{
"nickname": "Frank Wright",
"password": "1234",
"id": "frank"
},
{
"nickname": "Grace Lee",
"password": "1234",
"id": "grace"
},
{
"nickname": "Hannah Scott",
"password": "1234",
"id": "hanna"
},
{
"nickname": "Ian Davis",
"password": "1234",
"id": "ianda"
},
{
"nickname": "Jill Thompson",
"password": "1234",
"id": "jillt"
}
]

69
stubs/api/change/index.js Normal file
View File

@@ -0,0 +1,69 @@
const changeRouter = require('express').Router();
module.exports = changeRouter;
const { users, getUserFromDB, deleteUserFromDB, addUserToDB } = require('../db');
const jwt = require("jsonwebtoken");
changeRouter.post('/nickname', (req, res) => {
const { id, newNickname } = req.body;
console.log("Request nickname in /change:", id);
const user = getUserFromDB(id);
// Invalid identification
if (!user) {
res.status(401).send({message: 'Invalid credentials (id)'});
return;
}
const updatedUser = {
"nickname": newNickname,
"password": user.password,
"id": user.id
};
// Delete the old one
deleteUserFromDB(id)
// Insert updated
addUserToDB(updatedUser);
res.status(200).send({});
});
changeRouter.post('/password', (req, res) => {
const { id, newPassword } = req.body;
console.log("Request password in /change:", id);
const user = getUserFromDB(id);
// Invalid identification
if (!user) {
res.status(401).send({message: 'Invalid credentials (id)'});
return;
}
// Delete the old one
deleteUserFromDB(id)
// Insert updated
const updatedUser = {
"nickname": user.nickname,
"password": newPassword,
"id": user.id
};
addUserToDB(updatedUser);
res.status(200).send({});
});
changeRouter.delete('/:id', (req, res) => {
const { id } = req.params;
console.log("Request delete in /change:", id);
deleteUserFromDB(id);
});

692
stubs/api/chat/chats.json Normal file
View File

@@ -0,0 +1,692 @@
[
{
"id1": "alice",
"id2": "bobsm",
"messages": [
{
"data": "Hello Bob!",
"senderId": "alice",
"recipientId": "bobsm",
"timestamp": "07:00:00 AM"
},
{
"data": "Hey Alice, how are you?",
"senderId": "bobsm",
"recipientId": "alice",
"timestamp": "07:05:00 AM"
},
{
"data": "I'm good, thanks for asking.",
"senderId": "alice",
"recipientId": "bobsm",
"timestamp": "07:10:00 AM"
},
{
"data": "Glad to hear!",
"senderId": "bobsm",
"recipientId": "alice",
"timestamp": "07:15:00 AM"
}
]
},
{
"id1": "alice",
"id2": "charl",
"messages": [
{
"data": "How's the project going?",
"senderId": "alice",
"recipientId": "charl",
"timestamp": "07:20:00 AM"
},
{
"data": "It's coming along, almost done!",
"senderId": "charl",
"recipientId": "alice",
"timestamp": "07:25:00 AM"
},
{
"data": "That's great to hear!",
"senderId": "alice",
"recipientId": "charl",
"timestamp": "07:30:00 AM"
},
{
"data": "Thanks for checking in.",
"senderId": "charl",
"recipientId": "alice",
"timestamp": "07:35:00 AM"
}
]
},
{
"id1": "alice",
"id2": "david",
"messages": [
{
"data": "Did you get the files?",
"senderId": "david",
"recipientId": "alice",
"timestamp": "07:40:00 AM"
},
{
"data": "Yes, I did. Thank you!",
"senderId": "alice",
"recipientId": "david",
"timestamp": "07:45:00 AM"
},
{
"data": "You're welcome.",
"senderId": "david",
"recipientId": "alice",
"timestamp": "07:50:00 AM"
},
{
"data": "Let me know if you need anything else.",
"senderId": "alice",
"recipientId": "david",
"timestamp": "07:55:00 AM"
}
]
},
{
"id1": "alice",
"id2": "evead",
"messages": [
{
"data": "Eve, do you have the meeting details?",
"senderId": "alice",
"recipientId": "evead",
"timestamp": "08:00:00 AM"
},
{
"data": "Yes, I just sent them to you.",
"senderId": "evead",
"recipientId": "alice",
"timestamp": "08:05:00 AM"
},
{
"data": "Got it, thanks!",
"senderId": "alice",
"recipientId": "evead",
"timestamp": "08:10:00 AM"
},
{
"data": "You're welcome.",
"senderId": "evead",
"recipientId": "alice",
"timestamp": "08:15:00 AM"
}
]
},
{
"id1": "alice",
"id2": "frank",
"messages": [
{
"data": "Can you review this document for me?",
"senderId": "alice",
"recipientId": "frank",
"timestamp": "08:20:00 AM"
},
{
"data": "Sure, I'll take a look.",
"senderId": "frank",
"recipientId": "alice",
"timestamp": "08:25:00 AM"
},
{
"data": "Thanks, much appreciated!",
"senderId": "alice",
"recipientId": "frank",
"timestamp": "08:30:00 AM"
},
{
"data": "No problem.",
"senderId": "frank",
"recipientId": "alice",
"timestamp": "08:35:00 AM"
}
]
},
{
"id1": "alice",
"id2": "grace",
"messages": [
{
"data": "Hey Grace, let's meet up for coffee!",
"senderId": "alice",
"recipientId": "grace",
"timestamp": "08:40:00 AM"
},
{
"data": "Sounds good, when are you free?",
"senderId": "grace",
"recipientId": "alice",
"timestamp": "08:45:00 AM"
},
{
"data": "How about tomorrow afternoon?",
"senderId": "alice",
"recipientId": "grace",
"timestamp": "08:50:00 AM"
},
{
"data": "Works for me!",
"senderId": "grace",
"recipientId": "alice",
"timestamp": "08:55:00 AM"
}
]
},
{
"id1": "alice",
"id2": "hanna",
"messages": [
{
"data": "Hannah, do you have a moment?",
"senderId": "alice",
"recipientId": "hanna",
"timestamp": "09:00:00 AM"
},
{
"data": "Sure, what's up?",
"senderId": "hanna",
"recipientId": "alice",
"timestamp": "09:05:00 AM"
},
{
"data": "Just wanted to check on the report.",
"senderId": "alice",
"recipientId": "hanna",
"timestamp": "09:10:00 AM"
},
{
"data": "I'll send it soon.",
"senderId": "hanna",
"recipientId": "alice",
"timestamp": "09:15:00 AM"
}
]
},
{
"id1": "alice",
"id2": "ianda",
"messages": [
{
"data": "Ian, have you completed the review?",
"senderId": "alice",
"recipientId": "ianda",
"timestamp": "09:20:00 AM"
},
{
"data": "Yes, I sent my feedback.",
"senderId": "ianda",
"recipientId": "alice",
"timestamp": "09:25:00 AM"
},
{
"data": "Thanks for that.",
"senderId": "alice",
"recipientId": "ianda",
"timestamp": "09:30:00 AM"
},
{
"data": "Anytime!",
"senderId": "ianda",
"recipientId": "alice",
"timestamp": "09:35:00 AM"
}
]
},
{
"id1": "alice",
"id2": "jillt",
"messages": [
{
"data": "Jill, let's schedule a catch-up meeting.",
"senderId": "alice",
"recipientId": "jillt",
"timestamp": "09:40:00 AM"
},
{
"data": "Sounds good, when works for you?",
"senderId": "jillt",
"recipientId": "alice",
"timestamp": "09:45:00 AM"
},
{
"data": "Tomorrow afternoon?",
"senderId": "alice",
"recipientId": "jillt",
"timestamp": "09:50:00 AM"
},
{
"data": "That works for me!",
"senderId": "jillt",
"recipientId": "alice",
"timestamp": "09:55:00 AM"
}
]
},
{
"id1": "alice",
"id2": "evead",
"messages": [
{
"data": "Eve, did you send the schedule?",
"senderId": "alice",
"recipientId": "evead",
"timestamp": "10:00:00 AM"
},
{
"data": "Yes, just sent it.",
"senderId": "evead",
"recipientId": "alice",
"timestamp": "10:05:00 AM"
},
{
"data": "Thanks, much appreciated!",
"senderId": "alice",
"recipientId": "evead",
"timestamp": "10:10:00 AM"
},
{
"data": "No problem!",
"senderId": "evead",
"recipientId": "alice",
"timestamp": "10:15:00 AM"
}
]
},
{
"id1": "bobsm",
"id2": "charl",
"messages": [
{
"data": "How's everything going?",
"senderId": "bobsm",
"recipientId": "charl",
"timestamp": "10:20:00 AM"
},
{
"data": "Pretty good, how about you?",
"senderId": "charl",
"recipientId": "bobsm",
"timestamp": "10:25:00 AM"
},
{
"data": "Can't complain!",
"senderId": "bobsm",
"recipientId": "charl",
"timestamp": "10:30:00 AM"
},
{
"data": "Glad to hear that.",
"senderId": "charl",
"recipientId": "bobsm",
"timestamp": "10:35:00 AM"
}
]
},
{
"id1": "bobsm",
"id2": "david",
"messages": [
{
"data": "Can you send the report?",
"senderId": "bobsm",
"recipientId": "david",
"timestamp": "10:40:00 AM"
},
{
"data": "I'll send it in an hour.",
"senderId": "david",
"recipientId": "bobsm",
"timestamp": "10:45:00 AM"
},
{
"data": "Perfect, thanks.",
"senderId": "bobsm",
"recipientId": "david",
"timestamp": "10:50:00 AM"
},
{
"data": "No problem.",
"senderId": "david",
"recipientId": "bobsm",
"timestamp": "10:55:00 AM"
}
]
},
{
"id1": "charl",
"id2": "evead",
"messages": [
{
"data": "Hey Eve, how's it going?",
"senderId": "charl",
"recipientId": "evead",
"timestamp": "11:00:00 AM"
},
{
"data": "Good, how about you?",
"senderId": "evead",
"recipientId": "charl",
"timestamp": "11:05:00 AM"
},
{
"data": "Can't complain!",
"senderId": "charl",
"recipientId": "evead",
"timestamp": "11:10:00 AM"
},
{
"data": "Glad to hear.",
"senderId": "evead",
"recipientId": "charl",
"timestamp": "11:15:00 AM"
}
]
},
{
"id1": "charl",
"id2": "frank",
"messages": [
{
"data": "Do you have time to talk today?",
"senderId": "charl",
"recipientId": "frank",
"timestamp": "11:20:00 AM"
},
{
"data": "I have a meeting, but I can chat afterward.",
"senderId": "frank",
"recipientId": "charl",
"timestamp": "11:25:00 AM"
},
{
"data": "Sounds good.",
"senderId": "charl",
"recipientId": "frank",
"timestamp": "11:30:00 AM"
},
{
"data": "I'll message you after.",
"senderId": "frank",
"recipientId": "charl",
"timestamp": "11:35:00 AM"
}
]
},
{
"id1": "david",
"id2": "frank",
"messages": [
{
"data": "Did you review the document?",
"senderId": "david",
"recipientId": "frank",
"timestamp": "11:40:00 AM"
},
{
"data": "Yes, it's all good.",
"senderId": "frank",
"recipientId": "david",
"timestamp": "11:45:00 AM"
},
{
"data": "Great, thanks for the quick turnaround!",
"senderId": "david",
"recipientId": "frank",
"timestamp": "11:50:00 AM"
},
{
"data": "No worries!",
"senderId": "frank",
"recipientId": "david",
"timestamp": "11:55:00 AM"
}
]
},
{
"id1": "david",
"id2": "grace",
"messages": [
{
"data": "Grace, can you send the updated schedule?",
"senderId": "david",
"recipientId": "grace",
"timestamp": "12:00:00 PM"
},
{
"data": "Yes, I'll send it in a few minutes.",
"senderId": "grace",
"recipientId": "david",
"timestamp": "12:05:00 PM"
},
{
"data": "Thanks, much appreciated!",
"senderId": "david",
"recipientId": "grace",
"timestamp": "12:10:00 PM"
},
{
"data": "You're welcome!",
"senderId": "grace",
"recipientId": "david",
"timestamp": "12:15:00 PM"
}
]
},
{
"id1": "frank",
"id2": "grace",
"messages": [
{
"data": "How are you today?",
"senderId": "frank",
"recipientId": "grace",
"timestamp": "12:20:00 PM"
},
{
"data": "I'm doing well, thanks for asking.",
"senderId": "grace",
"recipientId": "frank",
"timestamp": "12:25:00 PM"
},
{
"data": "Glad to hear that.",
"senderId": "frank",
"recipientId": "grace",
"timestamp": "12:30:00 PM"
},
{
"data": "How about you?",
"senderId": "grace",
"recipientId": "frank",
"timestamp": "12:35:00 PM"
}
]
},
{
"id1": "frank",
"id2": "hanna",
"messages": [
{
"data": "Did you attend the meeting?",
"senderId": "frank",
"recipientId": "hanna",
"timestamp": "12:40:00 PM"
},
{
"data": "Yes, it was productive.",
"senderId": "hanna",
"recipientId": "frank",
"timestamp": "12:45:00 PM"
},
{
"data": "Good to hear!",
"senderId": "frank",
"recipientId": "hanna",
"timestamp": "12:50:00 PM"
},
{
"data": "Indeed, lots to follow up on.",
"senderId": "hanna",
"recipientId": "frank",
"timestamp": "12:55:00 PM"
}
]
},
{
"id1": "grace",
"id2": "hanna",
"messages": [
{
"data": "Can we meet later today?",
"senderId": "grace",
"recipientId": "hanna",
"timestamp": "01:00:00 PM"
},
{
"data": "Sure, what's a good time?",
"senderId": "hanna",
"recipientId": "grace",
"timestamp": "01:05:00 PM"
},
{
"data": "How about 3 PM?",
"senderId": "grace",
"recipientId": "hanna",
"timestamp": "01:10:00 PM"
},
{
"data": "Works for me.",
"senderId": "hanna",
"recipientId": "grace",
"timestamp": "01:15:00 PM"
}
]
},
{
"id1": "grace",
"id2": "ianda",
"messages": [
{
"data": "Ian, did you get the message I sent?",
"senderId": "grace",
"recipientId": "ianda",
"timestamp": "01:20:00 PM"
},
{
"data": "Yes, I'll respond soon.",
"senderId": "ianda",
"recipientId": "grace",
"timestamp": "01:25:00 PM"
},
{
"data": "Thanks, appreciate it!",
"senderId": "grace",
"recipientId": "ianda",
"timestamp": "01:30:00 PM"
},
{
"data": "You're welcome!",
"senderId": "ianda",
"recipientId": "grace",
"timestamp": "01:35:00 PM"
}
]
},
{
"id1": "hanna",
"id2": "ianda",
"messages": [
{
"data": "Ian, do you have a minute?",
"senderId": "hanna",
"recipientId": "ianda",
"timestamp": "01:40:00 PM"
},
{
"data": "Yes, what do you need?",
"senderId": "ianda",
"recipientId": "hanna",
"timestamp": "01:45:00 PM"
},
{
"data": "Just a quick update on the project.",
"senderId": "hanna",
"recipientId": "ianda",
"timestamp": "01:50:00 PM"
},
{
"data": "I'll email you the details.",
"senderId": "ianda",
"recipientId": "hanna",
"timestamp": "01:55:00 PM"
}
]
},
{
"id1": "hanna",
"id2": "jillt",
"messages": [
{
"data": "Jill, can we talk tomorrow?",
"senderId": "hanna",
"recipientId": "jillt",
"timestamp": "02:00:00 PM"
},
{
"data": "Yes, I'm free after 2 PM.",
"senderId": "jillt",
"recipientId": "hanna",
"timestamp": "02:05:00 PM"
},
{
"data": "Perfect, see you then.",
"senderId": "hanna",
"recipientId": "jillt",
"timestamp": "02:10:00 PM"
},
{
"data": "Looking forward to it.",
"senderId": "jillt",
"recipientId": "hanna",
"timestamp": "02:15:00 PM"
}
]
},
{
"id1": "ianda",
"id2": "jillt",
"messages": [
{
"data": "Jill, I have the files you requested.",
"senderId": "ianda",
"recipientId": "jillt",
"timestamp": "02:20:00 PM"
},
{
"data": "Thanks, please send them over.",
"senderId": "jillt",
"recipientId": "ianda",
"timestamp": "02:25:00 PM"
},
{
"data": "I'll send them right now.",
"senderId": "ianda",
"recipientId": "jillt",
"timestamp": "02:30:00 PM"
},
{
"data": "Great, thanks again!",
"senderId": "jillt",
"recipientId": "ianda",
"timestamp": "02:35:00 PM"
}
]
}
]

91
stubs/api/chat/index.js Normal file
View File

@@ -0,0 +1,91 @@
const chatRouter = require('express').Router();
module.exports = chatRouter;
const { getChatFromDB, getUsersChats, addChatToDB, getUserFromDB,
addMessageToChat} = require('../db');
chatRouter.get('/item/:id1/:id2', (req, res) => {
const { id1, id2 } = req.params;
console.log("Request get in /chat:", id1, id2);
if (id1 === id2) {
res.status(400).send({message: 'Ids should be different'});
return;
}
const chat = getChatFromDB(id1, id2);
if (chat) {
res.status(200).send({chat});
} else {
res.status(404).send({message: 'Chat was not found'});
}
})
chatRouter.post('/item/:id1/:id2', (req, res) => {
const { id1, id2 } = req.params;
console.log("Request post in /chat:", id1, id2);
if (id1 === id2) {
res.status(400).send({message: 'Ids should be different'});
return;
}
const chat = getChatFromDB(id1, id2);
if (chat) {
// Chat already exists
res.status(200).send({chat});
} else {
if (!getUserFromDB(id1) || !getUserFromDB(id2)) {
res.status(404).send({message: 'Such interlocutor does not exist'});
} else {
// Creating new chat
const newChat = {
id1: id1,
id2: id2,
messages: []
}
addChatToDB(newChat);
res.status(200).send({newChat});
}
}
})
chatRouter.get('/list/:id', (req, res) => {
const { id } = req.params;
console.log("Request get /list in /chat:", id);
const userChats = getUsersChats(id);
if (!userChats) {
res.status(404).send({message: 'Error with retrieving chats'});
} else {
res.status(200).send({chats: userChats});
}
})
chatRouter.post('/message/:sender/:receiver', (req, res) => {
const { sender, receiver } = req.params;
const { message } = req.body;
console.log("Request post /message in /chat:", sender, receiver, message);
const chat = getChatFromDB(sender, receiver);
if (!chat) {
// Chat already exists
res.status(400).send({message: "Such chat does not exist"});
} else {
if (!getUserFromDB(sender) || !getUserFromDB(receiver)) {
res.status(404).send({message: 'Such people do not exist'});
} else {
// Add new message
addMessageToChat(chat, message);
res.status(200).send({});
}
}
})

74
stubs/api/db.js Normal file
View File

@@ -0,0 +1,74 @@
// Read already defined users (pseudo-DB)
const users = require('./auth/users.json');
const chats = require('./chat/chats.json');
const getUserFromDB = (userID) => {
if (!userID) {return false;}
// Accessing 'DB'
const user = users.find((user) => user.id === userID);
if (user) {
return user;
} else {
return false;
}
}
const deleteUserFromDB = (userID) => {
const index = users.findIndex(item => item.id === userID);
if (index !== -1) {
users.splice(index, 1);
}
}
const addUserToDB = (user) => {
users.push(user);
}
const getChatFromDB = (firstID, secondID) => {
if (!firstID || !secondID) {return false;}
// Accessing 'DB'
const chat = chats.find((item) =>
(item.id1 === firstID && item.id2 === secondID) || (item.id1 === secondID && item.id2 === firstID));
if (chat) {
return chat;
} else {
return false;
}
}
const getUsersChats = (userID) => {
if (!userID) {return false;}
const userChats = chats.filter((chat) => (chat.id1 === userID || chat.id2 === userID));
if (userChats) {
return userChats;
} else {
return false;
}
}
const addMessageToChat = (chat, msg) => {
chat.messages.push(msg);
}
const deleteChatFromDB = (firstID, secondID) => {
const index = chats.findIndex(item =>
(item.id1 === firstID && item.id2 === secondID) || (item.id1 === secondID && item.id2 === firstID));
if (index !== -1) {
chats.splice(index, 1);
}
}
const addChatToDB = (chat) => {
chats.push(chat);
}
module.exports = {users, chats, getUserFromDB, getChatFromDB, addUserToDB,
deleteUserFromDB, addChatToDB, deleteChatFromDB, getUsersChats, addMessageToChat}

View File

@@ -1,3 +1,17 @@
const changeRouter = require("./change");
const authRouter = require("./auth");
const chatRouter = require("./chat");
const router = require('express').Router();
const delay = require('./middlewares/delay');
const verify = require('./middlewares/verify');
module.exports = router;
// router.use(delay(300));
// router.use('/books', delay, booksRouter);
router.use('/enterfront/auth', authRouter);
router.use('/enterfront/change', verify, changeRouter);
router.use('/enterfront/chat', verify, chatRouter)

View File

@@ -0,0 +1,5 @@
const delay = (ms = 1000) => (req, res, next) => {
setTimeout(next, ms)
}
module.exports = delay

View File

@@ -0,0 +1,23 @@
const jwt = require('jsonwebtoken');
require('dotenv').config();
const TOKEN_KEY = process.env.TOKEN_KEY;
function verifyToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) {
return res.status(403).send({ message: 'No token provided' });
}
// Verify token
jwt.verify(token, TOKEN_KEY, (err, decoded) => {
if (err) {
return res.status(401).send({ message: 'Unauthorized' });
}
next(); // Proceed to the next middleware or route
});
}
module.exports = verifyToken;