diff --git a/apps/backend/server.js b/apps/backend/server.js index 2bf948c..d6d10c0 100644 --- a/apps/backend/server.js +++ b/apps/backend/server.js @@ -290,6 +290,19 @@ if (!pgPool) { try { await dbRun('ALTER TABLE users ADD COLUMN longitude REAL'); } catch {} } + // Add new fields for profile redesign + if (pgPool) { + await pgPool.query(` + ALTER TABLE users ADD COLUMN IF NOT EXISTS first_name TEXT; + ALTER TABLE users ADD COLUMN IF NOT EXISTS last_name TEXT; + ALTER TABLE users ADD COLUMN IF NOT EXISTS avatar_url TEXT; + `); + } else { + try { await dbRun('ALTER TABLE users ADD COLUMN first_name TEXT'); } catch {} + try { await dbRun('ALTER TABLE users ADD COLUMN last_name TEXT'); } catch {} + try { await dbRun('ALTER TABLE users ADD COLUMN avatar_url TEXT'); } catch {} + } + // Add user_id to push tables (SQLite — no cross-DB FK) try { await dbRun('ALTER TABLE push_subscriptions ADD COLUMN user_id INTEGER'); } catch {} try { await dbRun('ALTER TABLE push_subscriptions_pharmacy ADD COLUMN user_id INTEGER'); } catch {} @@ -313,6 +326,32 @@ if (!pgPool) { } catch (e) { if (!/table already exists/i.test(e.message)) throw e; } + + // Search history for "find pharmacies near me" + if (pgPool) { + await pgPool.query(` + CREATE TABLE IF NOT EXISTS search_history ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(id), + address TEXT NOT NULL, + latitude DOUBLE PRECISION, + longitude DOUBLE PRECISION, + created_at TIMESTAMPTZ DEFAULT NOW() + ) + `); + } else { + await dbRun(` + CREATE TABLE IF NOT EXISTS search_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + address TEXT NOT NULL, + latitude REAL, + longitude REAL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) + ) + `); + } } catch (err) { console.error('initDatabase failed:', err); throw err; @@ -664,6 +703,9 @@ app.post('/api/auth/login', loginLimiter, async (req, res) => { user: { id: user.id, username: user.username, + first_name: user.first_name || null, + last_name: user.last_name || null, + avatar_url: user.avatar_url || null, is_admin: Boolean(user.is_admin), address: user.address || null, latitude: user.latitude, @@ -710,6 +752,9 @@ app.post('/api/auth/register', registerLimiter, async (req, res) => { user: { id: result.lastID, username: u, + first_name: null, + last_name: null, + avatar_url: null, is_admin: false, address: null, latitude: null, @@ -738,7 +783,7 @@ app.get('/api/auth/check', async (req, res) => { try { if (req.session && req.session.userId) { const user = await userDbGet( - 'SELECT id, username, is_admin, address, latitude, longitude FROM users WHERE id = ?', + 'SELECT id, username, first_name, last_name, avatar_url, is_admin, address, latitude, longitude FROM users WHERE id = ?', [req.session.userId] ); if (!user) { @@ -750,6 +795,9 @@ app.get('/api/auth/check', async (req, res) => { user: { id: user.id, username: user.username, + first_name: user.first_name || null, + last_name: user.last_name || null, + avatar_url: user.avatar_url || null, is_admin: Boolean(user.is_admin), address: user.address || null, latitude: user.latitude, @@ -768,13 +816,16 @@ app.get('/api/auth/check', async (req, res) => { app.get('/api/users/me', requireAuth, async (req, res) => { try { const user = await userDbGet( - 'SELECT id, username, is_admin, address, latitude, longitude FROM users WHERE id = ?', + 'SELECT id, username, first_name, last_name, avatar_url, is_admin, address, latitude, longitude FROM users WHERE id = ?', [req.session.userId] ); if (!user) return res.status(404).json({ error: 'Not found' }); res.json({ id: user.id, username: user.username, + first_name: user.first_name || null, + last_name: user.last_name || null, + avatar_url: user.avatar_url || null, is_admin: Boolean(user.is_admin), address: user.address || null, latitude: user.latitude, @@ -789,7 +840,12 @@ app.get('/api/users/me', requireAuth, async (req, res) => { // Update the current user's address + coordinates app.put('/api/users/me', requireAuth, async (req, res) => { try { - const { address, latitude, longitude } = req.body || {}; + const { first_name, last_name, avatar_url, address, latitude, longitude } = req.body || {}; + + const fName = first_name == null ? null : String(first_name).trim().slice(0, 100); + const lName = last_name == null ? null : String(last_name).trim().slice(0, 100); + const avatar = avatar_url == null ? null : String(avatar_url).slice(0, 500); + const addr = address == null || String(address).trim() === '' ? null @@ -811,17 +867,20 @@ app.put('/api/users/me', requireAuth, async (req, res) => { } await userDbRun( - 'UPDATE users SET address = ?, latitude = ?, longitude = ? WHERE id = ?', - [addr, lat, lon, req.session.userId] + 'UPDATE users SET first_name = ?, last_name = ?, avatar_url = ?, address = ?, latitude = ?, longitude = ? WHERE id = ?', + [fName, lName, avatar, addr, lat, lon, req.session.userId] ); const user = await userDbGet( - 'SELECT id, username, is_admin, address, latitude, longitude FROM users WHERE id = ?', + 'SELECT id, username, first_name, last_name, avatar_url, is_admin, address, latitude, longitude FROM users WHERE id = ?', [req.session.userId] ); res.json({ id: user.id, username: user.username, + first_name: user.first_name || null, + last_name: user.last_name || null, + avatar_url: user.avatar_url || null, is_admin: Boolean(user.is_admin), address: user.address || null, latitude: user.latitude, @@ -919,6 +978,56 @@ app.delete('/api/alerts/:id', requireAuth, async (req, res) => { } }); +// ========== SEARCH HISTORY API ========== + +// Get search history for current user +app.get('/api/search-history', requireAuth, async (req, res) => { + try { + const rows = await userDbAll( + 'SELECT id, address, latitude, longitude, created_at FROM search_history WHERE user_id = ? ORDER BY created_at DESC LIMIT 20', + [req.session.userId] + ); + res.json(rows); + } catch (error) { + console.error('Error fetching search history:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Add new search to history +app.post('/api/search-history', requireAuth, async (req, res) => { + try { + const { address, latitude, longitude } = req.body || {}; + if (!address) { + return res.status(400).json({ error: 'Address is required' }); + } + + const result = await userDbRun( + 'INSERT INTO search_history (user_id, address, latitude, longitude) VALUES (?, ?, ?, ?)', + [req.session.userId, address, latitude || null, longitude || null] + ); + + res.json({ id: result.lastID, address, latitude, longitude }); + } catch (error) { + console.error('Error adding search history:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Delete search history item +app.delete('/api/search-history/:id', requireAuth, async (req, res) => { + try { + await userDbRun( + 'DELETE FROM search_history WHERE id = ? AND user_id = ?', + [req.params.id, req.session.userId] + ); + res.json({ ok: true }); + } catch (error) { + console.error('Error deleting search history:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + // Geocode for any authenticated user (rate-limited). Same impl as /api/admin/geocode. app.get('/api/geocode', requireAuth, geocodeLimiter, async (req, res) => { const q = (req.query.q || '').trim();