diff --git a/back/api/auth.js b/back/api/auth.js index 4fa2558..d137163 100644 --- a/back/api/auth.js +++ b/back/api/auth.js @@ -31,6 +31,11 @@ router.post('/register', async (req, res) => { return res.status(401).send({ error: 'Username already exists' }); } + if (!/^[a-zA-Z0-9-_]+$/.test(username)) { + connection.end(); + return res.status(400).send({ error: 'Invalid username' }); + } + const hash = sha256(password); await addUser(connection, username, hash); connection.end(); diff --git a/back/api/channels.js b/back/api/channels.js index 388052d..11a1c5d 100644 --- a/back/api/channels.js +++ b/back/api/channels.js @@ -1,6 +1,6 @@ const express = require('express'); const jwt = require('jsonwebtoken'); -const { getConnection, getUser, getChannels, getChannel, addChannel, getMessages, addMessage, deleteMessage } = require('../libs/mysql'); +const { getConnection, getUser, getChannels, getChannel, addChannel, getMessages, addMessage, deleteMessage, getLastMessages } = require('../libs/mysql'); const router = express.Router(); diff --git a/back/api/lastmessages.js b/back/api/lastmessages.js new file mode 100644 index 0000000..745b1f7 --- /dev/null +++ b/back/api/lastmessages.js @@ -0,0 +1,14 @@ +const express = require('express'); +const jwt = require('jsonwebtoken'); +const { getConnection, getLastMessages } = require('../libs/mysql'); + +const router = express.Router(); + +router.get('/', async (req, res) => { + const connection = await getConnection(); + const messages = await getLastMessages(connection); + connection.end(); + res.send(messages); +}); + +module.exports = router; \ No newline at end of file diff --git a/back/api/users.js b/back/api/users.js index 4fb42c4..bb9e705 100644 --- a/back/api/users.js +++ b/back/api/users.js @@ -1,19 +1,26 @@ const express = require('express'); -const sha256 = require("sha256"); -const { getConnection, getUser, addUser } = require('../libs/mysql'); +const { getConnection, getUserByUsername, getUserLastMessages } = require('../libs/mysql'); const router = express.Router(); -router.get('/:id', async (req, res) => { - const id = req.params.id; +router.get('/:username', async (req, res) => { + const username = req.params.username; const connection = await getConnection(); - const users = await getUser(connection, id); + const user = await getUserByUsername(connection, username); connection.end(); - if (users[0]) { - res.send({id: users[0].id, username: users[0].username, admin: users[0].admin}); + if (user[0]) { + res.send({id: user[0].id, username: user[0].username, admin: user[0].admin}); } else { res.send('No user found'); } }); +router.get('/:username/lastmessages', async (req, res) => { + const username = req.params.username; + const connection = await getConnection(); + const messages = await getUserLastMessages(connection, username); + connection.end(); + res.send(messages); +}); + module.exports = router; \ No newline at end of file diff --git a/back/libs/mysql.js b/back/libs/mysql.js index d07eb21..60b452a 100644 --- a/back/libs/mysql.js +++ b/back/libs/mysql.js @@ -51,6 +51,20 @@ function addUser(connection, username, password) { }); } +function getUserLastMessages(connection, username) { + return new Promise((resolve, reject) => { + connection.query( + `SELECT messages.id, user_id, username, content, date, channels.name AS channel_name FROM messages JOIN users ON messages.user_id = users.id JOIN channels ON messages.channel_id = channels.id WHERE username = "${username}" ORDER BY date DESC LIMIT 5`, + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + function getChannels(connection) { return new Promise((resolve, reject) => { connection.query( @@ -96,7 +110,21 @@ function addChannel(connection, name, description, owner_id) { function getMessages(connection, channel_id) { return new Promise((resolve, reject) => { connection.query( - `SELECT messages.id, user_id, username, content, date FROM messages JOIN users ON messages.user_id = users.id WHERE channel_id = ${channel_id} ORDER BY date DESC`, + `SELECT messages.id, user_id, username, content, date, channels.name AS channel_name FROM messages JOIN users ON messages.user_id = users.id JOIN channels ON messages.channel_id = channels.id WHERE channel_id = ${channel_id} ORDER BY date DESC`, + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function getLastMessages(connection) { + return new Promise((resolve, reject) => { + connection.query( + `SELECT messages.id, user_id, username, content, date, channels.name AS channel_name FROM messages JOIN users ON messages.user_id = users.id JOIN channels ON messages.channel_id = channels.id ORDER BY date DESC LIMIT 5`, (error, result) => { if (error) { reject(new Error(error)); @@ -140,10 +168,12 @@ module.exports = { getUser, getUserByUsername, addUser, + getUserLastMessages, getChannels, getChannel, addChannel, getMessages, + getLastMessages, addMessage, deleteMessage }; diff --git a/front/src/main.tsx b/front/src/main.tsx index ab5df2c..a424e72 100644 --- a/front/src/main.tsx +++ b/front/src/main.tsx @@ -3,7 +3,8 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom' import './index.css' import Home from './pages/Home' -import Channel from './pages/Channel' +import ChannelPage from './pages/ChannelPage' +import UserPage from './pages/UserPage' import Login from './pages/Login' import Register from './pages/Register' import CreateChannel from './pages/CreateChannel' @@ -12,7 +13,8 @@ createRoot(document.getElementById('root')!).render( } /> - } /> + } /> + } /> } /> } /> } /> diff --git a/front/src/pages/Channel.tsx b/front/src/pages/ChannelPage.tsx similarity index 93% rename from front/src/pages/Channel.tsx rename to front/src/pages/ChannelPage.tsx index 95ead4a..dadf85d 100644 --- a/front/src/pages/Channel.tsx +++ b/front/src/pages/ChannelPage.tsx @@ -1,11 +1,11 @@ import { useParams, Link } from "react-router-dom"; import React, { useEffect, useState } from "react"; -import { Channel_type, Messages, User } from "../types"; +import { Channel, Messages, User } from "../types"; import axios from "axios"; -export default function Channel() { +export default function ChannelPage() { const { name } = useParams(); - const [channel, setChannel] = useState(); + const [channel, setChannel] = useState(); const [messages, setMessages] = useState(); const [token, setToken] = useState(""); const [user, setUser] = useState(); @@ -98,7 +98,7 @@ export default function Channel() {
    {messages.slice(0, maxMessageToShown).map((message) => (
  • -

    {message.username}: {message.content}

    +

    {message.username}: {message.content}

    {new Date(message.date * 1000).toLocaleString()}

    {(user?.id === message.user_id || user?.id === channel.owner_id || user?.admin === 1) && ( diff --git a/front/src/pages/Home.tsx b/front/src/pages/Home.tsx index 47ac63f..4169990 100644 --- a/front/src/pages/Home.tsx +++ b/front/src/pages/Home.tsx @@ -1,29 +1,41 @@ import { useState, useEffect } from "react" import { Link } from "react-router-dom" -import { Channels, User } from "../types" +import { Channels, User, Messages } from "../types" import axios from "axios" export default function Home() { const [user, setUser] = useState(); const [channels, setChannels] = useState(); + const [messages , setMessages] = useState(); useEffect(() => { const token = localStorage.getItem("token") if (token) { - axios.post("/api/auth/me", { - token: token - }).then((res) => { - setUser(res.data) - }).catch((err) => { + axios + .post("/api/auth/me", { + token: token + }) + .then((res) => { + setUser(res.data) + }) + .catch((err) => { + console.error(err) + }) + } + + axios + .get("/api/channels").then((res) => { + setChannels(res.data) + }) + .catch((err) => { console.error(err) }) - } - - axios.get("/api/channels").then((res) => { - setChannels(res.data) - }).catch((err) => { - console.error(err) - }) + + axios + .get("/api/lastmessages").then((res) => { + setMessages(res.data) + } + ) }, []) return ( @@ -52,6 +64,16 @@ export default function Home() {
  • ))}
+

Last messages

+
    + {messages?.map((message) => ( +
  • +

    {message.username}: {message.content}

    +

    In {message.channel_name}

    +

    {new Date(message.date * 1000).toLocaleString()}

    +
  • + ))} +
cat ) diff --git a/front/src/pages/UserPage.tsx b/front/src/pages/UserPage.tsx new file mode 100644 index 0000000..937bb18 --- /dev/null +++ b/front/src/pages/UserPage.tsx @@ -0,0 +1,42 @@ +import { useParams, Link } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { User, Messages } from "../types"; +import axios from "axios"; + +export default function UserPage() { + const { username } = useParams(); + const [user, setUser] = useState(); + const [messages, setMessages] = useState(); + + useEffect(() => { + axios.get(`/api/users/${username}`).then((res) => { + setUser(res.data); + }); + + axios.get(`/api/users/${username}/lastmessages`).then((res) => { + setMessages(res.data); + }); + }, [username]); + + if (!user) { + return
Loading...
; + } + + return ( +
+ Home +

{user.username}

+ {user.admin ?

Admin

:

User

} +

Last messages

+
    + {messages?.map((message) => ( +
  • +

    {message.username}: {message.content}

    +

    In {message.channel_name}

    +

    {new Date(message.date * 1000).toLocaleString()}

    +
  • + ))} +
+
+ ); +} \ No newline at end of file diff --git a/front/src/types.tsx b/front/src/types.tsx index 1169c1a..dcbc2c6 100644 --- a/front/src/types.tsx +++ b/front/src/types.tsx @@ -1,4 +1,4 @@ -export type Channel_type ={ +export type Channel ={ id: number, name: string description: string @@ -6,14 +6,16 @@ export type Channel_type ={ owner_username: string } -export type Channels = Channel_type[] +export type Channels = Channel[] export type Message = { id: number, user_id: number, username: string, content: string, - date: number + date: number, + channel_id: number, + channel_name: string } export type Messages = Message[]