Compare commits
18 Commits
v0.2.3
...
13f4d43761
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13f4d43761 | ||
|
|
22a549e269 | ||
|
|
25c3e16c74 | ||
|
|
86db5df813 | ||
|
|
d1e824ab77 | ||
|
|
073c61977f | ||
|
|
1301c145e8 | ||
|
|
ac6dffa129 | ||
|
|
8e4cad4c85 | ||
|
|
d1091e570b | ||
|
|
4a5041a65e | ||
|
|
8d0fadc906 | ||
|
|
6bea0428f4 | ||
|
|
660f2e9d5c | ||
|
|
a9b683797b | ||
|
|
a3484f4525 | ||
|
|
876ef28221 | ||
|
|
e6231f86b4 |
@@ -23,5 +23,6 @@ module.exports = {
|
||||
},
|
||||
config: {
|
||||
"enterfront.api": "/api",
|
||||
// paste stand URL to config
|
||||
},
|
||||
};
|
||||
|
||||
153
package-lock.json
generated
153
package-lock.json
generated
@@ -1,25 +1,28 @@
|
||||
{
|
||||
"name": "enterfront",
|
||||
"version": "0.2.3",
|
||||
"version": "0.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "enterfront",
|
||||
"version": "0.2.3",
|
||||
"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",
|
||||
"ws": "^8.18.0"
|
||||
@@ -3843,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",
|
||||
@@ -4078,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",
|
||||
@@ -4684,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",
|
||||
@@ -4694,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",
|
||||
@@ -6695,6 +6734,40 @@
|
||||
"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",
|
||||
@@ -6735,6 +6808,27 @@
|
||||
"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",
|
||||
@@ -6815,6 +6909,48 @@
|
||||
"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",
|
||||
@@ -8140,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",
|
||||
|
||||
@@ -5,13 +5,16 @@
|
||||
"@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",
|
||||
"ws": "^8.18.0"
|
||||
@@ -24,5 +27,5 @@
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"name": "enterfront",
|
||||
"version": "0.2.3"
|
||||
"version": "0.3.0"
|
||||
}
|
||||
|
||||
34
src/app.tsx
34
src/app.tsx
@@ -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(
|
||||
<BrowserRouter>
|
||||
<Dashboard />
|
||||
</BrowserRouter>
|
||||
<div>
|
||||
<BrowserRouter>
|
||||
<Dashboard />
|
||||
</BrowserRouter>
|
||||
|
||||
<ToastContainer/>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
60
src/backend/api.js
Normal file
60
src/backend/api.js
Normal 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};
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
51
src/components/account/AccountButtons.jsx
Normal file
51
src/components/account/AccountButtons.jsx
Normal 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;
|
||||
11
src/components/account/ActionButton.jsx
Normal file
11
src/components/account/ActionButton.jsx
Normal 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;
|
||||
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 an account :(</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HelloItem;
|
||||
54
src/components/account/index.css
Normal file
54
src/components/account/index.css
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
11
src/components/home/Search.jsx
Normal file
11
src/components/home/Search.jsx
Normal 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;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -1,7 +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;
|
||||
|
||||
@@ -2,6 +2,9 @@ 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 = [
|
||||
"😀",
|
||||
@@ -74,7 +77,7 @@ const emojis = [
|
||||
];
|
||||
|
||||
const Chat = () => {
|
||||
const [interlocutorId, setInterlocutorId] = useState(0);
|
||||
const [interlocutorId, setInterlocutorId] = useState("");
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [newMessage, setNewMessage] = useState("");
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||
@@ -82,10 +85,21 @@ const Chat = () => {
|
||||
const chatRef = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [myId, setMyId] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const id = parseInt(localStorage.getItem("interlocutorId"), 10) || 0;
|
||||
// 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 = () => {
|
||||
@@ -113,23 +127,53 @@ const Chat = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
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 = {
|
||||
type: "message",
|
||||
senderId: "yourUserId",
|
||||
senderId: myId,
|
||||
recipientId: interlocutorId,
|
||||
message: newMessage,
|
||||
data: newMessage,
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
};
|
||||
socket.current.send(JSON.stringify(messageData));
|
||||
setMessages((prev) => [...prev, messageData]);
|
||||
|
||||
sendMessageToDB(messageData).then();
|
||||
|
||||
console.log('format:', messageData);
|
||||
|
||||
setNewMessage("");
|
||||
}
|
||||
};
|
||||
@@ -162,12 +206,12 @@ const Chat = () => {
|
||||
<div
|
||||
key={index}
|
||||
className={`message-bubble ${
|
||||
msg.senderId === "yourUserId" ? "sent" : "received"
|
||||
msg.senderId === myId ? "sent" : "received"
|
||||
}`}
|
||||
>
|
||||
<div className="message-content">
|
||||
<b>{msg.senderId === "yourUserId" ? "You" : "Interlocutor"}:</b>{" "}
|
||||
{msg.message}
|
||||
<b>{msg.senderId === myId ? "You" : "They"}:</b>{" "}
|
||||
{msg.data}
|
||||
</div>
|
||||
<span className="message-timestamp">{msg.timestamp}</span>
|
||||
</div>
|
||||
@@ -180,7 +224,7 @@ const Chat = () => {
|
||||
onChange={(e) => setNewMessage(e.target.value)}
|
||||
placeholder="Type a message..."
|
||||
className="chat-input"
|
||||
onKeyPress={handleKeyPress}
|
||||
onKeyDown={handleKeyPress}
|
||||
/>
|
||||
<button
|
||||
className="emoji-button"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) => {
|
||||
console.log('Sign In!')
|
||||
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 (
|
||||
|
||||
@@ -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}/>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import url("css/init.css");
|
||||
@import url("css/home.css");
|
||||
@import url("css/input.css");
|
||||
@import url("css/input.css");
|
||||
@import url("css/account.css");
|
||||
77
stubs/api/auth/index.js
Normal file
77
stubs/api/auth/index.js
Normal 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
52
stubs/api/auth/users.json
Normal 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
69
stubs/api/change/index.js
Normal 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
692
stubs/api/chat/chats.json
Normal 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
91
stubs/api/chat/index.js
Normal 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
74
stubs/api/db.js
Normal 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}
|
||||
@@ -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)
|
||||
|
||||
5
stubs/api/middlewares/delay.js
Normal file
5
stubs/api/middlewares/delay.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const delay = (ms = 1000) => (req, res, next) => {
|
||||
setTimeout(next, ms)
|
||||
}
|
||||
|
||||
module.exports = delay
|
||||
23
stubs/api/middlewares/verify.js
Normal file
23
stubs/api/middlewares/verify.js
Normal 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;
|
||||
Reference in New Issue
Block a user