From 37c170b265920a5ae0cc821da22260169ae16950 Mon Sep 17 00:00:00 2001 From: leca Date: Thu, 6 Feb 2025 15:22:32 +0300 Subject: [PATCH] done chat --- public/css/index.css | 10 ++++++++- public/js/chat.js | 45 ++++++++++++++++++++++++++++++++++------- sample.env | 3 ++- src/controllers/api.js | 20 ++++++++++++++++++ src/messages.js | 2 ++ src/middlewares/auth.js | 1 - src/routers/api.js | 3 +++ src/services/api.js | 15 ++++++++++++++ views/chat.pug | 2 +- 9 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 src/controllers/api.js create mode 100644 src/services/api.js diff --git a/public/css/index.css b/public/css/index.css index a974847..4722460 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -97,13 +97,14 @@ canvas { } .chat-container { - overflow: scroll; border: black solid } .messages-container { width: 100%; height: 75%; + overflow: scroll; + overflow-wrap: anywhere; border: gray solid; } @@ -125,4 +126,11 @@ canvas { font-size: 15pt; margin: 0; padding: 0 +} + +.chat-message { + margin-top: 8px; + margin-bottom: 8px; + margin-left: 8px; + margin-right: 16px; } \ No newline at end of file diff --git a/public/js/chat.js b/public/js/chat.js index f8d15d0..493d043 100644 --- a/public/js/chat.js +++ b/public/js/chat.js @@ -4,21 +4,50 @@ function getCookie(name) { if (parts.length === 2) return parts.pop().split(';').shift(); } -$(document).ready(() => { - //prod - // const socket = new WebSocket('wss://auth.foxarmy.org'); - //dev - const socket = new WebSocket('ws://localhost:3000'); +$(document).ready(async () => { + const wsConnectionString = (await fetch(`/api/websocketConnection`)).text() + const socket = new WebSocket(wsConnectionString); + let lastMessageNumber = 0; + let page = 20; + const fetchMoreMessages = async () => { + if (lastMessageNumber < 0 ) return []; + let messages = await (await fetch(`/api/getChatMessages/${lastMessageNumber}/${page}`)).json(); + lastMessageNumber = messages.length < page? -1 : lastMessageNumber + messages.length + return messages + } const appendMessageToChat = (author, content) => { - const messageDiv = document.createElement('div')//`
<${message.author}> ${message.content}

` + const messageDiv = document.createElement('div') messageDiv.className = "chat-message" messageDiv.textContent = `<${author}> ${content}` document.getElementsByClassName("messages-container")[0].appendChild(messageDiv) } - socket.onmessage = message => { + const prependMessageToChat = (author, content) => { + const messageDiv = document.createElement('div') + messageDiv.className = "chat-message" + messageDiv.textContent = `<${author}> ${content}` + const messageContainer = document.getElementsByClassName("messages-container")[0] + messageContainer.firstChild.insertAdjacentElement('afterend', messageDiv) + } + const onFetchMessageButtonClick = async () => { + let messages = await fetchMoreMessages(lastMessageNumber, page); + if (messages.length == 0) return; + + messages.forEach(message => { + prependMessageToChat(message.author, message.content); + }); + } + + $("#fetch-messages-button").click(onFetchMessageButtonClick); + + await onFetchMessageButtonClick(); + + + + + socket.onmessage = message => { try { message = JSON.parse(message.data) } catch(e) { @@ -40,6 +69,8 @@ $(document).ready(() => { socket.send(JSON.stringify(message)); appendMessageToChat(message.author, message.content) $("#chat-input").val("") + let messageContainer = $(".messages-container") + messageContainer.animate({ scrollTop: messageContainer.prop("scrollHeight")}, 1000); } $("#send-message-button").click(sendData); diff --git a/sample.env b/sample.env index c241b43..eed77c1 100644 --- a/sample.env +++ b/sample.env @@ -6,4 +6,5 @@ DBPORT=5432 DBPASS=GENERATE_A_STRONG_PASSWORD_HERE PORT=3000 REQUIRE_TOKEN=false -DELETE_TOKEN_ON_USE=true \ No newline at end of file +DELETE_TOKEN_ON_USE=true +WS_CONNECTION_STRING=wss://auth.foxarmy.org diff --git a/src/controllers/api.js b/src/controllers/api.js new file mode 100644 index 0000000..60f0619 --- /dev/null +++ b/src/controllers/api.js @@ -0,0 +1,20 @@ +import ApiService from "../services/api.js"; +import dotenv from 'dotenv'; + +dotenv.config({path: ".env"}); + +class ApiController { + async getChatMessages(req, res) { + const {limit, offset} = req.params; + + const messages = await ApiService.getChatMessages(limit, offset); + + return res.status(200).send(messages); + } + + async getWebsocketConnection(req, res) { + return res.status(200).send(process.env.WS_CONNECTION_STRING) + } +} + +export default new ApiController(); \ No newline at end of file diff --git a/src/messages.js b/src/messages.js index f82ba1a..4c959aa 100644 --- a/src/messages.js +++ b/src/messages.js @@ -91,6 +91,8 @@ const startChat = async () => { origin: "website" }) }] + }).catch(e => { + console.log(e) }); // we are not sending this message to all websockets right now, because plugin will emit // a chat event once it'll catch this message from kafka, triggering a new message diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index bf15825..891e838 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -26,7 +26,6 @@ const validateInviteToken = async (req, res, next) => { let tokenValid = false; inviteTokens.forEach((token) => { - console.log(`${token} == ${inviteToken}`) if (token == inviteToken) tokenValid = true; }); diff --git a/src/routers/api.js b/src/routers/api.js index 6744c44..7d96573 100644 --- a/src/routers/api.js +++ b/src/routers/api.js @@ -6,6 +6,7 @@ import auth from '../middlewares/auth.js'; import utils from '../utils.js'; import UserController from '../controllers/user.js'; +import ApiController from '../controllers/api.js'; const ApiRouter = new Router(); @@ -16,5 +17,7 @@ ApiRouter.post('/changepassword', auth.authenticate, existance.userExist, UserCo ApiRouter.post('/uploadSkin', existance.userExist, auth.authenticate, utils.upload.single('file'), requiredParameters.requireFile, UserController.uploadSkin); ApiRouter.post('/uploadCape', existance.userExist, auth.authenticate, auth.canHaveCloak, utils.upload.single('file'), requiredParameters.requireFile, UserController.uploadCape); ApiRouter.get('/getUsername', existance.userExist, auth.authenticate, UserController.getUsername); +ApiRouter.get('/getChatMessages/:offset/:limit', auth.authenticate, ApiController.getChatMessages); +ApiRouter.get('/webSocketConnection', auth.authenticate, ApiController.getWebsocketConnection) export default ApiRouter; \ No newline at end of file diff --git a/src/services/api.js b/src/services/api.js new file mode 100644 index 0000000..9aef7bb --- /dev/null +++ b/src/services/api.js @@ -0,0 +1,15 @@ +import db from '../db.js'; + +class ApiService { + async getChatMessages(limit, offset) { + try { + const messages = (await db.query("SELECT * FROM chat_messages ORDER BY ID DESC LIMIT $1 OFFSET $2", [limit, offset])).rows + + return messages + } catch(e) { + console.log(e) + } + } +} + +export default new ApiService(); \ No newline at end of file diff --git a/views/chat.pug b/views/chat.pug index 02d52bd..97e798c 100644 --- a/views/chat.pug +++ b/views/chat.pug @@ -15,7 +15,7 @@ html h1 Чат div(class="chat-container") div(class="messages-container") - + button(id="fetch-messages-button") Загрузить ещё div(class="input-container") input(type="text" id="chat-input") button(id="send-message-button") Отправить \ No newline at end of file