first test
This commit is contained in:
113
routes/authRoutes.js
Normal file
113
routes/authRoutes.js
Normal file
@@ -0,0 +1,113 @@
|
||||
// routes/authRoutes.js
|
||||
// Handles user registration, login, and logout
|
||||
|
||||
const express = require('express');
|
||||
const bcrypt = require('bcrypt');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const db = require('../db'); // Import database query function
|
||||
require('dotenv').config();
|
||||
|
||||
const router = express.Router();
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '1h';
|
||||
const SALT_ROUNDS = 10; // Cost factor for bcrypt hashing
|
||||
|
||||
// POST /api/auth/register - User Registration
|
||||
router.post('/register', async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
// Basic validation
|
||||
if (!username || !password) {
|
||||
return res.status(400).json({ message: 'Benutzername und Passwort sind erforderlich.' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if user already exists
|
||||
const userCheck = await db.query('SELECT * FROM users WHERE username = $1', [username]);
|
||||
if (userCheck.rows.length > 0) {
|
||||
return res.status(409).json({ message: 'Benutzername bereits vergeben.' }); // 409 Conflict
|
||||
}
|
||||
|
||||
// Hash the password
|
||||
const passwordHash = await bcrypt.hash(password, SALT_ROUNDS);
|
||||
|
||||
// Insert new user into the database
|
||||
const newUser = await db.query(
|
||||
'INSERT INTO users (username, password_hash) VALUES ($1, $2) RETURNING id, username',
|
||||
[username, passwordHash]
|
||||
);
|
||||
|
||||
console.log(`User registered: ${newUser.rows[0].username}`);
|
||||
// Respond with success message (or automatically log them in)
|
||||
// For simplicity, we just confirm registration here. User needs to login separately.
|
||||
res.status(201).json({ message: 'Registrierung erfolgreich. Bitte einloggen.' });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Registration Error:', error);
|
||||
res.status(500).json({ message: 'Serverfehler bei der Registrierung.' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/auth/login - User Login
|
||||
router.post('/login', async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
if (!username || !password) {
|
||||
return res.status(400).json({ message: 'Benutzername und Passwort sind erforderlich.' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Find user by username
|
||||
const result = await db.query('SELECT * FROM users WHERE username = $1', [username]);
|
||||
const user = result.rows[0];
|
||||
|
||||
// Check if user exists and password is correct
|
||||
if (!user || !(await bcrypt.compare(password, user.password_hash))) {
|
||||
return res.status(401).json({ message: 'Ungültiger Benutzername oder Passwort.' }); // 401 Unauthorized
|
||||
}
|
||||
|
||||
// User authenticated successfully, create JWT payload
|
||||
const payload = {
|
||||
id: user.id,
|
||||
username: user.username
|
||||
// Add other relevant non-sensitive user info if needed
|
||||
};
|
||||
|
||||
// Sign the JWT
|
||||
const token = jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
|
||||
|
||||
// Set JWT as an HTTP-Only cookie
|
||||
// HttpOnly: Prevents client-side JS access (safer against XSS)
|
||||
// Secure: Transmit cookie only over HTTPS (set to true in production with HTTPS)
|
||||
// SameSite: Controls cross-site request behavior ('Strict' or 'Lax' recommended)
|
||||
res.cookie('token', token, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production', // Use secure cookies in production
|
||||
maxAge: parseInt(JWT_EXPIRES_IN) * 1000 || 3600000, // Cookie expiry in milliseconds (e.g., 1h)
|
||||
sameSite: 'Lax' // Or 'Strict'
|
||||
});
|
||||
|
||||
console.log(`User logged in: ${user.username}`);
|
||||
// Send success response (client-side JS will redirect)
|
||||
res.status(200).json({ message: 'Login erfolgreich.' });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Login Error:', error);
|
||||
res.status(500).json({ message: 'Serverfehler beim Login.' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/auth/logout - User Logout
|
||||
router.post('/logout', (req, res) => {
|
||||
// Clear the authentication cookie
|
||||
res.clearCookie('token', {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'Lax'
|
||||
});
|
||||
console.log('User logged out');
|
||||
res.status(200).json({ message: 'Logout erfolgreich.' });
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
108
routes/todoRoutes.js
Normal file
108
routes/todoRoutes.js
Normal file
@@ -0,0 +1,108 @@
|
||||
// routes/todoRoutes.js
|
||||
// Handles CRUD operations for todo items for the logged-in user
|
||||
|
||||
const express = require('express');
|
||||
const db = require('../db');
|
||||
const authenticateToken = require('../middleware/authMiddleware'); // Import auth middleware
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// All routes in this file require authentication
|
||||
router.use(authenticateToken);
|
||||
|
||||
// GET /api/todos - Get all todos for the logged-in user
|
||||
router.get('/', async (req, res) => {
|
||||
const userId = req.user.id; // Get user ID from the authenticated token payload
|
||||
|
||||
try {
|
||||
const result = await db.query(
|
||||
'SELECT id, task, is_completed FROM todos WHERE user_id = $1 ORDER BY created_at DESC',
|
||||
[userId]
|
||||
);
|
||||
res.status(200).json(result.rows);
|
||||
} catch (error) {
|
||||
console.error('Error fetching todos:', error);
|
||||
res.status(500).json({ message: 'Fehler beim Abrufen der Todos.' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/todos - Create a new todo for the logged-in user
|
||||
router.post('/', async (req, res) => {
|
||||
const userId = req.user.id;
|
||||
const { task } = req.body;
|
||||
|
||||
if (!task || task.trim() === '') {
|
||||
return res.status(400).json({ message: 'Aufgabeninhalt darf nicht leer sein.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await db.query(
|
||||
'INSERT INTO todos (user_id, task) VALUES ($1, $2) RETURNING id, task, is_completed',
|
||||
[userId, task.trim()]
|
||||
);
|
||||
res.status(201).json(result.rows[0]); // Return the newly created todo
|
||||
} catch (error) {
|
||||
console.error('Error creating todo:', error);
|
||||
res.status(500).json({ message: 'Fehler beim Erstellen des Todos.' });
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/todos/:id - Update a todo's completion status
|
||||
router.put('/:id', async (req, res) => {
|
||||
const userId = req.user.id;
|
||||
const todoId = parseInt(req.params.id, 10);
|
||||
const { is_completed } = req.body; // Expecting { is_completed: true/false }
|
||||
|
||||
if (isNaN(todoId)) {
|
||||
return res.status(400).json({ message: 'Ungültige Todo ID.' });
|
||||
}
|
||||
if (typeof is_completed !== 'boolean') {
|
||||
return res.status(400).json({ message: 'Ungültiger Statuswert.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await db.query(
|
||||
'UPDATE todos SET is_completed = $1 WHERE id = $2 AND user_id = $3 RETURNING id, task, is_completed',
|
||||
[is_completed, todoId, userId]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
// Either todo doesn't exist or doesn't belong to the user
|
||||
return res.status(404).json({ message: 'Todo nicht gefunden oder Zugriff verweigert.' });
|
||||
}
|
||||
|
||||
res.status(200).json(result.rows[0]); // Return the updated todo
|
||||
} catch (error) {
|
||||
console.error('Error updating todo:', error);
|
||||
res.status(500).json({ message: 'Fehler beim Aktualisieren des Todos.' });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/todos/:id - Delete a todo
|
||||
router.delete('/:id', async (req, res) => {
|
||||
const userId = req.user.id;
|
||||
const todoId = parseInt(req.params.id, 10);
|
||||
|
||||
if (isNaN(todoId)) {
|
||||
return res.status(400).json({ message: 'Ungültige Todo ID.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await db.query(
|
||||
'DELETE FROM todos WHERE id = $1 AND user_id = $2 RETURNING id',
|
||||
[todoId, userId]
|
||||
);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
// Either todo doesn't exist or doesn't belong to the user
|
||||
return res.status(404).json({ message: 'Todo nicht gefunden oder Zugriff verweigert.' });
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Todo erfolgreich gelöscht.' }); // Or use 204 No Content
|
||||
} catch (error) {
|
||||
console.error('Error deleting todo:', error);
|
||||
res.status(500).json({ message: 'Fehler beim Löschen des Todos.' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
50
routes/viewRoutes.js
Normal file
50
routes/viewRoutes.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// routes/viewRoutes.js
|
||||
// Handles serving the HTML pages
|
||||
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const authenticateToken = require('../middleware/authMiddleware'); // Import auth middleware
|
||||
const jwt = require('jsonwebtoken');
|
||||
require('dotenv').config();
|
||||
|
||||
const router = express.Router();
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
|
||||
// Helper function to check if a user is already logged in (valid token exists)
|
||||
const checkAlreadyLoggedIn = (req, res, next) => {
|
||||
const token = req.cookies.token;
|
||||
if (token) {
|
||||
jwt.verify(token, JWT_SECRET, (err, user) => {
|
||||
if (!err && user) {
|
||||
// If token is valid, redirect logged-in users away from login/register pages
|
||||
return res.redirect('/');
|
||||
}
|
||||
// If token is invalid, clear it and proceed
|
||||
res.clearCookie('token');
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
// No token, proceed
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Serve the main todo app page (index.html) - Requires authentication
|
||||
// The authenticateToken middleware will redirect to /login if not authenticated
|
||||
router.get('/', authenticateToken, (req, res) => {
|
||||
// The user is authenticated, serve the main app page
|
||||
res.sendFile(path.join(__dirname, '..', 'public', 'index.html'));
|
||||
});
|
||||
|
||||
// Serve the login page - If already logged in, redirect to '/'
|
||||
router.get('/login', checkAlreadyLoggedIn, (req, res) => {
|
||||
res.sendFile(path.join(__dirname, '..', 'public', 'login.html'));
|
||||
});
|
||||
|
||||
// Serve the registration page - If already logged in, redirect to '/'
|
||||
router.get('/register', checkAlreadyLoggedIn, (req, res) => {
|
||||
res.sendFile(path.join(__dirname, '..', 'public', 'register.html'));
|
||||
});
|
||||
|
||||
module.exports = router;
|
Reference in New Issue
Block a user