Spaces:
Runtime error
Runtime error
Commit
·
8830402
1
Parent(s):
a5be118
added login and signup
Browse files- client/.gitignore +3 -0
- client/package-lock.json +39 -0
- client/package.json +1 -0
- client/src/App.js +21 -21
- client/src/ChatPage/ChatPage.jsx +43 -0
- client/src/ChatPage/index.jsx +3 -0
- client/src/LoginPage/LoginPage.jsx +72 -0
- client/src/LoginPage/LoginPage.module.css +65 -0
- client/src/LoginPage/index.jsx +3 -0
- client/src/PrivateRoutes.jsx +27 -0
- client/src/SignupPage/SignupPage.jsx +84 -0
- client/src/SignupPage/SignupPage.module.css +65 -0
- client/src/SignupPage/index.jsx +3 -0
- server/auth.ts +1 -1
- server/index.ts +40 -8
- server/schemas.ts +2 -1
client/.gitignore
CHANGED
|
@@ -21,3 +21,6 @@
|
|
| 21 |
npm-debug.log*
|
| 22 |
yarn-debug.log*
|
| 23 |
yarn-error.log*
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
npm-debug.log*
|
| 22 |
yarn-debug.log*
|
| 23 |
yarn-error.log*
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
.env
|
client/package-lock.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
| 14 |
"@testing-library/user-event": "^13.5.0",
|
| 15 |
"react": "^18.2.0",
|
| 16 |
"react-dom": "^18.2.0",
|
|
|
|
| 17 |
"react-scripts": "^5.0.1",
|
| 18 |
"socket.io-client": "^4.7.4",
|
| 19 |
"web-vitals": "^2.1.4"
|
|
@@ -3362,6 +3363,14 @@
|
|
| 3362 |
}
|
| 3363 |
}
|
| 3364 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3365 |
"node_modules/@rollup/plugin-babel": {
|
| 3366 |
"version": "5.3.1",
|
| 3367 |
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
|
@@ -15053,6 +15062,36 @@
|
|
| 15053 |
"node": ">=0.10.0"
|
| 15054 |
}
|
| 15055 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15056 |
"node_modules/react-scripts": {
|
| 15057 |
"version": "5.0.1",
|
| 15058 |
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
|
|
|
|
| 14 |
"@testing-library/user-event": "^13.5.0",
|
| 15 |
"react": "^18.2.0",
|
| 16 |
"react-dom": "^18.2.0",
|
| 17 |
+
"react-router-dom": "^6.22.2",
|
| 18 |
"react-scripts": "^5.0.1",
|
| 19 |
"socket.io-client": "^4.7.4",
|
| 20 |
"web-vitals": "^2.1.4"
|
|
|
|
| 3363 |
}
|
| 3364 |
}
|
| 3365 |
},
|
| 3366 |
+
"node_modules/@remix-run/router": {
|
| 3367 |
+
"version": "1.15.2",
|
| 3368 |
+
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz",
|
| 3369 |
+
"integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==",
|
| 3370 |
+
"engines": {
|
| 3371 |
+
"node": ">=14.0.0"
|
| 3372 |
+
}
|
| 3373 |
+
},
|
| 3374 |
"node_modules/@rollup/plugin-babel": {
|
| 3375 |
"version": "5.3.1",
|
| 3376 |
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
|
|
|
| 15062 |
"node": ">=0.10.0"
|
| 15063 |
}
|
| 15064 |
},
|
| 15065 |
+
"node_modules/react-router": {
|
| 15066 |
+
"version": "6.22.2",
|
| 15067 |
+
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz",
|
| 15068 |
+
"integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==",
|
| 15069 |
+
"dependencies": {
|
| 15070 |
+
"@remix-run/router": "1.15.2"
|
| 15071 |
+
},
|
| 15072 |
+
"engines": {
|
| 15073 |
+
"node": ">=14.0.0"
|
| 15074 |
+
},
|
| 15075 |
+
"peerDependencies": {
|
| 15076 |
+
"react": ">=16.8"
|
| 15077 |
+
}
|
| 15078 |
+
},
|
| 15079 |
+
"node_modules/react-router-dom": {
|
| 15080 |
+
"version": "6.22.2",
|
| 15081 |
+
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz",
|
| 15082 |
+
"integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==",
|
| 15083 |
+
"dependencies": {
|
| 15084 |
+
"@remix-run/router": "1.15.2",
|
| 15085 |
+
"react-router": "6.22.2"
|
| 15086 |
+
},
|
| 15087 |
+
"engines": {
|
| 15088 |
+
"node": ">=14.0.0"
|
| 15089 |
+
},
|
| 15090 |
+
"peerDependencies": {
|
| 15091 |
+
"react": ">=16.8",
|
| 15092 |
+
"react-dom": ">=16.8"
|
| 15093 |
+
}
|
| 15094 |
+
},
|
| 15095 |
"node_modules/react-scripts": {
|
| 15096 |
"version": "5.0.1",
|
| 15097 |
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
|
client/package.json
CHANGED
|
@@ -9,6 +9,7 @@
|
|
| 9 |
"@testing-library/user-event": "^13.5.0",
|
| 10 |
"react": "^18.2.0",
|
| 11 |
"react-dom": "^18.2.0",
|
|
|
|
| 12 |
"react-scripts": "^5.0.1",
|
| 13 |
"socket.io-client": "^4.7.4",
|
| 14 |
"web-vitals": "^2.1.4"
|
|
|
|
| 9 |
"@testing-library/user-event": "^13.5.0",
|
| 10 |
"react": "^18.2.0",
|
| 11 |
"react-dom": "^18.2.0",
|
| 12 |
+
"react-router-dom": "^6.22.2",
|
| 13 |
"react-scripts": "^5.0.1",
|
| 14 |
"socket.io-client": "^4.7.4",
|
| 15 |
"web-vitals": "^2.1.4"
|
client/src/App.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
| 1 |
import './App.css';
|
| 2 |
-
import
|
| 3 |
-
import
|
| 4 |
-
import
|
|
|
|
|
|
|
| 5 |
|
| 6 |
function App() {
|
| 7 |
-
// const socket = io("ws://localhost:3000");
|
| 8 |
-
|
| 9 |
-
// socket.on("connect",()=>{
|
| 10 |
-
// console.log("hi");
|
| 11 |
-
// })
|
| 12 |
|
| 13 |
-
|
| 14 |
-
method:"POST",
|
| 15 |
-
credentials:"include",
|
| 16 |
-
headers:{
|
| 17 |
-
"Content-Type":"Application/json",
|
| 18 |
-
},
|
| 19 |
-
body:JSON.stringify({
|
| 20 |
-
"username":"anuj",
|
| 21 |
-
"password":"123"
|
| 22 |
-
})
|
| 23 |
-
}).then(res=>res.json()).then(data=>console.log(data));
|
| 24 |
|
| 25 |
return (
|
| 26 |
<div className="App">
|
| 27 |
-
<
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
</div>
|
| 30 |
);
|
| 31 |
}
|
|
|
|
| 1 |
import './App.css';
|
| 2 |
+
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
| 3 |
+
import ChatPage from './ChatPage';
|
| 4 |
+
import LoginPage from './LoginPage';
|
| 5 |
+
import SignupPage from './SignupPage';
|
| 6 |
+
import PrivateRoutes from './PrivateRoutes';
|
| 7 |
|
| 8 |
function App() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
+
// console.log(process.env.REACT_APP_BACKEND_URL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
return (
|
| 13 |
<div className="App">
|
| 14 |
+
<Router>
|
| 15 |
+
<Routes>
|
| 16 |
+
<Route element={<LoginPage />} path="/login" />
|
| 17 |
+
<Route element={<SignupPage />} path="/signup" />
|
| 18 |
+
<Route element={<ChatPage />} path="/" />
|
| 19 |
+
|
| 20 |
+
{/* <Route element={<PrivateRoutes />}>
|
| 21 |
+
<Route element={<ChatPage />} path="/" />
|
| 22 |
+
</Route> */}
|
| 23 |
+
|
| 24 |
+
</Routes>
|
| 25 |
+
</Router>
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
|
| 29 |
</div>
|
| 30 |
);
|
| 31 |
}
|
client/src/ChatPage/ChatPage.jsx
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import Chat from '../components/Chat/Chat';
|
| 3 |
+
import Contacts from '../components/Contacts/Contacts';
|
| 4 |
+
import { io } from "socket.io-client";
|
| 5 |
+
|
| 6 |
+
function ChatPage() {
|
| 7 |
+
// fetch("http://localhost:3000/signin", {
|
| 8 |
+
// method: "POST",
|
| 9 |
+
// credentials: "include",
|
| 10 |
+
// headers: {
|
| 11 |
+
// "Content-Type": "Application/json",
|
| 12 |
+
// },
|
| 13 |
+
// body: JSON.stringify({
|
| 14 |
+
// "username": "anuj",
|
| 15 |
+
// "password": "123"
|
| 16 |
+
// })
|
| 17 |
+
// }).then(res => res.json()).then(data => console.log(data));
|
| 18 |
+
|
| 19 |
+
const socket = io("ws://localhost:3000", {
|
| 20 |
+
withCredentials: true,
|
| 21 |
+
});
|
| 22 |
+
|
| 23 |
+
socket.on("connect", () => {
|
| 24 |
+
console.log("connected", socket.id);
|
| 25 |
+
})
|
| 26 |
+
|
| 27 |
+
socket.on("error", () => {
|
| 28 |
+
console.log("socket error");
|
| 29 |
+
})
|
| 30 |
+
|
| 31 |
+
// setInterval(()=>{
|
| 32 |
+
// socket.emit("foobar","im alive")
|
| 33 |
+
// },2000);
|
| 34 |
+
|
| 35 |
+
return (
|
| 36 |
+
<>
|
| 37 |
+
<Contacts />
|
| 38 |
+
<Chat />
|
| 39 |
+
</>
|
| 40 |
+
);
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
export default ChatPage;
|
client/src/ChatPage/index.jsx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ChatPage from "./ChatPage";
|
| 2 |
+
|
| 3 |
+
export default ChatPage;
|
client/src/LoginPage/LoginPage.jsx
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState } from 'react'
|
| 2 |
+
import styles from "./LoginPage.module.css";
|
| 3 |
+
import { Link, redirect,useNavigate } from 'react-router-dom';
|
| 4 |
+
|
| 5 |
+
function LoginPage() {
|
| 6 |
+
|
| 7 |
+
const login_url = process.env.REACT_APP_BACKEND_URL + "/login";
|
| 8 |
+
const [errors, setErrors] = useState({});
|
| 9 |
+
const navigate = useNavigate();
|
| 10 |
+
|
| 11 |
+
const handleSubmit = async (e) => {
|
| 12 |
+
e.preventDefault();
|
| 13 |
+
// console.log(e.target);
|
| 14 |
+
|
| 15 |
+
setErrors({});
|
| 16 |
+
const res = await fetch(login_url, {
|
| 17 |
+
method: "POST",
|
| 18 |
+
credentials: "include",
|
| 19 |
+
body: new FormData(e.target),
|
| 20 |
+
})
|
| 21 |
+
|
| 22 |
+
if (!res.ok) {
|
| 23 |
+
// setErrors(re)
|
| 24 |
+
const data = await res.json();
|
| 25 |
+
if ("error" in data) {
|
| 26 |
+
setErrors(data);
|
| 27 |
+
}
|
| 28 |
+
if ("field_error" in data) {
|
| 29 |
+
setErrors(data.field_error);
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
else {
|
| 33 |
+
// handle redirect to home page
|
| 34 |
+
console.log("success");
|
| 35 |
+
navigate("/");
|
| 36 |
+
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
return (
|
| 43 |
+
<div id={styles.login_page}>
|
| 44 |
+
<form id={styles.login_form} onSubmit={handleSubmit}>
|
| 45 |
+
<h2>Login</h2>
|
| 46 |
+
{errors.error && (
|
| 47 |
+
<p className={styles.error_msg}>{errors.error}</p>
|
| 48 |
+
)}
|
| 49 |
+
<div className={styles.input_box}>
|
| 50 |
+
<label htmlFor="username">Username</label>
|
| 51 |
+
<input type="text" name="username" id="username" />
|
| 52 |
+
{errors.username && (
|
| 53 |
+
<p className={styles.error_msg}>{errors.username[0]}</p>
|
| 54 |
+
)}
|
| 55 |
+
</div>
|
| 56 |
+
<div className={styles.input_box}>
|
| 57 |
+
<label htmlFor="username">Password</label>
|
| 58 |
+
<input type="password" name="password" id="password" />
|
| 59 |
+
{errors.password && (
|
| 60 |
+
<p className={styles.error_msg}>{errors.password[0]}</p>
|
| 61 |
+
)}
|
| 62 |
+
</div>
|
| 63 |
+
|
| 64 |
+
<input id={styles.submit_btn} type="submit" value="login" />
|
| 65 |
+
|
| 66 |
+
<Link to="/signup" id={styles.redirect_link}>Create new account</Link>
|
| 67 |
+
</form>
|
| 68 |
+
</div>
|
| 69 |
+
)
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
export default LoginPage
|
client/src/LoginPage/LoginPage.module.css
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#login_page{
|
| 2 |
+
width:100%;
|
| 3 |
+
min-height:100vh;
|
| 4 |
+
display:flex;
|
| 5 |
+
flex-direction:column;
|
| 6 |
+
align-items: center;
|
| 7 |
+
justify-content: center;
|
| 8 |
+
background-color: rgb(255, 255, 255);
|
| 9 |
+
}
|
| 10 |
+
#login_form{
|
| 11 |
+
display:flex;
|
| 12 |
+
flex-flow: column nowrap;
|
| 13 |
+
gap:1em;
|
| 14 |
+
padding:3em 2em;
|
| 15 |
+
background-color: aliceblue;
|
| 16 |
+
border-radius: 10px;
|
| 17 |
+
border:1px solid rgb(156, 156, 156);
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
.input_box{
|
| 21 |
+
display:flex;
|
| 22 |
+
flex-direction: column;
|
| 23 |
+
gap:0.2em;
|
| 24 |
+
width:250px;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.input_box>input{
|
| 28 |
+
border-radius: 8px;
|
| 29 |
+
border:1px solid gray;
|
| 30 |
+
font-size:1em;
|
| 31 |
+
padding:0.3em;
|
| 32 |
+
|
| 33 |
+
&:focus{
|
| 34 |
+
|
| 35 |
+
outline:2px solid rgb(137, 137, 255);
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
#submit_btn{
|
| 40 |
+
padding:1em;
|
| 41 |
+
border-radius: 20px;
|
| 42 |
+
border:1px solid black;
|
| 43 |
+
background-color: greenyellow;
|
| 44 |
+
cursor:pointer;
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
&:hover{
|
| 48 |
+
/* opacity: 0.9; */
|
| 49 |
+
filter:saturate(1.2);
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
.error_msg{
|
| 55 |
+
font-size:0.8em;
|
| 56 |
+
color:red;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
#redirect_link{
|
| 60 |
+
text-align: center;
|
| 61 |
+
color:rgb(90, 90, 90);
|
| 62 |
+
&:hover{
|
| 63 |
+
color:black;
|
| 64 |
+
}
|
| 65 |
+
}
|
client/src/LoginPage/index.jsx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import LoginPage from "./LoginPage";
|
| 2 |
+
|
| 3 |
+
export default LoginPage;
|
client/src/PrivateRoutes.jsx
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react'
|
| 2 |
+
import {Outlet,Navigate} from "react-router-dom";
|
| 3 |
+
|
| 4 |
+
function PrivateRoutes() {
|
| 5 |
+
const getAuthStatus = async ()=>{
|
| 6 |
+
|
| 7 |
+
const res = await fetch(process.env.REACT_APP_BACKEND_URL+"/session",{
|
| 8 |
+
credentials:"include"
|
| 9 |
+
})
|
| 10 |
+
|
| 11 |
+
return res.ok;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
const isAuth = getAuthStatus();
|
| 15 |
+
|
| 16 |
+
if (isAuth === undefined) {
|
| 17 |
+
return <p>
|
| 18 |
+
Loading ...
|
| 19 |
+
</p>
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
return (
|
| 23 |
+
isAuth ? <Outlet/>:<Navigate to="/login" />
|
| 24 |
+
);
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
export default PrivateRoutes
|
client/src/SignupPage/SignupPage.jsx
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState } from 'react'
|
| 2 |
+
import styles from "./SignupPage.module.css";
|
| 3 |
+
import { Link, useNavigate } from 'react-router-dom';
|
| 4 |
+
|
| 5 |
+
function SignupPage() {
|
| 6 |
+
|
| 7 |
+
const signup_url = process.env.REACT_APP_BACKEND_URL + "/signup";
|
| 8 |
+
const [errors, setErrors] = useState({});
|
| 9 |
+
const navigate = useNavigate();
|
| 10 |
+
|
| 11 |
+
const handleSubmit = async (e) => {
|
| 12 |
+
e.preventDefault();
|
| 13 |
+
// console.log(e.target);
|
| 14 |
+
|
| 15 |
+
setErrors({});
|
| 16 |
+
const res = await fetch(signup_url, {
|
| 17 |
+
method: "POST",
|
| 18 |
+
credentials: "include",
|
| 19 |
+
body: new FormData(e.target),
|
| 20 |
+
})
|
| 21 |
+
|
| 22 |
+
if (!res.ok) {
|
| 23 |
+
// setErrors(re)
|
| 24 |
+
const data = await res.json();
|
| 25 |
+
if ("error" in data) {
|
| 26 |
+
setErrors(data);
|
| 27 |
+
}
|
| 28 |
+
if ("field_error" in data) {
|
| 29 |
+
setErrors(data.field_error);
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
else {
|
| 33 |
+
// handle redirect to home page
|
| 34 |
+
console.log("success");
|
| 35 |
+
navigate("/");
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
return (
|
| 42 |
+
<div id={styles.signup_page}>
|
| 43 |
+
<form id={styles.signup_form} onSubmit={handleSubmit}>
|
| 44 |
+
<h2>Signup</h2>
|
| 45 |
+
{errors.error && (
|
| 46 |
+
<p className={styles.error_msg}>{errors.error}</p>
|
| 47 |
+
)}
|
| 48 |
+
<div className={styles.input_box}>
|
| 49 |
+
<label htmlFor="name">Name</label>
|
| 50 |
+
<input type="text" name="name" id="name" />
|
| 51 |
+
{errors.name && (
|
| 52 |
+
<p className={styles.error_msg}>{errors.name[0]}</p>
|
| 53 |
+
)}
|
| 54 |
+
</div>
|
| 55 |
+
<div className={styles.input_box}>
|
| 56 |
+
<label htmlFor="username">Username</label>
|
| 57 |
+
<input type="text" name="username" id="username" />
|
| 58 |
+
{errors.username && (
|
| 59 |
+
<p className={styles.error_msg}>{errors.username[0]}</p>
|
| 60 |
+
)}
|
| 61 |
+
</div>
|
| 62 |
+
<div className={styles.input_box}>
|
| 63 |
+
<label htmlFor="username">Password</label>
|
| 64 |
+
<input type="password" name="password" id="password" />
|
| 65 |
+
{errors.password && (
|
| 66 |
+
<p className={styles.error_msg}>{errors.password[0]}</p>
|
| 67 |
+
)}
|
| 68 |
+
</div>
|
| 69 |
+
<div className={styles.input_box}>
|
| 70 |
+
<label htmlFor="username">Confirm Password</label>
|
| 71 |
+
<input type="password" name="confirm_password" id="confirm_password" />
|
| 72 |
+
{errors.confirm_password && (
|
| 73 |
+
<p className={styles.error_msg}>{errors.confirm_password[0]}</p>
|
| 74 |
+
)}
|
| 75 |
+
</div>
|
| 76 |
+
|
| 77 |
+
<input id={styles.submit_btn} type="submit" value="login" />
|
| 78 |
+
<Link to="/login" id={styles.redirect_link}>Already have an account?</Link>
|
| 79 |
+
</form>
|
| 80 |
+
</div>
|
| 81 |
+
)
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
export default SignupPage
|
client/src/SignupPage/SignupPage.module.css
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#signup_page{
|
| 2 |
+
width:100%;
|
| 3 |
+
min-height:100vh;
|
| 4 |
+
display:flex;
|
| 5 |
+
flex-direction:column;
|
| 6 |
+
align-items: center;
|
| 7 |
+
justify-content: center;
|
| 8 |
+
background-color: rgb(255, 255, 255);
|
| 9 |
+
}
|
| 10 |
+
#signup_form{
|
| 11 |
+
display:flex;
|
| 12 |
+
flex-flow: column nowrap;
|
| 13 |
+
gap:1em;
|
| 14 |
+
padding:3em 2em;
|
| 15 |
+
background-color: aliceblue;
|
| 16 |
+
border-radius: 10px;
|
| 17 |
+
border:1px solid rgb(156, 156, 156);
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
.input_box{
|
| 21 |
+
display:flex;
|
| 22 |
+
flex-direction: column;
|
| 23 |
+
gap:0.2em;
|
| 24 |
+
width:250px;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.input_box>input{
|
| 28 |
+
border-radius: 8px;
|
| 29 |
+
border:1px solid gray;
|
| 30 |
+
font-size:1em;
|
| 31 |
+
padding:0.3em;
|
| 32 |
+
|
| 33 |
+
&:focus{
|
| 34 |
+
|
| 35 |
+
outline:2px solid rgb(137, 137, 255);
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
#submit_btn{
|
| 40 |
+
padding:1em;
|
| 41 |
+
border-radius: 20px;
|
| 42 |
+
border:1px solid black;
|
| 43 |
+
background-color: greenyellow;
|
| 44 |
+
cursor:pointer;
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
&:hover{
|
| 48 |
+
/* opacity: 0.9; */
|
| 49 |
+
filter:saturate(1.2);
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
.error_msg{
|
| 55 |
+
font-size:0.8em;
|
| 56 |
+
color:red;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
#redirect_link{
|
| 60 |
+
text-align: center;
|
| 61 |
+
color:rgb(90, 90, 90);
|
| 62 |
+
&:hover{
|
| 63 |
+
color:black;
|
| 64 |
+
}
|
| 65 |
+
}
|
client/src/SignupPage/index.jsx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import SignupPage from "./SignupPage";
|
| 2 |
+
|
| 3 |
+
export default SignupPage;
|
server/auth.ts
CHANGED
|
@@ -8,7 +8,7 @@ const db = getDB();
|
|
| 8 |
const adapter = new BetterSqlite3Adapter(db,{user:"users",session:"sessions"});
|
| 9 |
|
| 10 |
export const lucia = new Lucia(adapter, {
|
| 11 |
-
sessionExpiresIn:new TimeSpan(10000,"
|
| 12 |
sessionCookie: {
|
| 13 |
// expires:false,
|
| 14 |
attributes: {
|
|
|
|
| 8 |
const adapter = new BetterSqlite3Adapter(db,{user:"users",session:"sessions"});
|
| 9 |
|
| 10 |
export const lucia = new Lucia(adapter, {
|
| 11 |
+
sessionExpiresIn:new TimeSpan(10000,"m"),
|
| 12 |
sessionCookie: {
|
| 13 |
// expires:false,
|
| 14 |
attributes: {
|
server/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import bodyParser from "body-parser";
|
|
| 3 |
import { getDB } from "./db.js";
|
| 4 |
import { lucia } from "./auth.js";
|
| 5 |
import { generateId, verifyRequestOrigin } from "lucia";
|
| 6 |
-
import {
|
| 7 |
import bcrypt from "bcrypt";
|
| 8 |
import dotenv from "dotenv";
|
| 9 |
import cors from "cors";
|
|
@@ -79,8 +79,8 @@ app.use(async (req, res, next) => {
|
|
| 79 |
}
|
| 80 |
res.locals.session = session;
|
| 81 |
res.locals.user = user;
|
| 82 |
-
console.log(res.locals.session);
|
| 83 |
-
console.log(res.locals.user);
|
| 84 |
return next();
|
| 85 |
|
| 86 |
|
|
@@ -91,8 +91,11 @@ app.use(async (req, res, next) => {
|
|
| 91 |
// res.send('Hello World!');
|
| 92 |
// })
|
| 93 |
|
| 94 |
-
app.get("/session",(req,res)=>{
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
| 96 |
})
|
| 97 |
app.post("/signup", async (req, res) => {
|
| 98 |
|
|
@@ -104,6 +107,16 @@ app.post("/signup", async (req, res) => {
|
|
| 104 |
}
|
| 105 |
|
| 106 |
var data = parsedData.data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
// check if username is already used
|
| 109 |
var row = db.prepare("select * from users where username=?").get(data.username);
|
|
@@ -140,10 +153,10 @@ app.post("/signup", async (req, res) => {
|
|
| 140 |
|
| 141 |
})
|
| 142 |
|
| 143 |
-
app.post("/
|
| 144 |
console.log(req.body);
|
| 145 |
|
| 146 |
-
const parsedData =
|
| 147 |
|
| 148 |
if (!parsedData.success) {
|
| 149 |
return res.status(403).json({
|
|
@@ -190,12 +203,31 @@ const io = new Server(server, {
|
|
| 190 |
}
|
| 191 |
});
|
| 192 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
io.on("connection", (socket) => {
|
| 194 |
console.log(`socket ${socket.id} connected`);
|
| 195 |
|
| 196 |
// send an event to the client
|
| 197 |
socket.emit("foo", "bar");
|
| 198 |
-
|
| 199 |
socket.on("foobar", (data) => {
|
| 200 |
// an event was received from the client
|
| 201 |
console.log("received:" + data);
|
|
|
|
| 3 |
import { getDB } from "./db.js";
|
| 4 |
import { lucia } from "./auth.js";
|
| 5 |
import { generateId, verifyRequestOrigin } from "lucia";
|
| 6 |
+
import { loginSchema, signUpSchema } from "./schemas.js";
|
| 7 |
import bcrypt from "bcrypt";
|
| 8 |
import dotenv from "dotenv";
|
| 9 |
import cors from "cors";
|
|
|
|
| 79 |
}
|
| 80 |
res.locals.session = session;
|
| 81 |
res.locals.user = user;
|
| 82 |
+
// console.log(res.locals.session);
|
| 83 |
+
// console.log(res.locals.user);
|
| 84 |
return next();
|
| 85 |
|
| 86 |
|
|
|
|
| 91 |
// res.send('Hello World!');
|
| 92 |
// })
|
| 93 |
|
| 94 |
+
app.get("/session", (req, res) => {
|
| 95 |
+
if(!res.locals.user){
|
| 96 |
+
res.status(403);
|
| 97 |
+
}
|
| 98 |
+
return res.status(200).send(res.locals.user);
|
| 99 |
})
|
| 100 |
app.post("/signup", async (req, res) => {
|
| 101 |
|
|
|
|
| 107 |
}
|
| 108 |
|
| 109 |
var data = parsedData.data;
|
| 110 |
+
// check if password and confirm_password matchs
|
| 111 |
+
if(data.password!=data.confirm_password){
|
| 112 |
+
return res.status(403).json({
|
| 113 |
+
field_error:{
|
| 114 |
+
confirm_password:[
|
| 115 |
+
"password doesn't match"
|
| 116 |
+
]
|
| 117 |
+
}
|
| 118 |
+
});
|
| 119 |
+
}
|
| 120 |
|
| 121 |
// check if username is already used
|
| 122 |
var row = db.prepare("select * from users where username=?").get(data.username);
|
|
|
|
| 153 |
|
| 154 |
})
|
| 155 |
|
| 156 |
+
app.post("/login", async (req, res) => {
|
| 157 |
console.log(req.body);
|
| 158 |
|
| 159 |
+
const parsedData = loginSchema.safeParse(req.body);
|
| 160 |
|
| 161 |
if (!parsedData.success) {
|
| 162 |
return res.status(403).json({
|
|
|
|
| 203 |
}
|
| 204 |
});
|
| 205 |
|
| 206 |
+
const isAuth = async (req, res, next) => {
|
| 207 |
+
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
| 208 |
+
// console.log(sessionId);
|
| 209 |
+
|
| 210 |
+
if (!sessionId) {
|
| 211 |
+
|
| 212 |
+
return next(new Error("error"));
|
| 213 |
+
}
|
| 214 |
+
const { session, user } = await lucia.validateSession(sessionId);
|
| 215 |
+
if (!session) {
|
| 216 |
+
// return res.end();
|
| 217 |
+
return next(new Error("error"));
|
| 218 |
+
}
|
| 219 |
+
return next();
|
| 220 |
+
};
|
| 221 |
+
|
| 222 |
+
io.engine.use(isAuth);
|
| 223 |
+
|
| 224 |
+
|
| 225 |
io.on("connection", (socket) => {
|
| 226 |
console.log(`socket ${socket.id} connected`);
|
| 227 |
|
| 228 |
// send an event to the client
|
| 229 |
socket.emit("foo", "bar");
|
| 230 |
+
// socket.on("")
|
| 231 |
socket.on("foobar", (data) => {
|
| 232 |
// an event was received from the client
|
| 233 |
console.log("received:" + data);
|
server/schemas.ts
CHANGED
|
@@ -4,8 +4,9 @@ export const signUpSchema = z.object({
|
|
| 4 |
username: z.string().toLowerCase().min(1),
|
| 5 |
name: z.string().toLowerCase().min(1),
|
| 6 |
password: z.string().min(1),
|
|
|
|
| 7 |
});
|
| 8 |
-
export const
|
| 9 |
username: z.string().toLowerCase().min(1),
|
| 10 |
password: z.string().min(1),
|
| 11 |
});
|
|
|
|
| 4 |
username: z.string().toLowerCase().min(1),
|
| 5 |
name: z.string().toLowerCase().min(1),
|
| 6 |
password: z.string().min(1),
|
| 7 |
+
confirm_password: z.string().min(1),
|
| 8 |
});
|
| 9 |
+
export const loginSchema = z.object({
|
| 10 |
username: z.string().toLowerCase().min(1),
|
| 11 |
password: z.string().min(1),
|
| 12 |
});
|