diff --git a/backend/src/tools/dateien/routes.js b/backend/src/tools/dateien/routes.js new file mode 100644 index 0000000..c95576c --- /dev/null +++ b/backend/src/tools/dateien/routes.js @@ -0,0 +1,136 @@ +const express = require('express'); +const multer = require('multer'); +const path = require('path'); +const fs = require('fs'); +const db = require('../../db'); +const { authenticate, requireAdmin } = require('../../middleware/auth'); + +const router = express.Router(); +const UPLOAD_DIR = process.env.UPLOAD_DIR || '/data/uploads'; +if (!fs.existsSync(UPLOAD_DIR)) fs.mkdirSync(UPLOAD_DIR, { recursive: true }); + +const getSetting = key => db.prepare('SELECT value FROM admin_settings WHERE key=?').get(key)?.value; + +const storage = multer.diskStorage({ + destination: UPLOAD_DIR, + filename: (req, file, cb) => cb(null, `${Date.now()}-${Math.random().toString(36).slice(2)}${path.extname(file.originalname)}`), +}); + +const upload = multer({ + storage, + limits: { fileSize: () => parseInt(getSetting('file_max_size_mb') || '50') * 1024 * 1024 }, + fileFilter: (req, file, cb) => { + const allowed = (getSetting('file_allowed_ext') || '').split(',').map(e => e.trim().toLowerCase()); + const ext = path.extname(file.originalname).toLowerCase(); + if (!allowed.length || allowed.includes(ext)) cb(null, true); + else cb(new Error(`Dateityp nicht erlaubt: ${ext}`)); + }, +}); + +// GET / – eigene Dateien + freigegebene +router.get('/', authenticate, (req, res) => { + const uid = req.user.id; + const own = db.prepare('SELECT f.*, u.username as owner FROM files f JOIN users u ON u.id=f.user_id WHERE f.user_id=? ORDER BY f.created_at DESC').all(uid); + const shared = db.prepare(` + SELECT f.*, u.username as owner, s.shared_by, sb.username as shared_by_name + FROM files f + JOIN users u ON u.id=f.user_id + JOIN file_shares s ON s.file_id=f.id + JOIN users sb ON sb.id=s.shared_by + WHERE s.shared_with=? + ORDER BY f.created_at DESC + `).all(uid); + res.json({ own, shared }); +}); + +// POST / – Datei hochladen +router.post('/', authenticate, (req, res) => { + const uid = req.user.id; + const maxCount = parseInt(getSetting('file_max_count') || '20'); + const count = db.prepare('SELECT COUNT(*) c FROM files WHERE user_id=?').get(uid).c; + if (count >= maxCount) return res.status(400).json({ error: `Maximum ${maxCount} Dateien erlaubt` }); + + upload.single('file')(req, res, err => { + if (err) return res.status(400).json({ error: err.message }); + if (!req.file) return res.status(400).json({ error: 'Keine Datei' }); + const r = db.prepare('INSERT INTO files (user_id,filename,originalname,mimetype,size) VALUES (?,?,?,?,?)') + .run(uid, req.file.filename, req.file.originalname, req.file.mimetype || 'application/octet-stream', req.file.size); + res.json(db.prepare('SELECT f.*, u.username as owner FROM files f JOIN users u ON u.id=f.user_id WHERE f.id=?').get(r.lastInsertRowid)); + }); +}); + +// GET /:id/download +router.get('/:id/download', authenticate, (req, res) => { + const uid = req.user.id; + const file = db.prepare('SELECT * FROM files WHERE id=?').get(req.params.id); + if (!file) return res.status(404).json({ error: 'Nicht gefunden' }); + // Zugriff: eigene Datei oder freigegeben + const hasAccess = file.user_id === uid || + db.prepare('SELECT id FROM file_shares WHERE file_id=? AND shared_with=?').get(file.id, uid); + if (!hasAccess) return res.status(403).json({ error: 'Kein Zugriff' }); + const filePath = path.join(UPLOAD_DIR, file.filename); + if (!fs.existsSync(filePath)) return res.status(404).json({ error: 'Datei fehlt auf Server' }); + res.download(filePath, file.originalname); +}); + +// GET /:id/shares – wer hat Zugriff auf diese Datei +router.get('/:id/shares', authenticate, (req, res) => { + const file = db.prepare('SELECT * FROM files WHERE id=? AND user_id=?').get(req.params.id, req.user.id); + if (!file) return res.status(404).json({ error: 'Nicht gefunden' }); + const shares = db.prepare(` + SELECT u.id, u.username FROM file_shares s + JOIN users u ON u.id=s.shared_with + WHERE s.file_id=? + `).all(file.id); + res.json(shares); +}); + +// DELETE /:id – nur eigene Dateien +router.delete('/:id', authenticate, (req, res) => { + const file = db.prepare('SELECT * FROM files WHERE id=? AND user_id=?').get(req.params.id, req.user.id); + if (!file) return res.status(404).json({ error: 'Nicht gefunden' }); + try { fs.unlinkSync(path.join(UPLOAD_DIR, file.filename)); } catch {} + db.prepare('DELETE FROM files WHERE id=?').run(file.id); + res.json({ success: true }); +}); + +// POST /:id/share – Datei freigeben +router.post('/:id/share', authenticate, (req, res) => { + const file = db.prepare('SELECT * FROM files WHERE id=? AND user_id=?').get(req.params.id, req.user.id); + if (!file) return res.status(404).json({ error: 'Nicht gefunden oder kein Zugriff' }); + const { username } = req.body; + const target = db.prepare('SELECT * FROM users WHERE username=?').get(username); + if (!target) return res.status(404).json({ error: `Benutzer "${username}" nicht gefunden` }); + if (target.id === req.user.id) return res.status(400).json({ error: 'Kannst du nicht mit dir selbst teilen' }); + try { + db.prepare('INSERT OR IGNORE INTO file_shares (file_id,shared_by,shared_with) VALUES (?,?,?)').run(file.id, req.user.id, target.id); + res.json({ success: true }); + } catch(e) { res.status(400).json({ error: e.message }); } +}); + +// DELETE /:id/share/:userId – Freigabe entfernen +router.delete('/:id/share/:userId', authenticate, (req, res) => { + const file = db.prepare('SELECT * FROM files WHERE id=? AND user_id=?').get(req.params.id, req.user.id); + if (!file) return res.status(404).json({ error: 'Nicht gefunden' }); + db.prepare('DELETE FROM file_shares WHERE file_id=? AND shared_with=?').run(file.id, req.params.userId); + res.json({ success: true }); +}); + +// GET /settings – Admin-Einstellungen lesen +router.get('/settings', authenticate, requireAdmin, (req, res) => { + const s = db.prepare('SELECT * FROM admin_settings').all(); + const obj = {}; for (const r of s) obj[r.key] = r.value; + res.json(obj); +}); + +// PUT /settings – Admin-Einstellungen speichern +router.put('/settings', authenticate, requireAdmin, (req, res) => { + const allowed = ['file_max_size_mb','file_max_count','file_allowed_ext']; + for (const key of allowed) { + if (req.body[key] !== undefined) + db.prepare('INSERT OR REPLACE INTO admin_settings (key,value) VALUES (?,?)').run(key, String(req.body[key])); + } + res.json({ success: true }); +}); + +module.exports = router;