feat: Add comprehensive backend validation, toast notifications, and Tokyo Night theme
- Backend: Add server-side validation with go-playground/validator/v10 - Frontend: Add toast notifications for API errors with @zerodevx/svelte-toast - UI: Complete Tokyo Night theme redesign with modern animations - Docs: Update COMPLETE_DOCUMENTATION.md and README.md with all enhancements - Validation: Email format, password strength, and input sanitization - UX: Real-time error feedback, loading states, and responsive design
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
const express = require('express');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const cors = require('cors');
|
||||
const bodyParser = require('body-parser');
|
||||
|
||||
const app = express();
|
||||
const PORT = 8080;
|
||||
const JWT_SECRET = 'your-secret-key'; // In production, use env var
|
||||
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// In-memory user store (for demo purposes)
|
||||
const users = [];
|
||||
|
||||
// Password validation regex
|
||||
const passwordRegex = /^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
||||
|
||||
// Middleware to verify token
|
||||
function authenticateToken(req, res, next) {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
|
||||
if (!token) return res.status(401).json({ error: 'Access token required' });
|
||||
|
||||
jwt.verify(token, JWT_SECRET, (err, user) => {
|
||||
if (err) return res.status(403).json({ error: 'Invalid token' });
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
// Auth routes
|
||||
app.post('/api/auth/register', async (req, res) => {
|
||||
const { email, username, password } = req.body;
|
||||
|
||||
if (!email || !username || !password) {
|
||||
return res.status(400).json({ error: 'All fields are required' });
|
||||
}
|
||||
|
||||
// Check if password meets requirements
|
||||
if (!passwordRegex.test(password)) {
|
||||
return res.status(400).json({
|
||||
error: 'Password must be at least 8 characters and include at least one uppercase letter, one number, and one symbol'
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
const existingUser = users.find(u => u.email === email || u.username === username);
|
||||
if (existingUser) {
|
||||
return res.status(400).json({ error: 'User already exists' });
|
||||
}
|
||||
|
||||
try {
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
const user = { id: users.length + 1, email, username, passwordHash };
|
||||
users.push(user);
|
||||
|
||||
const token = jwt.sign({ id: user.id, email: user.email, username: user.username }, JWT_SECRET);
|
||||
res.json({ token, user: { id: user.id, email: user.email, username: user.username } });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Registration failed' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
const { login, password } = req.body; // login can be email or username
|
||||
|
||||
if (!login || !password) {
|
||||
return res.status(400).json({ error: 'Username and password are required' });
|
||||
}
|
||||
|
||||
const user = users.find(u => u.email === login || u.username === login);
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
try {
|
||||
const isValid = await bcrypt.compare(password, user.passwordHash);
|
||||
if (!isValid) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
const token = jwt.sign({ id: user.id, email: user.email, username: user.username }, JWT_SECRET);
|
||||
res.json({ token, user: { id: user.id, email: user.email, username: user.username } });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Login failed' });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/auth/profile', authenticateToken, (req, res) => {
|
||||
res.json(req.user);
|
||||
});
|
||||
|
||||
// Stub routes for ebooks (to avoid errors)
|
||||
app.get('/api/ebooks', authenticateToken, (req, res) => {
|
||||
res.json([]);
|
||||
});
|
||||
|
||||
app.get('/api/ebooks/:id', authenticateToken, (req, res) => {
|
||||
res.json({ id: req.params.id, title: 'Stub' });
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on http://localhost:${PORT}`);
|
||||
});
|
||||
Reference in New Issue
Block a user