const sqlite3 = require('better-sqlite3') const db = sqlite3(`./data/log.sqlite`) db.pragma('journal_mode = DELETE') db.pragma('synchronous = 0') db.exec(` create table if not exists http (time integer default (strftime('%s','now')), method text, url text, headers text, body text, status text, duration integer); create table if not exists mtproto (time integer default (strftime('%s','now')), message text); create table if not exists error (time integer default (strftime('%s','now')), message text, stack text); `) function http (req, res, duration) { const data = { method: req.method, url: req.url, status: res.statusCode, duration }; ['headers', 'body'].forEach(key => { const value = req.body[key] try { data[key] = value instanceof Array && value.length < 1_000_000 || value instanceof Object ? JSON.stringify(value) : value?.toString() || null } catch (err) { error(err) data[key] = value?.toString() || null } }) db .prepare(`insert into http (method, url, headers, body, status, duration) values (:method, :url, :headers, :body, :status, :duration)`) .run(data) } function safeStringify(obj, indent = 2) { const cache = new Set() return JSON.stringify(obj, (key, value) => { if (typeof value === 'object' && value !== null) { if (cache.has(value)) return '[Circular]' cache.add(value) } return value }, indent) } function mtproto (update) { const message = update message.self = message db .prepare(`insert into mtproto (message) values (:message)`) .run({ message: safeStringify(message) }) } function error (...args) { const stacks = [] const message = args.map(arg => { if (arg instanceof Error) { stacks.push(arg.stack) return 'Error: ' + arg.message } else if (arg instanceof Object) { try { return JSON.stringify(arg) } catch (err) { } } return String(arg) }).join('\n') if (stacks.length == 0) stacks.push(new Error().stack) console.error(message) db .prepare(`insert into error (message, stack) values (:message, :stack)`) .run({ message, stack: stacks.join('\n\n') }) } module.exports = { http, mtproto, error }