diff --git a/public/js/logs.js b/public/js/logs.js index 2e0a92c..374c46e 100644 --- a/public/js/logs.js +++ b/public/js/logs.js @@ -20,6 +20,7 @@ if (!res.ok) throw new Error(await res.text()); const logs = await res.json(); + console.log(logs); if (logs.length === 0) { tbody.innerHTML = 'No logs found'; diff --git a/src/middleware/authCheck.js b/src/middleware/authCheck.js new file mode 100644 index 0000000..4660673 --- /dev/null +++ b/src/middleware/authCheck.js @@ -0,0 +1,86 @@ +// middleware/authCheck.js +const fetch = require("node-fetch"); + +const VERIFY_URL = process.env.AUTH_VERIFY; +const CACHE_TTL = parseInt(process.env.AUTH_CACHE_TTL) || 120000; // 2 minutes default + +// Simple in-memory cache +const authCache = new Map(); + +// Helper to generate cache key +function getCacheKey(cookie, authHeader) { + return `${cookie}:${authHeader}`; +} + +// Helper to check if cache entry is valid +function isCacheValid(entry) { + return entry && Date.now() - entry.timestamp < CACHE_TTL; +} + +// Clean expired cache entries periodically +setInterval(() => { + const now = Date.now(); + for (const [key, entry] of authCache.entries()) { + if (now - entry.timestamp >= CACHE_TTL) { + authCache.delete(key); + } + } +}, CACHE_TTL); // Clean up when entries would expire + +module.exports = async (req, res, next) => { + const cookie = req.headers["cookie"] || ""; + const authHeader = req.headers["authorization"] || ""; + const cacheKey = getCacheKey(cookie, authHeader); + + // Check cache first + const cached = authCache.get(cacheKey); + if (isCacheValid(cached)) { + req.isAuthenticated = cached.isAuthenticated; + return next(); + } + + // Default to unauthenticated + req.isAuthenticated = false; + + try { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 5000); // 5 second timeout + + const resVerify = await fetch(VERIFY_URL, { + headers: { + cookie, + authorization: authHeader, + }, + credentials: "include", + signal: controller.signal, + }); + + clearTimeout(timeout); + + const isAuthenticated = resVerify.status === 200; + + // Cache the result + authCache.set(cacheKey, { + isAuthenticated, + timestamp: Date.now(), + }); + + req.isAuthenticated = isAuthenticated; + } catch (err) { + // Auth server down/timeout - silently fail, don't crash the app + req.isAuthenticated = false; + + // Optional: Log for debugging, but don't spam logs + if (req.log) { + req.log.warn( + "[AuthCheck] Auth server unavailable, continuing unauthenticated" + ); + } else { + console.warn( + "[AuthCheck] Auth server unavailable, continuing unauthenticated" + ); + } + } + + next(); +}; diff --git a/src/middleware/hbs.js b/src/middleware/hbs.js index 47dcb3b..08f9660 100644 --- a/src/middleware/hbs.js +++ b/src/middleware/hbs.js @@ -12,7 +12,9 @@ helpers: { section(name, options) { this._sections ??= {}; - this._sections[name] = options.fn(this); + this._sections[name] ??= ""; + this._sections[name] += options.fn(this); + req.log.debug(name, this._sections); return null; }, }, diff --git a/src/middleware/index.js b/src/middleware/index.js index 2cd52b9..ebce2ee 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -11,7 +11,7 @@ const errorHandler = require("./errorHandler"); const baseContext = require("./baseContext"); const hbs = require("./hbs"); -const authentication = require("./authentication.js"); +const authCheck = require("./authCheck"); const { loggingMiddleware, @@ -22,7 +22,7 @@ function setupApp() { const app = express(); - const excludedPaths = ['/contact', '/analytics', '/track']; + const excludedPaths = ["/contact", "/analytics", "/track"]; const DATA_LIMIT_BYTES = 10 * 1024; // 10k // General parsers for non-excluded routes @@ -30,18 +30,22 @@ if (excludedPaths.includes(req.path)) return next(); express.json({ limit: DATA_LIMIT_BYTES })(req, res, (err) => { if (err) return next(err); - express.urlencoded({ extended: false, limit: DATA_LIMIT_BYTES })(req, res, next); + express.urlencoded({ extended: false, limit: DATA_LIMIT_BYTES })( + req, + res, + next + ); }); }); // Raw parser + manual truncation for excluded routes - const rawBodyParser = express.raw({ type: '*/*', limit: '100kb' }); + const rawBodyParser = express.raw({ type: "*/*", limit: "100kb" }); app.use((req, res, next) => { if (!excludedPaths.includes(req.path)) return next(); rawBodyParser(req, res, (err) => { if (err) return next(err); try { - const raw = req.body.toString('utf8'); + const raw = req.body.toString("utf8"); const truncated = raw.slice(0, DATA_LIMIT_BYTES); req.body = JSON.parse(truncated); } catch (e) { @@ -51,17 +55,15 @@ }); }); - - app.use(hbs); // Setup logging app.use(logEvent, morganInfo, morganWarn, morganError, loggingMiddleware); - app.use(authentication); - + app.use(authCheck); + // Setup handlebars - app.use(baseContext) + app.use(baseContext); // Setup production environment if (process.env.NODE_ENV === "production") { diff --git a/src/routes/logs.js b/src/routes/logs.js index 7fc4776..b4076da 100644 --- a/src/routes/logs.js +++ b/src/routes/logs.js @@ -6,7 +6,6 @@ const secured = require("../middleware/secured"); const allowedLevels = ["warn", "error", "info", "debug", "functions", "notice"]; -const allowedTypes = ["testing", "live", "dev"]; const dbPath = path.resolve(__dirname, "../../data/logs.sqlite3"); @@ -27,16 +26,16 @@ }); router.post("/logs", secured, (req, res) => { - const log_type = req.query.log_type || "*"; + // const log_type = req.query.log_type || "*"; const log_level = req.query.log_level || "*"; const date = req.query.date || "*"; if (log_level !== "*" && !allowedLevels.includes(log_level)) { return res.status(400).json({ error: "Invalid log_level" }); } - if (log_type !== "*" && !allowedTypes.includes(log_type)) { - return res.status(400).json({ error: "Invalid log_type" }); - } + // if (log_type !== "*" && !allowedTypes.includes(log_type)) { + // return res.status(400).json({ error: "Invalid log_type" }); + // } const conditions = []; const params = []; @@ -51,14 +50,14 @@ params.push(date); } - if (log_type !== "*") { - conditions.push(`EXISTS ( - SELECT 1 FROM log_metadata m - JOIN keys k ON k.id = m.key_id - WHERE m.log_id = l.id AND k.key = 'type' AND m.value = ? - )`); - params.push(log_type); - } + // if (log_type !== "*") { + // conditions.push(`EXISTS ( + // SELECT 1 FROM log_metadata m + // JOIN keys k ON k.id = m.key_id + // WHERE m.log_id = l.id AND k.key = 'type' AND m.value = ? + // )`); + // params.push(log_type); + // } const whereClause = conditions.length ? "WHERE " + conditions.join(" AND ") @@ -103,6 +102,7 @@ ...meta, }; }); + console.log(logs.length); res.json(logs); } catch { diff --git a/src/views/pages/logs.handlebars b/src/views/pages/logs.handlebars index 69eb7bb..7ead8ea 100644 --- a/src/views/pages/logs.handlebars +++ b/src/views/pages/logs.handlebars @@ -8,6 +8,7 @@

Log Viewer

+ {{!-- + --}}