Spaces:
Runtime error
Runtime error
Commit
Β·
c13f601
1
Parent(s):
e4ce9c9
umm
Browse files- client/src/components/Chat/Chat.jsx +42 -18
- client/src/components/ChatFooter/ChatFooter.jsx +3 -7
- client/src/components/ChatMain/ChatMain.jsx +4 -4
- client/src/components/Contact/Contact.jsx +8 -1
- client/src/components/Contacts/Contacts.jsx +3 -3
- client/src/components/Search/Search.jsx +5 -2
- client/src/components/SideBar/SideBar.jsx +23 -3
- client/src/contexts/ChatProvider.jsx +2 -0
- server/index.ts +0 -399
- server/package.json +4 -4
- server/{auth.ts β src/auth.ts} +0 -0
- server/src/authHandler.ts +163 -0
- server/src/chatHandler.ts +175 -0
- server/{db.ts β src/db.ts} +0 -0
- server/src/index.ts +176 -0
- server/{middleware.ts β src/middleware.ts} +0 -0
- server/{schemas.ts β src/schemas.ts} +0 -0
- server/{scripts/init_db.ts β src/scripts/init_db.js} +13 -15
- server/src/scripts/init_db.ts +55 -0
- server/src/seeder.ts +69 -0
- server/tsconfig.json +1 -1
client/src/components/Chat/Chat.jsx
CHANGED
@@ -19,10 +19,10 @@ function Chat({ }) {
|
|
19 |
|
20 |
const [messages, setMessages] = useState([]);
|
21 |
const socket = useContext(SocketContext);
|
22 |
-
const {user} = useContext(UserContext);
|
23 |
const chatMainRef = useRef();
|
24 |
-
|
25 |
-
|
26 |
|
27 |
|
28 |
// to get Messages of currently opened chat
|
@@ -36,8 +36,8 @@ function Chat({ }) {
|
|
36 |
if (!socket) return;
|
37 |
// console.log()
|
38 |
socket.on("messages", (messages) => {
|
39 |
-
|
40 |
-
setMessages(messages);
|
41 |
});
|
42 |
|
43 |
socket.emit("get_messages", currentChat.id);
|
@@ -54,25 +54,44 @@ function Chat({ }) {
|
|
54 |
useEffect(() => {
|
55 |
if (socket) {
|
56 |
socket.on("receive_message", (message) => {
|
57 |
-
|
|
|
|
|
|
|
|
|
58 |
setMessages((prev) => [...prev, message]);
|
59 |
}
|
60 |
|
61 |
-
const ismessageOfContact = (contact) => (contact.id == message.send_to) || (contact.id == message.send_from);
|
62 |
-
var updatedContacts = contacts.map((contact) => (
|
63 |
-
ismessageOfContact(contact) ?
|
64 |
-
{ ...contact, last_message: message } : contact
|
65 |
-
));
|
66 |
|
67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
// console.log(updatedContacts);
|
69 |
|
70 |
setContacts(updatedContacts);
|
71 |
-
|
72 |
// if message from a new Contact
|
73 |
-
const contact = contacts.find(contact=>contact.id==message.
|
74 |
-
if(!contact && message.
|
75 |
-
socket.emit("get_contact",message.
|
76 |
}
|
77 |
|
78 |
});
|
@@ -95,7 +114,12 @@ function Chat({ }) {
|
|
95 |
scrollToBottom();
|
96 |
}, [messages]);
|
97 |
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
// Until no chat is selected
|
101 |
if (Object.keys(currentChat).length === 0) {
|
@@ -110,7 +134,7 @@ function Chat({ }) {
|
|
110 |
// console.log("currentChat",currentChat);
|
111 |
// console.log("currentChat",currentChat.id);
|
112 |
// once a chat is selected
|
113 |
-
return (
|
114 |
<div className='chat'>
|
115 |
<ChatHeader name={currentChat.username} />
|
116 |
<ChatMain messages={messages} ref={chatMainRef} />
|
|
|
19 |
|
20 |
const [messages, setMessages] = useState([]);
|
21 |
const socket = useContext(SocketContext);
|
22 |
+
const { user } = useContext(UserContext);
|
23 |
const chatMainRef = useRef();
|
24 |
+
|
25 |
+
|
26 |
|
27 |
|
28 |
// to get Messages of currently opened chat
|
|
|
36 |
if (!socket) return;
|
37 |
// console.log()
|
38 |
socket.on("messages", (messages) => {
|
39 |
+
console.log("on-messages", messages);
|
40 |
+
setMessages(messages.reverse());
|
41 |
});
|
42 |
|
43 |
socket.emit("get_messages", currentChat.id);
|
|
|
54 |
useEffect(() => {
|
55 |
if (socket) {
|
56 |
socket.on("receive_message", (message) => {
|
57 |
+
console.log("receive_message:", message);
|
58 |
+
|
59 |
+
|
60 |
+
// if message is received in the current open chat
|
61 |
+
if (message.sender_id == currentChat.id || message.sender_id == user.id) {
|
62 |
setMessages((prev) => [...prev, message]);
|
63 |
}
|
64 |
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
+
const isMsgOfOpenedChat = (contact) => (contact.id == message.receiver_id) || (contact.id == message.sender_id);
|
67 |
+
const updateUnseenCount = (contact) => {
|
68 |
+
if (isMsgOfOpenedChat(contact)) {
|
69 |
+
return contact.unseen_count;
|
70 |
+
}
|
71 |
+
else {
|
72 |
+
return contact.unseen_count + 1;
|
73 |
+
}
|
74 |
+
};
|
75 |
+
var updatedContacts = contacts.map((contact) => {
|
76 |
+
var chat_data = { ...contact.chat_data };
|
77 |
+
// console.log(contact);
|
78 |
+
if (contact.id == message.receiver_id || contact.id == message.sender_id) {
|
79 |
+
chat_data.last_message = message;
|
80 |
+
}
|
81 |
+
chat_data.unseen_count = updateUnseenCount(contact);
|
82 |
+
|
83 |
+
return { ...contact, chat_data };
|
84 |
+
});
|
85 |
+
|
86 |
+
updatedContacts.sort((a, b) => Date.parse(b.chat_data.last_message.timestamp) - Date.parse(a.chat_data.last_message.timestamp));
|
87 |
// console.log(updatedContacts);
|
88 |
|
89 |
setContacts(updatedContacts);
|
90 |
+
|
91 |
// if message from a new Contact
|
92 |
+
const contact = contacts.find(contact => contact.id == message.sender_id);
|
93 |
+
if (!contact && message.sender_id != user.id) {
|
94 |
+
socket.emit("get_contact", message.sender_id);
|
95 |
}
|
96 |
|
97 |
});
|
|
|
114 |
scrollToBottom();
|
115 |
}, [messages]);
|
116 |
|
117 |
+
useEffect(() => {
|
118 |
+
if (messages.length === 0) return;
|
119 |
+
const unseen_messages = messages.filter(message => message.is_seen == 0);
|
120 |
+
console.log(unseen_messages);
|
121 |
+
socket.emit("mark_messages_seen", unseen_messages.map(msg => msg.id));
|
122 |
+
}, [messages]);
|
123 |
|
124 |
// Until no chat is selected
|
125 |
if (Object.keys(currentChat).length === 0) {
|
|
|
134 |
// console.log("currentChat",currentChat);
|
135 |
// console.log("currentChat",currentChat.id);
|
136 |
// once a chat is selected
|
137 |
+
return (
|
138 |
<div className='chat'>
|
139 |
<ChatHeader name={currentChat.username} />
|
140 |
<ChatMain messages={messages} ref={chatMainRef} />
|
client/src/components/ChatFooter/ChatFooter.jsx
CHANGED
@@ -12,11 +12,6 @@ function ChatFooter({
|
|
12 |
|
13 |
|
14 |
|
15 |
-
useEffect(() => {
|
16 |
-
if (messageRef.current) {
|
17 |
-
messageRef.current.focus();
|
18 |
-
}
|
19 |
-
}) // risky this running all the time
|
20 |
|
21 |
const sendMessage = (message) => {
|
22 |
|
@@ -24,8 +19,9 @@ function ChatFooter({
|
|
24 |
|
25 |
|
26 |
const message_bundle = {
|
27 |
-
message,
|
28 |
-
|
|
|
29 |
}
|
30 |
|
31 |
if (socket) {
|
|
|
12 |
|
13 |
|
14 |
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
const sendMessage = (message) => {
|
17 |
|
|
|
19 |
|
20 |
|
21 |
const message_bundle = {
|
22 |
+
content:message,
|
23 |
+
receiver_id: currentChatId,
|
24 |
+
type:"text",
|
25 |
}
|
26 |
|
27 |
if (socket) {
|
client/src/components/ChatMain/ChatMain.jsx
CHANGED
@@ -10,13 +10,13 @@ const ChatMain = React.forwardRef(({
|
|
10 |
const { user } = useContext(UserContext);
|
11 |
|
12 |
const timeToReadable = (timestamp) => {
|
13 |
-
var d = new Date(timestamp
|
14 |
return d.toLocaleTimeString();
|
15 |
// return timestamp;
|
16 |
}
|
17 |
|
18 |
const isReceived = (message) => {
|
19 |
-
return user && message.
|
20 |
};
|
21 |
|
22 |
return (
|
@@ -26,8 +26,8 @@ const ChatMain = React.forwardRef(({
|
|
26 |
messages.map((message, idx) => (
|
27 |
<Message
|
28 |
key={idx}
|
29 |
-
message={message.
|
30 |
-
time={timeToReadable(message.
|
31 |
isReceived={isReceived(message)}
|
32 |
/>
|
33 |
))
|
|
|
10 |
const { user } = useContext(UserContext);
|
11 |
|
12 |
const timeToReadable = (timestamp) => {
|
13 |
+
var d = new Date(timestamp);
|
14 |
return d.toLocaleTimeString();
|
15 |
// return timestamp;
|
16 |
}
|
17 |
|
18 |
const isReceived = (message) => {
|
19 |
+
return user && message.sender_id != user.id;
|
20 |
};
|
21 |
|
22 |
return (
|
|
|
26 |
messages.map((message, idx) => (
|
27 |
<Message
|
28 |
key={idx}
|
29 |
+
message={message.content}
|
30 |
+
time={timeToReadable(message.timestamp)}
|
31 |
isReceived={isReceived(message)}
|
32 |
/>
|
33 |
))
|
client/src/components/Contact/Contact.jsx
CHANGED
@@ -7,9 +7,13 @@ function Contact({
|
|
7 |
lastmessage = "",
|
8 |
onClick,
|
9 |
isActive = false,
|
10 |
-
|
11 |
}) {
|
12 |
|
|
|
|
|
|
|
|
|
13 |
|
14 |
return (
|
15 |
<div className={(isActive ? "active" : "") + ' contact'} onClick={onClick}>
|
@@ -20,6 +24,9 @@ function Contact({
|
|
20 |
<p className={(is_new ? "new":"") + ' contact__lastmsg'}>{lastmessage}</p>
|
21 |
}
|
22 |
</div>
|
|
|
|
|
|
|
23 |
</div>
|
24 |
)
|
25 |
}
|
|
|
7 |
lastmessage = "",
|
8 |
onClick,
|
9 |
isActive = false,
|
10 |
+
unseen_count,
|
11 |
}) {
|
12 |
|
13 |
+
var is_new=false;
|
14 |
+
if(unseen_count>0){
|
15 |
+
is_new=true;
|
16 |
+
}
|
17 |
|
18 |
return (
|
19 |
<div className={(isActive ? "active" : "") + ' contact'} onClick={onClick}>
|
|
|
24 |
<p className={(is_new ? "new":"") + ' contact__lastmsg'}>{lastmessage}</p>
|
25 |
}
|
26 |
</div>
|
27 |
+
{unseen_count>0 && (
|
28 |
+
<span>{unseen_count}</span>
|
29 |
+
)}
|
30 |
</div>
|
31 |
)
|
32 |
}
|
client/src/components/Contacts/Contacts.jsx
CHANGED
@@ -53,9 +53,9 @@ function Contacts({ }) {
|
|
53 |
key={contact.id}
|
54 |
onClick={() => { setCurrentChat(contact) }}
|
55 |
profile_pic={"logo512.png"}
|
56 |
-
name={contact.
|
57 |
-
lastmessage={contact.last_message.
|
58 |
-
|
59 |
isActive={currentChat.id == contact.id}
|
60 |
/>
|
61 |
))
|
|
|
53 |
key={contact.id}
|
54 |
onClick={() => { setCurrentChat(contact) }}
|
55 |
profile_pic={"logo512.png"}
|
56 |
+
name={contact.id}
|
57 |
+
lastmessage={contact.chat_data.last_message ? contact.chat_data.last_message.content:null}
|
58 |
+
unseen_count={contact.chat_data.unseen_count ? contact.chat_data.unseen_count:null}
|
59 |
isActive={currentChat.id == contact.id}
|
60 |
/>
|
61 |
))
|
client/src/components/Search/Search.jsx
CHANGED
@@ -1,11 +1,14 @@
|
|
1 |
-
import React from 'react';
|
2 |
import "./Search.css";
|
3 |
import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'
|
|
|
4 |
|
5 |
function Search() {
|
|
|
|
|
6 |
return (
|
7 |
<div className='search'>
|
8 |
-
<input type="text" placeholder='Search' className='search__input'/>
|
9 |
<MagnifyingGlassIcon className='search__icon' color='black' />
|
10 |
</div>
|
11 |
)
|
|
|
1 |
+
import React, { useContext } from 'react';
|
2 |
import "./Search.css";
|
3 |
import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'
|
4 |
+
import { ChatContext } from '../../contexts/ChatProvider';
|
5 |
|
6 |
function Search() {
|
7 |
+
const { search, setSearch } = useContext(ChatContext);
|
8 |
+
|
9 |
return (
|
10 |
<div className='search'>
|
11 |
+
<input type="text" placeholder='Search' className='search__input' value={search} onChange={e=>setSearch(e.target.value)} />
|
12 |
<MagnifyingGlassIcon className='search__icon' color='black' />
|
13 |
</div>
|
14 |
)
|
client/src/components/SideBar/SideBar.jsx
CHANGED
@@ -11,8 +11,10 @@ function SideBar() {
|
|
11 |
const socket = useContext(SocketContext);
|
12 |
|
13 |
const {
|
|
|
14 |
setContacts,
|
15 |
// setLastMessages,
|
|
|
16 |
} = useContext(ChatContext);
|
17 |
|
18 |
useEffect(() => {
|
@@ -22,9 +24,9 @@ function SideBar() {
|
|
22 |
if (!socket) return;
|
23 |
socket.on("contacts", (contacts) => {
|
24 |
// console.log(contacts);
|
25 |
-
var newContacts = contacts;
|
26 |
-
|
27 |
-
newContacts.sort((a,b)=>b.last_message.send_at - a.last_message.send_at);
|
28 |
|
29 |
setContacts(newContacts); // set Contacts
|
30 |
// setLastMessages(contacts.map((contact) => contact.last_message)); // set Last messages
|
@@ -41,6 +43,24 @@ function SideBar() {
|
|
41 |
}
|
42 |
}, [socket]);
|
43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
return (
|
45 |
|
46 |
<div className='side_bar'>
|
|
|
11 |
const socket = useContext(SocketContext);
|
12 |
|
13 |
const {
|
14 |
+
contacts,
|
15 |
setContacts,
|
16 |
// setLastMessages,
|
17 |
+
search,
|
18 |
} = useContext(ChatContext);
|
19 |
|
20 |
useEffect(() => {
|
|
|
24 |
if (!socket) return;
|
25 |
socket.on("contacts", (contacts) => {
|
26 |
// console.log(contacts);
|
27 |
+
var newContacts = contacts;
|
28 |
+
|
29 |
+
newContacts.sort((a, b) => b.last_message.send_at - a.last_message.send_at);
|
30 |
|
31 |
setContacts(newContacts); // set Contacts
|
32 |
// setLastMessages(contacts.map((contact) => contact.last_message)); // set Last messages
|
|
|
43 |
}
|
44 |
}, [socket]);
|
45 |
|
46 |
+
useEffect(() => {
|
47 |
+
|
48 |
+
if (!socket) return;
|
49 |
+
|
50 |
+
if (search.trim() == "") return;
|
51 |
+
|
52 |
+
socket.on("search_results",(contacts)=>{
|
53 |
+
setContacts(contacts);
|
54 |
+
})
|
55 |
+
socket.emit("search_contacts",search);
|
56 |
+
|
57 |
+
const cleanUp = () => {
|
58 |
+
socket.off("search_results");
|
59 |
+
}
|
60 |
+
return cleanUp;
|
61 |
+
}, [socket, search])
|
62 |
+
|
63 |
+
|
64 |
return (
|
65 |
|
66 |
<div className='side_bar'>
|
client/src/contexts/ChatProvider.jsx
CHANGED
@@ -6,12 +6,14 @@ export const ChatProvider = ({ children }) => {
|
|
6 |
const [contacts, setContacts] = useState([]);
|
7 |
const [currentChat, setCurrentChat] = useState({});
|
8 |
const [lastMessages, setLastMessages] = useState([]);
|
|
|
9 |
|
10 |
return (
|
11 |
<ChatContext.Provider value={{
|
12 |
contacts, setContacts,
|
13 |
currentChat, setCurrentChat,
|
14 |
lastMessages, setLastMessages,
|
|
|
15 |
}}>
|
16 |
{children}
|
17 |
</ChatContext.Provider>
|
|
|
6 |
const [contacts, setContacts] = useState([]);
|
7 |
const [currentChat, setCurrentChat] = useState({});
|
8 |
const [lastMessages, setLastMessages] = useState([]);
|
9 |
+
const [search,setSearch] = useState("");
|
10 |
|
11 |
return (
|
12 |
<ChatContext.Provider value={{
|
13 |
contacts, setContacts,
|
14 |
currentChat, setCurrentChat,
|
15 |
lastMessages, setLastMessages,
|
16 |
+
search,setSearch,
|
17 |
}}>
|
18 |
{children}
|
19 |
</ChatContext.Provider>
|
server/index.ts
DELETED
@@ -1,399 +0,0 @@
|
|
1 |
-
import express from "express";
|
2 |
-
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 { loginSchema, signUpSchema } from "./schemas.js";
|
7 |
-
import bcrypt from "bcrypt";
|
8 |
-
import dotenv from "dotenv";
|
9 |
-
import cors from "cors";
|
10 |
-
import mutler from "multer";
|
11 |
-
import { Server, Socket } from "socket.io";
|
12 |
-
import http from "http";
|
13 |
-
import path from "path";
|
14 |
-
|
15 |
-
dotenv.config();
|
16 |
-
|
17 |
-
const __dirname = path.resolve();
|
18 |
-
var db = getDB();
|
19 |
-
|
20 |
-
|
21 |
-
var app = express();
|
22 |
-
const server = http.createServer(app);
|
23 |
-
|
24 |
-
const allowedOrigins = process.env.ALLOWED_ORIGINS || 'http://localhost:3001'
|
25 |
-
const allowed_origins = allowedOrigins.split(",").map(item => item.trim());
|
26 |
-
|
27 |
-
|
28 |
-
app.use(cors({
|
29 |
-
credentials: true,
|
30 |
-
// origin: true,
|
31 |
-
origin: allowed_origins,
|
32 |
-
}));
|
33 |
-
|
34 |
-
// parse application/json
|
35 |
-
app.use(bodyParser.json());
|
36 |
-
|
37 |
-
// parse application/x-www-form-urlencoded
|
38 |
-
app.use(bodyParser.urlencoded({ extended: false }));
|
39 |
-
|
40 |
-
// app.use(express.urlencoded());
|
41 |
-
app.use(mutler().array(""));
|
42 |
-
|
43 |
-
// The entire build/web directory is statically served.
|
44 |
-
app.use(express.static(path.join(__dirname, "../client/build")));
|
45 |
-
|
46 |
-
// middleware
|
47 |
-
// app.use((req,res,next)=>{
|
48 |
-
// if(req.method==="GET"){
|
49 |
-
// return next();
|
50 |
-
// }
|
51 |
-
|
52 |
-
// const originHeader = req.headers.origin ?? null;
|
53 |
-
// const hostHeader = req.headers.host ?? null;
|
54 |
-
// if(!originHeader || !hostHeader || !verifyRequestOrigin(originHeader,[hostHeader])){
|
55 |
-
// return res.status(403).end();
|
56 |
-
// }
|
57 |
-
// return next();
|
58 |
-
// })
|
59 |
-
|
60 |
-
// middleware
|
61 |
-
app.use(async (req, res, next) => {
|
62 |
-
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
63 |
-
if (!sessionId) {
|
64 |
-
res.locals.user = null;
|
65 |
-
res.locals.session = null;
|
66 |
-
return next();
|
67 |
-
}
|
68 |
-
const { session, user } = await lucia.validateSession(sessionId);
|
69 |
-
if (session && session.fresh) {
|
70 |
-
res.appendHeader("Set-Cookie", lucia.createSessionCookie(session.id).serialize());
|
71 |
-
}
|
72 |
-
if (!session) {
|
73 |
-
res.appendHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
|
74 |
-
}
|
75 |
-
res.locals.session = session;
|
76 |
-
res.locals.user = user;
|
77 |
-
// console.log(res.locals.session);
|
78 |
-
// console.log(res.locals.user);
|
79 |
-
return next();
|
80 |
-
|
81 |
-
})
|
82 |
-
|
83 |
-
|
84 |
-
const isAuthAPIMiddleware = async (req, res, next) => {
|
85 |
-
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
86 |
-
// console.log(sessionId);
|
87 |
-
|
88 |
-
if (!sessionId) {
|
89 |
-
|
90 |
-
return res.status(403).send({ error: "unauthorized" });
|
91 |
-
}
|
92 |
-
const { session, user } = await lucia.validateSession(sessionId);
|
93 |
-
if (!session) {
|
94 |
-
return res.status(403).send({ error: "unauthorized" });
|
95 |
-
}
|
96 |
-
return next();
|
97 |
-
};
|
98 |
-
|
99 |
-
|
100 |
-
// app.get('/', (req, res) => {
|
101 |
-
// // res.cookie("msg","hmmmm");
|
102 |
-
// res.send('Hello World!');
|
103 |
-
// })
|
104 |
-
|
105 |
-
app.get("/session", (req, res) => {
|
106 |
-
if (!res.locals.user) {
|
107 |
-
return res.status(403).send();
|
108 |
-
}
|
109 |
-
return res.status(200).send(res.locals.user);
|
110 |
-
})
|
111 |
-
app.post("/signup", async (req, res) => {
|
112 |
-
|
113 |
-
// console.log(req.params);
|
114 |
-
// console.log(req.body);
|
115 |
-
const parsedData = signUpSchema.safeParse(req.body);
|
116 |
-
if (!parsedData.success) {
|
117 |
-
return res.status(403).json({ field_error: parsedData.error.flatten().fieldErrors }); // 403 is for validation error
|
118 |
-
}
|
119 |
-
|
120 |
-
var data = parsedData.data;
|
121 |
-
// check if password and confirm_password matchs
|
122 |
-
if (data.password != data.confirm_password) {
|
123 |
-
return res.status(403).json({
|
124 |
-
field_error: {
|
125 |
-
confirm_password: [
|
126 |
-
"password doesn't match"
|
127 |
-
]
|
128 |
-
}
|
129 |
-
});
|
130 |
-
}
|
131 |
-
|
132 |
-
// check if username is already used
|
133 |
-
var row = db.prepare("select * from users where username=?").get(data.username);
|
134 |
-
if (row) {
|
135 |
-
return res.status(403).json({
|
136 |
-
field_error: {
|
137 |
-
username: [
|
138 |
-
"username already used."
|
139 |
-
]
|
140 |
-
}
|
141 |
-
})
|
142 |
-
}
|
143 |
-
|
144 |
-
// save to data se
|
145 |
-
const userId = generateId(15);
|
146 |
-
const hashedPassword = await bcrypt.hash(data.password, 10);
|
147 |
-
|
148 |
-
await db.prepare("insert into users(id,name,username,password) values(?,?,?,?)").run(
|
149 |
-
userId,
|
150 |
-
data.name,
|
151 |
-
data.username,
|
152 |
-
hashedPassword
|
153 |
-
);
|
154 |
-
|
155 |
-
const session = await lucia.createSession(userId, {});
|
156 |
-
const sessionCookie = lucia.createSessionCookie(session.id);
|
157 |
-
|
158 |
-
res.cookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
|
159 |
-
|
160 |
-
res.json({
|
161 |
-
"status": "success",
|
162 |
-
"msg": "user created successfully",
|
163 |
-
});
|
164 |
-
|
165 |
-
})
|
166 |
-
|
167 |
-
app.post("/login", async (req, res) => {
|
168 |
-
console.log(req.body);
|
169 |
-
|
170 |
-
const parsedData = loginSchema.safeParse(req.body);
|
171 |
-
|
172 |
-
if (!parsedData.success) {
|
173 |
-
return res.status(403).json({
|
174 |
-
"field_error": parsedData.error.flatten().fieldErrors
|
175 |
-
})
|
176 |
-
}
|
177 |
-
|
178 |
-
// check if user exists
|
179 |
-
const row = db.prepare("select * from users where username=?").get(parsedData.data.username);
|
180 |
-
// console.log(row);
|
181 |
-
|
182 |
-
if (!row) {
|
183 |
-
return res.status(403).json({
|
184 |
-
"error": "Incorrect username or password"
|
185 |
-
})
|
186 |
-
}
|
187 |
-
|
188 |
-
// check password
|
189 |
-
if (!await bcrypt.compare(parsedData.data.password, row.password)) {
|
190 |
-
return res.status(403).json({
|
191 |
-
"error": "Incorrect username or password"
|
192 |
-
})
|
193 |
-
}
|
194 |
-
|
195 |
-
// save session
|
196 |
-
const session = await lucia.createSession(row.id, {});
|
197 |
-
const sessionCookie = lucia.createSessionCookie(session.id);
|
198 |
-
|
199 |
-
// console.log(sessionCookie.attributes);
|
200 |
-
res.cookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
|
201 |
-
|
202 |
-
res.json({
|
203 |
-
"status": "success",
|
204 |
-
"msg": "logged in successfully",
|
205 |
-
});
|
206 |
-
|
207 |
-
})
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
// get all contacts route
|
212 |
-
app.get("/contacts", isAuthAPIMiddleware, async (req, res) => {
|
213 |
-
const contacts = await db.prepare("select id,username from users where id!=?").all(res.locals.user.id);
|
214 |
-
// console.log(contacts);
|
215 |
-
res.json({ "contacts": contacts });
|
216 |
-
})
|
217 |
-
|
218 |
-
// get currentContact's messsages route
|
219 |
-
app.get("/messages/:userid", isAuthAPIMiddleware, async (req, res) => {
|
220 |
-
|
221 |
-
// req.query.id
|
222 |
-
|
223 |
-
var messages = await db.prepare(`select * from messages
|
224 |
-
where (send_to=? and send_from=?)
|
225 |
-
or
|
226 |
-
(send_to=? and send_from=?)`).all(
|
227 |
-
res.locals.user.id,
|
228 |
-
req.params.userid,
|
229 |
-
|
230 |
-
req.params.userid,
|
231 |
-
res.locals.user.id,
|
232 |
-
);
|
233 |
-
// const messages = await db.prepare("select * from messages").all();
|
234 |
-
|
235 |
-
res.json({ "messages": messages });
|
236 |
-
})
|
237 |
-
|
238 |
-
// Catch all. If we want to add pages later, then we should probably change this.
|
239 |
-
app.get("*", (_req, res) => {
|
240 |
-
res.sendFile(path.join(__dirname + "/../client/build/index.html"));
|
241 |
-
});
|
242 |
-
|
243 |
-
|
244 |
-
const io = new Server(server, {
|
245 |
-
cors: {
|
246 |
-
origin: allowed_origins,
|
247 |
-
methods: ["GET", "POST"],
|
248 |
-
credentials: true,
|
249 |
-
}
|
250 |
-
});
|
251 |
-
|
252 |
-
const isAuth = async (req: Request, res: Response, next) => {
|
253 |
-
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
254 |
-
// console.log(sessionId);
|
255 |
-
|
256 |
-
if (!sessionId) {
|
257 |
-
|
258 |
-
return next(new Error("error"));
|
259 |
-
}
|
260 |
-
const { session, user } = await lucia.validateSession(sessionId);
|
261 |
-
if (!session) {
|
262 |
-
// return res.end();
|
263 |
-
return next(new Error("error"));
|
264 |
-
}
|
265 |
-
|
266 |
-
return next();
|
267 |
-
};
|
268 |
-
|
269 |
-
io.engine.use(isAuth);
|
270 |
-
|
271 |
-
async function getLastMessage({ user_id, contact_id }: { user_id: string, contact_id: string }) {
|
272 |
-
|
273 |
-
var last_message = await db.prepare(`select * from messages
|
274 |
-
where (send_to=? and send_from=?)
|
275 |
-
or
|
276 |
-
(send_to=? and send_from=?)
|
277 |
-
ORDER BY send_at DESC
|
278 |
-
`).get(
|
279 |
-
user_id,
|
280 |
-
contact_id,
|
281 |
-
|
282 |
-
contact_id,
|
283 |
-
user_id,
|
284 |
-
);
|
285 |
-
|
286 |
-
// console.log(last_message);
|
287 |
-
if (last_message) {
|
288 |
-
return last_message;
|
289 |
-
}
|
290 |
-
else {
|
291 |
-
return {};
|
292 |
-
}
|
293 |
-
|
294 |
-
}
|
295 |
-
|
296 |
-
io.on("connection", async (socket) => {
|
297 |
-
console.log(`socket ${socket.id} connected`);
|
298 |
-
|
299 |
-
// add user and session to the socket.data object
|
300 |
-
const sessionId = lucia.readSessionCookie(socket.handshake.headers.cookie ?? "");
|
301 |
-
const { session, user } = await lucia.validateSession(sessionId as string);
|
302 |
-
socket.data.user = user;
|
303 |
-
socket.data.session = session;
|
304 |
-
|
305 |
-
socket.on("get_contacts", async () => {
|
306 |
-
|
307 |
-
// get all contacts to client
|
308 |
-
var contacts = await db.prepare("select id,username from users where id!=?").all(socket.data.user.id);
|
309 |
-
contacts = await Promise.all(contacts.map(async (contact: any) => {
|
310 |
-
return { ...contact, last_message: await getLastMessage({ user_id: socket.data.user.id, contact_id: contact.id }) }
|
311 |
-
}))
|
312 |
-
|
313 |
-
// console.log(contacts);
|
314 |
-
socket.emit("contacts", contacts);
|
315 |
-
// socket
|
316 |
-
})
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
socket.on("get_messages", async (contact_id) => {
|
321 |
-
var messages = await db.prepare(`select * from messages
|
322 |
-
where (send_to=? and send_from=?)
|
323 |
-
or
|
324 |
-
(send_to=? and send_from=?)
|
325 |
-
ORDER BY send_at ASC
|
326 |
-
`).all(
|
327 |
-
socket.data.user.id,
|
328 |
-
contact_id,
|
329 |
-
|
330 |
-
contact_id,
|
331 |
-
socket.data.user.id,
|
332 |
-
);
|
333 |
-
|
334 |
-
socket.emit("messages", messages);
|
335 |
-
});
|
336 |
-
|
337 |
-
socket.on("send_message", async (data) => {
|
338 |
-
// an event was received from the client
|
339 |
-
|
340 |
-
const msg_id = generateId(15);
|
341 |
-
const send_at = Math.floor(new Date().getTime() / 1000);
|
342 |
-
|
343 |
-
const stmt = db.prepare("insert into messages(id,message,send_to,send_from,send_at) values(?,?,?,?,?)");
|
344 |
-
|
345 |
-
await stmt.run(
|
346 |
-
msg_id,
|
347 |
-
data.message,
|
348 |
-
data.send_to,
|
349 |
-
socket.data.user.id,
|
350 |
-
send_at,
|
351 |
-
);
|
352 |
-
|
353 |
-
console.log("saved message:", data.message);
|
354 |
-
|
355 |
-
const sockets = await io.fetchSockets();
|
356 |
-
const message_bundle = {
|
357 |
-
...data,
|
358 |
-
"id": msg_id,
|
359 |
-
"send_to": data.send_to,
|
360 |
-
"send_from": socket.data.user.id,
|
361 |
-
"send_at": send_at,
|
362 |
-
"is_seen": 0,
|
363 |
-
};
|
364 |
-
|
365 |
-
socket.emit("receive_message", message_bundle);
|
366 |
-
sockets.forEach((client) => {
|
367 |
-
if (client.data.user.id == data.send_to) {
|
368 |
-
// console.log("heyy");
|
369 |
-
return socket.to(client.id).emit("receive_message", message_bundle);
|
370 |
-
}
|
371 |
-
})
|
372 |
-
|
373 |
-
});
|
374 |
-
|
375 |
-
socket.on("get_contact", async contact_id => {
|
376 |
-
const contact = await db.prepare("select * from users where id = ?").get(contact_id);
|
377 |
-
|
378 |
-
|
379 |
-
socket.emit("contact", {
|
380 |
-
...contact,
|
381 |
-
last_message: await getLastMessage({ user_id: socket.data.user.id, contact_id: contact_id }),
|
382 |
-
});
|
383 |
-
})
|
384 |
-
|
385 |
-
// upon disconnection
|
386 |
-
socket.on("disconnect", (reason) => {
|
387 |
-
console.log(`socket ${socket.id} disconnected due to ${reason}`);
|
388 |
-
});
|
389 |
-
});
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
const port = 3000;
|
396 |
-
|
397 |
-
server.listen(port, () => {
|
398 |
-
console.log(`Running at port ${port}`);
|
399 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
server/package.json
CHANGED
@@ -4,10 +4,10 @@
|
|
4 |
"main": "index.js",
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
-
"
|
8 |
-
"
|
9 |
-
"
|
10 |
-
"dbinit": "npx tsx scripts/init_db.ts",
|
11 |
"both": "cd .. && cd client && npm run build && cd .. && cd server && npm run dev",
|
12 |
"test": "echo \"Error: no test specified\" && exit 1"
|
13 |
},
|
|
|
4 |
"main": "index.js",
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
+
"dev": "npx tsx watch src/index.ts",
|
8 |
+
"server": "npx tsx src/index.ts",
|
9 |
+
"seed": "npx tsx src/seeder.ts",
|
10 |
+
"dbinit": "npx tsx src/scripts/init_db.ts",
|
11 |
"both": "cd .. && cd client && npm run build && cd .. && cd server && npm run dev",
|
12 |
"test": "echo \"Error: no test specified\" && exit 1"
|
13 |
},
|
server/{auth.ts β src/auth.ts}
RENAMED
File without changes
|
server/src/authHandler.ts
ADDED
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { lucia } from "./auth.js";
|
2 |
+
import { generateId, Lucia } from "lucia";
|
3 |
+
import { loginSchema, signUpSchema } from "./schemas.js";
|
4 |
+
import bcrypt from "bcrypt";
|
5 |
+
import { Database } from "better-sqlite3";
|
6 |
+
import { Socket } from "socket.io";
|
7 |
+
import { getDB } from "./db.js";
|
8 |
+
|
9 |
+
var db: Database;
|
10 |
+
db = getDB();
|
11 |
+
|
12 |
+
// middleware
|
13 |
+
export const authCookieMiddleware = (async (req: any, res: any, next: any) => {
|
14 |
+
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
15 |
+
if (!sessionId) {
|
16 |
+
res.locals.user = null;
|
17 |
+
res.locals.session = null;
|
18 |
+
return next();
|
19 |
+
}
|
20 |
+
const { session, user } = await lucia.validateSession(sessionId);
|
21 |
+
if (session && session.fresh) {
|
22 |
+
res.appendHeader("Set-Cookie", lucia.createSessionCookie(session.id).serialize());
|
23 |
+
}
|
24 |
+
if (!session) {
|
25 |
+
res.appendHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
|
26 |
+
}
|
27 |
+
res.locals.session = session;
|
28 |
+
res.locals.user = user;
|
29 |
+
// console.log(res.locals.session);
|
30 |
+
// console.log(res.locals.user);
|
31 |
+
return next();
|
32 |
+
|
33 |
+
})
|
34 |
+
|
35 |
+
export const isAuthAPIMiddleware = async (req: any, res: any, next: any) => {
|
36 |
+
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
37 |
+
// console.log(sessionId);
|
38 |
+
|
39 |
+
if (!sessionId) {
|
40 |
+
|
41 |
+
return res.status(403).send({ error: "unauthorized" });
|
42 |
+
}
|
43 |
+
const { session, user } = await lucia.validateSession(sessionId);
|
44 |
+
if (!session) {
|
45 |
+
return res.status(403).send({ error: "unauthorized" });
|
46 |
+
}
|
47 |
+
return next();
|
48 |
+
};
|
49 |
+
|
50 |
+
export const isAuthSocketMiddleware = async (socket: Socket, next: any) => {
|
51 |
+
const sessionId = lucia.readSessionCookie(socket.handshake.headers.cookie ?? "");
|
52 |
+
console.log(sessionId);
|
53 |
+
if (!sessionId) return next(new Error("error"));
|
54 |
+
|
55 |
+
const { session, user } = await lucia.validateSession(sessionId);
|
56 |
+
if (!session) {
|
57 |
+
// return res.end();
|
58 |
+
return next(new Error("error"));
|
59 |
+
}
|
60 |
+
|
61 |
+
// add user and session to the socket.data object
|
62 |
+
socket.data.user = user;
|
63 |
+
socket.data.session = session;
|
64 |
+
|
65 |
+
return next();
|
66 |
+
};
|
67 |
+
|
68 |
+
|
69 |
+
export const signup_handler = async (req: any, res: any) => {
|
70 |
+
|
71 |
+
// console.log(req.params);
|
72 |
+
// console.log(req.body);
|
73 |
+
const parsedData = signUpSchema.safeParse(req.body);
|
74 |
+
if (!parsedData.success) {
|
75 |
+
return res.status(403).json({ field_error: parsedData.error.flatten().fieldErrors }); // 403 is for validation error
|
76 |
+
}
|
77 |
+
|
78 |
+
var data = parsedData.data;
|
79 |
+
// check if password and confirm_password matchs
|
80 |
+
if (data.password != data.confirm_password) {
|
81 |
+
return res.status(403).json({
|
82 |
+
field_error: {
|
83 |
+
confirm_password: [
|
84 |
+
"password doesn't match"
|
85 |
+
]
|
86 |
+
}
|
87 |
+
});
|
88 |
+
}
|
89 |
+
|
90 |
+
// check if username is already used
|
91 |
+
var row = db.prepare("select * from users where id=?").get(data.username);
|
92 |
+
if (row) {
|
93 |
+
return res.status(403).json({
|
94 |
+
field_error: {
|
95 |
+
username: [
|
96 |
+
"username already used."
|
97 |
+
]
|
98 |
+
}
|
99 |
+
})
|
100 |
+
}
|
101 |
+
|
102 |
+
// save to data se
|
103 |
+
const hashedPassword = await bcrypt.hash(data.password, 10);
|
104 |
+
|
105 |
+
await db.prepare("insert into users(id,name,password) values(?,?,?)").run(
|
106 |
+
data.username,
|
107 |
+
data.name,
|
108 |
+
hashedPassword
|
109 |
+
);
|
110 |
+
|
111 |
+
const session = await lucia.createSession(data.username, {});
|
112 |
+
const sessionCookie = lucia.createSessionCookie(session.id);
|
113 |
+
|
114 |
+
res.cookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
|
115 |
+
|
116 |
+
res.json({
|
117 |
+
"status": "success",
|
118 |
+
"msg": "user created successfully",
|
119 |
+
});
|
120 |
+
|
121 |
+
};
|
122 |
+
|
123 |
+
export const login_handler = (async (req: any, res: any) => {
|
124 |
+
// console.log(req.body);
|
125 |
+
|
126 |
+
const parsedData = loginSchema.safeParse(req.body);
|
127 |
+
|
128 |
+
if (!parsedData.success) {
|
129 |
+
return res.status(403).json({
|
130 |
+
"field_error": parsedData.error.flatten().fieldErrors
|
131 |
+
})
|
132 |
+
}
|
133 |
+
|
134 |
+
// check if user exists
|
135 |
+
const row: any = db.prepare("select * from users where id=?").get(parsedData.data.username);
|
136 |
+
// console.log(row);
|
137 |
+
|
138 |
+
if (!row) {
|
139 |
+
return res.status(403).json({
|
140 |
+
"error": "Incorrect username or password"
|
141 |
+
})
|
142 |
+
}
|
143 |
+
|
144 |
+
// check password
|
145 |
+
if (!await bcrypt.compare(parsedData.data.password, row.password)) {
|
146 |
+
return res.status(403).json({
|
147 |
+
"error": "Incorrect username or password"
|
148 |
+
})
|
149 |
+
}
|
150 |
+
|
151 |
+
// save session
|
152 |
+
const session = await lucia.createSession(row.id, {});
|
153 |
+
const sessionCookie = lucia.createSessionCookie(session.id);
|
154 |
+
|
155 |
+
// console.log(sessionCookie.attributes);
|
156 |
+
res.cookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
|
157 |
+
|
158 |
+
res.json({
|
159 |
+
"status": "success",
|
160 |
+
"msg": "logged in successfully",
|
161 |
+
});
|
162 |
+
|
163 |
+
})
|
server/src/chatHandler.ts
ADDED
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { lucia } from "./auth.js";
|
2 |
+
import { generateId, Lucia } from "lucia";
|
3 |
+
import bcrypt from "bcrypt";
|
4 |
+
import { Database } from "better-sqlite3";
|
5 |
+
import { getDB } from "./db.js";
|
6 |
+
|
7 |
+
var db: Database;
|
8 |
+
db = getDB();
|
9 |
+
|
10 |
+
|
11 |
+
|
12 |
+
type MessageType = {
|
13 |
+
id: string,
|
14 |
+
content: string,
|
15 |
+
type: string,
|
16 |
+
timestamp: number,
|
17 |
+
sender_id: string,
|
18 |
+
receiver_id: string,
|
19 |
+
status: "sent" | "received" | "seen",
|
20 |
+
};
|
21 |
+
|
22 |
+
type ContactType = {
|
23 |
+
id: string,
|
24 |
+
name: string,
|
25 |
+
chat_data: {
|
26 |
+
last_message: MessageType,
|
27 |
+
unseen_count: number,
|
28 |
+
}
|
29 |
+
};
|
30 |
+
|
31 |
+
export function searchContacts(user_id: string, search_string: string) {
|
32 |
+
var contacts = db.prepare(`SELECT id,name FROM users WHERE id LIKE ? AND id!=?`)
|
33 |
+
.all("%" + search_string + "%",user_id);
|
34 |
+
|
35 |
+
|
36 |
+
// console.log(contacts);
|
37 |
+
contacts = contacts.map((contact: any) => {
|
38 |
+
var last_msg: any = getLastMessage(user_id, contact.id);
|
39 |
+
|
40 |
+
// console.log(user_id, contact.id, last_msg);
|
41 |
+
|
42 |
+
var chat_data: any = {};
|
43 |
+
if (last_msg.id) {
|
44 |
+
chat_data.last_message = last_msg;
|
45 |
+
chat_data.unseen_count = getUnseenCount(user_id, contact.id);
|
46 |
+
}
|
47 |
+
|
48 |
+
return { ...contact, chat_data }
|
49 |
+
})
|
50 |
+
|
51 |
+
return contacts as ContactType[];
|
52 |
+
}
|
53 |
+
|
54 |
+
|
55 |
+
export function getContact(user_id: string, other_contact_id: string) {
|
56 |
+
var contact: any = {};
|
57 |
+
var user: any = db.prepare(`SELECT id,name from users
|
58 |
+
WHERE id=?
|
59 |
+
`).get(other_contact_id);
|
60 |
+
|
61 |
+
contact.id = user.id;
|
62 |
+
contact.name = user.name;
|
63 |
+
contact.chat_data = {
|
64 |
+
last_message: getLastMessage(user_id, other_contact_id),
|
65 |
+
unseen_count: getUnseenCount(user_id, other_contact_id),
|
66 |
+
}
|
67 |
+
|
68 |
+
return contact as ContactType;
|
69 |
+
}
|
70 |
+
|
71 |
+
export const getMyChats = (user_id: string) => {
|
72 |
+
// var contacts = db.prepare("SELECT send_to,send_from FROM messages WHERE send_to=? or send_from=?").all(user_id,user_id);
|
73 |
+
|
74 |
+
var chat_ids: any = db.prepare(`
|
75 |
+
SELECT Distinct(sender_id) as id FROM messages WHERE receiver_id=?
|
76 |
+
UNION
|
77 |
+
SELECT Distinct(receiver_id) as id FROM messages WHERE sender_id=?
|
78 |
+
`).all(user_id, user_id);
|
79 |
+
|
80 |
+
// get data of all
|
81 |
+
var contacts: any = [];
|
82 |
+
for (var i = 0; i < chat_ids.length; i++) {
|
83 |
+
contacts.push(getContact(user_id, chat_ids[i].id));
|
84 |
+
}
|
85 |
+
return contacts as ContactType[];
|
86 |
+
};
|
87 |
+
|
88 |
+
|
89 |
+
export function getLastMessage(user_id: string, other_contact_id: string) {
|
90 |
+
const msg_stmt = db.prepare(`SELECT id,content,type,MAX(timestamp) as timestamp,sender_id,receiver_id,status
|
91 |
+
FROM messages
|
92 |
+
WHERE sender_id in (?,?) AND receiver_id in (?,?)`)
|
93 |
+
|
94 |
+
const last_message = msg_stmt.get(user_id, other_contact_id, user_id, other_contact_id);
|
95 |
+
return last_message as MessageType;
|
96 |
+
}
|
97 |
+
|
98 |
+
export function getUnseenCount(user_id: string, other_contact_id: string) {
|
99 |
+
const stmt = db.prepare(`SELECT COUNT(id) as unseen_count FROM messages
|
100 |
+
WHERE sender_id=? AND receiver_id=? AND status!='seen'
|
101 |
+
`)
|
102 |
+
|
103 |
+
const unseen_count: any = stmt.get(other_contact_id, user_id);
|
104 |
+
return unseen_count.unseen_count;
|
105 |
+
}
|
106 |
+
|
107 |
+
export function addMessage({
|
108 |
+
content,
|
109 |
+
type = "text",
|
110 |
+
sender_id,
|
111 |
+
receiver_id,
|
112 |
+
}: {
|
113 |
+
content: string,
|
114 |
+
type: string,
|
115 |
+
sender_id: string,
|
116 |
+
receiver_id: string,
|
117 |
+
|
118 |
+
}) {
|
119 |
+
|
120 |
+
type = type.toLowerCase();
|
121 |
+
const id = generateId(10);
|
122 |
+
const stmt = db.prepare(`INSERT INTO messages(id,content,type,sender_id,receiver_id) VALUES(?,?,?,?,?)`);
|
123 |
+
|
124 |
+
stmt.run(
|
125 |
+
id,
|
126 |
+
content,
|
127 |
+
type,
|
128 |
+
sender_id,
|
129 |
+
receiver_id
|
130 |
+
);
|
131 |
+
|
132 |
+
return db.prepare(`SELECT * FROM messages WHERE id=?`).get(id) as MessageType;
|
133 |
+
}
|
134 |
+
|
135 |
+
export function markMessageSeen(user_id: string, msg_id: string) {
|
136 |
+
const stmt = db.prepare(`UPDATE messages SET status='seen' WHERE sender_id!=? AND id=?`);
|
137 |
+
stmt.run(user_id, msg_id);
|
138 |
+
}
|
139 |
+
|
140 |
+
export function getAllMessages(user_id: string, other_contact_id: string) {
|
141 |
+
const stmt = db.prepare(`SELECT * from messages
|
142 |
+
WHERE sender_id in (?,?) AND receiver_id in (?,?)
|
143 |
+
ORDER BY messages.timestamp DESC
|
144 |
+
`);
|
145 |
+
|
146 |
+
const msgs = stmt.all(user_id, other_contact_id, user_id, other_contact_id);
|
147 |
+
return msgs as MessageType[];
|
148 |
+
}
|
149 |
+
|
150 |
+
export function deleteChat(user_id: string, other_contact_id: string) {
|
151 |
+
db.prepare(`DELETE FROM message WHERE sender_id in (?,?) AND receiver_id in (?,?) `)
|
152 |
+
.run(user_id, other_contact_id, user_id, other_contact_id);
|
153 |
+
return true;
|
154 |
+
}
|
155 |
+
|
156 |
+
// console.log(searchContacts("user1","u"))
|
157 |
+
// console.log(getMyChats("user1"))
|
158 |
+
// console.log(getMyChats("user2"))
|
159 |
+
// console.log(getMyChats("user3"))
|
160 |
+
|
161 |
+
// console.log(getLastMessage("user1","user1"))
|
162 |
+
// console.log(getUnseenCount("user1", "user2"))
|
163 |
+
|
164 |
+
// // console.log(addMessage({
|
165 |
+
// // message: "new_msg",
|
166 |
+
// // type: "text",
|
167 |
+
// // send_from: "user3",
|
168 |
+
// // send_to: "wx6uv",
|
169 |
+
// // }))
|
170 |
+
|
171 |
+
// console.log(getUnseenCount("user1", "wx6uv"))
|
172 |
+
// // markMessageSeen("user1", "33w87");
|
173 |
+
// console.log(getAllMessages("user1", "wx6uv"))
|
174 |
+
|
175 |
+
// console.log(deleteChat('user2',"sblua"))
|
server/{db.ts β src/db.ts}
RENAMED
File without changes
|
server/src/index.ts
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import express from "express";
|
2 |
+
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 { loginSchema, signUpSchema } from "./schemas.js";
|
7 |
+
import bcrypt from "bcrypt";
|
8 |
+
import dotenv from "dotenv";
|
9 |
+
import cors from "cors";
|
10 |
+
import mutler from "multer";
|
11 |
+
import { Server, Socket } from "socket.io";
|
12 |
+
import http from "http";
|
13 |
+
import path from "path";
|
14 |
+
import { authCookieMiddleware, isAuthAPIMiddleware, isAuthSocketMiddleware, login_handler, signup_handler } from "./authHandler.js";
|
15 |
+
import {
|
16 |
+
getMyChats,
|
17 |
+
getLastMessage,
|
18 |
+
getUnseenCount,
|
19 |
+
|
20 |
+
searchContacts,
|
21 |
+
|
22 |
+
addMessage,
|
23 |
+
markMessageSeen,
|
24 |
+
getAllMessages,
|
25 |
+
getContact,
|
26 |
+
} from "./chatHandler.js";
|
27 |
+
|
28 |
+
dotenv.config();
|
29 |
+
|
30 |
+
const __dirname = path.resolve();
|
31 |
+
var db = getDB();
|
32 |
+
|
33 |
+
|
34 |
+
var app = express();
|
35 |
+
const server = http.createServer(app);
|
36 |
+
|
37 |
+
const allowedOrigins = process.env.ALLOWED_ORIGINS || 'http://localhost:3001'
|
38 |
+
const allowed_origins = allowedOrigins.split(",").map(item => item.trim());
|
39 |
+
|
40 |
+
|
41 |
+
app.use(cors({
|
42 |
+
credentials: true,
|
43 |
+
// origin: true,
|
44 |
+
origin: allowed_origins,
|
45 |
+
}));
|
46 |
+
|
47 |
+
// parse application/json
|
48 |
+
app.use(bodyParser.json());
|
49 |
+
|
50 |
+
// parse application/x-www-form-urlencoded
|
51 |
+
app.use(bodyParser.urlencoded({ extended: false }));
|
52 |
+
|
53 |
+
// app.use(express.urlencoded());
|
54 |
+
app.use(mutler().array(""));
|
55 |
+
|
56 |
+
// The entire build/web directory is statically served.
|
57 |
+
app.use(express.static(path.join(__dirname, "../client/build")));
|
58 |
+
|
59 |
+
|
60 |
+
|
61 |
+
app.use(authCookieMiddleware);
|
62 |
+
|
63 |
+
|
64 |
+
// http://localhost:3000/session
|
65 |
+
app.get("/session", (req, res) => {
|
66 |
+
if (!res.locals.user) {
|
67 |
+
return res.status(403).send();
|
68 |
+
}
|
69 |
+
return res.status(200).send(res.locals.user);
|
70 |
+
})
|
71 |
+
|
72 |
+
app.post("/signup", signup_handler);
|
73 |
+
app.post("/login", login_handler);
|
74 |
+
|
75 |
+
app.get("/test", (req,res)=>{
|
76 |
+
console.log(req);
|
77 |
+
// console.log(res);
|
78 |
+
res.send("ok");
|
79 |
+
});
|
80 |
+
|
81 |
+
|
82 |
+
// Catch all. If we want to add pages later, then we should probably change this.
|
83 |
+
app.get("*", (_req, res) => {
|
84 |
+
res.sendFile(path.join(__dirname + "/../client/build/index.html"));
|
85 |
+
});
|
86 |
+
|
87 |
+
// socket.io server
|
88 |
+
const io = new Server(server, {
|
89 |
+
cors: {
|
90 |
+
origin: allowed_origins,
|
91 |
+
methods: ["GET", "POST"],
|
92 |
+
credentials: true,
|
93 |
+
}
|
94 |
+
});
|
95 |
+
|
96 |
+
io.use(isAuthSocketMiddleware);
|
97 |
+
|
98 |
+
io.on("connection", async (socket) => {
|
99 |
+
console.log(`socket ${socket.id} connected`);
|
100 |
+
// client.emit("search_contacts","ankit")
|
101 |
+
socket.on("search_contacts",(search_string:string)=>{
|
102 |
+
const contacts = searchContacts(socket.data.user.id,search_string);
|
103 |
+
socket.emit("search_results",contacts);
|
104 |
+
})
|
105 |
+
|
106 |
+
//Old chats
|
107 |
+
socket.on("get_chats", async () => {
|
108 |
+
|
109 |
+
// get all chats of client
|
110 |
+
const chats = getMyChats(socket.data.user.id);
|
111 |
+
socket.emit("chats", chats);
|
112 |
+
})
|
113 |
+
|
114 |
+
socket.on("get_messages", async (chat_id) => {
|
115 |
+
var messages = getAllMessages(socket.data.user.id, chat_id);
|
116 |
+
// console.log(socket.data.user.id, chat_id);
|
117 |
+
// console.log(messages);
|
118 |
+
socket.emit("messages", messages);
|
119 |
+
});
|
120 |
+
|
121 |
+
socket.on("send_message", async (data) => {
|
122 |
+
// an event was received from the client
|
123 |
+
|
124 |
+
console.log(data);
|
125 |
+
const message = addMessage({
|
126 |
+
content: data.content,
|
127 |
+
type: data.type,
|
128 |
+
sender_id: socket.data.user.id,
|
129 |
+
receiver_id: data.receiver_id,
|
130 |
+
})
|
131 |
+
|
132 |
+
console.log("saved message:", message.content , "from:",message.sender_id);
|
133 |
+
|
134 |
+
|
135 |
+
|
136 |
+
const sockets = await io.fetchSockets();
|
137 |
+
const receivers = sockets.filter((client)=>client.data.user.id==data.receiver_id);
|
138 |
+
|
139 |
+
receivers.forEach((client)=>{
|
140 |
+
client.emit("receive_message",message);
|
141 |
+
})
|
142 |
+
socket.emit("receive_message",message);
|
143 |
+
|
144 |
+
});
|
145 |
+
|
146 |
+
socket.on("mark_messages_seen", async (msg_ids: any) => {
|
147 |
+
// console.log(messages.length)
|
148 |
+
|
149 |
+
msg_ids.forEach((msg_id:any)=>{
|
150 |
+
markMessageSeen(socket.data.user.id,msg_id);
|
151 |
+
})
|
152 |
+
|
153 |
+
})
|
154 |
+
|
155 |
+
socket.on("get_contact", async contact_id => {
|
156 |
+
const contact: any = getContact(socket.data.user.id,contact_id);
|
157 |
+
|
158 |
+
|
159 |
+
socket.emit("contact", contact);
|
160 |
+
})
|
161 |
+
|
162 |
+
// upon disconnection
|
163 |
+
socket.on("disconnect", (reason) => {
|
164 |
+
console.log(`socket ${socket.id} disconnected due to ${reason}`);
|
165 |
+
});
|
166 |
+
});
|
167 |
+
|
168 |
+
|
169 |
+
|
170 |
+
|
171 |
+
|
172 |
+
const port = 3000;
|
173 |
+
|
174 |
+
server.listen(port, () => {
|
175 |
+
console.log(`Running at port ${port}`);
|
176 |
+
});
|
server/{middleware.ts β src/middleware.ts}
RENAMED
File without changes
|
server/{schemas.ts β src/schemas.ts}
RENAMED
File without changes
|
server/{scripts/init_db.ts β src/scripts/init_db.js}
RENAMED
@@ -1,21 +1,16 @@
|
|
1 |
import database from "better-sqlite3";
|
2 |
import dotenv from "dotenv";
|
3 |
import fs from "fs";
|
4 |
-
|
5 |
dotenv.config();
|
6 |
-
|
7 |
-
const db_path = process.env.DB_NAME as string;
|
8 |
console.log(db_path);
|
9 |
fs.unlink(db_path, (err) => {
|
10 |
if (err) {
|
11 |
console.error(err);
|
12 |
}
|
13 |
else {
|
14 |
-
console.log(`deleted old database:\t${db_path}`)
|
15 |
-
|
16 |
-
|
17 |
const db = database(db_path);
|
18 |
-
|
19 |
db.exec(`
|
20 |
create table users(
|
21 |
id Text not null primary key,
|
@@ -24,7 +19,6 @@ fs.unlink(db_path, (err) => {
|
|
24 |
password varchar(20) NOT NULL
|
25 |
);
|
26 |
`);
|
27 |
-
|
28 |
db.exec(`
|
29 |
create table sessions(
|
30 |
id Text not null primary key,
|
@@ -33,20 +27,24 @@ fs.unlink(db_path, (err) => {
|
|
33 |
FOREIGN KEY (user_id) REFERENCES users(id)
|
34 |
);
|
35 |
`);
|
36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
db.exec(`
|
38 |
create table messages(
|
39 |
id Text not null primary key,
|
40 |
send_at INTEGER NOT NULL,
|
41 |
message Text not null,
|
42 |
-
send_to TEXT NOT NULL,
|
43 |
send_from TEXT NOT NULL,
|
|
|
44 |
is_seen TINYINT DEFAULT 0,
|
45 |
-
FOREIGN KEY (
|
46 |
-
FOREIGN KEY (
|
47 |
);
|
48 |
`);
|
49 |
-
|
50 |
-
|
51 |
}
|
52 |
-
})
|
|
|
1 |
import database from "better-sqlite3";
|
2 |
import dotenv from "dotenv";
|
3 |
import fs from "fs";
|
|
|
4 |
dotenv.config();
|
5 |
+
const db_path = process.env.DB_NAME;
|
|
|
6 |
console.log(db_path);
|
7 |
fs.unlink(db_path, (err) => {
|
8 |
if (err) {
|
9 |
console.error(err);
|
10 |
}
|
11 |
else {
|
12 |
+
console.log(`deleted old database:\t${db_path}`);
|
|
|
|
|
13 |
const db = database(db_path);
|
|
|
14 |
db.exec(`
|
15 |
create table users(
|
16 |
id Text not null primary key,
|
|
|
19 |
password varchar(20) NOT NULL
|
20 |
);
|
21 |
`);
|
|
|
22 |
db.exec(`
|
23 |
create table sessions(
|
24 |
id Text not null primary key,
|
|
|
27 |
FOREIGN KEY (user_id) REFERENCES users(id)
|
28 |
);
|
29 |
`);
|
30 |
+
db.exec(`
|
31 |
+
create table chats(
|
32 |
+
id Text not null primary key,
|
33 |
+
member_id TEXT NOT NULL,
|
34 |
+
FOREIGN KEY (member_id) REFERENCES users(id)
|
35 |
+
);
|
36 |
+
`);
|
37 |
db.exec(`
|
38 |
create table messages(
|
39 |
id Text not null primary key,
|
40 |
send_at INTEGER NOT NULL,
|
41 |
message Text not null,
|
|
|
42 |
send_from TEXT NOT NULL,
|
43 |
+
chat_id TEXT NOT NULL,
|
44 |
is_seen TINYINT DEFAULT 0,
|
45 |
+
FOREIGN KEY (send_from) REFERENCES users(id),
|
46 |
+
FOREIGN KEY (chat_id) REFERENCES users(id)
|
47 |
);
|
48 |
`);
|
|
|
|
|
49 |
}
|
50 |
+
});
|
server/src/scripts/init_db.ts
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import database from "better-sqlite3";
|
2 |
+
import dotenv from "dotenv";
|
3 |
+
import fs from "fs";
|
4 |
+
|
5 |
+
dotenv.config();
|
6 |
+
|
7 |
+
const db_path = process.env.DB_NAME as string;
|
8 |
+
console.log(db_path);
|
9 |
+
fs.unlink(db_path, (err) => {
|
10 |
+
if (err) {
|
11 |
+
console.error(err);
|
12 |
+
}
|
13 |
+
else {
|
14 |
+
console.log(`deleted old database:\t${db_path}`)
|
15 |
+
|
16 |
+
|
17 |
+
const db = database(db_path);
|
18 |
+
|
19 |
+
db.exec(`
|
20 |
+
create table users(
|
21 |
+
id Text not null PRIMARY KEY,
|
22 |
+
name varchar(40) NOT NULL,
|
23 |
+
password varchar(20) NOT NULL
|
24 |
+
);
|
25 |
+
`);
|
26 |
+
|
27 |
+
db.exec(`
|
28 |
+
create table sessions(
|
29 |
+
id Text not null PRIMARY KEY,
|
30 |
+
expires_at INTEGER NOT NULL,
|
31 |
+
user_id TEXT NOT NULL,
|
32 |
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
33 |
+
);
|
34 |
+
`);
|
35 |
+
|
36 |
+
|
37 |
+
db.exec(`
|
38 |
+
create table messages(
|
39 |
+
id Text not null primary key,
|
40 |
+
content Text not null,
|
41 |
+
type Text NOT NULL DEFAULT "text",
|
42 |
+
sender_id TEXT NOT NULL,
|
43 |
+
receiver_id TEXT NOT NULL,
|
44 |
+
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
45 |
+
status DEFAULT "sent", /* "sent", "received", "seen" */
|
46 |
+
FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE,
|
47 |
+
FOREIGN KEY (receiver_id) REFERENCES users(id) ON DELETE CASCADE
|
48 |
+
);
|
49 |
+
`);
|
50 |
+
|
51 |
+
|
52 |
+
|
53 |
+
|
54 |
+
}
|
55 |
+
})
|
server/src/seeder.ts
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { getDB } from "./db.js";
|
2 |
+
import bcrypt from "bcrypt";
|
3 |
+
import { generateId, verifyRequestOrigin } from "lucia";
|
4 |
+
|
5 |
+
|
6 |
+
import dotenv from "dotenv";
|
7 |
+
dotenv.config();
|
8 |
+
|
9 |
+
var db = getDB();
|
10 |
+
|
11 |
+
const users = [
|
12 |
+
{
|
13 |
+
username: "user1",
|
14 |
+
password: await bcrypt.hash("123", 10),
|
15 |
+
},
|
16 |
+
{
|
17 |
+
username: "user2",
|
18 |
+
password: await bcrypt.hash("123", 10),
|
19 |
+
},
|
20 |
+
{
|
21 |
+
username: "user3",
|
22 |
+
password: await bcrypt.hash("123", 10),
|
23 |
+
},
|
24 |
+
];
|
25 |
+
|
26 |
+
|
27 |
+
|
28 |
+
function seedUsers() {
|
29 |
+
|
30 |
+
const stmt = db.prepare("INSERT INTO users(id,name,password) VALUES(?,?,?)");
|
31 |
+
|
32 |
+
users.forEach(user => {
|
33 |
+
stmt.run(user.username, user.username, user.password);
|
34 |
+
})
|
35 |
+
|
36 |
+
}
|
37 |
+
|
38 |
+
function seedMessages() {
|
39 |
+
|
40 |
+
const stmt = db.prepare(`INSERT INTO messages(id,content,type,sender_id,receiver_id) VALUES(?,?,?,?,?)`);
|
41 |
+
|
42 |
+
for (var i = 0; i < users.length; i++) {
|
43 |
+
for (var j = 0; j < users.length; j++) {
|
44 |
+
if(i==j) continue;
|
45 |
+
var message_id = generateId(5);
|
46 |
+
var content = "hi";
|
47 |
+
var type = "text";
|
48 |
+
var sender_id = users[i].username;
|
49 |
+
var receiver_id = users[j].username;
|
50 |
+
|
51 |
+
stmt.run(
|
52 |
+
message_id,
|
53 |
+
content,
|
54 |
+
type,
|
55 |
+
sender_id,
|
56 |
+
receiver_id,
|
57 |
+
);
|
58 |
+
|
59 |
+
}
|
60 |
+
|
61 |
+
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
|
66 |
+
db.prepare("delete from users").run();
|
67 |
+
db.prepare("delete from messages").run();
|
68 |
+
seedUsers()
|
69 |
+
seedMessages()
|
server/tsconfig.json
CHANGED
@@ -27,7 +27,7 @@
|
|
27 |
/* Modules */
|
28 |
"module": "ESNext" /* Specify what module code is generated. */,
|
29 |
"moduleResolution": "NodeNext",
|
30 |
-
|
31 |
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
32 |
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
33 |
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
|
|
27 |
/* Modules */
|
28 |
"module": "ESNext" /* Specify what module code is generated. */,
|
29 |
"moduleResolution": "NodeNext",
|
30 |
+
"rootDir": "./src/", /* Specify the root folder within your source files. */
|
31 |
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
32 |
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
33 |
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|