diff --git a/backend/server.js b/backend/server.js index 6659dfc..831f724 100644 --- a/backend/server.js +++ b/backend/server.js @@ -98,6 +98,72 @@ app.get('/api/auth/me', authenticate, (req, res) => { }); }); +// Change Password +app.put('/api/auth/password', authenticate, (req, res) => { + const { currentPassword, newPassword } = req.body; + if (!currentPassword || !newPassword) return res.status(400).json({ error: 'Current and new password required' }); + if (newPassword.length < 4) return res.status(400).json({ error: 'New password must be at least 4 characters' }); + + db.get('SELECT * FROM users WHERE id = ?', [req.userId], async (err, user) => { + if (err || !user) return res.status(404).json({ error: 'User not found' }); + if (!user.password) return res.status(400).json({ error: 'Guest accounts cannot change password' }); + + const match = await bcrypt.compare(currentPassword, user.password); + if (!match) return res.status(400).json({ error: 'Current password is incorrect' }); + + const hashedPassword = await bcrypt.hash(newPassword, 10); + db.run('UPDATE users SET password = ? WHERE id = ?', [hashedPassword, req.userId], (err) => { + if (err) return res.status(500).json({ error: 'Failed to update password' }); + res.json({ success: true }); + }); + }); +}); + +// Delete Account +app.delete('/api/auth/account', authenticate, async (req, res) => { + const { password } = req.body; + if (!password) return res.status(400).json({ error: 'Password is required' }); + + // Verify password first + const user = await new Promise((resolve, reject) => { + db.get('SELECT * FROM users WHERE id = ?', [req.userId], (err, row) => { + if (err) reject(err); + else resolve(row); + }); + }).catch(() => null); + + if (!user) return res.status(404).json({ error: 'User not found' }); + if (!user.password) return res.status(400).json({ error: 'Guest accounts cannot be deleted this way' }); + + const match = await bcrypt.compare(password, user.password); + if (!match) return res.status(400).json({ error: 'Incorrect password' }); + + // Proceed with deletion — first unlink partner if any + db.get('SELECT partner_code FROM users WHERE id = ?', [req.userId], (err, userData) => { + if (err) return res.status(500).json({ error: 'Failed to delete account' }); + + const cleanup = () => { + // Delete all user data + db.run('DELETE FROM swipes WHERE user_id = ?', [req.userId], () => { + db.run('DELETE FROM watched WHERE user_id = ?', [req.userId], () => { + db.run('DELETE FROM users WHERE id = ?', [req.userId], (err) => { + if (err) return res.status(500).json({ error: 'Failed to delete account' }); + res.json({ success: true }); + }); + }); + }); + }; + + if (userData && userData.partner_code) { + db.run('UPDATE users SET partner_code = NULL WHERE code = ?', [userData.partner_code], () => { + cleanup(); + }); + } else { + cleanup(); + } + }); +}); + // Update Genres app.post('/api/user/genres', authenticate, (req, res) => { const { genres } = req.body; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e5a79d1..05ec027 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -3,17 +3,19 @@ import { AppProvider } from './store/AppContext'; import { Home } from './pages/Home'; import { Swipe } from './pages/Swipe'; import { Matches } from './pages/Matches'; +import { Settings } from './pages/Settings'; import { Navbar } from './components/Navbar'; function App() { return ( -
+
} /> } /> } /> + } />
diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index 8b332ad..6ebe39f 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -1,23 +1,38 @@ import React from 'react'; import { Link, useLocation } from 'react-router-dom'; -import { Film, Heart, Users } from 'lucide-react'; +import { Film, Heart, Users, Settings } from 'lucide-react'; import { useAppContext } from '../store/AppContext'; export const Navbar: React.FC = () => { const location = useLocation(); - const { matches } = useAppContext(); + const { user, matches } = useAppContext(); const navItems = [ { path: '/', icon: Users, label: 'Partner' }, { path: '/swipe', icon: Film, label: 'Discover' }, { path: '/matches', icon: Heart, label: 'Matches', badge: matches.length }, + { path: '/settings', icon: Settings, label: 'Settings', requiresAuth: true }, ]; return ( -