Dateien nach "backend/src/tools/dateien" hochladen
This commit is contained in:
@@ -15,26 +15,109 @@ const storage = multer.diskStorage({
|
|||||||
destination: UPLOAD_DIR,
|
destination: UPLOAD_DIR,
|
||||||
filename: (req, file, cb) => cb(null, `${Date.now()}-${Math.random().toString(36).slice(2)}${path.extname(file.originalname)}`),
|
filename: (req, file, cb) => cb(null, `${Date.now()}-${Math.random().toString(36).slice(2)}${path.extname(file.originalname)}`),
|
||||||
});
|
});
|
||||||
|
const getUpload = () => multer({
|
||||||
const upload = multer({
|
|
||||||
storage,
|
storage,
|
||||||
limits: { fileSize: () => parseInt(getSetting('file_max_size_mb') || '50') * 1024 * 1024 },
|
limits: { fileSize: parseInt(getSetting('file_max_size_mb') || '50') * 1024 * 1024 },
|
||||||
fileFilter: (req, file, cb) => {
|
fileFilter: (req, file, cb) => {
|
||||||
const allowed = (getSetting('file_allowed_ext') || '').split(',').map(e => e.trim().toLowerCase());
|
const allowed = (getSetting('file_allowed_ext') || '').split(',').map(e => e.trim().toLowerCase()).filter(Boolean);
|
||||||
|
if (!allowed.length) return cb(null, true);
|
||||||
const ext = path.extname(file.originalname).toLowerCase();
|
const ext = path.extname(file.originalname).toLowerCase();
|
||||||
if (!allowed.length || allowed.includes(ext)) cb(null, true);
|
if (allowed.includes(ext)) cb(null, true);
|
||||||
else cb(new Error(`Dateityp nicht erlaubt: ${ext}`));
|
else cb(new Error(`Dateityp nicht erlaubt: ${ext}. Erlaubt: ${allowed.join(', ')}`));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET / – eigene Dateien + freigegebene
|
const hasFileAccess = (fileId, userId) => {
|
||||||
|
const f = db.prepare('SELECT * FROM files WHERE id=?').get(fileId);
|
||||||
|
if (!f) return null;
|
||||||
|
if (f.user_id === userId) return f;
|
||||||
|
if (db.prepare('SELECT id FROM file_shares WHERE file_id=? AND shared_with=?').get(fileId, userId)) return f;
|
||||||
|
// Check folder share
|
||||||
|
if (f.folder_id && db.prepare('SELECT id FROM folder_shares WHERE folder_id=? AND shared_with=?').get(f.folder_id, userId)) return f;
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Ordner ────────────────────────────────────────────────────────────────────
|
||||||
|
// GET /folders?parent_id=
|
||||||
|
router.get('/folders', authenticate, (req, res) => {
|
||||||
|
const uid = req.user.id;
|
||||||
|
const parentId = req.query.parent_id || null;
|
||||||
|
const own = db.prepare('SELECT * FROM folders WHERE user_id=? AND parent_id IS ? ORDER BY name').all(uid, parentId);
|
||||||
|
// Shared folders at root level
|
||||||
|
const shared = parentId ? [] : db.prepare(`
|
||||||
|
SELECT f.*, u.username as owner, s.shared_by
|
||||||
|
FROM folders f
|
||||||
|
JOIN users u ON u.id=f.user_id
|
||||||
|
JOIN folder_shares s ON s.folder_id=f.id
|
||||||
|
WHERE s.shared_with=?
|
||||||
|
ORDER BY f.name
|
||||||
|
`).all(uid);
|
||||||
|
res.json({ own, shared });
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /folders
|
||||||
|
router.post('/folders', authenticate, (req, res) => {
|
||||||
|
const { name, parent_id=null } = req.body;
|
||||||
|
if (!name?.trim()) return res.status(400).json({ error: 'Name erforderlich' });
|
||||||
|
if (parent_id) {
|
||||||
|
const parent = db.prepare('SELECT * FROM folders WHERE id=? AND user_id=?').get(parent_id, req.user.id);
|
||||||
|
if (!parent) return res.status(404).json({ error: 'Überordner nicht gefunden' });
|
||||||
|
}
|
||||||
|
const r = db.prepare('INSERT INTO folders (user_id,name,parent_id) VALUES (?,?,?)').run(req.user.id, name.trim(), parent_id);
|
||||||
|
res.json(db.prepare('SELECT * FROM folders WHERE id=?').get(r.lastInsertRowid));
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /folders/:id
|
||||||
|
router.delete('/folders/:id', authenticate, (req, res) => {
|
||||||
|
const folder = db.prepare('SELECT * FROM folders WHERE id=? AND user_id=?').get(req.params.id, req.user.id);
|
||||||
|
if (!folder) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||||
|
// Delete all files inside
|
||||||
|
const files = db.prepare('SELECT * FROM files WHERE folder_id=? AND user_id=?').all(folder.id, req.user.id);
|
||||||
|
for (const f of files) {
|
||||||
|
try { fs.unlinkSync(path.join(UPLOAD_DIR, f.filename)); } catch {}
|
||||||
|
}
|
||||||
|
db.prepare('DELETE FROM files WHERE folder_id=? AND user_id=?').run(folder.id, req.user.id);
|
||||||
|
db.prepare('DELETE FROM folders WHERE id=?').run(folder.id);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /folders/:id/share
|
||||||
|
router.post('/folders/:id/share', authenticate, (req, res) => {
|
||||||
|
const folder = db.prepare('SELECT * FROM folders WHERE id=? AND user_id=?').get(req.params.id, req.user.id);
|
||||||
|
if (!folder) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||||
|
const target = db.prepare('SELECT * FROM users WHERE username=?').get(req.body.username);
|
||||||
|
if (!target) return res.status(404).json({ error: `Benutzer nicht gefunden` });
|
||||||
|
if (target.id === req.user.id) return res.status(400).json({ error: 'Kannst nicht mit dir selbst teilen' });
|
||||||
|
db.prepare('INSERT OR IGNORE INTO folder_shares (folder_id,shared_by,shared_with) VALUES (?,?,?)').run(folder.id, req.user.id, target.id);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// GET /folders/:id/shares
|
||||||
|
router.get('/folders/:id/shares', authenticate, (req, res) => {
|
||||||
|
const folder = db.prepare('SELECT * FROM folders WHERE id=? AND user_id=?').get(req.params.id, req.user.id);
|
||||||
|
if (!folder) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||||
|
const shares = db.prepare('SELECT u.id,u.username FROM folder_shares s JOIN users u ON u.id=s.shared_with WHERE s.folder_id=?').all(folder.id);
|
||||||
|
res.json(shares);
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /folders/:id/share/:userId
|
||||||
|
router.delete('/folders/:id/share/:userId', authenticate, (req, res) => {
|
||||||
|
const folder = db.prepare('SELECT * FROM folders WHERE id=? AND user_id=?').get(req.params.id, req.user.id);
|
||||||
|
if (!folder) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||||
|
db.prepare('DELETE FROM folder_shares WHERE folder_id=? AND shared_with=?').run(folder.id, req.params.userId);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Dateien ───────────────────────────────────────────────────────────────────
|
||||||
|
// GET /?folder_id=
|
||||||
router.get('/', authenticate, (req, res) => {
|
router.get('/', authenticate, (req, res) => {
|
||||||
const uid = req.user.id;
|
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 folderId = req.query.folder_id || null;
|
||||||
const shared = db.prepare(`
|
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=? AND f.folder_id IS ? ORDER BY f.created_at DESC').all(uid, folderId);
|
||||||
|
// Shared files (root only, folders handled separately)
|
||||||
|
const shared = folderId ? [] : db.prepare(`
|
||||||
SELECT f.*, u.username as owner, s.shared_by, sb.username as shared_by_name
|
SELECT f.*, u.username as owner, s.shared_by, sb.username as shared_by_name
|
||||||
FROM files f
|
FROM files f JOIN users u ON u.id=f.user_id
|
||||||
JOIN users u ON u.id=f.user_id
|
|
||||||
JOIN file_shares s ON s.file_id=f.id
|
JOIN file_shares s ON s.file_id=f.id
|
||||||
JOIN users sb ON sb.id=s.shared_by
|
JOIN users sb ON sb.id=s.shared_by
|
||||||
WHERE s.shared_with=?
|
WHERE s.shared_with=?
|
||||||
@@ -43,49 +126,32 @@ router.get('/', authenticate, (req, res) => {
|
|||||||
res.json({ own, shared });
|
res.json({ own, shared });
|
||||||
});
|
});
|
||||||
|
|
||||||
// POST / – Datei hochladen
|
// POST / – Upload
|
||||||
router.post('/', authenticate, (req, res) => {
|
router.post('/', authenticate, (req, res) => {
|
||||||
const uid = req.user.id;
|
const uid = req.user.id;
|
||||||
const maxCount = parseInt(getSetting('file_max_count') || '20');
|
const maxCount = parseInt(getSetting('file_max_count') || '20');
|
||||||
const count = db.prepare('SELECT COUNT(*) c FROM files WHERE user_id=?').get(uid).c;
|
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` });
|
if (count >= maxCount) return res.status(400).json({ error: `Maximum ${maxCount} Dateien erlaubt` });
|
||||||
|
getUpload().single('file')(req, res, err => {
|
||||||
upload.single('file')(req, res, err => {
|
|
||||||
if (err) return res.status(400).json({ error: err.message });
|
if (err) return res.status(400).json({ error: err.message });
|
||||||
if (!req.file) return res.status(400).json({ error: 'Keine Datei' });
|
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 (?,?,?,?,?)')
|
const folderId = req.body.folder_id || null;
|
||||||
.run(uid, req.file.filename, req.file.originalname, req.file.mimetype || 'application/octet-stream', req.file.size);
|
const r = db.prepare('INSERT INTO files (user_id,filename,originalname,mimetype,size,folder_id) VALUES (?,?,?,?,?,?)')
|
||||||
|
.run(uid, req.file.filename, req.file.originalname, req.file.mimetype||'application/octet-stream', req.file.size, folderId);
|
||||||
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));
|
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
|
// GET /:id/download
|
||||||
router.get('/:id/download', authenticate, (req, res) => {
|
router.get('/:id/download', authenticate, (req, res) => {
|
||||||
const uid = req.user.id;
|
const file = hasFileAccess(req.params.id, req.user.id);
|
||||||
const file = db.prepare('SELECT * FROM files WHERE id=?').get(req.params.id);
|
if (!file) return res.status(403).json({ error: 'Kein Zugriff' });
|
||||||
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);
|
const filePath = path.join(UPLOAD_DIR, file.filename);
|
||||||
if (!fs.existsSync(filePath)) return res.status(404).json({ error: 'Datei fehlt auf Server' });
|
if (!fs.existsSync(filePath)) return res.status(404).json({ error: 'Datei fehlt' });
|
||||||
res.download(filePath, file.originalname);
|
res.download(filePath, file.originalname);
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET /:id/shares – wer hat Zugriff auf diese Datei
|
// DELETE /:id
|
||||||
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) => {
|
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);
|
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' });
|
if (!file) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||||
@@ -94,21 +160,25 @@ router.delete('/:id', authenticate, (req, res) => {
|
|||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
// POST /:id/share – Datei freigeben
|
// GET /:id/shares
|
||||||
router.post('/:id/share', authenticate, (req, res) => {
|
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);
|
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' });
|
if (!file) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||||
const { username } = req.body;
|
res.json(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));
|
||||||
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
|
// POST /:id/share
|
||||||
|
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' });
|
||||||
|
const target = db.prepare('SELECT * FROM users WHERE username=?').get(req.body.username);
|
||||||
|
if (!target) return res.status(404).json({ error: `Benutzer nicht gefunden` });
|
||||||
|
if (target.id === req.user.id) return res.status(400).json({ error: 'Kann nicht mit dir selbst teilen' });
|
||||||
|
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 });
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /:id/share/:userId
|
||||||
router.delete('/:id/share/:userId', authenticate, (req, res) => {
|
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);
|
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' });
|
if (!file) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||||
@@ -116,17 +186,14 @@ router.delete('/:id/share/:userId', authenticate, (req, res) => {
|
|||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET /settings – Admin-Einstellungen lesen
|
// Settings
|
||||||
router.get('/settings', authenticate, requireAdmin, (req, res) => {
|
router.get('/settings', authenticate, requireAdmin, (req, res) => {
|
||||||
const s = db.prepare('SELECT * FROM admin_settings').all();
|
const s = db.prepare('SELECT * FROM admin_settings').all();
|
||||||
const obj = {}; for (const r of s) obj[r.key] = r.value;
|
const obj = {}; for (const r of s) obj[r.key] = r.value;
|
||||||
res.json(obj);
|
res.json(obj);
|
||||||
});
|
});
|
||||||
|
|
||||||
// PUT /settings – Admin-Einstellungen speichern
|
|
||||||
router.put('/settings', authenticate, requireAdmin, (req, res) => {
|
router.put('/settings', authenticate, requireAdmin, (req, res) => {
|
||||||
const allowed = ['file_max_size_mb','file_max_count','file_allowed_ext'];
|
for (const key of ['file_max_size_mb','file_max_count','file_allowed_ext']) {
|
||||||
for (const key of allowed) {
|
|
||||||
if (req.body[key] !== undefined)
|
if (req.body[key] !== undefined)
|
||||||
db.prepare('INSERT OR REPLACE INTO admin_settings (key,value) VALUES (?,?)').run(key, String(req.body[key]));
|
db.prepare('INSERT OR REPLACE INTO admin_settings (key,value) VALUES (?,?)').run(key, String(req.body[key]));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user