diff --git a/back/.gitignore b/back/.gitignore index 9c90b3b..f88ca2a 100644 --- a/back/.gitignore +++ b/back/.gitignore @@ -1,3 +1,4 @@ /node_modules .env -package-lock.json \ No newline at end of file +package-lock.json +data \ No newline at end of file diff --git a/back/api/auth.js b/back/api/auth.js index d1fd1eb..04aae8a 100644 --- a/back/api/auth.js +++ b/back/api/auth.js @@ -1,8 +1,16 @@ const express = require('express'); const sha256 = require("sha256"); const jwt = require('jsonwebtoken'); -const { getConnection, getUserByUsername, addUser, getUser } = require('../libs/mysql'); +const { getConnection, getUserByUsername, addUser, setUserPfp, setUserUsername, setUserPassword } = require('../libs/mysql'); const { checkAuth } = require('../libs/middlewares'); +const multer = require('multer') +const fs = require('node:fs'); + +const upload = multer({ dest: 'data/pfps/' }) +upload.limits = { + fileSize: 1024 * 1024 * 5, + files: 1, +}; const router = express.Router(); @@ -59,10 +67,75 @@ router.post('/register', async (req, res) => { res.send({ message: 'User added' }); }); -router.use('/me', checkAuth); -router.post('/me', async (req, res) => { +router.post('/me', checkAuth, async (req, res) => { const user = req.user; res.send({ id: user.id, username: user.username, admin: user.admin }); }); +router.post('/me/uploadpfp', upload.single('pfp'), checkAuth, async (req, res) => { + const fileName = req.file.filename; + const user = req.user; + + if (user.pfp && fs.existsSync(`data/pfps/${user.pfp}`)) { + fs.unlinkSync(`data/pfps/${user.pfp}`); + } + + const connection = await getConnection(); + await setUserPfp(connection, user.id, fileName); + connection.end(); + res.send({ message: 'Profile picture uploaded.' }); +}); + +router.post('/me/deletepfp', checkAuth, async (req, res) => { + const user = req.user; + + if (user.pfp && fs.existsSync(`data/pfps/${user.pfp}`)) { + fs.unlinkSync(`data/pfps/${user.pfp}`); + } + + const connection = await getConnection(); + await setUserPfp(connection, user.id, null); + connection.end(); + res.send({ message: 'Profile picture deleted.' }); +}); + +router.post('/me/setusername', checkAuth, async (req, res) => { + const { username } = req.body; + const user = req.user; + + if (!username) { + return res.status(400).send({ error: 'Invalid username' }); + } + + if (!/^[a-zA-Z0-9-_]+$/.test(username)) { + return res.status(400).send({ error: 'Invalid username' }); + } + + const connection = await getConnection(); + const userExists = await getUserByUsername(connection, username); + if (userExists[0]) { + connection.end(); + return res.status(401).send({ error: 'Username already exists' }); + } + + await setUserUsername(connection, user.id, username); + connection.end(); + res.send({ message: 'Username changed.' }); +}); + + +router.post('/me/setpassword', checkAuth, async (req, res) => { + const { oldPassword, password } = req.body; + const user = req.user; + + if (!password || !oldPassword || sha256(oldPassword) !== user.password) { + return res.status(400).send({ error: 'Invalid password' }); + } + + const connection = await getConnection(); + await setUserPassword(connection, user.id, sha256(password)); + connection.end(); + res.send({ message: 'Password changed.' }); +}); + module.exports = router; \ No newline at end of file diff --git a/back/api/channels.js b/back/api/channels.js index 6d6189d..53d1393 100644 --- a/back/api/channels.js +++ b/back/api/channels.js @@ -123,8 +123,7 @@ router.get('/:name/messages', async (req, res) => { res.send(messages); }); -router.use('/:name/messages/send', checkAuth); -router.post('/:name/messages/send', async (req, res) => { +router.post('/:name/messages/send', checkAuth, async (req, res) => { const { message } = req.body; const name = req.params.name; const user = req.user; @@ -165,8 +164,7 @@ router.post('/:name/messages/send', async (req, res) => { res.send({ message: 'Message sent' }); }); -router.use('/:name/messages/delete', checkAuth); -router.post('/:name/messages/delete', async (req, res) => { +router.post('/:name/messages/delete', checkAuth, async (req, res) => { const { message_id } = req.body; const name = req.params.name; const user = req.user; @@ -207,8 +205,7 @@ router.post('/:name/messages/delete', async (req, res) => { res.send({ message: 'Message deleted' }); }); -router.use('/add', checkAuth); -router.post('/add', async (req, res) => { +router.post('/add', checkAuth, async (req, res) => { const { name, description } = req.body; const user = req.user; diff --git a/back/api/users.js b/back/api/users.js index 8711040..1dadcf8 100644 --- a/back/api/users.js +++ b/back/api/users.js @@ -1,6 +1,7 @@ const express = require('express'); const { getConnection, getUsers, getUserByUsername, getUserLastMessages, getMentions, deleteUser, deleteUserMessages, deleteUserMentions } = require('../libs/mysql'); const { checkAuth } = require("../libs/middlewares") +const path = require('path'); const router = express.Router(); @@ -41,8 +42,26 @@ router.get('/:username/lastmessages', async (req, res) => { res.send(messages); }); -router.use("/:username/delete", checkAuth); -router.post('/:username/delete', async (req, res) => { +router.get('/:username/pfp', async (req, res) => { + const username = req.params.username; + const connection = await getConnection(); + const user = await getUserByUsername(connection, username); + connection.end(); + + if (!user[0]) { + return res.status(400).send({ error: 'No user found' }); + } + + const pfp = user[0].pfp; + + if (!pfp) { + return res.sendFile(path.join(__dirname, `../images/default-pfp.png`), { headers: { 'Content-Type': 'image' } }); + } + + res.sendFile(path.join(__dirname, `../data/pfps/${pfp}`), { headers: { 'Content-Type': 'image' } }); +}); + +router.post('/:username/delete', checkAuth, async (req, res) => { const username = req.params.username; const user = req.user; diff --git a/back/images/default-pfp.png b/back/images/default-pfp.png new file mode 100644 index 0000000..e4c96e6 Binary files /dev/null and b/back/images/default-pfp.png differ diff --git a/back/libs/mysql.js b/back/libs/mysql.js index d46d458..fadd56f 100644 --- a/back/libs/mysql.js +++ b/back/libs/mysql.js @@ -118,6 +118,51 @@ function deleteUser(connection, id) { }); }; +function setUserPfp(connection, id, pfp) { + return new Promise((resolve, reject) => { + connection.query( + `UPDATE users SET pfp = ? WHERE id = ?`, + [pfp, id], // Use parameterized query + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function setUserUsername(connection, id, username) { + return new Promise((resolve, reject) => { + connection.query( + `UPDATE users SET username = ? WHERE id = ?`, + [username, id], // Use parameterized query + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function setUserPassword(connection, id, password) { + return new Promise((resolve, reject) => { + connection.query( + `UPDATE users SET password = ? WHERE id = ?`, + [password, id], // Use parameterized query + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + function getChannels(connection) { return new Promise((resolve, reject) => { connection.query( @@ -440,6 +485,9 @@ module.exports = { getUserByUsername, addUser, deleteUser, + setUserPfp, + setUserUsername, + setUserPassword, getUserLastMessages, getChannels, getActiveChannels, diff --git a/back/package.json b/back/package.json index 9f40754..de04f80 100644 --- a/back/package.json +++ b/back/package.json @@ -20,6 +20,7 @@ "fs": "^0.0.1-security", "https": "^1.0.0", "jsonwebtoken": "^9.0.2", + "multer": "^1.4.5-lts.2", "mysql": "^2.18.1", "sha256": "^0.2.0" } diff --git a/docker-compose.yml b/docker-compose.yml index efa7e27..c378db5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,8 @@ services: - "traefik.http.services.forum.loadbalancer.server.port=3000" networks: - traefik + volumes: + - ./data:/app/data networks: traefik: diff --git a/front/src/components/MessageComponent.tsx b/front/src/components/MessageComponent.tsx index bce2ee6..32ef3e4 100644 --- a/front/src/components/MessageComponent.tsx +++ b/front/src/components/MessageComponent.tsx @@ -13,20 +13,24 @@ export default function MessageComponent({ message, user, channel, deleteMessage return (
{new Date(message.date * 1000).toLocaleString()}
Please log in to edit your profile
+ Login +Admin
:User
} + {pageUser.id === user?.id && ( + Edit profile + )}