diff --git a/back/api/@me.js b/back/api/@me.js index 28b5bac..080731e 100644 --- a/back/api/@me.js +++ b/back/api/@me.js @@ -1,11 +1,11 @@ const express = require('express'); const router = express.Router(); -const { getConnection, getUserAccounts, getUserCards } = require('../libs/mysql'); +const { getConnection, getUserAccounts, getUserCards, getUserTransfers, setAccountBalance, getAccount, addTransfer } = require('../libs/mysql'); const { checkAuth } = require('../libs/middlewares'); router.post('/', checkAuth, async (req, res) => { const user = req.user; - res.send({ id: user.id, name: user.name, lastname: user.lastname, admin: user.admin }); + res.send(user); }); router.post('/accounts', checkAuth, async (req, res) => { @@ -13,11 +13,7 @@ router.post('/accounts', checkAuth, async (req, res) => { const connection = await getConnection(); const accounts = await getUserAccounts(connection, user.id); connection.end(); - - if (!accounts[0]) { - return res.status(404).send({ error: 'No accounts found' }); - } - res.send({ accounts: accounts }); + res.send(accounts); }); router.post('/cards', checkAuth, async (req, res) => { @@ -25,11 +21,47 @@ router.post('/cards', checkAuth, async (req, res) => { const connection = await getConnection(); const cards = await getUserCards(connection, user.id); connection.end(); + res.send(cards); +}); - if (!cards[0]) { - return res.status(404).send({ error: 'No cards found' }); +router.post('/transfers', checkAuth, async (req, res) => { + const user = req.user; + const connection = await getConnection(); + const transfers = await getUserTransfers(connection, user.id); + connection.end(); + res.send(transfers); +}); + +router.post('/send-money', checkAuth, async (req, res) => { + const user = req.user; + const { account_from_id, account_to_id, amount, name } = req.body; + + if (!account_from_id || !account_to_id || !amount || !name) { + return res.status(400).send({ error: 'Missing required fields' }); } - res.send({ cards: cards }); + + const connection = await getConnection(); + const accountFrom = await getAccount(connection, account_from_id); + const accountTo = await getAccount(connection, account_to_id); + + if (!accountFrom[0] || !accountTo[0]) { + return res.status(400).send({ error: 'Invalid account ID' }); + } + + if (accountFrom[0].client_id !== user.id) { + return res.status(403).send({ error: 'You are not authorized to send money from this account' }); + } + + if (accountFrom[0].balance < amount) { + return res.status(400).send({ error: 'Insufficient funds' }); + } + + await setAccountBalance(connection, account_from_id, accountFrom[0].balance - amount); + await setAccountBalance(connection, account_to_id, accountTo[0].balance + amount); + await addTransfer(connection, account_from_id, account_to_id, name, amount); + connection.end(); + + res.send({ message: 'Money sent successfully' }); }); module.exports = router; \ No newline at end of file diff --git a/back/api/accounts.js b/back/api/accounts.js deleted file mode 100644 index 51a18c9..0000000 --- a/back/api/accounts.js +++ /dev/null @@ -1,9 +0,0 @@ -const express = require('express'); -const { getConnection } = require('../libs/mysql'); -const { checkAuth } = require('../libs/middlewares'); - -const router = express.Router(); - - - -module.exports = router; \ No newline at end of file diff --git a/back/api/users.js b/back/api/users.js index 9e53487..eab56f5 100644 --- a/back/api/users.js +++ b/back/api/users.js @@ -1,9 +1,212 @@ const express = require('express'); -const { getConnection, addUser } = require('../libs/mysql'); +const { getConnection, addUser, getUsers, getUser, getUserAccounts, addAccount, removeAccount, getUserCards, addCard, removeCard, setAccountBalance, getAccount } = require('../libs/mysql'); const { checkAuth } = require('../libs/middlewares'); const router = express.Router(); +router.post('/', checkAuth, async (req, res) => { + const user = req.user; + + if (!user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + const connection = await getConnection(); + const users = await getUsers(connection); + connection.end(); + + res.send(users ); +}); + +router.post('/:id', checkAuth, async (req, res) => { + const this_user = req.user; + const { id } = req.params; + + if (!this_user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + const connection = await getConnection(); + const user = await getUser(connection, id); + connection.end(); + + if (!user[0]) { + return res.status(404).send({ error: 'User not found' }); + } + + res.send(user[0]); +}); + +router.post('/:id/accounts', checkAuth, async (req, res) => { + const this_user = req.user; + const { id } = req.params; + + if (!this_user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + const connection = await getConnection(); + const user = await getUser(connection, id); + + if (!user[0]) { + return res.status(404).send({ error: 'User not found' }); + } + + const accounts = await getUserAccounts(connection, id); + connection.end(); + + res.send(accounts); +}); + +router.post('/:user_id/accounts/:account_id/delete', checkAuth, async (req, res) => { + const this_user = req.user; + const { user_id, account_id } = req.params; + + if (!this_user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + const connection = await getConnection(); + const user = await getUser(connection, user_id); + + if (!user[0]) { + return res.status(404).send({ error: 'User not found' }); + } + + await removeAccount(connection, account_id); + connection.end(); + + res.send({ message: 'Account removed' }); +}); + +router.post('/:user_id/accounts/:account_id/add-balance', checkAuth, async (req, res) => { + const this_user = req.user; + const { user_id, account_id } = req.params; + const { balance } = req.body; + if (!this_user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + if (!balance) { + return res.status(400).json({ error: 'Balance is required' }); + } + + const connection = await getConnection(); + const user = await getUser(connection, user_id); + + if (!user[0]) { + return res.status(404).send({ error: 'User not found' }); + } + + const account = await getAccount(connection, account_id); + + if (!account[0]) { + return res.status(404).send({ error: 'Account not found' }); + } + + await setAccountBalance(connection, account_id, account[0].balance + balance); + connection.end(); + + res.send({ message: 'Balance added' }); +}); + + +router.post('/:id/create-account', checkAuth, async (req, res) => { + const this_user = req.user; + const { name } = req.body; + const { id } = req.params; + + if (!this_user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + if (!name) { + return res.status(400).json({ error: 'Name is required' }); + } + + const connection = await getConnection(); + const user = await getUser(connection, id); + + if (!user[0]) { + return res.status(404).send({ error: 'User not found' }); + } + + await addAccount(connection, id, name); + connection.end(); + + res.send({ message: 'Account created' }); +}); + +router.post('/:id/cards', checkAuth, async (req, res) => { + const this_user = req.user; + const { id } = req.params; + + if (!this_user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + const connection = await getConnection(); + const user = await getUser(connection, id); + + if (!user[0]) { + return res.status(404).send({ error: 'User not found' }); + } + + const cards = await getUserCards(connection, id); + connection.end(); + + res.send(cards); +}); + +router.post('/:user_id/cards/:card_id/delete', checkAuth, async (req, res) => { + const this_user = req.user; + const { user_id, card_id } = req.params; + + if (!this_user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + const connection = await getConnection(); + const user = await getUser(connection, user_id); + + if (!user[0]) { + return res.status(404).send({ error: 'User not found' }); + } + + await removeCard(connection, card_id); + connection.end(); + + res.send({ message: 'Card removed' }); +}); + +router.post('/:user_id/create-card', checkAuth, async (req, res) => { + const this_user = req.user; + const { user_id } = req.params; + const { account_id } = req.body; + + if (!this_user.admin) { + return res.status(403).json({ error: 'Permission denied' }); + } + + const connection = await getConnection(); + const user = await getUser(connection, user_id); + + if (!user[0]) { + return res.status(404).send({ error: 'User not found' }); + } + + const numero = Math.floor(Math.random() * 1000000000); + const expiration = new Date(); + expiration.setFullYear(expiration.getFullYear() + 5); + const expirationString = `${expiration.getMonth() + 1}/${expiration.getFullYear() % 100}`; + const cvc = Math.floor(Math.random() * 1000); + + await addCard(connection, account_id, numero, expirationString, cvc); + connection.end(); + + res.send({ message: 'Card created' }); +}); + router.post('/add', checkAuth, async (req, res) => { const user = req.user; const { name, lastname, email, numero, password } = req.body; diff --git a/back/bank.sql b/back/bank.sql index ab82d52..abd6e08 100644 --- a/back/bank.sql +++ b/back/bank.sql @@ -2,8 +2,8 @@ -- version 5.2.1 -- https://www.phpmyadmin.net/ -- --- Host: leizour.fr --- Generation Time: Apr 09, 2025 at 02:41 PM +-- Host: mysql +-- Generation Time: Apr 27, 2025 at 01:27 PM -- Server version: 10.11.3-MariaDB-1:10.11.3+maria~ubu2204 -- PHP Version: 8.1.19 @@ -20,6 +20,8 @@ SET time_zone = "+00:00"; -- -- Database: `bank` -- +CREATE DATABASE IF NOT EXISTS `bank` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; +USE `bank`; -- -------------------------------------------------------- @@ -27,12 +29,14 @@ SET time_zone = "+00:00"; -- Table structure for table `accounts` -- -CREATE TABLE `accounts` ( - `id` int(11) NOT NULL, - `balance` bigint(20) NOT NULL, +CREATE TABLE IF NOT EXISTS `accounts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `balance` bigint(20) NOT NULL DEFAULT 0, `client_id` int(11) NOT NULL, `name` varchar(20) NOT NULL, - `interest` float NOT NULL DEFAULT 0 + `interest` float NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `client_id` (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- -------------------------------------------------------- @@ -41,12 +45,14 @@ CREATE TABLE `accounts` ( -- Table structure for table `cards` -- -CREATE TABLE `cards` ( - `id` int(11) NOT NULL, +CREATE TABLE IF NOT EXISTS `cards` ( + `id` int(11) NOT NULL AUTO_INCREMENT, `account_id` int(11) NOT NULL, `number` int(11) NOT NULL, `expiration` timestamp NOT NULL, - `cvc` int(11) NOT NULL + `cvc` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `account_id` (`account_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- -------------------------------------------------------- @@ -55,12 +61,15 @@ CREATE TABLE `cards` ( -- Table structure for table `transfers` -- -CREATE TABLE `transfers` ( - `id` int(11) NOT NULL, +CREATE TABLE IF NOT EXISTS `transfers` ( + `id` int(11) NOT NULL AUTO_INCREMENT, `account_from_id` int(11) NOT NULL, `account_to_id` int(11) NOT NULL, `name` varchar(30) NOT NULL, - `value` int(11) NOT NULL + `value` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `account_from_id` (`account_from_id`), + KEY `account_to_id` (`account_to_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- -------------------------------------------------------- @@ -69,71 +78,39 @@ CREATE TABLE `transfers` ( -- Table structure for table `users` -- -CREATE TABLE `users` ( - `id` int(11) NOT NULL, +CREATE TABLE IF NOT EXISTS `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `lastname` varchar(20) NOT NULL, `password` varchar(150) NOT NULL, `email` varchar(100) NOT NULL, `numero` varchar(20) NOT NULL, - `admin` tinyint(1) NOT NULL + `admin` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- --- Indexes for dumped tables +-- Constraints for dumped tables -- -- --- Indexes for table `accounts` +-- Constraints for table `accounts` -- ALTER TABLE `accounts` - ADD PRIMARY KEY (`id`); + ADD CONSTRAINT `client_id` FOREIGN KEY (`client_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; -- --- Indexes for table `cards` +-- Constraints for table `cards` -- ALTER TABLE `cards` - ADD PRIMARY KEY (`id`); + ADD CONSTRAINT `account_id` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; -- --- Indexes for table `transfers` +-- Constraints for table `transfers` -- ALTER TABLE `transfers` - ADD PRIMARY KEY (`id`); - --- --- Indexes for table `users` --- -ALTER TABLE `users` - ADD PRIMARY KEY (`id`); - --- --- AUTO_INCREMENT for dumped tables --- - --- --- AUTO_INCREMENT for table `accounts` --- -ALTER TABLE `accounts` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; - --- --- AUTO_INCREMENT for table `cards` --- -ALTER TABLE `cards` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; - --- --- AUTO_INCREMENT for table `transfers` --- -ALTER TABLE `transfers` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; - --- --- AUTO_INCREMENT for table `users` --- -ALTER TABLE `users` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + ADD CONSTRAINT `account_from_id` FOREIGN KEY (`account_from_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + ADD CONSTRAINT `account_to_id` FOREIGN KEY (`account_to_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; COMMIT; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/back/libs/mysql.js b/back/libs/mysql.js index 6120765..98433bb 100644 --- a/back/libs/mysql.js +++ b/back/libs/mysql.js @@ -91,6 +91,66 @@ function getUserAccounts(connection, id) { }); } +function getAccount(connection, id) { + return new Promise((resolve, reject) => { + connection.query( + `SELECT * FROM accounts WHERE id = ?`, + [id], + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function addAccount(connection, client_id, name) { + return new Promise((resolve, reject) => { + connection.query( + `INSERT INTO accounts (client_id, name) VALUES (?, ?)`, + [client_id, name], + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function removeAccount(connection, id) { + return new Promise((resolve, reject) => { + connection.query( + `DELETE FROM accounts WHERE id = ?`, + [id], + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function setAccountBalance(connection, id, balance) { + return new Promise((resolve, reject) => { + connection.query( + `UPDATE accounts SET balance = ? WHERE id = ?`, + [balance, id], + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + // +-------------------------------+ // | Cards | // +-------------------------------+ @@ -98,10 +158,11 @@ function getUserAccounts(connection, id) { function getUserCards(connection, id) { return new Promise((resolve, reject) => { connection.query( - `SELECT cards.* + `SELECT cards.*, accounts.name AS account_name FROM cards JOIN accounts ON cards.account_id = accounts.id - WHERE client_id = ?`, + JOIN users ON accounts.client_id = users.id + WHERE users.id = ?`, [id], (error, result) => { if (error) { @@ -113,6 +174,74 @@ function getUserCards(connection, id) { }); } +function addCard(connection, account_id, number, expiration, cvc) { + return new Promise((resolve, reject) => { + connection.query( + `INSERT INTO cards (account_id, number, expiration, cvc) VALUES (?, ?, ?, ?)`, + [account_id, number, expiration, cvc], + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function removeCard(connection, id) { + return new Promise((resolve, reject) => { + connection.query( + `DELETE FROM cards WHERE id = ?`, + [id], + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +// +-------------------------------+ +// | Transfers | +// +-------------------------------+ + +function getUserTransfers(connection, id) { + return new Promise((resolve, reject) => { + connection.query( + `SELECT * + FROM transfers + JOIN accounts as sender ON transfers.account_from_id = sender.id + JOIN accounts as receiver ON transfers.account_to_id = receiver.id + WHERE sender.client_id = ? OR receiver.client_id = ?`, + [id, id], + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function addTransfer(connection, account_from_id, account_to_id, name, amount) { + return new Promise((resolve, reject) => { + connection.query( + `INSERT INTO transfers (account_from_id, account_to_id, name, value) VALUES (?, ?, ?, ?)`, + [account_from_id, account_to_id, name, amount], + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + module.exports = { getConnection, @@ -122,6 +251,15 @@ module.exports = { getUserByEmail, getUserAccounts, + getAccount, + addAccount, + removeAccount, + setAccountBalance, getUserCards, + addCard, + removeCard, + + getUserTransfers, + addTransfer, }; diff --git a/front/src/components/TopBar.tsx b/front/src/components/TopBar.tsx new file mode 100644 index 0000000..e6ddb70 --- /dev/null +++ b/front/src/components/TopBar.tsx @@ -0,0 +1,67 @@ +import { useState } from "react" +import { Link } from "react-router-dom" +import { User } from "../types" + +export default function TopBar({ user }: { user: User | undefined }) { + const [burgerMenuOpen, setBurgerMenuOpen] = useState(false) + + return ( +
Welcome Admin
-Manage users, accounts, and cards.
+ {user.name} - {user.lastname} +
+ View Details +No users found.
+ )} +Welcome {user.name} {user.lastname}
-Welcome {user.name} {user.lastname}
+No transfers found.
+ )} +No accounts found.
)}Email: {user.email}
+Phone Number: {user.numero}
+Admin: {user.admin === 1 ? 'Yes' : 'No'}
++ Name: {account.name} - Balance: {account.balance} - Interest: {account.interest} +
+ +No accounts found.
+ )} ++ Card Account: {card.account_name} - Card Number: {card.number} - Expiration: {card.expiration} - CVC: {card.cvc} +
+ +No cards found.
+ )} +