Notify to PGSql instead of SQLite
This commit is contained in:
+72
-33
@@ -253,6 +253,41 @@ async function initDatabase() {
|
||||
`);
|
||||
}
|
||||
|
||||
// Push subscription tables — PG when available, SQLite fallback
|
||||
if (pgPool) {
|
||||
await pgPool.query(`
|
||||
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
medicine_nregistro TEXT NOT NULL,
|
||||
medicine_name TEXT,
|
||||
endpoint TEXT NOT NULL,
|
||||
p256dh TEXT NOT NULL,
|
||||
auth TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(user_id, medicine_nregistro, endpoint)
|
||||
)
|
||||
`);
|
||||
await pgPool.query(`CREATE INDEX IF NOT EXISTS idx_push_subscriptions_med ON push_subscriptions(medicine_nregistro)`);
|
||||
await pgPool.query(`CREATE INDEX IF NOT EXISTS idx_push_subscriptions_user ON push_subscriptions(user_id)`);
|
||||
|
||||
await pgPool.query(`
|
||||
CREATE TABLE IF NOT EXISTS push_subscriptions_pharmacy (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
medicine_nregistro TEXT NOT NULL,
|
||||
medicine_name TEXT,
|
||||
pharmacy_id INTEGER NOT NULL,
|
||||
endpoint TEXT NOT NULL,
|
||||
p256dh TEXT NOT NULL,
|
||||
auth TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(user_id, medicine_nregistro, pharmacy_id, endpoint)
|
||||
)
|
||||
`);
|
||||
await pgPool.query(`CREATE INDEX IF NOT EXISTS idx_push_subs_pharm ON push_subscriptions_pharmacy(medicine_nregistro, pharmacy_id)`);
|
||||
await pgPool.query(`CREATE INDEX IF NOT EXISTS idx_push_subs_pharm_user ON push_subscriptions_pharmacy(user_id)`);
|
||||
} else {
|
||||
await dbRun(`
|
||||
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -281,6 +316,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) {
|
||||
// SQLite-only migrations for existing deployments
|
||||
@@ -307,9 +343,11 @@ if (!pgPool) {
|
||||
try { await dbRun('ALTER TABLE users ADD COLUMN city TEXT'); } catch {}
|
||||
}
|
||||
|
||||
// Add user_id to push tables (SQLite — no cross-DB FK)
|
||||
// Add user_id to push tables (SQLite only — PG tables already have it)
|
||||
if (!pgPool) {
|
||||
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 {}
|
||||
}
|
||||
|
||||
// Create alerts table for persistent Avisos data
|
||||
try {
|
||||
@@ -1733,35 +1771,35 @@ app.post('/api/notifications/subscribe', requireAuth, async (req, res) => {
|
||||
}
|
||||
|
||||
if (pharmacy_id) {
|
||||
const existing = await dbGet(
|
||||
'SELECT id FROM push_subscriptions_pharmacy WHERE medicine_nregistro = ? AND pharmacy_id = ? AND endpoint = ?',
|
||||
[medicine_nregistro, pharmacy_id, endpoint]
|
||||
const existing = await userDbGet(
|
||||
'SELECT id FROM push_subscriptions_pharmacy WHERE user_id = ? AND medicine_nregistro = ? AND pharmacy_id = ? AND endpoint = ?',
|
||||
[userId, medicine_nregistro, pharmacy_id, endpoint]
|
||||
);
|
||||
if (existing) {
|
||||
await dbRun(
|
||||
'UPDATE push_subscriptions_pharmacy SET medicine_name = ?, p256dh = ?, auth = ?, user_id = ? WHERE id = ?',
|
||||
[medicine_name || null, p256dh, auth, userId, existing.id]
|
||||
await userDbRun(
|
||||
'UPDATE push_subscriptions_pharmacy SET medicine_name = ?, p256dh = ?, auth = ? WHERE id = ? AND user_id = ?',
|
||||
[medicine_name || null, p256dh, auth, existing.id, userId]
|
||||
);
|
||||
} else {
|
||||
await dbRun(
|
||||
'INSERT INTO push_subscriptions_pharmacy (medicine_nregistro, medicine_name, pharmacy_id, endpoint, p256dh, auth, user_id) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
||||
[medicine_nregistro, medicine_name || null, pharmacy_id, endpoint, p256dh, auth, userId]
|
||||
await userDbRun(
|
||||
'INSERT INTO push_subscriptions_pharmacy (user_id, medicine_nregistro, medicine_name, pharmacy_id, endpoint, p256dh, auth) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
||||
[userId, medicine_nregistro, medicine_name || null, pharmacy_id, endpoint, p256dh, auth]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const existing = await dbGet(
|
||||
'SELECT id FROM push_subscriptions WHERE medicine_nregistro = ? AND endpoint = ?',
|
||||
[medicine_nregistro, endpoint]
|
||||
const existing = await userDbGet(
|
||||
'SELECT id FROM push_subscriptions WHERE user_id = ? AND medicine_nregistro = ? AND endpoint = ?',
|
||||
[userId, medicine_nregistro, endpoint]
|
||||
);
|
||||
if (existing) {
|
||||
await dbRun(
|
||||
'UPDATE push_subscriptions SET medicine_name = ?, p256dh = ?, auth = ?, user_id = ? WHERE id = ?',
|
||||
[medicine_name || null, p256dh, auth, userId, existing.id]
|
||||
await userDbRun(
|
||||
'UPDATE push_subscriptions SET medicine_name = ?, p256dh = ?, auth = ? WHERE id = ? AND user_id = ?',
|
||||
[medicine_name || null, p256dh, auth, existing.id, userId]
|
||||
);
|
||||
} else {
|
||||
await dbRun(
|
||||
'INSERT INTO push_subscriptions (medicine_nregistro, medicine_name, endpoint, p256dh, auth, user_id) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[medicine_nregistro, medicine_name || null, endpoint, p256dh, auth, userId]
|
||||
await userDbRun(
|
||||
'INSERT INTO push_subscriptions (user_id, medicine_nregistro, medicine_name, endpoint, p256dh, auth) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[userId, medicine_nregistro, medicine_name || null, endpoint, p256dh, auth]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1776,21 +1814,22 @@ app.post('/api/notifications/subscribe', requireAuth, async (req, res) => {
|
||||
app.delete('/api/notifications/unsubscribe', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const { medicine_nregistro, endpoint, pharmacy_id } = req.body || {};
|
||||
const userId = req.session.userId;
|
||||
if (!endpoint) {
|
||||
return res.status(400).json({ error: 'endpoint is required' });
|
||||
}
|
||||
if (pharmacy_id) {
|
||||
await dbRun(
|
||||
'DELETE FROM push_subscriptions_pharmacy WHERE medicine_nregistro = ? AND pharmacy_id = ? AND endpoint = ?',
|
||||
[medicine_nregistro, pharmacy_id, endpoint]
|
||||
await userDbRun(
|
||||
'DELETE FROM push_subscriptions_pharmacy WHERE user_id = ? AND medicine_nregistro = ? AND pharmacy_id = ? AND endpoint = ?',
|
||||
[userId, medicine_nregistro, pharmacy_id, endpoint]
|
||||
);
|
||||
} else if (medicine_nregistro) {
|
||||
await dbRun(
|
||||
'DELETE FROM push_subscriptions WHERE medicine_nregistro = ? AND endpoint = ?',
|
||||
[medicine_nregistro, endpoint]
|
||||
await userDbRun(
|
||||
'DELETE FROM push_subscriptions WHERE user_id = ? AND medicine_nregistro = ? AND endpoint = ?',
|
||||
[userId, medicine_nregistro, endpoint]
|
||||
);
|
||||
} else {
|
||||
await dbRun('DELETE FROM push_subscriptions WHERE endpoint = ?', [endpoint]);
|
||||
await userDbRun('DELETE FROM push_subscriptions WHERE user_id = ? AND endpoint = ?', [userId, endpoint]);
|
||||
}
|
||||
res.status(204).end();
|
||||
} catch (error) {
|
||||
@@ -1803,14 +1842,14 @@ app.delete('/api/notifications/unsubscribe', requireAuth, async (req, res) => {
|
||||
app.get('/api/notifications/mine', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const globalRows = await dbAll(
|
||||
const globalRows = await userDbAll(
|
||||
`SELECT id, medicine_nregistro, medicine_name, created_at
|
||||
FROM push_subscriptions
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC`,
|
||||
[userId]
|
||||
);
|
||||
const pharmacyRows = await dbAll(
|
||||
const pharmacyRows = await userDbAll(
|
||||
`SELECT ps.id, ps.medicine_nregistro, ps.medicine_name, ps.pharmacy_id,
|
||||
ps.created_at, p.name AS pharmacy_name, p.address AS pharmacy_address
|
||||
FROM push_subscriptions_pharmacy ps
|
||||
@@ -1839,12 +1878,12 @@ app.delete('/api/notifications/mine', requireAuth, async (req, res) => {
|
||||
return res.status(400).json({ error: 'id must be a positive integer' });
|
||||
}
|
||||
if (scope === 'pharmacy') {
|
||||
await dbRun(
|
||||
await userDbRun(
|
||||
'DELETE FROM push_subscriptions_pharmacy WHERE id = ? AND user_id = ?',
|
||||
[rowId, userId]
|
||||
);
|
||||
} else if (scope === 'global') {
|
||||
await dbRun(
|
||||
await userDbRun(
|
||||
'DELETE FROM push_subscriptions WHERE id = ? AND user_id = ?',
|
||||
[rowId, userId]
|
||||
);
|
||||
@@ -1861,11 +1900,11 @@ app.delete('/api/notifications/mine', requireAuth, async (req, res) => {
|
||||
async function sendPushForMedicine({ medicine_nregistro, medicine_name, pharmacy }) {
|
||||
if (!PUSH_ENABLED) return { sent: 0, failed: 0, pruned: 0 };
|
||||
|
||||
const globalSubs = await dbAll(
|
||||
const globalSubs = await userDbAll(
|
||||
'SELECT id, endpoint, p256dh, auth FROM push_subscriptions WHERE medicine_nregistro = ?',
|
||||
[medicine_nregistro]
|
||||
);
|
||||
const pharmacySubs = pharmacy?.id ? await dbAll(
|
||||
const pharmacySubs = pharmacy?.id ? await userDbAll(
|
||||
'SELECT id, endpoint, p256dh, auth FROM push_subscriptions_pharmacy WHERE medicine_nregistro = ? AND pharmacy_id = ?',
|
||||
[medicine_nregistro, pharmacy.id]
|
||||
) : [];
|
||||
@@ -1907,7 +1946,7 @@ async function sendPushForMedicine({ medicine_nregistro, medicine_name, pharmacy
|
||||
const deleteQuery = sub._pharmSub
|
||||
? 'DELETE FROM push_subscriptions_pharmacy WHERE id = ?'
|
||||
: 'DELETE FROM push_subscriptions WHERE id = ?';
|
||||
await dbRun(deleteQuery, [sub.id]);
|
||||
await userDbRun(deleteQuery, [sub.id]);
|
||||
pruned++;
|
||||
} catch {}
|
||||
} else {
|
||||
|
||||
+56
-17
@@ -1,8 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DeviceStreaming">
|
||||
<option name="defaultDeviceApplied" value="true" />
|
||||
<option name="deviceSelectionList">
|
||||
<list>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="redfin" />
|
||||
<option name="id" value="redfin" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 5" />
|
||||
<option name="screenDensity" value="440" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
<option name="selected" value="true" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="Sony" />
|
||||
@@ -171,6 +190,18 @@
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="a03sutfn" />
|
||||
<option name="id" value="a03sutfn" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy A03s" />
|
||||
<option name="screenDensity" value="280" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="35" />
|
||||
<option name="brand" value="samsung" />
|
||||
@@ -531,6 +562,18 @@
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3088" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="b4qsqw" />
|
||||
<option name="id" value="b4qsqw" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Z Flip4" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2640" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
@@ -1681,6 +1724,18 @@
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="r0qksx" />
|
||||
<option name="id" value="r0qksx" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S22" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="google" />
|
||||
@@ -1785,23 +1840,6 @@
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="redfin" />
|
||||
<option name="id" value="redfin" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 5" />
|
||||
<option name="screenDensity" value="440" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="accessStatus">
|
||||
<list>
|
||||
@@ -2186,5 +2224,6 @@
|
||||
</PersistentDeviceSelectionData>
|
||||
</list>
|
||||
</option>
|
||||
<option name="selectedCloudProject" value="farmaclic-53c42" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+1
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Reference in New Issue
Block a user