From a1c08ab9dc7399f4fd1a7d7c0316697ea619f32b Mon Sep 17 00:00:00 2001 From: gprunet Date: Sat, 10 Jan 2026 16:27:15 +0100 Subject: [PATCH] register + login + mdp hash --- docker/frontend/dockerfile | 0 srcs/backend/db.js | 81 +++++++++++++++++++++++++++++++++ srcs/backend/dockerfile | 2 + srcs/backend/index.js | 76 +++---------------------------- srcs/backend/middleware/auth.js | 21 +++++++++ srcs/backend/routes/auth.js | 22 +++++++++ srcs/backend/services/auth.js | 60 ++++++++++++++++++++++++ 7 files changed, 192 insertions(+), 70 deletions(-) delete mode 100644 docker/frontend/dockerfile create mode 100644 srcs/backend/db.js create mode 100644 srcs/backend/middleware/auth.js create mode 100644 srcs/backend/routes/auth.js create mode 100644 srcs/backend/services/auth.js diff --git a/docker/frontend/dockerfile b/docker/frontend/dockerfile deleted file mode 100644 index e69de29..0000000 diff --git a/srcs/backend/db.js b/srcs/backend/db.js new file mode 100644 index 0000000..88e3557 --- /dev/null +++ b/srcs/backend/db.js @@ -0,0 +1,81 @@ +require('dotenv').config(); +const { Pool } = require('pg'); + +const pool = new Pool +({ + user: process.env.POSTGRES_USER, + host: process.env.POSTGRES_HOST, + database: process.env.POSTGRES_DB, + password: process.env.POSTGRES_PASSWORD, + port: 5432, +}); + +async function waitForDb(retries = 10, delay = 2000) +{ + for (let i = 0; i < retries; i++) + { + try + { + await pool.query('SELECT 1'); + console.log('Database is ready!'); + return ; + } + catch (err) + { + await new Promise(r => setTimeout(r, delay)); + } + } + throw new Error('Could not connect to database after multiple attempts'); +} + +async function createTables() +{ + try + { + await pool.query(` + CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + username VARCHAR(50) UNIQUE NOT NULL, + password_hash TEXT NOT NULL, + email VARCHAR(100), + created_at TIMESTAMP DEFAULT NOW() + ); + + CREATE TABLE IF NOT EXISTS messages( + id SERIAL PRIMARY KEY, + sender_id INT REFERENCES users(id), + received_id INT REFERENCES users(id), + content TEXT, + created_at TIMESTAMP DEFAULT NOW() + ); + + CREATE TABLE IF NOT EXISTS friendship ( + id_user1 INT NOT NULL, + id_user2 INT NOT NULL, + status VARCHAR(20) NOT NULL, + created_at TIMESTAMP DEFAULT NOW(), + CHECK (id_user1 < id_user2), + PRIMARY KEY (id_user1, id_user2), + FOREIGN KEY (id_user1) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (id_user2) REFERENCES users(id) ON DELETE CASCADE + ); + `); + console.log('Tables created!'); + } + catch (err) + { + console.error('Error creating tables:', err); + } +} + +async function query(text, params) +{ + return (pool.query(text, params)); +} + +module.exports = +{ + waitForDb, + createTables, + query +}; \ No newline at end of file diff --git a/srcs/backend/dockerfile b/srcs/backend/dockerfile index 5621044..b1ef9c2 100644 --- a/srcs/backend/dockerfile +++ b/srcs/backend/dockerfile @@ -7,6 +7,8 @@ COPY package*.json ./ RUN npm install RUN npm install dotenv RUN npm install pg +RUN npm install bcrypt +RUN npm install jsonwebtoken COPY . . diff --git a/srcs/backend/index.js b/srcs/backend/index.js index b40a53a..b957f69 100644 --- a/srcs/backend/index.js +++ b/srcs/backend/index.js @@ -1,81 +1,17 @@ -require('dotenv').config(); -const { Pool } = require('pg'); - -const pool = new Pool -({ - user: process.env.POSTGRES_USER, - host: process.env.POSTGRES_HOST, - database: process.env.POSTGRES_DB, - password: process.env.POSTGRES_PASSWORD, - port: 5432, -}); - -async function waitForDb(retries = 10, delay = 2000) -{ - for (let i = 0; i < retries; i++) - { - try - { - await pool.query('SELECT 1'); - console.log('Database is ready!'); - return ; - } - catch (err) - { - await new Promise(r => setTimeout(r, delay)); - } - } - throw new Error('Could not connect to database after multiple attempts'); -} - -async function createTables() -{ - try - { - await pool.query(` - CREATE TABLE IF NOT EXISTS users ( - id SERIAL PRIMARY KEY, - username VARCHAR(50) UNIQUE NOT NULL, - email VARCHAR(100), - created_at TIMESTAMP DEFAULT NOW() - ); - - CREATE TABLE IF NOT EXISTS messages( - id SERIAL PRIMARY KEY, - sender_id INT REFERENCES users(id), - received_id INT REFERENCES users(id), - content TEXT, - created_at TIMESTAMP DEFAULT NOW() - ); - - CREATE TABLE IF NOT EXISTS friendship ( - id_user1 INT NOT NULL, - id_user2 INT NOT NULL, - status VARCHAR(20) NOT NULL, - created_at TIMESTAMP DEFAULT NOW(), - CHECK (id_user1 < id_user2), - PRIMARY KEY (id_user1, id_user2), - FOREIGN KEY (id_user1) REFERENCES users(id) ON DELETE CASCADE, - FOREIGN KEY (id_user2) REFERENCES users(id) ON DELETE CASCADE - ); - `); - console.log('Tables created!'); - } - catch (err) - { - console.error('Error creating tables:', err); - } -} - const express = require('express'); +const authRouter = require('./routes/auth'); +const {waitForDb, createTables} = require('./db'); + const app = express(); +app.use(express.json()); + async function startServer() { await waitForDb(); - await createTables(); + app.use('/api/auth', authRouter); app.get('/api/', (req, res) => res.send('Backend running')); app.listen(3001, () => diff --git a/srcs/backend/middleware/auth.js b/srcs/backend/middleware/auth.js new file mode 100644 index 0000000..1f8c432 --- /dev/null +++ b/srcs/backend/middleware/auth.js @@ -0,0 +1,21 @@ +const jwt = require('jsonwebtoken'); + +module.exports = function authMiddleware(req, res, next) +{ + const header = req.headers.authorization; + if (!header) + return (res.status(401).json({error: 'Missing token'})); + + const token = header.split(' ')[1]; + + try + { + const payload = jwt.verify(token, process.env.JWT_SECRET); + req.user = payload; + next(); + } + catch + { + res.status(401).json({error: 'Invalid token'}); + } +}; \ No newline at end of file diff --git a/srcs/backend/routes/auth.js b/srcs/backend/routes/auth.js new file mode 100644 index 0000000..b3e8d5e --- /dev/null +++ b/srcs/backend/routes/auth.js @@ -0,0 +1,22 @@ +const express = require('express'); +const router = express.Router(); +const authService = require('../services/auth'); + +router.post('/register', async(req, res) => +{ + const {username, password} = req.body; + if (!username || !password) + return (res.status(400).json({error: 'Missing fields'})); + + const result = await authService.register(username, password); + res.status(result.status).json(result.data); +}); + +router.post('/login', async(req, res) => +{ + const {username, password} = req.body; + const result = await authService.login(username, password); + res.status(result.status).json(result.data); +}); + +module.exports = router; \ No newline at end of file diff --git a/srcs/backend/services/auth.js b/srcs/backend/services/auth.js new file mode 100644 index 0000000..63d928d --- /dev/null +++ b/srcs/backend/services/auth.js @@ -0,0 +1,60 @@ +const bcrypt = require('bcrypt'); +const jwt = require('jsonwebtoken'); +const {query} = require('../db'); + +async function login(username, password) +{ + try + { + const result = await query + ( + `SELECT id, password_hash FROM users WHERE username = $1`, + [username] + ); + if (result.rows.length === 0) + return ({status: 401, data: {error: 'Invalid credentials'}}); + + const user = result.rows[0]; + const match = await bcrypt.compare(password, user.password_hash); + if (!match) + return ({status: 401, data: {error: 'Invalid credentials'}}); + + const token = jwt.sign + ( + {userId: user.id}, + process.env.JWT_SECRET, + {expiresIn: '1h'} + ); + + return ({status: 200, data: {token}}); + } + catch (err) + { + console.error(err); + return ({status: 500, data: {error: 'Server error'}}); + } +}; + +async function register(username, password) +{ + try + { + const password_hash = await bcrypt.hash(password, 10); + await query + ( + `INSERT INTO users (username, password_hash) VALUES ($1, $2)`, + [username, password_hash] + ); + return ({status: 201, data: {message: 'User created'}}); + } + catch (err) + { + if (err.code === '23505') + return ({status: 409, data: {error: 'Username already exists'}}); + + console.error(err); + return ({status: 500, data: {error: 'Server error'}}); + } +}; + +module.exports = {register, login};