diff --git a/server.js b/server.js
index 96451f0..2f1bccc 100644
--- a/server.js
+++ b/server.js
@@ -1639,6 +1639,15 @@ initializeDatabase().then(async () => {
// to be present inside any protected route or background-per-user task.
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///), creating it
// on first use. Used for every attachment / signature / photo path so users'
// files are isolated on disk the same way their DB rows are.
@@ -3206,7 +3215,7 @@ initializeDatabase().then(async () => {
: sanitizeInput(original.replace(/\.[^.]+$/, ''));
await dbRun(
'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');
@@ -3262,8 +3271,10 @@ initializeDatabase().then(async () => {
try {
if (err) console.error('Signature upload error:', err.message);
if (req.file) {
- // keep only the newly uploaded file (in the user's subdir)
- const dir = userStorageDir(signaturDir);
+ // keep only the newly uploaded file (in the user's subdir). Resolve the
+ // 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) => {
if (f !== req.file.filename) fs.promises.unlink(path.join(dir, f)).catch(() => {});
});
@@ -3300,8 +3311,9 @@ initializeDatabase().then(async () => {
try {
if (err) console.error('Photo upload error:', err.message);
if (req.file) {
- // keep only the newly uploaded file (in the user's subdir)
- const dir = userStorageDir(fotoDir);
+ // keep only the newly uploaded file (in the user's subdir). Resolve the
+ // dir from req.user — see /unterschrift handler for why (multer callback).
+ const dir = userStorageDirForReq(fotoDir, req);
fs.readdirSync(dir).forEach((f) => {
if (f !== req.file.filename) fs.promises.unlink(path.join(dir, f)).catch(() => {});
});
@@ -3374,7 +3386,7 @@ initializeDatabase().then(async () => {
: sanitizeInput(original.replace(/\.[^.]+$/, ''));
await dbRun(
'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);