auth with api
This commit is contained in:
parent
a3484f4525
commit
a9b683797b
149
package-lock.json
generated
149
package-lock.json
generated
@ -13,13 +13,16 @@
|
|||||||
"@ijl/cli": "^5.1.0",
|
"@ijl/cli": "^5.1.0",
|
||||||
"@types/react": "^18.3.5",
|
"@types/react": "^18.3.5",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
"emoji-mart": "^5.6.0",
|
"emoji-mart": "^5.6.0",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-emoji-picker": "^1.0.13",
|
"react-emoji-picker": "^1.0.13",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-router-dom": "^6.26.1",
|
"react-router-dom": "^6.26.1",
|
||||||
|
"react-toastify": "^10.0.5",
|
||||||
"styled-components": "^6.1.13",
|
"styled-components": "^6.1.13",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
@ -3843,6 +3846,12 @@
|
|||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"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": {
|
"node_modules/buffer-from": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
@ -4078,6 +4087,15 @@
|
|||||||
"node": ">=6"
|
"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": {
|
"node_modules/collection-visit": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
|
||||||
@ -4684,6 +4702,18 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
"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": {
|
"node_modules/duplexer": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||||
@ -4694,6 +4724,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
"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": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@ -6695,6 +6734,40 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"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": {
|
"node_modules/jstransform": {
|
||||||
"version": "11.0.3",
|
"version": "11.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.3.tgz",
|
||||||
@ -6735,6 +6808,27 @@
|
|||||||
"node": ">=0.8.0"
|
"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": {
|
"node_modules/keygrip": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
|
||||||
@ -6815,6 +6909,48 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
"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": {
|
"node_modules/loose-envify": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
@ -8140,6 +8276,19 @@
|
|||||||
"react-dom": ">=16.8"
|
"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": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
@ -7,11 +7,13 @@
|
|||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"emoji-mart": "^5.6.0",
|
"emoji-mart": "^5.6.0",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-emoji-picker": "^1.0.13",
|
"react-emoji-picker": "^1.0.13",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-router-dom": "^6.26.1",
|
"react-router-dom": "^6.26.1",
|
||||||
|
"react-toastify": "^10.0.5",
|
||||||
"styled-components": "^6.1.13",
|
"styled-components": "^6.1.13",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
|
32
src/app.tsx
32
src/app.tsx
@ -1,21 +1,39 @@
|
|||||||
import React from 'react';
|
import React, {useEffect} from 'react';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
|
|
||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { Dashboard } from './dashboard';
|
import { Dashboard } from './dashboard';
|
||||||
|
|
||||||
import { getConfigValue } from "@brojs/cli";
|
import {ToastContainer} from "react-toastify";
|
||||||
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
|
|
||||||
|
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
import {displayMessage} from "./backend/notifications/notifications.js";
|
||||||
const BASE_API_URL = getConfigValue("enterfront.api");
|
import {MessageType} from "./backend/notifications/message";
|
||||||
|
|
||||||
// fetch(`${BASE_API_URL}/books/list`)
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = 'Enterfront';
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const msg = localStorage.getItem('message');
|
||||||
|
|
||||||
|
if (!msg) return;
|
||||||
|
|
||||||
|
displayMessage(msg, MessageType.SUCCESS);
|
||||||
|
localStorage.removeItem('message');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return(
|
return(
|
||||||
|
<div>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Dashboard />
|
<Dashboard />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|
||||||
|
<ToastContainer/>
|
||||||
|
</div>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
45
src/backend/api.js
Normal file
45
src/backend/api.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import {getConfigValue} from "@brojs/cli";
|
||||||
|
|
||||||
|
export const BASE_API_URL = "http://localhost:8099" + getConfigValue("enterfront.api");
|
||||||
|
|
||||||
|
// fetch(`${BASE_API_URL}/books/list`)
|
||||||
|
|
||||||
|
export async function post(path, body) {
|
||||||
|
const res = await fetch(`${BASE_API_URL}${path}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
const data = await res.json();
|
||||||
|
console.log("Received post:", data);
|
||||||
|
|
||||||
|
return {ok: true, data: data};
|
||||||
|
} else {
|
||||||
|
const errorData = await res.json();
|
||||||
|
console.log("Error during post:", errorData.message);
|
||||||
|
|
||||||
|
return {ok: false, data: errorData};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(path){
|
||||||
|
const res = await fetch(`${BASE_API_URL}${path}`, {
|
||||||
|
method: "GET"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
const data = await res.json();
|
||||||
|
console.log("Received get:", data);
|
||||||
|
|
||||||
|
return {ok: true, data: data};
|
||||||
|
} else {
|
||||||
|
const errorData = await res.json();
|
||||||
|
console.log("Error during get:", errorData.message);
|
||||||
|
|
||||||
|
return {ok: false, data: errorData};
|
||||||
|
}
|
||||||
|
}
|
6
src/backend/notifications/message.tsx
Normal file
6
src/backend/notifications/message.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export enum MessageType {
|
||||||
|
ERROR,
|
||||||
|
SUCCESS,
|
||||||
|
INFO,
|
||||||
|
WARN
|
||||||
|
}
|
28
src/backend/notifications/notifications.js
Normal file
28
src/backend/notifications/notifications.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -5,10 +5,13 @@ import ActionButton from "./ActionButton.jsx";
|
|||||||
const AccountButtons = (props) => {
|
const AccountButtons = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className="account-buttons">
|
<div className="account-buttons">
|
||||||
|
{props.registered ? (
|
||||||
|
<>
|
||||||
<ActionButton title={"Exit"} action={props.exitHandler}/>
|
<ActionButton title={"Exit"} action={props.exitHandler}/>
|
||||||
<ActionButton title={"Change Name"} action={props.changeNameHandler}/>
|
<ActionButton title={"Change Name"} action={props.changeNameHandler}/>
|
||||||
<ActionButton title={"Change Pass"} action={props.changePassHandler}/>
|
<ActionButton title={"Change Pass"} action={props.changePassHandler}/>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
<a className="MyButton mclaren-regular" href={URLs.home.url}>Back</a>
|
<a className="MyButton mclaren-regular" href={URLs.home.url}>Back</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
18
src/components/account/HelloItem.jsx
Normal file
18
src/components/account/HelloItem.jsx
Normal 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 account :(</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HelloItem;
|
@ -3,6 +3,8 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.account-buttons a {
|
.account-buttons a {
|
||||||
@ -20,10 +22,33 @@
|
|||||||
color: black;
|
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) {
|
@media only screen and (max-width: 800px) {
|
||||||
.account-buttons a {
|
.account-buttons a {
|
||||||
font-size: 2.5vh;
|
font-size: 2.5vh;
|
||||||
width: 60vw;
|
width: 60vw;
|
||||||
margin-top: 3vh;
|
margin-top: 3vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hello-item-class h1 {
|
||||||
|
font-size: 5vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hello-item-class p {
|
||||||
|
font-size: 2vh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,53 @@
|
|||||||
import React from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import AccountButtons from "../components/account/AccountButtons.jsx";
|
import AccountButtons from "../components/account/AccountButtons.jsx";
|
||||||
import userIcon from "../../images/user.svg";
|
import userIcon from "../../images/user.svg";
|
||||||
|
import {get} from "../backend/api";
|
||||||
|
import {displayMessage} from "../backend/notifications/notifications";
|
||||||
|
import {MessageType} from "../backend/notifications/message";
|
||||||
|
import HelloItem from "../components/account/HelloItem.jsx";
|
||||||
|
|
||||||
const Account = () => {
|
const Account = () => {
|
||||||
const exitHandler = () => {}
|
const exitHandler = () => {
|
||||||
|
localStorage.removeItem("username");
|
||||||
|
localStorage.removeItem("token");
|
||||||
|
|
||||||
|
localStorage.setItem("message", "Exited successfully!");
|
||||||
|
window.location.href = "/";
|
||||||
|
}
|
||||||
const changeNameHandler = () => {}
|
const changeNameHandler = () => {}
|
||||||
const changePassHandler = () => {}
|
const changePassHandler = () => {}
|
||||||
|
|
||||||
|
const [nickname, setNickname] = useState("");
|
||||||
|
const [id, setId] = useState("");
|
||||||
|
|
||||||
|
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", MessageType.ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setNickname(data.user.nickname);
|
||||||
|
setId(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {getUser().then()}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="account-items">
|
<div className="account-items">
|
||||||
<img src={userIcon} alt="user" />
|
<img src={userIcon} alt="user" />
|
||||||
|
<HelloItem nickname={nickname} id={id} />
|
||||||
<AccountButtons
|
<AccountButtons
|
||||||
exitHandler={exitHandler}
|
exitHandler={exitHandler}
|
||||||
changeNameHandler={changeNameHandler}
|
changeNameHandler={changeNameHandler}
|
||||||
changePassHandler={changePassHandler}
|
changePassHandler={changePassHandler}
|
||||||
|
registered={!!nickname}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -3,12 +3,41 @@ import InputField from "../components/reg/InputField.jsx";
|
|||||||
import LoginButtons from "../components/reg/LoginButtons.jsx";
|
import LoginButtons from "../components/reg/LoginButtons.jsx";
|
||||||
import LoginTitle from "../components/reg/loginTitle.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 SignIn = () => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
|
|
||||||
const submit = (e) => {
|
const [nameErrorsCounter, setNameErrorsCounter] = useState(0);
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
console.log('Sign In!')
|
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 (
|
return (
|
||||||
|
@ -2,20 +2,59 @@ import React, {useState} from 'react';
|
|||||||
import InputField from "../components/reg/InputField.jsx";
|
import InputField from "../components/reg/InputField.jsx";
|
||||||
import LoginButtons from "../components/reg/LoginButtons.jsx";
|
import LoginButtons from "../components/reg/LoginButtons.jsx";
|
||||||
import LoginTitle from "../components/reg/loginTitle.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 SignUp = () => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
|
const [nickname, setNickname] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [repeatPassword, setRepeatPassword] = useState("");
|
const [repeatPassword, setRepeatPassword] = useState("");
|
||||||
|
|
||||||
const submit = (e) => {
|
async function login(name, password) {
|
||||||
console.log('Sign Up!')
|
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 (
|
return (
|
||||||
<div className="LoginList">
|
<div className="LoginList">
|
||||||
<LoginTitle/>
|
<LoginTitle/>
|
||||||
<InputField title="Name" setValue={setName} value={name}/>
|
<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="Password" setValue={setPassword} value={password}/>
|
||||||
<InputField title="Repeat Password" setValue={setRepeatPassword} value={repeatPassword}/>
|
<InputField title="Repeat Password" setValue={setRepeatPassword} value={repeatPassword}/>
|
||||||
|
|
||||||
|
87
stubs/api/auth/index.js
Normal file
87
stubs/api/auth/index.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
const authRouter = require('express').Router();
|
||||||
|
|
||||||
|
// For cryptography
|
||||||
|
// const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
|
// For creating tokens
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const TOKEN_KEY = "5frv12e4few3r"
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = authRouter;
|
||||||
|
|
||||||
|
// Read already defined users (pseudo-DB)
|
||||||
|
const users = require('./users.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)'});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid authentication
|
||||||
|
if (!password || password !== user.password) {
|
||||||
|
res.status(401).send({message: 'Invalid credentials (password)'});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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'});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name || !password || !nickname) {
|
||||||
|
res.status(401).send({message: 'Empty or invalid fields'});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to 'DB'
|
||||||
|
const newUser = {id: name, password: password, nickname: nickname};
|
||||||
|
users.push(newUser);
|
||||||
|
|
||||||
|
res.status(200).send({user: newUser});
|
||||||
|
})
|
12
stubs/api/auth/users.json
Normal file
12
stubs/api/auth/users.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"nickname": "Nick",
|
||||||
|
"password": "1234",
|
||||||
|
"id": "Nickolaus_SDR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nickname": "User",
|
||||||
|
"password": "1234",
|
||||||
|
"id": "id"
|
||||||
|
}
|
||||||
|
]
|
@ -1,8 +1,13 @@
|
|||||||
const booksRouter = require("./books");
|
const booksRouter = require("./books");
|
||||||
|
const authRouter = require("./auth");
|
||||||
|
|
||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
|
|
||||||
const delay = require('./middlewares/delay');
|
const delay = require('./middlewares/delay');
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
// router.use(delay(300));
|
// router.use(delay(300));
|
||||||
router.use('/books', delay, booksRouter);
|
// router.use('/books', delay, booksRouter);
|
||||||
|
|
||||||
|
router.use('/auth', authRouter);
|
||||||
|
Loading…
Reference in New Issue
Block a user