From 404568191349fb8889673c725acc1cc6f0f4c125 Mon Sep 17 00:00:00 2001 From: Lukian Date: Wed, 9 Apr 2025 15:26:48 +0200 Subject: [PATCH] add: added a user page and button to delete them --- back/api/auth.js | 6 ++ back/api/users.js | 43 +++++++++++++- back/libs/mysql.js | 63 ++++++++++++++++++++ front/src/components/TopBar.tsx | 1 + front/src/main.tsx | 2 + front/src/pages/UsersPage.tsx | 100 ++++++++++++++++++++++++++++++++ front/src/styles/UsersPage.css | 19 ++++++ front/src/types.tsx | 2 + 8 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 front/src/pages/UsersPage.tsx create mode 100644 front/src/styles/UsersPage.css diff --git a/back/api/auth.js b/back/api/auth.js index 59bb84b..d1fd1eb 100644 --- a/back/api/auth.js +++ b/back/api/auth.js @@ -50,6 +50,12 @@ router.post('/register', async (req, res) => { const hash = sha256(password); await addUser(connection, username, hash); connection.end(); + + req.sockets.emit({ + type: 'new_user', + username: username, + }); + res.send({ message: 'User added' }); }); diff --git a/back/api/users.js b/back/api/users.js index 6d6450a..8711040 100644 --- a/back/api/users.js +++ b/back/api/users.js @@ -1,8 +1,16 @@ const express = require('express'); -const { getConnection, getUserByUsername, getUserLastMessages, getMentions } = require('../libs/mysql'); +const { getConnection, getUsers, getUserByUsername, getUserLastMessages, getMentions, deleteUser, deleteUserMessages, deleteUserMentions } = require('../libs/mysql'); +const { checkAuth } = require("../libs/middlewares") const router = express.Router(); +router.get('/', async (req, res) => { + const connection = await getConnection(); + const users = await getUsers(connection); + connection.end(); + res.send(users); +}); + router.get('/:username', async (req, res) => { const username = req.params.username; const connection = await getConnection(); @@ -33,4 +41,37 @@ router.get('/:username/lastmessages', async (req, res) => { res.send(messages); }); +router.use("/:username/delete", checkAuth); +router.post('/:username/delete', async (req, res) => { + const username = req.params.username; + const user = req.user; + + const connection = await getConnection(); + + const userToDelete = await getUserByUsername(connection, username); + + if (!userToDelete[0]) { + connection.end(); + return res.status(400).send({ error: 'No user found' }); + } + + if (user.admin !== 1) { + connection.end(); + return res.status(401).send({ error: 'Unauthorized' }); + } + + await deleteUserMentions(connection, userToDelete[0].id); + await deleteUserMessages(connection, userToDelete[0].id); + await deleteUser(connection, userToDelete[0].id); + + connection.end(); + + req.sockets.emit({ + type: 'delete_user', + user_id: userToDelete[0].id, + }); + + res.send({ message: 'User deleted' }); +}); + module.exports = router; \ No newline at end of file diff --git a/back/libs/mysql.js b/back/libs/mysql.js index 7f8a29d..d46d458 100644 --- a/back/libs/mysql.js +++ b/back/libs/mysql.js @@ -9,6 +9,20 @@ function getConnection() { }); } +function getUsers(connection) { + return new Promise((resolve, reject) => { + connection.query( + `SELECT id, username, admin FROM users`, + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + function getUser(connection, id) { return new Promise((resolve, reject) => { connection.query( @@ -89,6 +103,21 @@ function getUserLastMessages(connection, username) { }); } +function deleteUser(connection, id) { + return new Promise((resolve, reject) => { + connection.query( + `DELETE FROM users WHERE id = ?`, + [id], // Use parameterized query + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +}; + function getChannels(connection) { return new Promise((resolve, reject) => { connection.query( @@ -310,6 +339,21 @@ function deleteChannelMessages(connection, channel_id) { }); } +function deleteUserMessages(connection, user_id) { + return new Promise((resolve, reject) => { + connection.query( + `DELETE FROM messages WHERE user_id = ?`, + [user_id], // Use parameterized query + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + function addMention(connection, message_id, user_id) { return new Promise((resolve, reject) => { connection.query( @@ -373,12 +417,29 @@ function deleteChannelMentions(connection, channel_id) { }); } +function deleteUserMentions(connection, user_id) { + return new Promise((resolve, reject) => { + connection.query( + `DELETE FROM mentions WHERE user_id = ?`, + [user_id], // Use parameterized query + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + module.exports = { + getUsers, getConnection, getUser, searchUser, getUserByUsername, addUser, + deleteUser, getUserLastMessages, getChannels, getActiveChannels, @@ -393,8 +454,10 @@ module.exports = { addMessage, deleteMessage, deleteChannelMessages, + deleteUserMessages, addMention, getMentions, deleMentions, + deleteUserMentions, deleteChannelMentions, }; diff --git a/front/src/components/TopBar.tsx b/front/src/components/TopBar.tsx index 9255162..ac57e1a 100644 --- a/front/src/components/TopBar.tsx +++ b/front/src/components/TopBar.tsx @@ -9,6 +9,7 @@ export default function TopBar({ user }: { user: User | undefined }) {
Home Channels + Users
{user && user.id == 5 ? ( diff --git a/front/src/main.tsx b/front/src/main.tsx index 5af230a..360bf70 100644 --- a/front/src/main.tsx +++ b/front/src/main.tsx @@ -9,6 +9,7 @@ import Login from './pages/Login' import Register from './pages/Register' import CreateChannel from './pages/CreateChannel' import ChannelsPage from './pages/ChannelsPage' +import UsersPage from './pages/UsersPage' const socket = new WebSocket("/api/ws"); @@ -31,6 +32,7 @@ createRoot(document.getElementById('root')!).render( } /> } /> } /> + } /> ) diff --git a/front/src/pages/UsersPage.tsx b/front/src/pages/UsersPage.tsx new file mode 100644 index 0000000..a84dd39 --- /dev/null +++ b/front/src/pages/UsersPage.tsx @@ -0,0 +1,100 @@ +import { useState, useEffect } from "react" +import { Link } from "react-router-dom" +import { User, Users } from "../types" +import axios from "axios" +import TopBar from "../components/TopBar" + +import "../styles/UsersPage.css" + +export default function UsersPage({socket}: {socket: WebSocket}) { + const [users, setUsers] = useState(); + const [search, setSearch] = useState(""); + const [token, setToken] = useState(""); + const [thisUser, setThisUser] = useState(); + + function deleteUser(username: string) { + axios + .post(`/api/users/${username}/delete`, { token }) + .catch((err) => { + console.error(err.response); + }); + } + + useEffect(() => { + const token = localStorage.getItem("token") + + if (token) { + setToken(token) + axios + .post("/api/auth/me", { + token: token + }) + .then((res) => { + setThisUser(res.data) + }) + } + axios + .get("/api/users") + .then((res) => { + setUsers(res.data) + }) + .catch((err) => { + console.error(err.response) + }) + }, []) + + useEffect(() => { + socket.addEventListener('message', function (event) { + const data = JSON.parse(event.data); + if (data.type === "new_user" || data.type === "delete_user") { + axios + .get("/api/users") + .then((res) => { + setUsers(res.data) + }) + .catch((err) => { + console.error(err.response) + }) + } + }); + }, []) + + if (!users) { + return ( +
+ +
+

Users

+

Loading...

+
+
+ ) + } + + return ( +
+ +
+

Channels

+ setSearch(e.target.value)} + /> +
    + {users?.sort().filter((channel) => channel.username.toLowerCase().includes(search.toLowerCase())).map((user) => ( +
  • + {user.username} + {thisUser?.admin == 1 && ( + + )} +
  • + ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/front/src/styles/UsersPage.css b/front/src/styles/UsersPage.css new file mode 100644 index 0000000..5f82da7 --- /dev/null +++ b/front/src/styles/UsersPage.css @@ -0,0 +1,19 @@ +.users-page { + display: flex; + flex-direction: column; + justify-content: start; + align-items: center; + width: 100%; + gap: 20px; +} + +.users-page-channels { + width: 97%; + border: 1px solid #270722; + padding: 10px; + background-color: #fff6fd; + display: flex; + flex-direction: column; + align-items: start; + gap: 10px; +} \ No newline at end of file diff --git a/front/src/types.tsx b/front/src/types.tsx index a5f44e1..c33d4cf 100644 --- a/front/src/types.tsx +++ b/front/src/types.tsx @@ -43,3 +43,5 @@ export type User = { username: string admin: number } + +export type Users = User[]