Pushover: Emergency-Priorität mit Retry, has_pushover Badge in Admin
This commit is contained in:
@@ -315,6 +315,13 @@ if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='boa
|
||||
`);
|
||||
}
|
||||
|
||||
// Migration: Pushover retry/expire
|
||||
const poCols = db.prepare("PRAGMA table_info(pushover_settings)").all().map(r => r.name);
|
||||
if (poCols.length && !poCols.includes('retry'))
|
||||
db.exec("ALTER TABLE pushover_settings ADD COLUMN retry INTEGER DEFAULT NULL");
|
||||
if (poCols.length && !poCols.includes('expire'))
|
||||
db.exec("ALTER TABLE pushover_settings ADD COLUMN expire INTEGER DEFAULT NULL");
|
||||
|
||||
// ── Push-Zeitplaner ───────────────────────────────────────────────────────────
|
||||
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='push_schedules'").get()) {
|
||||
db.exec(`
|
||||
|
||||
@@ -89,12 +89,18 @@ app.listen(4000, () => console.log('🚀 Dicken Dock läuft auf Port 4000'));
|
||||
// ── Push-Zeitplaner Hintergrund-Job ──────────────────────────────────────────
|
||||
const db = require('./db');
|
||||
|
||||
async function sendPushoverMsg(userKey, appToken, message) {
|
||||
async function sendPushoverMsg(userKey, appToken, message, opts = {}) {
|
||||
try {
|
||||
const params = { token: appToken, user: userKey, title: 'DickenDock Erinnerung', message };
|
||||
if (opts.retry && opts.expire) {
|
||||
params.priority = 2;
|
||||
params.retry = opts.retry;
|
||||
params.expire = opts.expire;
|
||||
}
|
||||
await fetch('https://api.pushover.net/1/messages.json', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({ token: appToken, user: userKey, title: 'DickenDock Erinnerung', message }),
|
||||
body: new URLSearchParams(params),
|
||||
});
|
||||
} catch(e) { console.error('Pushover Fehler:', e.message); }
|
||||
}
|
||||
@@ -110,13 +116,14 @@ async function runScheduler() {
|
||||
try {
|
||||
const now = db.prepare("SELECT datetime('now','localtime') as t").get().t;
|
||||
const due = db.prepare(`
|
||||
SELECT s.*, p.user_key, p.app_token
|
||||
SELECT s.*, p.user_key, p.app_token, p.retry, p.expire
|
||||
FROM push_schedules s
|
||||
JOIN pushover_settings p ON p.user_id = s.user_id
|
||||
WHERE s.sent = 0 AND s.scheduled_at <= ?
|
||||
`).all(now);
|
||||
for (const row of due) {
|
||||
await sendPushoverMsg(row.user_key, row.app_token, row.message);
|
||||
await sendPushoverMsg(row.user_key, row.app_token, row.message,
|
||||
{ retry: row.retry, expire: row.expire });
|
||||
db.prepare('UPDATE push_schedules SET sent=1 WHERE id=?').run(row.id);
|
||||
console.log(`✓ Push gesendet an user ${row.user_id}: "${row.message}" (fällig: ${row.scheduled_at})`);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,13 @@ router.post('/restore', authenticate, requireAdmin, upload.single('database'), (
|
||||
|
||||
// GET /api/admin/users – alle Benutzer mit Statistiken
|
||||
router.get('/users', authenticate, requireAdmin, (req, res) => {
|
||||
const users = db.prepare("SELECT id, username, role, created_at, last_active_at FROM users ORDER BY role DESC, username").all();
|
||||
const users = db.prepare(`
|
||||
SELECT u.id, u.username, u.role, u.created_at, u.last_active_at,
|
||||
CASE WHEN p.user_id IS NOT NULL THEN 1 ELSE 0 END as has_pushover
|
||||
FROM users u
|
||||
LEFT JOIN pushover_settings p ON p.user_id = u.id
|
||||
ORDER BY u.role DESC, u.username
|
||||
`).all();
|
||||
const stats = users.map(u => {
|
||||
const archiv = db.prepare('SELECT COUNT(*) c FROM calculations WHERE user_id=?').get(u.id).c;
|
||||
const bestellung = db.prepare('SELECT COUNT(*) c FROM orders WHERE user_id=?').get(u.id).c;
|
||||
|
||||
@@ -4,12 +4,18 @@ const { authenticate } = require('../../middleware/auth');
|
||||
const router = express.Router();
|
||||
|
||||
// ── Pushover Helper ───────────────────────────────────────────────────────────
|
||||
async function sendPushover(userKey, appToken, title, message) {
|
||||
async function sendPushover(userKey, appToken, title, message, opts = {}) {
|
||||
try {
|
||||
const params = { token: appToken, user: userKey, title, message };
|
||||
if (opts.retry && opts.expire) {
|
||||
params.priority = 2;
|
||||
params.retry = opts.retry;
|
||||
params.expire = opts.expire;
|
||||
}
|
||||
await fetch('https://api.pushover.net/1/messages.json', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({ token: appToken, user: userKey, title, message }),
|
||||
body: new URLSearchParams(params),
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
@@ -96,7 +102,8 @@ router.post('/messages/:userId', authenticate, async (req, res) => {
|
||||
const senderName = db.prepare('SELECT username FROM users WHERE id = ?').get(me)?.username || 'Jemand';
|
||||
const pushover = db.prepare('SELECT * FROM pushover_settings WHERE user_id = ?').get(recipId);
|
||||
if (pushover) {
|
||||
await sendPushover(pushover.user_key, pushover.app_token, 'DickenDock', `Neue Nachricht von ${senderName}`);
|
||||
await sendPushover(pushover.user_key, pushover.app_token, 'DickenDock', `Neue Nachricht von ${senderName}`,
|
||||
{ retry: pushover.retry, expire: pushover.expire });
|
||||
}
|
||||
|
||||
res.json({ id: result.lastInsertRowid });
|
||||
@@ -132,18 +139,19 @@ router.delete('/my-messages', authenticate, (req, res) => {
|
||||
|
||||
// ── Pushover Settings ─────────────────────────────────────────────────────────
|
||||
router.get('/pushover', authenticate, (req, res) => {
|
||||
const row = db.prepare('SELECT user_key, app_token FROM pushover_settings WHERE user_id = ?').get(req.user.id);
|
||||
res.json(row || { user_key: '', app_token: '' });
|
||||
const row = db.prepare('SELECT user_key, app_token, retry, expire FROM pushover_settings WHERE user_id = ?').get(req.user.id);
|
||||
res.json(row || { user_key: '', app_token: '', retry: null, expire: null });
|
||||
});
|
||||
|
||||
router.post('/pushover', authenticate, (req, res) => {
|
||||
const { user_key, app_token } = req.body;
|
||||
const { user_key, app_token, retry, expire } = req.body;
|
||||
if (!user_key || !app_token) return res.status(400).json({ error: 'Beide Felder erforderlich' });
|
||||
db.prepare(`
|
||||
INSERT INTO pushover_settings (user_id, user_key, app_token)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET user_key=excluded.user_key, app_token=excluded.app_token
|
||||
`).run(req.user.id, user_key, app_token);
|
||||
INSERT INTO pushover_settings (user_id, user_key, app_token, retry, expire)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET user_key=excluded.user_key, app_token=excluded.app_token,
|
||||
retry=excluded.retry, expire=excluded.expire
|
||||
`).run(req.user.id, user_key, app_token, retry || null, expire || null);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
@@ -155,7 +163,12 @@ router.delete('/pushover', authenticate, (req, res) => {
|
||||
router.post('/pushover/test', authenticate, async (req, res) => {
|
||||
const row = db.prepare('SELECT * FROM pushover_settings WHERE user_id = ?').get(req.user.id);
|
||||
if (!row) return res.status(400).json({ error: 'Keine Pushover-Einstellungen gespeichert' });
|
||||
await sendPushover(row.user_key, row.app_token, 'DickenDock', 'Pushover-Verbindung erfolgreich! 🎉');
|
||||
const useEmergency = req.body?.emergency === true && row.retry && row.expire;
|
||||
const msg = useEmergency
|
||||
? `🚨 Emergency-Test! Wiederholt alle ${row.retry}s für max. ${row.expire}s. Bitte in Pushover quittieren.`
|
||||
: 'Pushover-Verbindung erfolgreich! 🎉';
|
||||
await sendPushover(row.user_key, row.app_token, 'DickenDock', msg,
|
||||
useEmergency ? { retry: row.retry, expire: row.expire } : {});
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user