Design hotfixes and improvements.
This commit is contained in:
+128
-6
@@ -65,7 +65,8 @@ const sessionConfig = {
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
secure: process.env.COOKIE_SECURE === 'true',
|
||||
sameSite: 'lax',
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
}
|
||||
@@ -225,6 +226,22 @@ async function initDatabase() {
|
||||
`);
|
||||
}
|
||||
|
||||
if (pgPool) {
|
||||
await pgPool.query(`
|
||||
CREATE TABLE IF NOT EXISTS user_alerts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
type TEXT NOT NULL CHECK (type IN ('reminder', 'availability', 'schedule')),
|
||||
medicine_nregistro TEXT,
|
||||
title TEXT,
|
||||
detail TEXT,
|
||||
schedule TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
await dbRun(`
|
||||
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -254,7 +271,7 @@ async function initDatabase() {
|
||||
`);
|
||||
await dbRun(`CREATE INDEX IF NOT EXISTS idx_push_subs_pharm ON push_subscriptions_pharmacy(medicine_nregistro, pharmacy_id)`);
|
||||
|
||||
if (!pgPool) {
|
||||
if (!pgPool) {
|
||||
// SQLite-only migrations for existing deployments
|
||||
try { await dbRun('ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0'); } catch {}
|
||||
try { await dbRun('ALTER TABLE users ADD COLUMN address TEXT'); } catch {}
|
||||
@@ -262,13 +279,32 @@ async function initDatabase() {
|
||||
try { await dbRun('ALTER TABLE users ADD COLUMN longitude REAL'); } catch {}
|
||||
}
|
||||
|
||||
// Add user_id to push tables (SQLite — no cross-DB FK)
|
||||
// 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 {}
|
||||
|
||||
console.log('Database initialized successfully');
|
||||
} catch (error) {
|
||||
console.error('Error initializing database:', error);
|
||||
// Create alerts table for persistent Avisos data
|
||||
try {
|
||||
await dbRun(`
|
||||
CREATE TABLE IF NOT EXISTS user_alerts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN ('reminder', 'availability', 'schedule')),
|
||||
medicine_nregistro TEXT,
|
||||
title TEXT,
|
||||
detail TEXT,
|
||||
schedule TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
} catch (e) {
|
||||
if (!/table already exists/i.test(e.message)) throw e;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('initDatabase failed:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,6 +666,92 @@ app.put('/api/users/me', requireAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ========== USER ALERTS API ==========
|
||||
|
||||
// Get all alerts for the current user
|
||||
app.get('/api/alerts', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const rows = await userDbAll(
|
||||
'SELECT id, type, medicine_nregistro, title, detail, schedule, created_at, updated_at FROM user_alerts WHERE user_id = ? ORDER BY created_at DESC',
|
||||
[userId]
|
||||
);
|
||||
res.json(rows);
|
||||
} catch (error) {
|
||||
console.error('Error fetching alerts:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new alert
|
||||
app.post('/api/alerts', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const { type, medicine_nregistro, title, detail, schedule } = req.body || {};
|
||||
|
||||
if (!type || !['reminder', 'availability', 'schedule'].includes(type)) {
|
||||
return res.status(400).json({ error: 'Valid type is required (reminder, availability, schedule)' });
|
||||
}
|
||||
|
||||
const result = await userDbRun(
|
||||
'INSERT INTO user_alerts (user_id, type, medicine_nregistro, title, detail, schedule) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[userId, type, medicine_nregistro || null, title || null, detail || null, schedule || null]
|
||||
);
|
||||
|
||||
const newAlert = await userDbGet('SELECT * FROM user_alerts WHERE id = ?', [result.lastID]);
|
||||
res.status(201).json(newAlert);
|
||||
} catch (error) {
|
||||
console.error('Error creating alert:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update an existing alert
|
||||
app.put('/api/alerts/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const alertId = parseInt(req.params.id);
|
||||
const { medicine_nregistro, title, detail, schedule } = req.body || {};
|
||||
|
||||
const result = await userDbRun(
|
||||
'UPDATE user_alerts SET medicine_nregistro = ?, title = ?, detail = ?, schedule = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?',
|
||||
[medicine_nregistro || null, title || null, detail || null, schedule || null, alertId, userId]
|
||||
);
|
||||
|
||||
if (result.changes === 0) {
|
||||
return res.status(404).json({ error: 'Alert not found' });
|
||||
}
|
||||
|
||||
const updatedAlert = await userDbGet('SELECT * FROM user_alerts WHERE id = ?', [alertId]);
|
||||
res.json(updatedAlert);
|
||||
} catch (error) {
|
||||
console.error('Error updating alert:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete an alert
|
||||
app.delete('/api/alerts/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const alertId = parseInt(req.params.id);
|
||||
|
||||
const result = await userDbRun(
|
||||
'DELETE FROM user_alerts WHERE id = ? AND user_id = ?',
|
||||
[alertId, userId]
|
||||
);
|
||||
|
||||
if (result.changes === 0) {
|
||||
return res.status(404).json({ error: 'Alert not found' });
|
||||
}
|
||||
|
||||
res.status(204).end();
|
||||
} catch (error) {
|
||||
console.error('Error deleting alert:', 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();
|
||||
|
||||
Reference in New Issue
Block a user