Dateien nach "backend/src/tools/nachrichten" hochladen
This commit is contained in:
171
backend/src/tools/nachrichten/routes.js
Normal file
171
backend/src/tools/nachrichten/routes.js
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const db = require('../../db');
|
||||||
|
const { authenticate } = require('../../middleware/auth');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
// ── Pushover Helper ───────────────────────────────────────────────────────────
|
||||||
|
async function sendPushover(userKey, appToken, title, message) {
|
||||||
|
try {
|
||||||
|
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 }),
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Public Keys ───────────────────────────────────────────────────────────────
|
||||||
|
router.get('/keys/:userId', authenticate, (req, res) => {
|
||||||
|
const row = db.prepare('SELECT public_key FROM user_keys WHERE user_id = ?').get(req.params.userId);
|
||||||
|
res.json({ public_key: row?.public_key || null });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/keys', authenticate, (req, res) => {
|
||||||
|
const { public_key } = req.body;
|
||||||
|
if (!public_key) return res.status(400).json({ error: 'Kein Schlüssel' });
|
||||||
|
db.prepare(`
|
||||||
|
INSERT INTO user_keys (user_id, public_key, updated_at)
|
||||||
|
VALUES (?, ?, CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT(user_id) DO UPDATE SET public_key=excluded.public_key, updated_at=CURRENT_TIMESTAMP
|
||||||
|
`).run(req.user.id, public_key);
|
||||||
|
res.json({ ok: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Users ─────────────────────────────────────────────────────────────────────
|
||||||
|
router.get('/users', authenticate, (req, res) => {
|
||||||
|
const me = req.user.id;
|
||||||
|
const users = db.prepare(`
|
||||||
|
SELECT u.id, u.username, uk.public_key,
|
||||||
|
(SELECT COUNT(*) FROM messages
|
||||||
|
WHERE sender_id = u.id AND recipient_id = ? AND read_by_recipient = 0 AND deleted_by_recipient = 0) AS unread,
|
||||||
|
(SELECT MAX(created_at) FROM messages
|
||||||
|
WHERE ((sender_id = u.id AND recipient_id = ? AND deleted_by_recipient = 0)
|
||||||
|
OR (sender_id = ? AND recipient_id = u.id AND deleted_by_sender = 0))) AS last_message_at
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN user_keys uk ON uk.user_id = u.id
|
||||||
|
WHERE u.id != ?
|
||||||
|
ORDER BY last_message_at DESC, u.username ASC
|
||||||
|
`).all(me, me, me, me);
|
||||||
|
res.json(users);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Unread Count ──────────────────────────────────────────────────────────────
|
||||||
|
router.get('/unread', authenticate, (req, res) => {
|
||||||
|
const row = db.prepare(
|
||||||
|
'SELECT COUNT(*) AS count FROM messages WHERE recipient_id = ? AND read_by_recipient = 0 AND deleted_by_recipient = 0'
|
||||||
|
).get(req.user.id);
|
||||||
|
res.json({ count: row.count });
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Messages ──────────────────────────────────────────────────────────────────
|
||||||
|
router.get('/messages/:userId', authenticate, (req, res) => {
|
||||||
|
const me = req.user.id;
|
||||||
|
const other = parseInt(req.params.userId);
|
||||||
|
|
||||||
|
// Mark incoming as read
|
||||||
|
db.prepare(`
|
||||||
|
UPDATE messages SET read_by_recipient = 1
|
||||||
|
WHERE recipient_id = ? AND sender_id = ? AND read_by_recipient = 0
|
||||||
|
`).run(me, other);
|
||||||
|
|
||||||
|
const msgs = db.prepare(`
|
||||||
|
SELECT m.id, m.sender_id, m.encrypted_content, m.iv, m.created_at,
|
||||||
|
(m.sender_id = ?) AS is_mine
|
||||||
|
FROM messages m
|
||||||
|
WHERE (m.sender_id = ? AND m.recipient_id = ? AND m.deleted_by_sender = 0)
|
||||||
|
OR (m.recipient_id = ? AND m.sender_id = ? AND m.deleted_by_recipient = 0)
|
||||||
|
ORDER BY m.created_at ASC
|
||||||
|
`).all(me, me, other, me, other);
|
||||||
|
|
||||||
|
res.json(msgs);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/messages/:userId', authenticate, async (req, res) => {
|
||||||
|
const me = req.user.id;
|
||||||
|
const recipId = parseInt(req.params.userId);
|
||||||
|
const { encrypted_content, iv } = req.body;
|
||||||
|
if (!encrypted_content || !iv) return res.status(400).json({ error: 'Fehlende Felder' });
|
||||||
|
|
||||||
|
const recipient = db.prepare('SELECT id FROM users WHERE id = ?').get(recipId);
|
||||||
|
if (!recipient) return res.status(404).json({ error: 'Benutzer nicht gefunden' });
|
||||||
|
|
||||||
|
const result = db.prepare(`
|
||||||
|
INSERT INTO messages (sender_id, recipient_id, encrypted_content, iv)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
`).run(me, recipId, encrypted_content, iv);
|
||||||
|
|
||||||
|
// Pushover notification
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ id: result.lastInsertRowid });
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE single message (own side only)
|
||||||
|
router.delete('/messages/:messageId', authenticate, (req, res) => {
|
||||||
|
const me = req.user.id;
|
||||||
|
const mid = parseInt(req.params.messageId);
|
||||||
|
const msg = db.prepare('SELECT * FROM messages WHERE id = ?').get(mid);
|
||||||
|
if (!msg) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||||
|
if (msg.sender_id !== me && msg.recipient_id !== me)
|
||||||
|
return res.status(403).json({ error: 'Kein Zugriff' });
|
||||||
|
|
||||||
|
if (msg.sender_id === me) {
|
||||||
|
db.prepare('UPDATE messages SET deleted_by_sender = 1 WHERE id = ?').run(mid);
|
||||||
|
} else {
|
||||||
|
db.prepare('UPDATE messages SET deleted_by_recipient = 1 WHERE id = ?').run(mid);
|
||||||
|
}
|
||||||
|
// Physical delete when both sides have deleted
|
||||||
|
db.prepare('DELETE FROM messages WHERE id = ? AND deleted_by_sender = 1 AND deleted_by_recipient = 1').run(mid);
|
||||||
|
res.json({ ok: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE entire conversation (own side)
|
||||||
|
router.delete('/conversation/:userId', authenticate, (req, res) => {
|
||||||
|
const me = req.user.id;
|
||||||
|
const other = parseInt(req.params.userId);
|
||||||
|
|
||||||
|
db.prepare('UPDATE messages SET deleted_by_sender = 1 WHERE sender_id = ? AND recipient_id = ?').run(me, other);
|
||||||
|
db.prepare('UPDATE messages SET deleted_by_recipient = 1 WHERE recipient_id = ? AND sender_id = ?').run(me, other);
|
||||||
|
// Physical cleanup where both deleted
|
||||||
|
db.prepare(`
|
||||||
|
DELETE FROM messages
|
||||||
|
WHERE deleted_by_sender = 1 AND deleted_by_recipient = 1
|
||||||
|
AND ((sender_id = ? AND recipient_id = ?) OR (sender_id = ? AND recipient_id = ?))
|
||||||
|
`).run(me, other, other, me);
|
||||||
|
res.json({ ok: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── 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: '' });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/pushover', authenticate, (req, res) => {
|
||||||
|
const { user_key, app_token } = 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);
|
||||||
|
res.json({ ok: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/pushover', authenticate, (req, res) => {
|
||||||
|
db.prepare('DELETE FROM pushover_settings WHERE user_id = ?').run(req.user.id);
|
||||||
|
res.json({ ok: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
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! 🎉');
|
||||||
|
res.json({ ok: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
Reference in New Issue
Block a user