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.
- package/docs/db.sql +4 -1
- package/index.js +4 -0
- package/lib/config/cookies.js +2 -0
- package/lib/pool.js +5 -184
- package/lib/routes/auth.js +14 -8
- package/lib/routes/dbLogs.js +42 -8
- package/lib/routes/misc.js +17 -13
- package/lib/utils/dbQueryLogger.js +247 -0
- package/package.json +33 -4
- package/public/main.css +947 -0
- package/public/main.js +6 -2
- package/views/head.handlebars +12 -0
- package/views/pages/accountSwitch.handlebars +37 -16
- package/views/pages/dbLogs.handlebars +379 -152
- package/views/{errorCodes.handlebars → pages/errorCodes.handlebars} +30 -26
- package/views/pages/info_mbkauthe.handlebars +15 -15
- package/views/pages/loginmbkauthe.handlebars +13 -9
- package/views/pages/test.handlebars +27 -15
- package/views/profilemenu.handlebars +53 -57
- package/views/sharedStyles.handlebars +1 -895
- package/views/showmessage.handlebars +52 -30
- package/views/versionInfo.handlebars +2 -2
package/docs/db.sql
CHANGED
|
@@ -138,7 +138,10 @@ INSERT INTO "Users" ("UserName", "Password", "Role", "Active", "HaveMailAccount"
|
|
|
138
138
|
VALUES ('support', '12345678', 'SuperAdmin', true, false, 'Support User')
|
|
139
139
|
ON CONFLICT ("UserName") DO NOTHING;
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
INSERT INTO "Users" ("UserName", "Password", "Role", "Active", "HaveMailAccount", "FullName")
|
|
142
|
+
VALUES ('admin', '12345678', 'SuperAdmin', true, false, 'Admin User')
|
|
143
|
+
ON CONFLICT ("UserName") DO NOTHING;
|
|
144
|
+
|
|
142
145
|
|
|
143
146
|
-- API Tokens for persistent programmatic access
|
|
144
147
|
CREATE TABLE IF NOT EXISTS "ApiTokens" (
|
package/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { engine } from "express-handlebars";
|
|
|
5
5
|
import path from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { renderError, renderPage } from "#response.js";
|
|
8
|
+
import { packageJson } from "#config.js";
|
|
8
9
|
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = path.dirname(__filename);
|
|
@@ -43,6 +44,9 @@ app.engine("handlebars", engine({
|
|
|
43
44
|
return []; // Return an empty array if obj is undefined, null, or not an object
|
|
44
45
|
}
|
|
45
46
|
return Object.entries(obj).map(([key, value]) => ({ key, value }));
|
|
47
|
+
},
|
|
48
|
+
cacheBuster: function () {
|
|
49
|
+
return "?v=" + packageJson.version;
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
|
package/lib/config/cookies.js
CHANGED
|
@@ -149,6 +149,8 @@ export const clearSessionCookies = (res) => {
|
|
|
149
149
|
res.clearCookie("mbkauthe.sid", cachedClearCookieOptions);
|
|
150
150
|
res.clearCookie("sessionId", cachedClearCookieOptions);
|
|
151
151
|
res.clearCookie("fullName", cachedClearCookieOptions);
|
|
152
|
+
res.clearCookie("profileImageUrl", cachedClearCookieOptions);
|
|
153
|
+
res.clearCookie("profileImageUser", cachedClearCookieOptions);
|
|
152
154
|
res.clearCookie("device_token", cachedClearCookieOptions);
|
|
153
155
|
};
|
|
154
156
|
|
package/lib/pool.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import pkg from "pg";
|
|
2
2
|
const { Pool } = pkg;
|
|
3
3
|
import { mbkautheVar } from "#config.js";
|
|
4
|
+
import { attachDevQueryLogger, runWithRequestContext, getRequestContext } from "./utils/dbQueryLogger.js";
|
|
4
5
|
import dotenv from "dotenv";
|
|
5
6
|
dotenv.config();
|
|
6
7
|
|
|
8
|
+
export { runWithRequestContext, getRequestContext };
|
|
9
|
+
|
|
7
10
|
const poolConfig = {
|
|
8
11
|
connectionString: mbkautheVar.LOGIN_DB,
|
|
9
12
|
ssl: {
|
|
@@ -21,190 +24,8 @@ const poolConfig = {
|
|
|
21
24
|
|
|
22
25
|
export const dblogin = new Pool(poolConfig);
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// For logging and testing purposes, we can track query counts and logs in development mode.
|
|
27
|
-
|
|
28
|
-
import path from "path";
|
|
29
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
30
|
-
|
|
31
|
-
const isDev = process.env.env === 'dev';
|
|
32
|
-
const requestContext = isDev ? new AsyncLocalStorage() : null;
|
|
33
|
-
|
|
34
|
-
export const runWithRequestContext = (req, fn) => {
|
|
35
|
-
if (!isDev || !requestContext) return fn();
|
|
36
|
-
return requestContext.run({ req }, fn);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const getRequestContext = () => {
|
|
40
|
-
if (!isDev || !requestContext) return undefined;
|
|
41
|
-
return requestContext.getStore();
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
if (isDev) {
|
|
45
|
-
|
|
46
|
-
// Simple counter for all DB requests made via this pool. This is intentionally
|
|
47
|
-
// lightweight.
|
|
48
|
-
let _dbQueryCount = 0;
|
|
49
|
-
const _dbQueryLog = [];
|
|
50
|
-
const _MAX_QUERY_LOG_ENTRIES = 1000;
|
|
51
|
-
|
|
52
|
-
const _origQuery = dblogin.query.bind(dblogin);
|
|
53
|
-
|
|
54
|
-
dblogin.query = (...args) => {
|
|
55
|
-
_dbQueryCount++;
|
|
56
|
-
|
|
57
|
-
// Track query text for debugging/metrics.
|
|
58
|
-
// `pg` supports (text, values, callback) or (config, callback).
|
|
59
|
-
let queryText = '';
|
|
60
|
-
let queryName = '';
|
|
61
|
-
let queryValues;
|
|
62
|
-
try {
|
|
63
|
-
if (typeof args[0] === 'string') {
|
|
64
|
-
queryText = args[0];
|
|
65
|
-
queryValues = Array.isArray(args[1]) ? args[1] : undefined;
|
|
66
|
-
} else if (args[0] && typeof args[0] === 'object') {
|
|
67
|
-
queryText = args[0].text || '';
|
|
68
|
-
queryName = args[0].name || '';
|
|
69
|
-
queryValues = Array.isArray(args[0].values) ? args[0].values : undefined;
|
|
70
|
-
}
|
|
71
|
-
} catch {
|
|
72
|
-
queryText = '';
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (!queryText) {
|
|
76
|
-
return _origQuery(...args);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const startTime = process.hrtime.bigint();
|
|
80
|
-
const toWorkspacePath = (filePath) => {
|
|
81
|
-
const rel = path.relative(process.cwd(), filePath) || filePath;
|
|
82
|
-
return rel.replace(/\\/g, '/');
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const buildCallsite = () => {
|
|
86
|
-
try {
|
|
87
|
-
const stack = new Error().stack || '';
|
|
88
|
-
const lines = stack.split('\n').map(l => l.trim());
|
|
89
|
-
// Skip frames from this wrapper and node internals; pick first app frame.
|
|
90
|
-
const frame = lines.find((line) =>
|
|
91
|
-
line.startsWith('at ') &&
|
|
92
|
-
!line.includes('/lib/pool.js') &&
|
|
93
|
-
!line.includes('node:internal') &&
|
|
94
|
-
!line.includes('internal/process')
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
if (!frame) return null;
|
|
98
|
-
|
|
99
|
-
const withFunc = /^at\s+([^\s(]+)\s+\((.+):([0-9]+):([0-9]+)\)$/.exec(frame);
|
|
100
|
-
const noFunc = /^at\s+(.+):([0-9]+):([0-9]+)$/.exec(frame);
|
|
101
|
-
|
|
102
|
-
if (withFunc) {
|
|
103
|
-
return {
|
|
104
|
-
function: withFunc[1],
|
|
105
|
-
file: toWorkspacePath(withFunc[2]),
|
|
106
|
-
line: Number(withFunc[3]),
|
|
107
|
-
column: Number(withFunc[4])
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
if (noFunc) {
|
|
111
|
-
return {
|
|
112
|
-
function: null,
|
|
113
|
-
file: toWorkspacePath(noFunc[1]),
|
|
114
|
-
line: Number(noFunc[2]),
|
|
115
|
-
column: Number(noFunc[3])
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
} catch {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
return null;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const buildRequestContext = () => {
|
|
125
|
-
const store = getRequestContext();
|
|
126
|
-
const req = store?.req;
|
|
127
|
-
if (!req) return null;
|
|
128
|
-
|
|
129
|
-
const user = req.session?.user || null;
|
|
130
|
-
return {
|
|
131
|
-
method: req.method,
|
|
132
|
-
url: req.originalUrl || req.url,
|
|
133
|
-
ip: req.ip,
|
|
134
|
-
userId: user?.id || null,
|
|
135
|
-
username: user?.username || null
|
|
136
|
-
};
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const callsiteSnapshot = buildCallsite();
|
|
140
|
-
|
|
141
|
-
const recordLog = (success, error) => {
|
|
142
|
-
const durationMs = Number(process.hrtime.bigint() - startTime) / 1_000_000;
|
|
143
|
-
const request = buildRequestContext();
|
|
144
|
-
|
|
145
|
-
_dbQueryLog.push({
|
|
146
|
-
time: new Date().toISOString(),
|
|
147
|
-
query: queryText,
|
|
148
|
-
name: queryName || undefined,
|
|
149
|
-
values: queryValues,
|
|
150
|
-
durationMs,
|
|
151
|
-
success,
|
|
152
|
-
error: error ? { message: error.message, code: error.code } : undefined,
|
|
153
|
-
request,
|
|
154
|
-
pool: {
|
|
155
|
-
total: dblogin.totalCount,
|
|
156
|
-
idle: dblogin.idleCount,
|
|
157
|
-
waiting: dblogin.waitingCount
|
|
158
|
-
},
|
|
159
|
-
callsite: callsiteSnapshot
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
if (_dbQueryLog.length > _MAX_QUERY_LOG_ENTRIES) {
|
|
163
|
-
_dbQueryLog.shift();
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const result = _origQuery(...args);
|
|
169
|
-
if (result && typeof result.then === 'function') {
|
|
170
|
-
return result
|
|
171
|
-
.then((res) => {
|
|
172
|
-
recordLog(true, null);
|
|
173
|
-
return res;
|
|
174
|
-
})
|
|
175
|
-
.catch((err) => {
|
|
176
|
-
recordLog(false, err);
|
|
177
|
-
throw err;
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
recordLog(true, null);
|
|
182
|
-
return result;
|
|
183
|
-
} catch (err) {
|
|
184
|
-
recordLog(false, err);
|
|
185
|
-
throw err;
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// Public helpers
|
|
190
|
-
|
|
191
|
-
dblogin.getQueryCount = () => _dbQueryCount;
|
|
192
|
-
dblogin.resetQueryCount = () => {
|
|
193
|
-
_dbQueryCount = 0;
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
dblogin.getQueryLog = (options = {}) => {
|
|
197
|
-
const { limit } = options;
|
|
198
|
-
if (typeof limit === 'number') {
|
|
199
|
-
return _dbQueryLog.slice(-limit);
|
|
200
|
-
}
|
|
201
|
-
return [..._dbQueryLog];
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
dblogin.resetQueryLog = () => {
|
|
205
|
-
_dbQueryLog.length = 0;
|
|
206
|
-
};
|
|
207
|
-
}
|
|
27
|
+
// Keep pool.js focused on pool setup; attach dev-only query logger from dedicated module.
|
|
28
|
+
attachDevQueryLogger(dblogin);
|
|
208
29
|
|
|
209
30
|
(async () => {
|
|
210
31
|
try {
|
package/lib/routes/auth.js
CHANGED
|
@@ -19,10 +19,13 @@ const router = express.Router();
|
|
|
19
19
|
|
|
20
20
|
// Helper function to clear profile picture cache
|
|
21
21
|
function clearProfilePicCache(req, username) {
|
|
22
|
-
if (req.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
if (!req || !req.res || !username) return;
|
|
23
|
+
|
|
24
|
+
const cookieUsername = req.cookies?.profileImageUser;
|
|
25
|
+
if (cookieUsername && cookieUsername !== username) return;
|
|
26
|
+
|
|
27
|
+
req.res.clearCookie('profileImageUrl', cachedClearCookieOptions);
|
|
28
|
+
req.res.clearCookie('profileImageUser', cachedClearCookieOptions);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
// Rate limiters for auth routes
|
|
@@ -285,6 +288,9 @@ export async function completeLoginProcess(req, res, user, redirectUrl = null, t
|
|
|
285
288
|
}
|
|
286
289
|
// Cache display name client-side to avoid extra DB lookups
|
|
287
290
|
res.cookie("fullName", req.session.user.fullname || username, { ...cachedCookieOptions, httpOnly: false });
|
|
291
|
+
const profileImageForCookie = loginProfileImage && typeof loginProfileImage === 'string' ? loginProfileImage : 'default';
|
|
292
|
+
res.cookie('profileImageUrl', profileImageForCookie, { ...cachedCookieOptions, httpOnly: false });
|
|
293
|
+
res.cookie('profileImageUser', username, { ...cachedCookieOptions, httpOnly: false });
|
|
288
294
|
// Record which method was used to login (client-visible badge)
|
|
289
295
|
if (method && typeof method === 'string') {
|
|
290
296
|
try {
|
|
@@ -561,9 +567,6 @@ router.post("/api/verify-2fa", TwoFALimit, csrfProtection, async (req, res) => {
|
|
|
561
567
|
const shouldTrustDevice = trustDevice === true || trustDevice === 'true';
|
|
562
568
|
|
|
563
569
|
try {
|
|
564
|
-
// Use cached allowedApps from preAuthUser to avoid extra database join
|
|
565
|
-
const cachedAllowedApps = req.session.preAuthUser?.allowedApps;
|
|
566
|
-
|
|
567
570
|
const query = `SELECT tfa."TwoFASecret" FROM "TwoFA" tfa WHERE tfa."UserName" = $1`;
|
|
568
571
|
const twoFAResult = await dblogin.query({ name: 'verify-2fa-secret', text: query, values: [username] });
|
|
569
572
|
|
|
@@ -574,7 +577,7 @@ router.post("/api/verify-2fa", TwoFALimit, csrfProtection, async (req, res) => {
|
|
|
574
577
|
}
|
|
575
578
|
|
|
576
579
|
const sharedSecret = twoFAResult.rows[0].TwoFASecret;
|
|
577
|
-
const allowedApps =
|
|
580
|
+
const allowedApps = req.session.preAuthUser?.allowedApps;
|
|
578
581
|
const tokenValidates = speakeasy.totp.verify({
|
|
579
582
|
secret: sharedSecret,
|
|
580
583
|
encoding: "base32",
|
|
@@ -771,6 +774,9 @@ router.post("/api/switch-session", LoginLimit, async (req, res) => {
|
|
|
771
774
|
|
|
772
775
|
// Sync sessionId cookie and remember list
|
|
773
776
|
res.cookie('fullName', fullName, { ...cachedCookieOptions, httpOnly: false });
|
|
777
|
+
const switchProfileForCookie = switchProfileImage && typeof switchProfileImage === 'string' ? switchProfileImage : 'default';
|
|
778
|
+
res.cookie('profileImageUrl', switchProfileForCookie, { ...cachedCookieOptions, httpOnly: false });
|
|
779
|
+
res.cookie('profileImageUser', row.UserName, { ...cachedCookieOptions, httpOnly: false });
|
|
774
780
|
const encryptedSid = encryptSessionId(row.sid);
|
|
775
781
|
if (encryptedSid) {
|
|
776
782
|
res.cookie('sessionId', encryptedSid, cachedCookieOptions);
|
package/lib/routes/dbLogs.js
CHANGED
|
@@ -6,6 +6,8 @@ import rateLimit from 'express-rate-limit';
|
|
|
6
6
|
|
|
7
7
|
const router = express.Router();
|
|
8
8
|
|
|
9
|
+
const isDbLogsEnabled = () => process.env.env === "dev" && process.env.dbLogs === "true";
|
|
10
|
+
|
|
9
11
|
// Rate limiter for info/test routes
|
|
10
12
|
const LogLimit = rateLimit({
|
|
11
13
|
windowMs: 1 * 60 * 1000,
|
|
@@ -23,33 +25,65 @@ const LogLimit = rateLimit({
|
|
|
23
25
|
// DB stats API (JSON)
|
|
24
26
|
router.get(["/db.json"], LogLimit, async (req, res) => {
|
|
25
27
|
try {
|
|
26
|
-
const
|
|
28
|
+
const isDev = isDbLogsEnabled();
|
|
27
29
|
const queryLimit = Number(req.query.limit) || 50;
|
|
28
|
-
const queryLog = typeof dblogin.getQueryLog === 'function' ? dblogin.getQueryLog({ limit: queryLimit }) : [];
|
|
29
|
-
const resetRequested = req.query.reset === '1';
|
|
30
30
|
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
if (!isDev) {
|
|
32
|
+
return res.status(403).json({
|
|
33
|
+
success: false,
|
|
34
|
+
message: "DB logs are disabled.",
|
|
35
|
+
isDev,
|
|
36
|
+
queryCount: 0,
|
|
37
|
+
queryLimit,
|
|
38
|
+
queryLog: []
|
|
39
|
+
});
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
const queryCount = typeof dblogin.getQueryCount === 'function' ? dblogin.getQueryCount() : null;
|
|
43
|
+
const queryLog = typeof dblogin.getQueryLog === 'function' ? dblogin.getQueryLog({ limit: queryLimit }) : [];
|
|
44
|
+
|
|
45
|
+
return res.json({ queryCount, queryLimit, queryLog, isDev });
|
|
37
46
|
} catch (err) {
|
|
38
47
|
console.error('[mbkauthe] /db.json route error:', err);
|
|
39
48
|
return res.status(500).json({ success: false, message: 'Could not fetch DB stats.' });
|
|
40
49
|
}
|
|
41
50
|
});
|
|
42
51
|
|
|
52
|
+
// Dedicated reset API for DB logs and counters
|
|
53
|
+
router.post(["/db/reset"], LogLimit, async (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const isDev = isDbLogsEnabled();
|
|
56
|
+
if (!isDev) {
|
|
57
|
+
return res.status(403).json({
|
|
58
|
+
success: false,
|
|
59
|
+
message: "DB logs are disabled.",
|
|
60
|
+
isDev
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof dblogin.resetQueryCount === 'function') dblogin.resetQueryCount();
|
|
65
|
+
if (typeof dblogin.resetQueryLog === 'function') dblogin.resetQueryLog();
|
|
66
|
+
|
|
67
|
+
return res.json({ success: true, message: 'Query log and count have been reset.' });
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error('[mbkauthe] /db/reset route error:', err);
|
|
70
|
+
return res.status(500).json({ success: false, message: 'Could not reset DB stats.' });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
43
74
|
// DB stats page (HTML)
|
|
44
75
|
router.get(["/db"], LogLimit, async (req, res) => {
|
|
45
76
|
try {
|
|
77
|
+
const isDev = isDbLogsEnabled();
|
|
46
78
|
const queryLimit = Number(req.query.limit) || 50;
|
|
47
79
|
const resetDone = req.query.resetDone === '1';
|
|
48
80
|
return res.render('pages/dbLogs.handlebars', {
|
|
49
81
|
layout: false,
|
|
50
82
|
appName: mbkautheVar.APP_NAME,
|
|
51
83
|
queryLimit,
|
|
52
|
-
resetDone
|
|
84
|
+
resetDone,
|
|
85
|
+
isDev,
|
|
86
|
+
disabledMessage: isDev ? null : 'DB logs are disabled.'
|
|
53
87
|
});
|
|
54
88
|
} catch (err) {
|
|
55
89
|
console.error('[mbkauthe] /db route error:', err);
|
package/lib/routes/misc.js
CHANGED
|
@@ -2,11 +2,11 @@ import express from "express";
|
|
|
2
2
|
import fetch from 'node-fetch';
|
|
3
3
|
import rateLimit from 'express-rate-limit';
|
|
4
4
|
import { mbkautheVar, packageJson, appVersion } from "#config.js";
|
|
5
|
-
import { renderError } from "#response.js";
|
|
5
|
+
import { renderError, renderPage } from "#response.js";
|
|
6
6
|
import { authenticate, validateSession, validateSessionAndRole } from "../middleware/auth.js";
|
|
7
7
|
import { ErrorCodes, ErrorMessages, createErrorResponse } from "../utils/errors.js";
|
|
8
8
|
import { dblogin } from "#pool.js";
|
|
9
|
-
import { clearSessionCookies, decryptSessionId } from "#cookies.js";
|
|
9
|
+
import { clearSessionCookies, decryptSessionId, cachedCookieOptions } from "#cookies.js";
|
|
10
10
|
import { fileURLToPath } from "url";
|
|
11
11
|
import path from "path";
|
|
12
12
|
import fs from "fs";
|
|
@@ -91,8 +91,12 @@ router.get('/user/profilepic', async (req, res) => {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
const username = req.session.user.username;
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
let imageUrl = null;
|
|
95
|
+
const cookieUser = req.cookies?.profileImageUser;
|
|
96
|
+
const cookieImageUrl = req.cookies?.profileImageUrl;
|
|
97
|
+
if (cookieUser === username && typeof cookieImageUrl === 'string' && cookieImageUrl.length > 0) {
|
|
98
|
+
imageUrl = cookieImageUrl;
|
|
99
|
+
}
|
|
96
100
|
|
|
97
101
|
// If not in cache, fetch from DB
|
|
98
102
|
if (!imageUrl) {
|
|
@@ -107,7 +111,8 @@ router.get('/user/profilepic', async (req, res) => {
|
|
|
107
111
|
} else {
|
|
108
112
|
imageUrl = 'default';
|
|
109
113
|
}
|
|
110
|
-
|
|
114
|
+
res.cookie('profileImageUrl', imageUrl, { ...cachedCookieOptions, httpOnly: false });
|
|
115
|
+
res.cookie('profileImageUser', username, { ...cachedCookieOptions, httpOnly: false });
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
// Generate ETag based on username and image URL
|
|
@@ -137,7 +142,8 @@ router.get('/user/profilepic', async (req, res) => {
|
|
|
137
142
|
|
|
138
143
|
if (!imageResponse.ok) {
|
|
139
144
|
console.warn(`[mbkauthe] Failed to fetch profile pic from ${imageUrl}, status: ${imageResponse.status}`);
|
|
140
|
-
|
|
145
|
+
res.cookie('profileImageUrl', 'default', { ...cachedCookieOptions, httpOnly: false });
|
|
146
|
+
res.cookie('profileImageUser', username, { ...cachedCookieOptions, httpOnly: false });
|
|
141
147
|
return serveDefaultIcon();
|
|
142
148
|
}
|
|
143
149
|
|
|
@@ -147,7 +153,8 @@ router.get('/user/profilepic', async (req, res) => {
|
|
|
147
153
|
imageResponse.body.pipe(res);
|
|
148
154
|
} catch (fetchErr) {
|
|
149
155
|
console.error('[mbkauthe] Error fetching external profile picture:', fetchErr);
|
|
150
|
-
|
|
156
|
+
res.cookie('profileImageUrl', 'default', { ...cachedCookieOptions, httpOnly: false });
|
|
157
|
+
res.cookie('profileImageUser', username, { ...cachedCookieOptions, httpOnly: false });
|
|
151
158
|
return serveDefaultIcon();
|
|
152
159
|
}
|
|
153
160
|
|
|
@@ -187,8 +194,7 @@ router.get(['/test', '/'], validateSession, LoginLimit, async (req, res) => {
|
|
|
187
194
|
? new Date(req.session.cookie.expires).toISOString()
|
|
188
195
|
: null;
|
|
189
196
|
|
|
190
|
-
return res
|
|
191
|
-
layout: false,
|
|
197
|
+
return renderPage(req, res, 'pages/test.handlebars', false, {
|
|
192
198
|
username,
|
|
193
199
|
fullname: fullname || 'N/A',
|
|
194
200
|
role,
|
|
@@ -439,8 +445,7 @@ router.get("/ErrorCode", (req, res) => {
|
|
|
439
445
|
}))
|
|
440
446
|
})).filter(category => category.errors.length > 0); // Remove empty categories
|
|
441
447
|
|
|
442
|
-
res
|
|
443
|
-
layout: false,
|
|
448
|
+
return renderPage(req, res, "pages/errorCodes.handlebars", false, {
|
|
444
449
|
pageTitle: 'Error Codes',
|
|
445
450
|
appName: mbkautheVar.APP_NAME,
|
|
446
451
|
errorCategories: categoriesWithErrors
|
|
@@ -505,8 +510,7 @@ router.get(["/info", "/i"], LoginLimit, async (req, res) => {
|
|
|
505
510
|
}
|
|
506
511
|
|
|
507
512
|
try {
|
|
508
|
-
res
|
|
509
|
-
layout: false,
|
|
513
|
+
renderPage(req, res, "pages/info_mbkauthe.handlebars", false, {
|
|
510
514
|
mbkautheVar: safe_mbkautheVar,
|
|
511
515
|
CurrentVersion: packageJson.version,
|
|
512
516
|
APP_VERSION: appVersion,
|