switch from req.session.jwt to cookies

This commit is contained in:
leca 2025-02-03 04:02:52 +03:00
parent c0ee036530
commit 3a742b1f34
12 changed files with 247 additions and 26 deletions

View File

@ -0,0 +1,64 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0" version="26.0.10">
<diagram name="Page-1" id="nvzCRnp9_quasQS-yvnN">
<mxGraphModel dx="1434" dy="751" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="69ARyxQlwhHyFWX5DNmJ-3" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="70" y="60" width="170" height="90" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-5" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="60" width="170" height="90" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-6" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="340" y="330" width="170" height="90" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-7" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="340" y="600" width="170" height="90" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-9" value="&lt;font style=&quot;font-size: 22px;&quot;&gt;Frontend&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="110" y="90" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-10" value="&lt;font style=&quot;font-size: 22px;&quot;&gt;Backend&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="700" y="90" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-11" value="&lt;div&gt;&lt;font style=&quot;font-size: 22px;&quot;&gt;Kafka&lt;/font&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="390" y="350" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-13" value="&lt;font style=&quot;font-size: 22px;&quot;&gt;MC Server&lt;br&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="390" y="630" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-14" value="" style="shape=flexArrow;endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="69ARyxQlwhHyFWX5DNmJ-3" target="69ARyxQlwhHyFWX5DNmJ-5">
<mxGeometry width="100" height="100" relative="1" as="geometry">
<mxPoint x="370" y="440" as="sourcePoint" />
<mxPoint x="470" y="340" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-16" value="websocket" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="390" y="60" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-17" value="" style="shape=flexArrow;endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.465;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;exitX=1;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="69ARyxQlwhHyFWX5DNmJ-6" target="69ARyxQlwhHyFWX5DNmJ-5">
<mxGeometry width="100" height="100" relative="1" as="geometry">
<mxPoint x="630" y="310" as="sourcePoint" />
<mxPoint x="470" y="340" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-18" value="" style="shape=flexArrow;endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="69ARyxQlwhHyFWX5DNmJ-7" target="69ARyxQlwhHyFWX5DNmJ-6">
<mxGeometry width="100" height="100" relative="1" as="geometry">
<mxPoint x="100" y="480" as="sourcePoint" />
<mxPoint x="200" y="380" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-19" value="&lt;div&gt;plugin&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="440" y="500" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-20" value="&lt;div&gt;KafkaJS&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="650" y="250" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="69ARyxQlwhHyFWX5DNmJ-21" value="Multiple clients" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="120" y="170" width="60" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

34
package-lock.json generated
View File

@ -16,9 +16,11 @@
"express-session": "^1.18.1",
"jimp": "^1.6.0",
"jsonwebtoken": "^9.0.2",
"kafkajs": "^2.2.4",
"multer": "^1.4.5-lts.1",
"pg": "^8.13.1",
"pug": "^3.0.3"
"pug": "^3.0.3",
"ws": "^8.18.0"
},
"devDependencies": {
"nodemon": "^3.1.9"
@ -1885,6 +1887,15 @@
"safe-buffer": "^5.0.1"
}
},
"node_modules/kafkajs": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-2.2.4.tgz",
"integrity": "sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@ -3367,6 +3378,27 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xml-parse-from-string": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",

View File

@ -27,9 +27,11 @@
"express-session": "^1.18.1",
"jimp": "^1.6.0",
"jsonwebtoken": "^9.0.2",
"kafkajs": "^2.2.4",
"multer": "^1.4.5-lts.1",
"pg": "^8.13.1",
"pug": "^3.0.3"
"pug": "^3.0.3",
"ws": "^8.18.0"
},
"devDependencies": {
"nodemon": "^3.1.9"

View File

@ -6,7 +6,7 @@ html {
}
body {
margin: 0;
margin: 0;
padding: 0;
height: 100%;
width: 100%;
@ -62,7 +62,7 @@ canvas {
padding-left: 3%;
padding-right: 3%;
margin-top: 0%;
border:solid 2px gray;
border: solid 2px gray;
box-shadow: 0px 20px 150px rgba(0, 0, 0, 1);
}
@ -100,3 +100,29 @@ canvas {
overflow: scroll;
border: black solid
}
.messages-container {
width: 100%;
height: 75%;
border: gray solid;
}
.input-container {
width: 100%;
height: 5%;
}
#chat-input {
width: calc(100% - 150px);
height: 100%;
margin: 0;
}
#send-message-button {
float: right;
width: 150px;
height: 100%;
font-size: 15pt;
margin: 0;
padding: 0
}

16
public/js/chat.js Normal file
View File

@ -0,0 +1,16 @@
$(document).ready(() => {
const sendData = async () => {
alert(1);
}
$("#send-message-button").click(sendData);
$('#chat-input').keyup(function(e){
if(e.keyCode == 13)
{
$(this).trigger("enterKey");
}
});
$("#chat-input").bind("enterKey", sendData);
});

View File

@ -31,7 +31,8 @@ class UserController {
utils.removeFromFile('./inviteTokens.txt', req.body.inviteToken);
}
req.session.jwt = jwt.sign({ username }, process.env.SECRET, {expiresIn: "1y"});
const token = jwt.sign({ username }, process.env.SECRET, {expiresIn: "1y"});
res.cookie("jwt", token);
return res.status(200).send("Ok");
}
@ -43,12 +44,13 @@ class UserController {
if (!bcrypt.compareSync(password, storedPassword)) {
return res.status(403).send("Password is not correct");
}
req.session.jwt = jwt.sign({ username }, process.env.SECRET, {expiresIn: "1y"});
const token = jwt.sign({ username }, process.env.SECRET, {expiresIn: "1y"});
res.cookie("jwt", token);
return res.status(200).send("Ok");
}
async changePassword(req, res) {
const token = req.session.jwt;
const token = req.cookies["jwt"];
const username = jwt.decode(token).username;
const { oldPassword, newPassword } = req.body;
@ -67,11 +69,12 @@ class UserController {
async logout(req, res) {
req.session.destroy();
res.clearCookie("jwt");
return res.redirect("/login");
}
async uploadSkin(req, res) {
const token = req.session.jwt;
const token = req.cookies["jwt"];
const decoded = jwt.decode(token);
const tempPath = req.file.path;
const targetPath = `/opt/skins/${decoded.username}.png`;
@ -93,7 +96,7 @@ class UserController {
}
async uploadCape(req, res) {
const token = req.session.jwt;
const token = req.cookies["jwt"];
const decoded = jwt.decode(token);
const tempPath = req.file.path;
const targetPath = `/opt/cloaks/${decoded.username}.png`;
@ -116,7 +119,7 @@ class UserController {
}
async getUsername(req, res) {
const token = req.session.jwt;
const token = req.cookies["jwt"];
return res.status(200).send(jwt.decode(token).username);
}
}

View File

@ -9,7 +9,7 @@ import UserRouter from './routers/user.js';
const app = express();
dotenv.config({path: ".env"});
dotenv.config({ path: ".env" });
app.use(session({
secret: process.env.SECRET,
@ -18,7 +18,7 @@ app.use(session({
cookie: { maxAge: 1000 * 60 * 60 * 24 }
}));
app.use(express.static(path.join('./public')));
app.use(express.urlencoded({extended: false}));
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cookieParser());
@ -27,6 +27,8 @@ app.set('view engine', 'pug');
app.use('/api', ApiRouter);
app.use('/', UserRouter);
app.listen(process.env.PORT, () => {
const server = app.listen(process.env.PORT, () => {
console.log("App has been started!");
});
});
export default server;

68
src/messages.js Normal file
View File

@ -0,0 +1,68 @@
import { Kafka } from "kafkajs";
import ws from 'ws';
import jwt from 'jwt';
import server from './index.js';
const kafka = new Kafka({
clientId: 'backend',
brokers: ['kafka:9092']
});
const wsClients = [];
const producer = kafka.producer();
const consumer = kafka.consumer();
await producer.connect();
await consumer.connect();
await consumer.subscribe({
topic: "chatMessage",
fromBeginning: true
});
const onMessageFromServer = async ({ topic, partition, message }) => {
wsClients.forEach(client => {
client.send({
message
})
})
};
await consumer.run({
eachMessage: onMessageFromServer
});
const wsServer = new ws.Server({ noServer: true });
wsServer.on('connection', socket => {
wsClients.push(socket);
socket.on('message', async (message) => {
const token = message.jwt;
if (!jwt.verify(token, process.env.secret)) {
socket.send("JWT is not valid.")
return;
}
await producer.send({
topic: 'chatMessage',
messages: [{
author: message.author,
content: message.content,
date: message.date
}]
});
});
socket.on('close', async () => {
wsClients = wsClients.filter(s => s !== socket);
await producer.disconnect();
});
});
server.on('upgrade', (request, socket, head) => {
wsServer.handleUpgrade(request, socket, head, socket => {
wsServer.emit('connection', socket, request);
})
})

View File

@ -7,9 +7,10 @@ import UserService from '../services/user.js';
dotenv.config({path: ".env"});
const authenticate = async (req, res, next) => {
const token = req.session.jwt;
const token = req.cookies["jwt"];
if (!token || !jwt.verify(token, process.env.SECRET)) {
req.session.destroy();
res.clearCookie("jwt");
return res.redirect("/login");
}
next();
@ -33,7 +34,7 @@ const validateInviteToken = async (req, res, next) => {
};
const canHaveCloak = async (req, res, next) => {
const { username } = jwt.decode(req.session.jwt);
const { username } = jwt.decode(req.cookies["jwt"]);
if (!(await UserService.canHaveCloak(username)))
return res.status(403).send("You cannot have cloak");
next();

View File

@ -19,11 +19,11 @@ const userExist = async (req, res, next) => {
let username;
if (req.body.username) {
username = req.body.username;
} else if (req.session.jwt) {
if (!req.session.jwt || !jwt.verify(req.session.jwt, process.env.SECRET)) {
} else if (req.cookies["jwt"]) {
if (!req.cookies["jwt"] || !jwt.verify(req.cookies["jwt"], process.env.SECRET)) {
return res.status(403).send("Unauthorized");
}
username = jwt.decode(req.session.jwt).username;
username = jwt.decode(req.cookies["jwt"]).username;
}
if (!(await UserService.exists(username))) {

View File

@ -10,7 +10,7 @@ dotenv.config({path: ".env"});
const UserRouter = new Router();
UserRouter.get('/register', async (req, res) => {
if (req.session.jwt && jwt.verify(req.session.jwt, process.env.SECRET))
if (req.cookies["jwt"] && jwt.verify(req.cookies["jwt"], process.env.SECRET))
return res.redirect("/index");
return res.render("register.pug", {
@ -19,7 +19,7 @@ UserRouter.get('/register', async (req, res) => {
});
UserRouter.get(['/', '/login'], async (req, res) => {
if(req.session.jwt && jwt.verify(req.session.jwt, process.env.SECRET)) {
if(req.cookies["jwt"] && jwt.verify(req.cookies["jwt"], process.env.SECRET)) {
return res.redirect("/index");
}
@ -27,7 +27,7 @@ UserRouter.get(['/', '/login'], async (req, res) => {
});
UserRouter.get(['/index', '/skin'], auth.authenticate, async (req, res) => {
const username = jwt.decode(req.session.jwt).username;
const username = jwt.decode(req.cookies["jwt"]).username;
return res.render('skin.pug', {
username: username,
@ -36,7 +36,7 @@ UserRouter.get(['/index', '/skin'], auth.authenticate, async (req, res) => {
});
UserRouter.get('/changepassword', auth.authenticate, async (req, res) => {
const username = jwt.decode(req.session.jwt).username;
const username = jwt.decode(req.cookies["jwt"]).username;
return res.render('changepassword.pug', {
can_have_cloak: await UserService.canHaveCloak(username)
@ -44,7 +44,7 @@ UserRouter.get('/changepassword', auth.authenticate, async (req, res) => {
});
UserRouter.get('/chat', auth.authenticate, async (req, res) => {
const username = jwt.decode(req.session.jwt).username;
const username = jwt.decode(req.cookies["jwt"]).username;
return res.render('chat.pug', {
can_have_cloak: await UserService.canHaveCloak(username)
@ -52,7 +52,7 @@ UserRouter.get('/chat', auth.authenticate, async (req, res) => {
});
UserRouter.get('/worldmap', auth.authenticate, async (req, res) => {
const username = jwt.decode(req.session.jwt).username;
const username = jwt.decode(req.cookies["jwt"]).username;
return res.render('worldmap.pug', {
can_have_cloak: await UserService.canHaveCloak(username)

View File

@ -4,6 +4,9 @@ html
title Личный кабинет | Чат
link(href="css/index.css" rel="stylesheet")
body
script(src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer")
script(type="text/javascript" src="js/chat.js")
iframe(name="hiddenFrame" style="position:absolute; top:-1px; left:-1px; width:1px; height:1px;")
include header.pug
@ -11,4 +14,8 @@ html
div(class="content")
h1 Чат
div(class="chat-container")
p Пока что тут пусто, но скоро будет кое-что интересное!
div(class="messages-container")
div(class="input-container")
input(type="text" id="chat-input")
button(id="send-message-button") Отправить