Redis and session timeout added

This commit is contained in:
Torsten Schulz
2024-10-27 14:15:44 +01:00
parent 7f8709516d
commit b78dfe6826
9 changed files with 320 additions and 6 deletions

View File

@@ -10,6 +10,7 @@ import contactRouter from './routers/contactRouter.js';
import cors from 'cors'; import cors from 'cors';
import socialnetworkRouter from './routers/socialnetworkRouter.js'; import socialnetworkRouter from './routers/socialnetworkRouter.js';
import forumRouter from './routers/forumRouter.js'; import forumRouter from './routers/forumRouter.js';
import './jobs/sessionCleanup.js';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);

View File

@@ -6,6 +6,7 @@ class AuthController {
this.login = this.login.bind(this); this.login = this.login.bind(this);
this.forgotPassword = this.forgotPassword.bind(this); this.forgotPassword = this.forgotPassword.bind(this);
this.activateAccount = this.activateAccount.bind(this); this.activateAccount = this.activateAccount.bind(this);
this.logout = this.logout.bind(this);
} }
async register(req, res) { async register(req, res) {
@@ -33,6 +34,11 @@ class AuthController {
} }
} }
async logout(req, res) {
const { userid: hashedUserId } = req.headers;
await userService.logoutUser(hashedUserId);
}
async forgotPassword(req, res) { async forgotPassword(req, res) {
const { email } = req.body; const { email } = req.body;
try { try {

View File

@@ -0,0 +1,5 @@
import { cleanupExpiredSessions } from '../utils/redis.js';
setInterval(async () => {
await cleanupExpiredSessions();
}, 5000);

View File

@@ -1,4 +1,5 @@
import User from '../models/community/user.js'; import User from '../models/community/user.js';
import { updateUserTimestamp } from '../utils/redis.js';
export const authenticate = async (req, res, next) => { export const authenticate = async (req, res, next) => {
const userId = req.headers.userid; const userId = req.headers.userid;
@@ -6,11 +7,14 @@ export const authenticate = async (req, res, next) => {
if (!userId || !authCode) { if (!userId || !authCode) {
return res.status(401).json({ error: 'Unauthorized: Missing credentials' }); return res.status(401).json({ error: 'Unauthorized: Missing credentials' });
} }
const user = await User.findOne({ where: { hashedId: userId, authCode } }); const user = await User.findOne({ where: { hashedId: userId, authCode } });
if (!user) { if (!user) {
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' }); return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
} }
try {
await updateUserTimestamp(userId);
} catch (error) {
console.error('Fehler beim Aktualisieren des Zeitstempels:', error);
}
next(); next();
}; };

View File

@@ -11,10 +11,12 @@
"dependencies": { "dependencies": {
"amqplib": "^0.10.4", "amqplib": "^0.10.4",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"connect-redis": "^7.1.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"dompurify": "^3.1.7", "dompurify": "^3.1.7",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"express-session": "^1.18.1",
"i18n": "^0.15.1", "i18n": "^0.15.1",
"joi": "^17.13.3", "joi": "^17.13.3",
"jsdom": "^25.0.1", "jsdom": "^25.0.1",
@@ -23,6 +25,7 @@
"nodemailer": "^6.9.14", "nodemailer": "^6.9.14",
"pg": "^8.12.0", "pg": "^8.12.0",
"pg-hstore": "^2.3.4", "pg-hstore": "^2.3.4",
"redis": "^4.7.0",
"sequelize": "^6.37.3", "sequelize": "^6.37.3",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"socket.io": "^4.7.5", "socket.io": "^4.7.5",
@@ -579,6 +582,59 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@redis/bloom": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/client": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz",
"integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==",
"dependencies": {
"cluster-key-slot": "1.1.2",
"generic-pool": "3.9.0",
"yallist": "4.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@redis/graph": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/json": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/search": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz",
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/time-series": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz",
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@sideway/address": { "node_modules/@sideway/address": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
@@ -960,6 +1016,14 @@
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
} }
}, },
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/color": { "node_modules/color": {
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -1081,6 +1145,17 @@
"proto-list": "~1.2.1" "proto-list": "~1.2.1"
} }
}, },
"node_modules/connect-redis": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-7.1.1.tgz",
"integrity": "sha512-M+z7alnCJiuzKa8/1qAYdGUXHYfDnLolOGAUjOioB07pP39qxjG+X9ibsud7qUBc4jMV5Mcy3ugGv8eFcgamJQ==",
"engines": {
"node": ">=16"
},
"peerDependencies": {
"express-session": ">=1"
}
},
"node_modules/console-control-strings": { "node_modules/console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -1611,6 +1686,69 @@
"node": ">= 0.10.0" "node": ">= 0.10.0"
} }
}, },
"node_modules/express-session": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz",
"integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==",
"dependencies": {
"cookie": "0.7.2",
"cookie-signature": "1.0.7",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.1",
"uid-safe": "~2.1.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/express-session/node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express-session/node_modules/cookie-signature": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="
},
"node_modules/express-session/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express-session/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/express-session/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/express/node_modules/debug": { "node_modules/express/node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -1828,6 +1966,14 @@
"is-property": "^1.0.2" "is-property": "^1.0.2"
} }
}, },
"node_modules/generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
"engines": {
"node": ">= 4"
}
},
"node_modules/get-caller-file": { "node_modules/get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -2806,6 +2952,14 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -3060,6 +3214,14 @@
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
}, },
"node_modules/random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/range-parser": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -3093,6 +3255,22 @@
"string_decoder": "~0.10.x" "string_decoder": "~0.10.x"
} }
}, },
"node_modules/redis": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz",
"integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==",
"workspaces": [
"./packages/*"
],
"dependencies": {
"@redis/bloom": "1.2.0",
"@redis/client": "1.6.0",
"@redis/graph": "1.1.1",
"@redis/json": "1.0.7",
"@redis/search": "1.2.0",
"@redis/time-series": "1.1.0"
}
},
"node_modules/require-directory": { "node_modules/require-directory": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -3715,6 +3893,17 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
}, },
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"dependencies": {
"random-bytes": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/umzug": { "node_modules/umzug": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz",

View File

@@ -13,10 +13,12 @@
"dependencies": { "dependencies": {
"amqplib": "^0.10.4", "amqplib": "^0.10.4",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"connect-redis": "^7.1.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"dompurify": "^3.1.7", "dompurify": "^3.1.7",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"express-session": "^1.18.1",
"i18n": "^0.15.1", "i18n": "^0.15.1",
"joi": "^17.13.3", "joi": "^17.13.3",
"jsdom": "^25.0.1", "jsdom": "^25.0.1",
@@ -25,6 +27,7 @@
"nodemailer": "^6.9.14", "nodemailer": "^6.9.14",
"pg": "^8.12.0", "pg": "^8.12.0",
"pg-hstore": "^2.3.4", "pg-hstore": "^2.3.4",
"redis": "^4.7.0",
"sequelize": "^6.37.3", "sequelize": "^6.37.3",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"socket.io": "^4.7.5", "socket.io": "^4.7.5",

View File

@@ -8,5 +8,6 @@ router.post('/register', authController.register);
router.post('/login', authController.login); router.post('/login', authController.login);
router.post('/forgot-password', authController.forgotPassword); router.post('/forgot-password', authController.forgotPassword);
router.post('/activate', authController.activateAccount); router.post('/activate', authController.activateAccount);
router.get('/logout', authController.logout);
export default router; export default router;

View File

@@ -6,6 +6,7 @@ import UserParam from '../models/community/user_param.js';
import UserParamType from '../models/type/user_param.js'; import UserParamType from '../models/type/user_param.js';
import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js'; import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js';
import { sequelize } from '../utils/sequelize.js'; import { sequelize } from '../utils/sequelize.js';
import { setUserSession, deleteUserSession } from '../utils/redis.js';
const saltRounds = 10; const saltRounds = 10;
@@ -31,22 +32,18 @@ export const registerUser = async ({ email, username, password, language }) => {
active: false, active: false,
registration_date: new Date() registration_date: new Date()
}); });
const languageType = await UserParamType.findOne({ where: { description: 'language' } }); const languageType = await UserParamType.findOne({ where: { description: 'language' } });
if (!languageType) { if (!languageType) {
throw new Error('languagenotfound'); throw new Error('languagenotfound');
} }
const languageParam = await UserParam.create({ const languageParam = await UserParam.create({
userId: user.id, userId: user.id,
paramTypeId: languageType.id, paramTypeId: languageType.id,
value: language value: language
}); });
const activationLink = `${process.env.FRONTEND_URL}/activate?token=${resetToken}`; const activationLink = `${process.env.FRONTEND_URL}/activate?token=${resetToken}`;
await sendAccountActivationEmail(email, activationLink, username, resetToken, language); await sendAccountActivationEmail(email, activationLink, username, resetToken, language);
const authCode = crypto.randomBytes(20).toString('hex'); const authCode = crypto.randomBytes(20).toString('hex');
return { id: user.hashedId, username: user.username, active: user.active, param: [languageParam], authCode }; return { id: user.hashedId, username: user.username, active: user.active, param: [languageParam], authCode };
}; };
@@ -62,6 +59,14 @@ export const loginUser = async ({ username, password }) => {
const authCode = crypto.randomBytes(20).toString('hex'); const authCode = crypto.randomBytes(20).toString('hex');
user.authCode = authCode; user.authCode = authCode;
await user.save(); await user.save();
const sessionData = {
id: user.hashedId,
username: user.username,
active: user.active,
authCode,
timestamp: Date.now()
};
await setUserSession(user.id, sessionData);
const params = await UserParam.findAll({ const params = await UserParam.findAll({
where: { where: {
userId: user.id userId: user.id
@@ -86,6 +91,24 @@ export const loginUser = async ({ username, password }) => {
}; };
}; };
export const logoutUser = async (hashedUserId) => {
try {
const user = User.findOne({
where: {
hashedId: hashedUserId
}
})
if (!user) {
return;
}
await deleteUserSession(user.id);
console.log('Benutzer erfolgreich aus Redis entfernt:', userId);
} catch (error) {
console.error('Fehler beim Logout:', error);
throw new Error('logoutfailed');
}
};
export const handleForgotPassword = async ({ email }) => { export const handleForgotPassword = async ({ email }) => {
const user = await User.findOne({ where: { email } }); const user = await User.findOne({ where: { email } });
if (!user) { if (!user) {

82
backend/utils/redis.js Normal file
View File

@@ -0,0 +1,82 @@
import dotenv from 'dotenv';
import { createClient } from 'redis';
import User from '../models/community/user.js';
dotenv.config();
const EXPIRATION_TIME = 30 * 60 * 1000;
const redisClient = createClient({
url: `redis://${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`,
password: process.env.REDIS_PASSWORD,
legacyMode: true,
});
redisClient.connect().catch(console.error);
const setUserSession = async (userId, sessionData) => {
try {
await redisClient.hSet(`user:${userId}`, sessionData);
} catch (error) {
console.error('Fehler beim Setzen der Benutzersitzung:', error);
}
};
const deleteUserSession = async (userId) => {
try {
await redisClient.del(`user:${userId}`);
} catch (error) {
console.error('Fehler beim Löschen der Benutzersitzung:', error);
}
};
const getUserSession = async (userId) => {
try {
return await redisClient.hGetAll(`user:${userId}`);
} catch (error) {
console.error('Fehler beim Abrufen der Benutzersitzung:', error);
return null;
}
};
const updateUserTimestamp = async (hashedId) => {
try {
const userKey = `user:${hashedId}`;
const userExists = await redisClient.exists(userKey);
if (userExists) {
await redisClient.hSet(userKey, 'timestamp', Date.now());
console.log(`Zeitstempel für Benutzer ${hashedId} aktualisiert.`);
} else {
console.warn(`Benutzer mit der hashedId ${hashedId} wurde nicht gefunden.`);
}
} catch (error) {
console.error('Fehler beim Aktualisieren des Zeitstempels:', error);
}
};
const cleanupExpiredSessions = async () => {
try {
const keys = await redisClient.keys('user:*');
const now = Date.now();
for (const key of keys) {
const session = await redisClient.hGetAll(key);
if (session.timestamp && now - parseInt(session.timestamp) > EXPIRATION_TIME) {
const userId = key.split(':')[1];
await redisClient.del(key);
await User.update({ authCode: '' }, { where: { hashedId: userId } });
console.log(`Abgelaufene Sitzung für Benutzer ${userId} gelöscht.`);
}
}
} catch (error) {
console.error('Fehler beim Bereinigen abgelaufener Sitzungen:', error);
}
};
export {
setUserSession,
deleteUserSession,
getUserSession,
updateUserTimestamp,
cleanupExpiredSessions,
redisClient
};