Added files of the Todo app
This commit is contained in:
25
Backend/middleware/auth.js
Normal file
25
Backend/middleware/auth.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import User from '../models/User.js';
|
||||
|
||||
export const authenticateToken = async (req, res, next) => {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({ message: 'Access token required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
const user = await User.findById(decoded.userId).select('-password');
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({ message: 'Invalid token' });
|
||||
}
|
||||
|
||||
req.user = user;
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(403).json({ message: 'Invalid or expired token' });
|
||||
}
|
||||
};
|
||||
37
Backend/models/Todo.js
Normal file
37
Backend/models/Todo.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const todoSchema = new mongoose.Schema({
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
trim: true,
|
||||
maxlength: 200
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
trim: true,
|
||||
maxlength: 1000
|
||||
},
|
||||
completed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
priority: {
|
||||
type: String,
|
||||
enum: ['low', 'medium', 'high'],
|
||||
default: 'medium'
|
||||
},
|
||||
dueDate: {
|
||||
type: Date,
|
||||
required: true
|
||||
},
|
||||
userId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true
|
||||
}
|
||||
}, {
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
export default mongoose.model('Todo', todoSchema);
|
||||
45
Backend/models/User.js
Normal file
45
Backend/models/User.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import mongoose from 'mongoose';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
username: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
trim: true,
|
||||
minlength: 3,
|
||||
maxlength: 30
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
trim: true,
|
||||
lowercase: true
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true,
|
||||
minlength: 6
|
||||
}
|
||||
}, {
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
userSchema.pre('save', async function(next) {
|
||||
if (!this.isModified('password')) return next();
|
||||
|
||||
try {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
this.password = await bcrypt.hash(this.password, salt);
|
||||
next();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
userSchema.methods.comparePassword = async function(candidatePassword) {
|
||||
return bcrypt.compare(candidatePassword, this.password);
|
||||
};
|
||||
|
||||
export default mongoose.model('User', userSchema);
|
||||
1587
Backend/package-lock.json
generated
Normal file
1587
Backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
Backend/package.json
Normal file
23
Backend/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "todo-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "Backend for Todo App with Authentication",
|
||||
"main": "server.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "nodemon server.js",
|
||||
"test-db": "node test-connection.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"mongoose": "^8.0.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.0.1"
|
||||
}
|
||||
}
|
||||
100
Backend/routes/auth.js
Normal file
100
Backend/routes/auth.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import express from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import User from '../models/User.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Register
|
||||
router.post('/register', async (req, res) => {
|
||||
try {
|
||||
const { username, email, password } = req.body;
|
||||
|
||||
// Validation
|
||||
if (!username || !email || !password) {
|
||||
return res.status(400).json({
|
||||
message: 'Username, email, and password are required'
|
||||
});
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
return res.status(400).json({
|
||||
message: 'Password must be at least 6 characters long'
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
const existingUser = await User.findOne({
|
||||
$or: [{ email }, { username }]
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return res.status(400).json({
|
||||
message: 'User with this email or username already exists'
|
||||
});
|
||||
}
|
||||
|
||||
// Create new user
|
||||
const user = new User({ username, email, password });
|
||||
await user.save();
|
||||
|
||||
// Generate JWT token
|
||||
const token = jwt.sign(
|
||||
{ userId: user._id },
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '7d' }
|
||||
);
|
||||
|
||||
res.status(201).json({
|
||||
message: 'User created successfully',
|
||||
token,
|
||||
user: {
|
||||
id: user._id,
|
||||
username: user.username,
|
||||
email: user.email
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Registration error:', error);
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Login
|
||||
router.post('/login', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
// Find user by email
|
||||
const user = await User.findOne({ email });
|
||||
if (!user) {
|
||||
return res.status(400).json({ message: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
// Check password
|
||||
const isMatch = await user.comparePassword(password);
|
||||
if (!isMatch) {
|
||||
return res.status(400).json({ message: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
const token = jwt.sign(
|
||||
{ userId: user._id },
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '7d' }
|
||||
);
|
||||
|
||||
res.json({
|
||||
message: 'Login successful',
|
||||
token,
|
||||
user: {
|
||||
id: user._id,
|
||||
username: user.username,
|
||||
email: user.email
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
92
Backend/routes/todos.js
Normal file
92
Backend/routes/todos.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import express from 'express';
|
||||
import Todo from '../models/Todo.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Get todos for a specific date
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const { date } = req.query;
|
||||
let query = { userId: req.user._id };
|
||||
|
||||
if (date) {
|
||||
const startDate = new Date(date);
|
||||
const endDate = new Date(date);
|
||||
endDate.setDate(endDate.getDate() + 1);
|
||||
|
||||
query.dueDate = {
|
||||
$gte: startDate,
|
||||
$lt: endDate
|
||||
};
|
||||
}
|
||||
|
||||
const todos = await Todo.find(query).sort({ createdAt: -1 });
|
||||
res.json(todos);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Create new todo
|
||||
router.post('/', async (req, res) => {
|
||||
try {
|
||||
const { title, description, priority, dueDate } = req.body;
|
||||
|
||||
const todo = new Todo({
|
||||
title,
|
||||
description,
|
||||
priority,
|
||||
dueDate: new Date(dueDate),
|
||||
userId: req.user._id
|
||||
});
|
||||
|
||||
await todo.save();
|
||||
res.status(201).json(todo);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Update todo
|
||||
router.put('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updates = req.body;
|
||||
|
||||
const todo = await Todo.findOneAndUpdate(
|
||||
{ _id: id, userId: req.user._id },
|
||||
updates,
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
if (!todo) {
|
||||
return res.status(404).json({ message: 'Todo not found' });
|
||||
}
|
||||
|
||||
res.json(todo);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete todo
|
||||
router.delete('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const todo = await Todo.findOneAndDelete({
|
||||
_id: id,
|
||||
userId: req.user._id
|
||||
});
|
||||
|
||||
if (!todo) {
|
||||
return res.status(404).json({ message: 'Todo not found' });
|
||||
}
|
||||
|
||||
res.json({ message: 'Todo deleted successfully' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
48
Backend/server.js
Normal file
48
Backend/server.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import express from 'express';
|
||||
import mongoose from 'mongoose';
|
||||
import cors from 'cors';
|
||||
import dotenv from 'dotenv';
|
||||
import authRoutes from './routes/auth.js';
|
||||
import todoRoutes from './routes/todos.js';
|
||||
import { authenticateToken } from './middleware/auth.js';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 5000;
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// Root route
|
||||
app.get('/', (req, res) => {
|
||||
res.json({ message: 'Todo App Backend API is running!' });
|
||||
});
|
||||
|
||||
// Routes
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/todos', authenticateToken, todoRoutes);
|
||||
|
||||
// Error handling middleware
|
||||
app.use((err, req, res, next) => {
|
||||
console.error('Error:', err);
|
||||
res.status(500).json({ message: 'Internal server error', error: err.message });
|
||||
});
|
||||
|
||||
// MongoDB connection
|
||||
mongoose.connect(process.env.MONGO_URL)
|
||||
.then(() => {
|
||||
console.log('✅ Connected to MongoDB');
|
||||
console.log('Database:', process.env.MONGO_URL);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('❌ MongoDB connection error:', err.message);
|
||||
console.log('Make sure MongoDB is running on your system');
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🚀 Server running on port ${PORT}`);
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user