mbkauthe 4.7.0 → 4.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,247 @@
1
+ import path from "path";
2
+ import { AsyncLocalStorage } from "async_hooks";
3
+
4
+ const isDev = process.env.env === "dev" && process.env.dbLogs === "true";
5
+ const requestContext = isDev ? new AsyncLocalStorage() : null;
6
+
7
+ export const runWithRequestContext = (req, fn) => {
8
+ if (!isDev || !requestContext) return fn();
9
+ return requestContext.run({ req }, fn);
10
+ };
11
+
12
+ export const getRequestContext = () => {
13
+ if (!isDev || !requestContext) return undefined;
14
+ return requestContext.getStore();
15
+ };
16
+
17
+ export const attachDevQueryLogger = (pool) => {
18
+ if (!isDev || !pool || pool.__mbkQueryLoggerInstalled) {
19
+ return;
20
+ }
21
+
22
+ pool.__mbkQueryLoggerInstalled = true;
23
+
24
+ // Simple counter for all DB requests made via this pool. This is intentionally lightweight.
25
+ let dbQueryCount = 0;
26
+ const dbQueryLog = [];
27
+ const MAX_QUERY_LOG_ENTRIES = 1000;
28
+
29
+ const originalQuery = pool.query.bind(pool);
30
+
31
+ const safeValue = (value, depth = 0, seen = new WeakSet()) => {
32
+ if (value == null) return value;
33
+ if (typeof value === "string") {
34
+ return value.length > 300 ? `${value.slice(0, 300)}...` : value;
35
+ }
36
+ if (typeof value === "number" || typeof value === "boolean") return value;
37
+ if (typeof value === "bigint") return value.toString();
38
+ if (value instanceof Date) return value.toISOString();
39
+ if (Buffer.isBuffer(value)) return `[buffer:${value.length}]`;
40
+
41
+ if (Array.isArray(value)) {
42
+ if (depth >= 4) return `[array:${value.length}]`;
43
+ const sample = value.slice(0, 8).map((v) => safeValue(v, depth + 1, seen));
44
+ if (value.length > 8) sample.push(`...(${value.length - 8} more)`);
45
+ return sample;
46
+ }
47
+
48
+ if (typeof value === "object") {
49
+ if (seen.has(value)) return "[circular]";
50
+ seen.add(value);
51
+
52
+ const keys = Object.keys(value);
53
+ if (depth >= 4) {
54
+ const head = keys.slice(0, 5).join(", ");
55
+ return keys.length > 5 ? `[object:${head}, ...]` : `[object:${head}]`;
56
+ }
57
+
58
+ const out = {};
59
+ const entries = Object.entries(value).slice(0, 20);
60
+ for (const [k, v] of entries) {
61
+ out[k] = safeValue(v, depth + 1, seen);
62
+ }
63
+ if (keys.length > 20) {
64
+ out.__truncated = `${keys.length - 20} more keys`;
65
+ }
66
+
67
+ seen.delete(value);
68
+ return out;
69
+ }
70
+
71
+ return String(value);
72
+ };
73
+
74
+ const buildReturnValue = (result) => {
75
+ if (!result || typeof result !== "object") return undefined;
76
+
77
+ const returnValue = {
78
+ command: result.command || undefined,
79
+ rowCount: typeof result.rowCount === "number" ? result.rowCount : undefined,
80
+ };
81
+
82
+ if (Array.isArray(result.rows)) {
83
+ const previewSize = 3;
84
+ returnValue.returnedRows = result.rows.length;
85
+ returnValue.rowsPreview = result.rows.slice(0, previewSize).map((row) => safeValue(row));
86
+ if (result.rows.length > previewSize) {
87
+ returnValue.rowsTruncated = true;
88
+ }
89
+ }
90
+
91
+ return returnValue;
92
+ };
93
+
94
+ pool.query = (...args) => {
95
+ dbQueryCount++;
96
+
97
+ // `pg` supports (text, values, callback) or (config, callback).
98
+ let queryText = "";
99
+ let queryName = "";
100
+ let queryValues;
101
+ try {
102
+ if (typeof args[0] === "string") {
103
+ queryText = args[0];
104
+ queryValues = Array.isArray(args[1]) ? args[1] : undefined;
105
+ } else if (args[0] && typeof args[0] === "object") {
106
+ queryText = args[0].text || "";
107
+ queryName = args[0].name || "";
108
+ queryValues = Array.isArray(args[0].values) ? args[0].values : undefined;
109
+ }
110
+ } catch {
111
+ queryText = "";
112
+ }
113
+
114
+ if (!queryText) {
115
+ return originalQuery(...args);
116
+ }
117
+
118
+ const startTime = process.hrtime.bigint();
119
+ const toWorkspacePath = (filePath) => {
120
+ const rel = path.relative(process.cwd(), filePath) || filePath;
121
+ return rel.replace(/\\/g, "/");
122
+ };
123
+
124
+ const buildCallsite = () => {
125
+ try {
126
+ const stack = new Error().stack || "";
127
+ const lines = stack.split("\n").map((l) => l.trim());
128
+ const frame = lines.find(
129
+ (line) =>
130
+ line.startsWith("at ") &&
131
+ !line.includes("/lib/utils/dbQueryLogger.js") &&
132
+ !line.includes("node:internal") &&
133
+ !line.includes("internal/process")
134
+ );
135
+
136
+ if (!frame) return null;
137
+
138
+ const withFunc = /^at\s+([^\s(]+)\s+\((.+):([0-9]+):([0-9]+)\)$/.exec(frame);
139
+ const noFunc = /^at\s+(.+):([0-9]+):([0-9]+)$/.exec(frame);
140
+
141
+ if (withFunc) {
142
+ return {
143
+ function: withFunc[1],
144
+ file: toWorkspacePath(withFunc[2]),
145
+ line: Number(withFunc[3]),
146
+ column: Number(withFunc[4]),
147
+ };
148
+ }
149
+
150
+ if (noFunc) {
151
+ return {
152
+ function: null,
153
+ file: toWorkspacePath(noFunc[1]),
154
+ line: Number(noFunc[2]),
155
+ column: Number(noFunc[3]),
156
+ };
157
+ }
158
+ } catch {
159
+ return null;
160
+ }
161
+ return null;
162
+ };
163
+
164
+ const buildRequestContext = () => {
165
+ const store = getRequestContext();
166
+ const req = store?.req;
167
+ if (!req) return null;
168
+
169
+ const user = req.session?.user || null;
170
+ return {
171
+ method: req.method,
172
+ url: req.originalUrl || req.url,
173
+ ip: req.ip,
174
+ userId: user?.id || null,
175
+ username: user?.username || null,
176
+ };
177
+ };
178
+
179
+ const callsiteSnapshot = buildCallsite();
180
+
181
+ const recordLog = (success, error, result) => {
182
+ const durationMs = Number(process.hrtime.bigint() - startTime) / 1_000_000;
183
+ const request = buildRequestContext();
184
+ const returnValue = buildReturnValue(result);
185
+
186
+ dbQueryLog.push({
187
+ time: new Date().toISOString(),
188
+ query: queryText,
189
+ name: queryName || undefined,
190
+ values: queryValues,
191
+ durationMs,
192
+ success,
193
+ error: error ? { message: error.message, code: error.code } : undefined,
194
+ returnValue,
195
+ request,
196
+ pool: {
197
+ total: pool.totalCount,
198
+ idle: pool.idleCount,
199
+ waiting: pool.waitingCount,
200
+ },
201
+ callsite: callsiteSnapshot,
202
+ });
203
+
204
+ if (dbQueryLog.length > MAX_QUERY_LOG_ENTRIES) {
205
+ dbQueryLog.shift();
206
+ }
207
+ };
208
+
209
+ try {
210
+ const result = originalQuery(...args);
211
+ if (result && typeof result.then === "function") {
212
+ return result
213
+ .then((res) => {
214
+ recordLog(true, null, res);
215
+ return res;
216
+ })
217
+ .catch((err) => {
218
+ recordLog(false, err);
219
+ throw err;
220
+ });
221
+ }
222
+
223
+ recordLog(true, null, result);
224
+ return result;
225
+ } catch (err) {
226
+ recordLog(false, err);
227
+ throw err;
228
+ }
229
+ };
230
+
231
+ pool.getQueryCount = () => dbQueryCount;
232
+ pool.resetQueryCount = () => {
233
+ dbQueryCount = 0;
234
+ };
235
+
236
+ pool.getQueryLog = (options = {}) => {
237
+ const { limit } = options;
238
+ if (typeof limit === "number") {
239
+ return dbQueryLog.slice(-limit);
240
+ }
241
+ return [...dbQueryLog];
242
+ };
243
+
244
+ pool.resetQueryLog = () => {
245
+ dbQueryLog.length = 0;
246
+ };
247
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "4.7.0",
3
+ "version": "4.7.2",
4
4
  "description": "MBKTech's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -53,20 +53,49 @@
53
53
  "registry": "https://registry.npmjs.org/"
54
54
  },
55
55
  "dependencies": {
56
+ "bytes": "^3.1.2",
56
57
  "connect-pg-simple": "^10.0.0",
58
+ "content-type": "^1.0.5",
59
+ "cookie": "^1.1.1",
57
60
  "cookie-parser": "^1.4.7",
58
61
  "csurf": "^1.2.2",
59
- "dotenv": "^16.4.7",
62
+ "debug": "^4.4.3",
63
+ "depd": "^2.0.0",
64
+ "dotenv": "^16.6.1",
65
+ "encodeurl": "^2.0.0",
66
+ "escape-html": "^1.0.3",
67
+ "etag": "^1.8.1",
60
68
  "express": "^5.1.0",
61
69
  "express-handlebars": "^8.0.1",
62
70
  "express-rate-limit": "^7.5.0",
63
71
  "express-session": "^1.18.1",
72
+ "graceful-fs": "^4.2.11",
73
+ "iconv-lite": "^0.7.2",
74
+ "inherits": "^2.0.4",
75
+ "ms": "^2.1.3",
64
76
  "node-fetch": "^3.3.2",
77
+ "on-finished": "^2.4.1",
78
+ "once": "^1.4.0",
79
+ "parseurl": "^1.3.3",
65
80
  "passport": "^0.7.0",
66
81
  "passport-github2": "^0.1.12",
67
82
  "passport-google-oauth20": "^2.0.0",
68
83
  "pg": "^8.14.1",
69
- "speakeasy": "^2.0.0"
84
+ "proxy-addr": "^2.0.7",
85
+ "qs": "^6.15.0",
86
+ "range-parser": "^1.2.1",
87
+ "safe-buffer": "^5.2.1",
88
+ "speakeasy": "^2.0.0",
89
+ "statuses": "^2.0.2",
90
+ "unpipe": "^1.0.0",
91
+ "utils-merge": "^1.0.1",
92
+ "vary": "^1.1.2",
93
+ "xtend": "^4.0.2"
94
+ },
95
+ "overrides": {
96
+ "csurf": {
97
+ "cookie": "^0.7.0"
98
+ }
70
99
  },
71
100
  "devDependencies": {
72
101
  "@types/express": "^5.0.6",
@@ -84,4 +113,4 @@
84
113
  "testTimeout": 10000,
85
114
  "transform": {}
86
115
  }
87
- }
116
+ }