Anlagen: user_id aus req.user statt AsyncLocalStorage im multer-Callback
In multer/busboy-Upload-Callbacks ist der per-request AsyncLocalStorage- Kontext nicht zuverlässig propagiert, sodass currentUserId() dort null liefern kann und basis_anhaenge-/interne_anhaenge-Zeilen mit user_id=NULL verwaisten (für den User unsichtbar). Die INSERTs nutzen jetzt uidFromReq (= req.user.id mit uid()-Fallback) wie bereits userStorageDirForReq für Signatur/Foto. Zwei bestehende verwaiste Zeilen wurden repariert. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1639,6 +1639,15 @@ initializeDatabase().then(async () => {
|
|||||||
// to be present inside any protected route or background-per-user task.
|
// to be present inside any protected route or background-per-user task.
|
||||||
const uid = () => currentUserId();
|
const uid = () => currentUserId();
|
||||||
|
|
||||||
|
// Inside a multer upload callback the per-request AsyncLocalStorage context
|
||||||
|
// (which uid() reads) is not reliably propagated — busboy drives the multipart
|
||||||
|
// stream from outside the userContext.run(...) scope, so currentUserId() can
|
||||||
|
// come back null there. The request object, by contrast, always carries the
|
||||||
|
// resolved user (set by the auth middleware before the route). Use this for
|
||||||
|
// any INSERT done from within an uploadX(req, res, cb) callback, so the row is
|
||||||
|
// not orphaned with user_id = NULL. Mirrors userStorageDirForReq's reasoning.
|
||||||
|
const uidFromReq = (req) => (req && req.user && req.user.id) || currentUserId();
|
||||||
|
|
||||||
// Resolve a per-user storage directory (data/<base>/<userId>/), creating it
|
// Resolve a per-user storage directory (data/<base>/<userId>/), creating it
|
||||||
// on first use. Used for every attachment / signature / photo path so users'
|
// on first use. Used for every attachment / signature / photo path so users'
|
||||||
// files are isolated on disk the same way their DB rows are.
|
// files are isolated on disk the same way their DB rows are.
|
||||||
@@ -3206,7 +3215,7 @@ initializeDatabase().then(async () => {
|
|||||||
: sanitizeInput(original.replace(/\.[^.]+$/, ''));
|
: sanitizeInput(original.replace(/\.[^.]+$/, ''));
|
||||||
await dbRun(
|
await dbRun(
|
||||||
'INSERT INTO basis_anhaenge (user_id, name, dateiname, mime, pfad) VALUES (?, ?, ?, ?, ?)',
|
'INSERT INTO basis_anhaenge (user_id, name, dateiname, mime, pfad) VALUES (?, ?, ?, ?, ?)',
|
||||||
[uid(), name, sanitizeInput(original), req.file.mimetype || 'application/octet-stream', req.file.filename]
|
[uidFromReq(req), name, sanitizeInput(original), req.file.mimetype || 'application/octet-stream', req.file.filename]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
res.redirect('/vorlagen');
|
res.redirect('/vorlagen');
|
||||||
@@ -3262,8 +3271,10 @@ initializeDatabase().then(async () => {
|
|||||||
try {
|
try {
|
||||||
if (err) console.error('Signature upload error:', err.message);
|
if (err) console.error('Signature upload error:', err.message);
|
||||||
if (req.file) {
|
if (req.file) {
|
||||||
// keep only the newly uploaded file (in the user's subdir)
|
// keep only the newly uploaded file (in the user's subdir). Resolve the
|
||||||
const dir = userStorageDir(signaturDir);
|
// dir from req.user (not currentUserId()) — this runs in the multer
|
||||||
|
// callback where the AsyncLocalStorage user context can be absent.
|
||||||
|
const dir = userStorageDirForReq(signaturDir, req);
|
||||||
fs.readdirSync(dir).forEach((f) => {
|
fs.readdirSync(dir).forEach((f) => {
|
||||||
if (f !== req.file.filename) fs.promises.unlink(path.join(dir, f)).catch(() => {});
|
if (f !== req.file.filename) fs.promises.unlink(path.join(dir, f)).catch(() => {});
|
||||||
});
|
});
|
||||||
@@ -3300,8 +3311,9 @@ initializeDatabase().then(async () => {
|
|||||||
try {
|
try {
|
||||||
if (err) console.error('Photo upload error:', err.message);
|
if (err) console.error('Photo upload error:', err.message);
|
||||||
if (req.file) {
|
if (req.file) {
|
||||||
// keep only the newly uploaded file (in the user's subdir)
|
// keep only the newly uploaded file (in the user's subdir). Resolve the
|
||||||
const dir = userStorageDir(fotoDir);
|
// dir from req.user — see /unterschrift handler for why (multer callback).
|
||||||
|
const dir = userStorageDirForReq(fotoDir, req);
|
||||||
fs.readdirSync(dir).forEach((f) => {
|
fs.readdirSync(dir).forEach((f) => {
|
||||||
if (f !== req.file.filename) fs.promises.unlink(path.join(dir, f)).catch(() => {});
|
if (f !== req.file.filename) fs.promises.unlink(path.join(dir, f)).catch(() => {});
|
||||||
});
|
});
|
||||||
@@ -3374,7 +3386,7 @@ initializeDatabase().then(async () => {
|
|||||||
: sanitizeInput(original.replace(/\.[^.]+$/, ''));
|
: sanitizeInput(original.replace(/\.[^.]+$/, ''));
|
||||||
await dbRun(
|
await dbRun(
|
||||||
'INSERT INTO interne_anhaenge (user_id, bewerbung_id, name, dateiname, mime, pfad) VALUES (?, ?, ?, ?, ?, ?)',
|
'INSERT INTO interne_anhaenge (user_id, bewerbung_id, name, dateiname, mime, pfad) VALUES (?, ?, ?, ?, ?, ?)',
|
||||||
[uid(), id, name, sanitizeInput(original), req.file.mimetype || 'application/octet-stream', req.file.filename]
|
[uidFromReq(req), id, name, sanitizeInput(original), req.file.mimetype || 'application/octet-stream', req.file.filename]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
res.redirect('/bewerbung/' + id);
|
res.redirect('/bewerbung/' + id);
|
||||||
|
|||||||
Reference in New Issue
Block a user