diff --git a/models/todo.js b/models/todo.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1d0597ce7aeca0e34c2daea4321697924cba7ca8 100644 --- a/models/todo.js +++ b/models/todo.js @@ -0,0 +1,9 @@ +const mongoose = require('mongoose'); + +const TodoSchema = new mongoose.Schema({ + description: { type: String, required: true }, + isDone: { type: Boolean, default: false }, + userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true } +}); + +module.exports = mongoose.model('Todo', TodoSchema); diff --git a/models/user.js b/models/user.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e7e80795cd87ae046d5d1057b76d8ffdb4134620 100644 --- a/models/user.js +++ b/models/user.js @@ -0,0 +1,11 @@ +const mongoose = require('mongoose'); +const { Schema } = mongoose; + +const userSchema = new Schema({ + username: { type: String, required: true, unique: true }, + password: { type: String, required: true } +}); + +const User = mongoose.model('User', userSchema); + +module.exports = User; diff --git a/mongodb.js b/mongodb.js index 783d98c786b2251bf5b77f379d3971ae7ec07e83..12594fef07de8219932c75fd842ef0fd0d72753b 100644 --- a/mongodb.js +++ b/mongodb.js @@ -1,15 +1,14 @@ const mongoose = require('mongoose'); +require('dotenv').config(); -// Function to connect to MongoDB using Mongoose and create collections -async function connectDB() { +const connectDB = async () => { try { - // Connect using the MongoDB URI from environment variables - const conn = await mongoose.connect(process.env.MONGO_URI || "mongodb://127.0.0.1:27017/todo_app"); + const conn = await mongoose.connect(process.env.MONGO_URI); console.log(`MongoDB connected: ${conn.connection.host}`); } catch (err) { console.error(`Error: ${err.message}`); - process.exit(1); // Exit the process with failure if the connection fails + process.exit(1); } -} +}; -module.exports = connectDB; \ No newline at end of file +module.exports = connectDB; diff --git a/package-lock.json b/package-lock.json index 0b39c86f625773f964dcd119189dee8b7fee08be..7839fab8540c18852e3b5ffb6d33e7f9c574a24a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3 +1,4 @@ + { "name": "cloudcomputing_act1", "version": "1.0.0", @@ -13,7 +14,7 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.21.0", - "express-session": "^1.18.0", + "express-session": "^1.18.1", "jsonwebtoken": "^9.0.2", "mongoose": "^8.7.0" } @@ -343,12 +344,12 @@ } }, "node_modules/express-session": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", - "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", "license": "MIT", "dependencies": { - "cookie": "0.6.0", + "cookie": "0.7.2", "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", @@ -361,6 +362,15 @@ "node": ">= 0.8.0" } }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/express-session/node_modules/cookie-signature": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", diff --git a/package.json b/package.json index dc76d49447dafe83c2498256b2b5c9ca906dd8e8..82c841411edee49995329b408ecc161626b96cfb 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.21.0", - "express-session": "^1.18.0", + "express-session": "^1.18.1", "jsonwebtoken": "^9.0.2", "mongoose": "^8.7.0" } diff --git a/public/css/index.css b/public/css/index.css new file mode 100644 index 0000000000000000000000000000000000000000..1d82d7159c154749dc311c4d00b434e977d4095d --- /dev/null +++ b/public/css/index.css @@ -0,0 +1,116 @@ +body { + font-family: "Arial", sans-serif; + background-color: #f5f5f5; + color: #333; + line-height: 1.6; + margin: 0; + padding: 0; +} +.container { + max-width: 600px; + margin: 2rem auto; + padding: 2rem; + background-color: #fff; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} +header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} +h1 { + font-size: 2rem; + font-weight: 300; + margin: 0; +} +#logout-btn { + background-color: transparent; + border: 1px solid #333; + color: #333; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + transition: all 0.3s ease; +} +#logout-btn:hover { + background-color: #333; + color: #fff; +} +#new-task-form { + display: flex; + margin-bottom: 2rem; +} +input[type="text"] { + flex-grow: 1; + padding: 0.75rem; + border: 1px solid #ddd; + border-radius: 4px 0 0 4px; + font-size: 1rem; +} +button[type="submit"] { + padding: 0.75rem 1.5rem; + background-color: #4caf50; + color: #fff; + border: none; + border-radius: 0 4px 4px 0; + cursor: pointer; + transition: background-color 0.3s ease; +} +button[type="submit"]:hover { + background-color: #45a049; +} +.task-list ul { + list-style-type: none; + padding: 0; +} +.task-list li { + background-color: #f9f9f9; + border-left: 4px solid #4caf50; + margin-bottom: 1rem; + padding: 1rem; + display: flex; + justify-content: space-between; + align-items: center; + transition: all 0.3s ease; +} +.task-list li:hover { + background-color: #f0f0f0; +} +.task-list li.completed { + border-left-color: #999; + opacity: 0.6; +} +.task-list li.completed span { + text-decoration: line-through; +} +.task-actions { + display: flex; + gap: 0.5rem; +} +.task-list li button { + background-color: transparent; + border: none; + cursor: pointer; + font-size: 1.2rem; + transition: color 0.3s ease; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} +.complete-btn { + color: #4caf50; +} +.complete-btn:hover { + color: #45a049; +} +.delete-btn { + color: #d9534f; +} +.delete-btn:hover { + color: #c9302c; +} diff --git a/public/css/login.css b/public/css/login.css index aa5f611921103f83459279bd55899f0e4794297a..6ef70239cf95241d645d6313778f47d1db83a27c 100644 --- a/public/css/login.css +++ b/public/css/login.css @@ -85,17 +85,19 @@ button:hover { } .success_message { display: none; + opacity: 100; color: #28a745; margin-top: 1rem; - font-size: 1rem; + font-size: 12px; text-align: center; - opacity: 0; transition: opacity 0.6s ease; } + .success_message.show { - display: block; + display: block; opacity: 1; } + .register-link { text-align: center; margin-top: 1.5rem; @@ -121,4 +123,4 @@ button:hover { } .login-link a:hover { text-decoration: underline; -} \ No newline at end of file +} diff --git a/public/css/main.css b/public/css/main.css index 6b5e56bc719a4e01c71b575410afae1a4226c0bb..8719569d25ff3b40ca7c710e83a726ba59cbda8f 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -1,18 +1,15 @@ -/* Reset default styles */ body { margin: 0; padding: 0; - font-family:Verdana, Geneva, Tahoma, sans-serif; + font-family:Helvetica, Arial, sans-serif; } -/* Container for the entire layout */ .container { display: flex; flex-direction: column; min-height: 100vh; } -/* Header styling */ .header { background-color: #ddf; color: #000; @@ -36,16 +33,14 @@ header button { gap: 10px; } -/* Main content styling */ .main-content { display: flex; - flex-grow: 1; /* Makes the main content stretch to fill the remaining space */ - overflow-y: hidden; /* Hides overflow within the main content */ + flex-grow: 1; + overflow-y: hidden; } -/* Sidebar styling */ .sidebar { - width: 200px; /* Adjust the width as needed */ + width: 200px; padding: 25px; background-color: #ddf; } @@ -55,13 +50,12 @@ header button { padding: 0; } -/* Task list styling */ .task-list { - flex-grow: 1; /* Makes the task list stretch to fill the remaining space */ + flex-grow: 1; padding: 10px; padding-top: 25px; - overflow-y: auto; /* Enables vertical scrolling within the task list */ - max-height: calc(100vh - 100px); /* Adjust the max-height as needed */ + overflow-y: auto; + max-height: calc(100vh - 100px); } .task { diff --git a/public/index.html b/public/index.html index ada57ea8c9c12520292fade7ef865dc207dc0bcd..ca170f1c94da69385f6698bfa665cb123e73c96c 100644 --- a/public/index.html +++ b/public/index.html @@ -1,117 +1,35 @@ <!DOCTYPE html> <html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>To-Do Dashboard</title> - <link rel="stylesheet" href="./css/main.css"> </head> -<body> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>ToDo</title> + <link rel="stylesheet" href="./css/index.css" /> + <script src="./js/index.js" defer></script> + </head> + <body> <div class="container"> - <header class="header"> - <h1>To-Do Dashboard</h1> - <button class="new-btn">New Task</button> - <div class="user-actions"> - <button class id="change-password-btn">Change Password</button> - <button id="logout-btn">Logout</button> - </div> - </header> - <main class="main-content"> - <aside class="sidebar"> - <h2>Status</h2> - <ul class="status-bar"> - <li><progress id="file" value="3" max="5"> 60% </progress></li> - <li>Total Tasks: <span id="total-tasks">5</span></li> - <li>Active: <span id="active-tasks">2</span></li> - <li>Hidden: <span id="hidden-tasks">3</span></li> - <li><button id="show-hidden-btn">Show Hidden</button></li> - </ul> - </aside> - <section class="task-list"> - <h2>Tasks</h2> - <ul> - <li class="task"> - <form method="POST" action="" autocomplete="off"> - <input type="text" name="task" style="width:60%;"> - <div class="task-actions"> - <button class="add-btn">Add</button> - </div> - </form> - </li> - <li class="task"> - <p>Finish Project Report</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - <li class="task"> - <p>Buy Groceries</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - <li class="task"> - <p>Finish Project Report</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - <li class="task"> - <p>Buy Groceries</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - <li class="task"> - <p>Finish Project Report</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - <li class="task"> - <p>Buy Groceries</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - <li class="task"> - <p>Finish Project Report</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - <li class="task"> - <p>Buy Groceries</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - <li class="task"> - <p>Finish Project Report</p> - <div class="task-actions"> - <button class="hide-btn">Done</button> - <button class="edit-btn">Edit</button> - <button class="delete-btn">Delete</button> - </div> - </li> - </ul> - </section> - </main> + <header> + <h1>ToDo List</h1> + <button id="logout-btn">Logout</button> + </header> + + <main> + <form id="new-task-form"> + <input + type="text" + name="task" + placeholder="Add a new task..." + required + /> + <button type="submit">Add</button> + </form> + + <!-- Todo List --> + <section class="task-list"> + <ul id="task-list"></ul> + </section> + </main> </div> - </body> -</html> \ No newline at end of file + </body> +</html> diff --git a/public/js/app.js b/public/js/app.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e76b1042dcc3e03f55e57e2bf9d7d4f958a95566 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -0,0 +1,17 @@ +// Obtener referencia a los elementos del DOM +const taskList = document.querySelector('.task-list ul'); +const newTaskForm = document.querySelector('form'); + +// Logout +document.getElementById('logout-btn').addEventListener('click', async () => { + try { + const res = await fetch('/api/users/logout', { method: 'POST' }); + if (res.ok) { + window.location.href = 'login.html'; + } else { + console.error('Logout failed'); + } + } catch (err) { + console.error('Error during logout:', err); + } +}); \ No newline at end of file diff --git a/public/js/auth.js b/public/js/auth.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2013da931bca405b3eccf2ace9ba0d7ac674df93 100644 --- a/public/js/auth.js +++ b/public/js/auth.js @@ -0,0 +1,76 @@ +document.addEventListener('DOMContentLoaded', () => { + + // Register + const registerForm = document.getElementById('register-form'); + if (registerForm) { + registerForm.addEventListener('submit', async (event) => { + event.preventDefault(); + + const username = document.getElementById('username').value; + const password = document.getElementById('password').value; + + try { + const response = await fetch('/api/users/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username, password }), + }); + + const result = await response.json(); + + if (response.ok) { + document.querySelector('.success_message').style.display = 'block'; + document.querySelector('.error_text').textContent = ''; + setTimeout(() => { + window.location.href = 'login.html'; + }, 2000); + } else { + document.querySelector('.error_text').textContent = result.error || 'Registration failed'; + document.querySelector('.success_message').style.display = 'none'; + } + } catch (error) { + console.error('Error:', error); + document.querySelector('.error_text').textContent = 'Server error. Please try again later.'; + } + }); + } + + // Login In + const loginForm = document.getElementById('login-form'); + if (loginForm) { + loginForm.addEventListener('submit', async (event) => { + event.preventDefault(); + + const username = document.getElementById('username').value; + const password = document.getElementById('password').value; + + try { + const response = await fetch('/api/users/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username, password }), + }); + + const result = await response.json(); + + if (response.ok) { + document.querySelector('.success_message').style.display = 'block'; + document.querySelector('.error_text').textContent = ''; + setTimeout(() => { + window.location.href = 'index.html'; + }, 2000); + } else { + document.querySelector('.error_text').textContent = result.error || 'Login failed'; + document.querySelector('.success_message').style.display = 'none'; + } + } catch (error) { + console.error('Error:', error); + document.querySelector('.error_text').textContent = 'Server error. Please try again later.'; + } + }); + } +}); \ No newline at end of file diff --git a/public/js/index.js b/public/js/index.js new file mode 100644 index 0000000000000000000000000000000000000000..5d8064574ffe7b926ddbcceae836114e138a021f --- /dev/null +++ b/public/js/index.js @@ -0,0 +1,17 @@ +document.addEventListener('DOMContentLoaded', async () => { + const logoutBtn = document.getElementById('logout-btn'); + + // Event for the logout button + logoutBtn.addEventListener('click', async () => { + try { + const res = await fetch('/api/users/logout', { method: 'POST' }); + if (res.ok) { + window.location.href = 'login.html'; + } else { + console.error('Logout failed'); + } + } catch (err) { + console.error('Error during logout:', err); + } + }); +}); \ No newline at end of file diff --git a/public/login.html b/public/login.html index 931d251ce346797a8e48af9edab290fdf86f007b..e38d18cb954dac68e665f36034c90039917cc1aa 100644 --- a/public/login.html +++ b/public/login.html @@ -4,36 +4,38 @@ <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Login</title> - <link rel="stylesheet" href="./css/login.css"> + <link rel="stylesheet" href="./css/login.css" /> </head> <body> <div class="login-container"> <h2>Login</h2> - <form id="login-form" action="login.php" method="POST"> + <form id="login-form"> <input type="text" class="uname" - name="username" + id="username" placeholder="Username" required /> <input type="password" class="pass" - name="password" + id="password" placeholder="Password" required /> - <button type="submit">Login</button> + <button type="submit" class="submit-login">Login</button> </form> - <span class="error_text"></span> - <div class="success_message"> - Login successful! Redirecting to your to-do list... + <span class="error_text" style="color: red"></span> + <div class="success_message" style="display: none; color: green"> + Login successful! Redirecting... </div> <div class="register-link"> - Don't have an account? <a href="register.html">Create one</a>. + Don't have an account? <a href="register.html">Register</a>. </div> </div> + + <script src="./js/auth.js"></script> </body> </html> diff --git a/public/register.html b/public/register.html index 053c7f838ca2682a9659bc62af0d3ffa4e7f530c..e0f407d64b35aded3843ece04badf624e6d5f2e8 100644 --- a/public/register.html +++ b/public/register.html @@ -1,38 +1,42 @@ <!DOCTYPE html> -<html lang="es"> +<html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Register</title> - <link rel="stylesheet" href="./css/login.css"> - + <link rel="stylesheet" href="./css/login.css" /> </head> <body> <div class="register-container"> <h2>Register</h2> - <form id="register-form" action="register.php" method="POST"> + <form id="register-form"> <input type="text" class="uname" - name="username" + id="username" placeholder="Username" required /> <input type="password" class="pass" - name="password" + id="password" placeholder="Password" required /> <button type="submit" class="submit-register">Register</button> </form> - <span class="error_text"></span> - <div class="success_message">Registration successful! Redirecting...</div> - <div class="login-link"> + <span class="error_text" style="color: red"></span> + <div class="success_message" style="display: none; color: green"> + Registration successful! Redirecting... + </div> + + <div class="login-link" style="margin-top: 20px"> Already have an account? <a href="login.html">Log in</a>. </div> </div> + + <script src="./js/auth.js"></script> </body> </html> diff --git a/routes/todos.js b/routes/todos.js index 49a3ea1b9d1095ee27014f0bf20d6505b62fbc13..8154cf9113a0a9ffa824114ea20067481d3df3eb 100644 --- a/routes/todos.js +++ b/routes/todos.js @@ -2,32 +2,24 @@ const express = require('express'); const router = express.Router(); const Todo = require('../models/todo'); -// Get all TODOs for the logged-in user -router.get('/', async (req, res) => { - const todos = await Todo.find({ userId: req.user.id }); - res.json(todos); -}); - -// Add a new TODO -router.post('/', async (req, res) => { - const { description } = req.body; - const todo = new Todo({ description, userId: req.user.id }); - await todo.save(); - res.json(todo); -}); +// Middleware to verify if the user is authenticated +function isAuthenticated(req, res, next) { + if (req.session.user) { + return next(); + } else { + return res.status(401).json({ error: 'Unauthorized' }); + } +} -// Mark TODO as done -router.put('/:id', async (req, res) => { - const todo = await Todo.findById(req.params.id); - todo.isDone = true; - await todo.save(); - res.json(todo); -}); -// Delete a TODO -router.delete('/:id', async (req, res) => { - await Todo.findByIdAndDelete(req.params.id); - res.json({ success: true }); +router.post('/logout', (req, res) => { + req.session.destroy((err) => { + if (err) { + console.error('Error al destruir la sesión:', err); + return res.status(500).json({ error: 'Error al cerrar sesión' }); + } + res.status(200).json({ message: 'Logout successful' }); + }); }); -module.exports = router; +module.exports = router; \ No newline at end of file diff --git a/routes/users.js b/routes/users.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..13da3ede5d57415fd6a474d353b623482b5b0a2a 100644 --- a/routes/users.js +++ b/routes/users.js @@ -0,0 +1,72 @@ +const express = require('express'); +const bcrypt = require('bcryptjs'); +const User = require('../models/user'); +const router = express.Router(); + +// Register User +router.post('/register', async (req, res) => { + const { username, password } = req.body; + + try { + // Verify if the user exists + const existingUser = await User.findOne({ username }); + if (existingUser) { + return res.status(400).json({ error: 'User already exists' }); + } + + // Encrypt the password + const hashedPassword = await bcrypt.hash(password, 10); + + // Creating a new user with the encrypted password + const user = new User({ username, password: hashedPassword }); + await user.save(); + + res.status(201).json({ success: true, message: 'User registered successfully' }); + } catch (error) { + console.error('Error saving user:', error); + res.status(500).json({ error: 'Server error' }); + } +}); + +// LogIn User +router.post('/login', async (req, res) => { + const { username, password } = req.body; + console.log('Attempting to log in:', username); + + try { + const user = await User.findOne({ username }); + console.log('User found:', user); + + if (!user) { + return res.status(400).json({ error: 'Invalid username or password' }); + } + + const isMatch = await bcrypt.compare(password, user.password); + console.log('Password match:', isMatch); + + if (!isMatch) { + return res.status(400).json({ error: 'Invalid username or password' }); + } + + req.session.user = { id: user._id, username: user.username }; + console.log('User logged in:', req.session.user); + + res.json({ success: true, message: 'Login successful' }); + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ error: 'Server error' }); + } +}); + + +// Closing user session +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + return res.status(500).json({ error: 'Logout failed' }); + } + res.json({ success: true, message: 'Logged out successfully' }); + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/server.js b/server.js index a48072d2b823f2ed8ac3d737526f4f331702ee32..8dc73f18130077caa361219022688b47c55e8c21 100644 --- a/server.js +++ b/server.js @@ -1,21 +1,31 @@ -// server.js const express = require('express'); const path = require('path'); -const connectDB = require('./mongodb'); // Import the MongoDB connection +const connectDB = require('./mongodb'); +const session = require('express-session'); const app = express(); // Middleware -app.use(express.json()); // Use express' built-in body-parser for JSON -app.use(express.static(path.join(__dirname, 'public'))); // Serve static files +app.use(express.json()); +app.use(express.static(path.join(__dirname, 'public'))); -// Connect to MongoDB -connectDB(); // Call the function to establish the database connection +// Session configuration +app.use( + session({ + secret: 'session_secret', + resave: false, + saveUninitialized: false, + cookie: { secure: false } + }) +); + +// Connection to MongoDB +connectDB(); // Routes app.use('/api/todos', require('./routes/todos')); app.use('/api/users', require('./routes/users')); -// Start the server +// Initialize the server const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log(`Server running on port ${PORT}`));