Genesis
This commit is contained in:
85
src/api/middleware/auth.js
Normal file
85
src/api/middleware/auth.js
Normal file
@@ -0,0 +1,85 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import env from '../../config/env.js';
|
||||
import * as db from '../../db/googleAuth.js';
|
||||
import { meJson } from '../routes/auth/meJson.js';
|
||||
|
||||
const userCache = new Map();
|
||||
const USER_CACHE_TTL = 10 * 60 * 1000;
|
||||
|
||||
export function getCachedUser(id) {
|
||||
const cached = userCache.get(id);
|
||||
if (cached && cached.expiresAt > Date.now()) {
|
||||
return cached.user;
|
||||
}
|
||||
userCache.delete(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
export function setCachedUser(id, user) {
|
||||
userCache.set(id, { user, expiresAt: Date.now() + USER_CACHE_TTL });
|
||||
if (userCache.size > 1000) {
|
||||
const now = Date.now();
|
||||
for (const [k, v] of userCache) {
|
||||
if (v.expiresAt <= now) userCache.delete(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function clearCachedUser(id) {
|
||||
userCache.delete(id);
|
||||
}
|
||||
|
||||
function authMeJson(res, req, status, body) {
|
||||
if (req.path === '/api/auth/me') {
|
||||
return meJson(res, status, body);
|
||||
}
|
||||
return res.status(status).json(body);
|
||||
}
|
||||
|
||||
export const authMiddleware = async (req, res, next) => {
|
||||
if (req.path === '/' || req.path === '/api/auth/login' || req.path === '/api/auth/logout' || req.path === '/api/health') {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (req.path.startsWith('/@') || req.path.startsWith('/node_modules') || req.path.startsWith('/src') || req.path === '/favicon.ico') {
|
||||
return next();
|
||||
}
|
||||
|
||||
const token = req.cookies.token;
|
||||
if (!token) return authMeJson(res, req, 401, { error: 'Unauthorized' });
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, env.jwtSecret);
|
||||
|
||||
let user = getCachedUser(decoded.id);
|
||||
if (!user) {
|
||||
user = db.getAllowedEmailById(decoded.id);
|
||||
if (user) setCachedUser(decoded.id, user);
|
||||
}
|
||||
if (!user) {
|
||||
return authMeJson(res, req, 403, { error: 'User is no longer authorized' });
|
||||
}
|
||||
|
||||
req.user = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
...(decoded.picture && { picture: decoded.picture }),
|
||||
};
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (decoded.exp - now < 2 * 60 * 60) {
|
||||
const { iat, exp, ...payload } = decoded;
|
||||
const newToken = jwt.sign(payload, env.jwtSecret, { expiresIn: '1d' });
|
||||
res.cookie('token', newToken, {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: 24 * 60 * 60 * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
} catch {
|
||||
return authMeJson(res, req, 401, { error: 'Invalid token' });
|
||||
}
|
||||
};
|
||||
19
src/api/middleware/logger.js
Normal file
19
src/api/middleware/logger.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
export const loggerMiddleware = (req, res, next) => {
|
||||
const start = Date.now();
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - start;
|
||||
const method = chalk.bold.blue(req.method.padEnd(7));
|
||||
const url = req.originalUrl;
|
||||
let status = res.statusCode;
|
||||
|
||||
if (status >= 500) status = chalk.red(status);
|
||||
else if (status >= 400) status = chalk.yellow(status);
|
||||
else if (status >= 300) status = chalk.cyan(status);
|
||||
else status = chalk.green(status);
|
||||
|
||||
console.log(`${method} ${url} ${status} ${chalk.gray(`(${duration}ms)`)}`);
|
||||
});
|
||||
next();
|
||||
};
|
||||
Reference in New Issue
Block a user