fix: Sync try-catch sauber, kein WITH RECURSIVE, zeigt Erfolg-Meldung
This commit is contained in:
@@ -229,65 +229,57 @@ router.post('/webdav/test', authenticate, requireAdmin, async (req, res) => {
|
|||||||
// ── WebDAV Sync: alle lokalen Dateien auf NAS kopieren ────────────────────
|
// ── WebDAV Sync: alle lokalen Dateien auf NAS kopieren ────────────────────
|
||||||
router.post('/webdav/sync', authenticate, requireAdmin, async (req, res) => {
|
router.post('/webdav/sync', authenticate, requireAdmin, async (req, res) => {
|
||||||
const webdav = require('../tools/dateien/webdav');
|
const webdav = require('../tools/dateien/webdav');
|
||||||
const path = require('path');
|
const nodePath = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const UPLOAD_DIR = process.env.UPLOAD_DIR || '/data/uploads';
|
const UPLOAD_DIR = process.env.UPLOAD_DIR || '/data/uploads';
|
||||||
|
|
||||||
const cfg = webdav.getConfig();
|
const cfg = webdav.getConfig();
|
||||||
if (!cfg.enabled) return res.status(400).json({ error: 'WebDAV nicht aktiviert' });
|
if (!cfg.enabled) return res.status(400).json({ error: 'WebDAV nicht aktiviert' });
|
||||||
|
|
||||||
// Ordnerpfad rekursiv aus DB auflösen
|
const results = { folders: 0, ok: 0, failed: 0, errors: [] };
|
||||||
|
|
||||||
|
try {
|
||||||
|
// ── Ordnerpfad rekursiv auflösen ───────────────────────────────────────
|
||||||
function getFolderPath(folderId) {
|
function getFolderPath(folderId) {
|
||||||
if (!folderId) return '';
|
if (!folderId) return '';
|
||||||
const parts = [];
|
const parts = [];
|
||||||
let current = folderId;
|
let cur = folderId;
|
||||||
const visited = new Set();
|
const seen = new Set();
|
||||||
while (current) {
|
while (cur) {
|
||||||
if (visited.has(current)) break;
|
if (seen.has(cur)) break; seen.add(cur);
|
||||||
visited.add(current);
|
const fo = db.prepare('SELECT name, parent_id FROM folders WHERE id=?').get(cur);
|
||||||
const folder = db.prepare('SELECT id, name, parent_id FROM folders WHERE id=?').get(current);
|
if (!fo) break;
|
||||||
if (!folder) break;
|
parts.unshift(fo.name.replace(/[\/]/g, '_'));
|
||||||
parts.unshift(folder.name.replace(/[\/\\]/g, '_'));
|
cur = fo.parent_id;
|
||||||
current = folder.parent_id;
|
|
||||||
}
|
}
|
||||||
return parts.join('/');
|
return parts.join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = { folders: 0, ok: 0, failed: 0, errors: [] };
|
// ── Schritt 1: User-Basisordner + alle DB-Ordner anlegen ───────────────
|
||||||
|
|
||||||
// ── Schritt 1: Alle User-Basisordner + Unterordner anlegen ───────────────
|
|
||||||
const users = db.prepare('SELECT id, username FROM users ORDER BY id').all();
|
const users = db.prepare('SELECT id, username FROM users ORDER BY id').all();
|
||||||
|
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
const userBase = webdav.userPath(cfg, user.username);
|
const userBase = webdav.userPath(cfg, user.username);
|
||||||
|
try { await webdav.mkdirp(userBase); results.folders++; } catch {}
|
||||||
|
try { await webdav.mkdirp(`${userBase}/_Upload-Freigaben`); } catch {}
|
||||||
|
|
||||||
// User-Basisordner
|
// Ordner iterativ sortieren (flach, nach Tiefe)
|
||||||
try { await webdav.mkdirp(userBase); results.folders++; } catch(e) {
|
const allFolders = db.prepare('SELECT id, name, parent_id FROM folders WHERE user_id=? ORDER BY id').all(user.id);
|
||||||
console.warn('[sync] mkdirp', userBase, e.message);
|
const depthCache = {};
|
||||||
}
|
function depth(fid) {
|
||||||
|
if (!fid) return 0;
|
||||||
// Upload-Freigabe Ordner
|
if (depthCache[fid] !== undefined) return depthCache[fid];
|
||||||
try { await webdav.mkdirp(`${userBase}/_Upload-Freigaben`); results.folders++; } catch {}
|
const fo = allFolders.find(f => f.id === fid);
|
||||||
|
depthCache[fid] = fo ? 1 + depth(fo.parent_id) : 0;
|
||||||
// Alle DB-Ordner dieses Users (sortiert nach parent_id damit Parents zuerst)
|
return depthCache[fid];
|
||||||
const folders = db.prepare(`
|
|
||||||
WITH RECURSIVE tree AS (
|
|
||||||
SELECT id, name, parent_id, 0 as depth FROM folders WHERE user_id=? AND parent_id IS NULL
|
|
||||||
UNION ALL
|
|
||||||
SELECT f.id, f.name, f.parent_id, t.depth+1 FROM folders f JOIN tree t ON f.parent_id=t.id
|
|
||||||
) SELECT * FROM tree ORDER BY depth, id
|
|
||||||
`).all(user.id);
|
|
||||||
|
|
||||||
for (const folder of folders) {
|
|
||||||
const folderPath = getFolderPath(folder.id);
|
|
||||||
const davPath = `${userBase}/${folderPath}`;
|
|
||||||
try { await webdav.mkdirp(davPath); results.folders++; } catch(e) {
|
|
||||||
results.errors.push(`Ordner ${davPath}: ${e.message}`);
|
|
||||||
}
|
}
|
||||||
|
const sorted = [...allFolders].sort((a, b) => depth(a.id) - depth(b.id));
|
||||||
|
for (const folder of sorted) {
|
||||||
|
const fp = getFolderPath(folder.id);
|
||||||
|
if (fp) try { await webdav.mkdirp(`${userBase}/${fp}`); results.folders++; } catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Schritt 2: Alle Dateien hochladen ────────────────────────────────────
|
// ── Schritt 2: Dateien hochladen ───────────────────────────────────────
|
||||||
const files = db.prepare(`
|
const files = db.prepare(`
|
||||||
SELECT f.*, u.username FROM files f
|
SELECT f.*, u.username FROM files f
|
||||||
JOIN users u ON u.id = f.user_id
|
JOIN users u ON u.id = f.user_id
|
||||||
@@ -295,7 +287,7 @@ router.post('/webdav/sync', authenticate, requireAdmin, async (req, res) => {
|
|||||||
`).all();
|
`).all();
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const localPath = path.join(UPLOAD_DIR, file.filename);
|
const localPath = nodePath.join(UPLOAD_DIR, file.filename);
|
||||||
if (!fs.existsSync(localPath)) {
|
if (!fs.existsSync(localPath)) {
|
||||||
results.failed++;
|
results.failed++;
|
||||||
results.errors.push(`${file.originalname}: lokale Datei fehlt`);
|
results.errors.push(`${file.originalname}: lokale Datei fehlt`);
|
||||||
@@ -303,11 +295,9 @@ router.post('/webdav/sync', authenticate, requireAdmin, async (req, res) => {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const buf = fs.readFileSync(localPath);
|
const buf = fs.readFileSync(localPath);
|
||||||
const folderPath = getFolderPath(file.folder_id);
|
const fp = getFolderPath(file.folder_id);
|
||||||
const userBase = webdav.userPath(cfg, file.username);
|
const base = webdav.userPath(cfg, file.username);
|
||||||
const davPath = folderPath
|
const davPath = fp ? `${base}/${fp}/${file.originalname}` : `${base}/${file.originalname}`;
|
||||||
? `${userBase}/${folderPath}/${file.originalname}`
|
|
||||||
: `${userBase}/${file.originalname}`;
|
|
||||||
await webdav.uploadFile(buf, davPath, file.mimetype || 'application/octet-stream');
|
await webdav.uploadFile(buf, davPath, file.mimetype || 'application/octet-stream');
|
||||||
results.ok++;
|
results.ok++;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
@@ -316,10 +306,9 @@ router.post('/webdav/sync', authenticate, requireAdmin, async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Schritt 3: Upload-Freigabe Dateien ───────────────────────────────────
|
// ── Schritt 3: Upload-Freigabe Dateien ────────────────────────────────
|
||||||
const uploadLogs = db.prepare(`
|
const uploadLogs = db.prepare(`
|
||||||
SELECT l.filename, l.originalname, l.mimetype, l.size,
|
SELECT l.filename, l.originalname, l.mimetype, u.username
|
||||||
s.user_id, u.username
|
|
||||||
FROM upload_share_logs l
|
FROM upload_share_logs l
|
||||||
JOIN upload_shares s ON s.id = l.share_id
|
JOIN upload_shares s ON s.id = l.share_id
|
||||||
JOIN users u ON u.id = s.user_id
|
JOIN users u ON u.id = s.user_id
|
||||||
@@ -327,13 +316,12 @@ router.post('/webdav/sync', authenticate, requireAdmin, async (req, res) => {
|
|||||||
`).all();
|
`).all();
|
||||||
|
|
||||||
for (const log of uploadLogs) {
|
for (const log of uploadLogs) {
|
||||||
const localPath = path.join(UPLOAD_DIR, log.filename);
|
const localPath = nodePath.join(UPLOAD_DIR, log.filename);
|
||||||
if (!fs.existsSync(localPath)) continue;
|
if (!fs.existsSync(localPath)) continue;
|
||||||
try {
|
try {
|
||||||
const buf = fs.readFileSync(localPath);
|
const buf = fs.readFileSync(localPath);
|
||||||
const userBase = webdav.userPath(cfg, log.username);
|
const base = webdav.userPath(cfg, log.username);
|
||||||
const davPath = `${userBase}/_Upload-Freigaben/${log.originalname || log.filename}`;
|
await webdav.uploadFile(buf, `${base}/_Upload-Freigaben/${log.originalname || log.filename}`, log.mimetype || 'application/octet-stream');
|
||||||
await webdav.uploadFile(buf, davPath, log.mimetype || 'application/octet-stream');
|
|
||||||
results.ok++;
|
results.ok++;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
results.failed++;
|
results.failed++;
|
||||||
@@ -344,8 +332,14 @@ router.post('/webdav/sync', authenticate, requireAdmin, async (req, res) => {
|
|||||||
res.json({
|
res.json({
|
||||||
total: files.length + uploadLogs.length,
|
total: files.length + uploadLogs.length,
|
||||||
...results,
|
...results,
|
||||||
message: `${results.folders} Ordner angelegt, ${results.ok} Dateien kopiert, ${results.failed} fehlgeschlagen`
|
message: `${results.folders} Ordner angelegt, ${results.ok} Dateien kopiert${results.failed ? ', ' + results.failed + ' fehlgeschlagen' : ' – alles erfolgreich ✓'}`,
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
console.error('[webdav sync error]', err);
|
||||||
|
res.status(500).json({ error: err.message, ...results });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user