feat: add profile fields and search history API

This commit is contained in:
Antoni Nuñez Romeu
2026-07-06 18:01:18 +02:00
parent 30a3444da5
commit 86a4e119b0
+115 -6
View File
@@ -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();