mbkauthe 1.1.0 → 1.1.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/index.js +1 -0
- package/lib/info.js +912 -153
- package/lib/main.js +2 -12
- package/lib/pool.js +13 -15
- package/lib/validateSessionAndRole.js +0 -2
- package/package.json +3 -1
package/index.js
CHANGED
package/lib/info.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import express from "express";
|
|
2
2
|
import fetch from 'node-fetch';
|
|
3
|
-
|
|
4
3
|
import { createRequire } from "module";
|
|
5
|
-
const require = createRequire(import.meta.url);
|
|
6
|
-
const packageJson = require("../package.json");
|
|
7
4
|
import fs from "fs";
|
|
8
5
|
import path from "path";
|
|
6
|
+
import { marked } from "marked";
|
|
7
|
+
import * as cheerio from 'cheerio';
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const packageJson = require("../package.json");
|
|
9
11
|
|
|
10
12
|
import dotenv from "dotenv";
|
|
11
13
|
dotenv.config();
|
|
@@ -13,173 +15,930 @@ const mbkautheVar = JSON.parse(process.env.mbkautheVar);
|
|
|
13
15
|
|
|
14
16
|
const router = express.Router();
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
router.get("/mbkauthe/package", async (_, res) => {
|
|
18
|
+
async function getLatestVersion() {
|
|
18
19
|
try {
|
|
19
|
-
const response = await fetch(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<p><strong>Current Version:</strong> ${JSON.stringify(packageJson, null, 2)}</p>
|
|
29
|
-
<p><strong>Latest Version:</strong> ${JSON.stringify(latestPackageData, null, 2)}</p>
|
|
30
|
-
</body>
|
|
31
|
-
</html>
|
|
32
|
-
`);
|
|
33
|
-
} catch (err) {
|
|
34
|
-
res.status(200).send(`
|
|
35
|
-
<html>
|
|
36
|
-
<head>
|
|
37
|
-
<title>Package Information</title>
|
|
38
|
-
</head>
|
|
39
|
-
<body>
|
|
40
|
-
<h1>Package Information</h1>
|
|
41
|
-
<p><strong>Current Version:</strong> ${JSON.stringify(packageJson, null, 2)}</p>
|
|
42
|
-
<p><strong>Latest Version:</strong> Failed to fetch latest package data, Erro:${err.message}</p>
|
|
43
|
-
</body>
|
|
44
|
-
</html>
|
|
45
|
-
`);
|
|
20
|
+
const response = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkauthe/main/package.json');
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new Error(`GitHub API responded with status ${response.status}`);
|
|
23
|
+
}
|
|
24
|
+
const latestPackageJson = await response.json();
|
|
25
|
+
return latestPackageJson.version;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Error fetching latest version from GitHub:', error);
|
|
28
|
+
return null;
|
|
46
29
|
}
|
|
47
|
-
}
|
|
30
|
+
}
|
|
48
31
|
|
|
49
|
-
|
|
50
|
-
router.get(["/mbkauthe/version", "/mbkauthe/v"], async (_, res) => {
|
|
51
|
-
try {
|
|
52
|
-
const response = await fetch("https://raw.githubusercontent.com/MIbnEKhalid/mbkauthe/refs/heads/main/package.json");
|
|
53
|
-
const latestPackageData = await response.json();
|
|
54
|
-
res.status(200).send(`
|
|
55
|
-
<html>
|
|
56
|
-
<head>
|
|
57
|
-
<title>Version Information</title>
|
|
58
|
-
</head>
|
|
59
|
-
<body>
|
|
60
|
-
<h1>Package Information</h1>
|
|
61
|
-
<p><strong>Current Version:</strong> ${JSON.stringify(packageJson.version, null, 2)}</p>
|
|
62
|
-
<p><strong>Latest Version:</strong> ${JSON.stringify(latestPackageData.version, null, 2)}</p>
|
|
63
|
-
</body>
|
|
64
|
-
</html>
|
|
65
|
-
`);
|
|
66
|
-
} catch (err) {
|
|
67
|
-
res.status(200).send(`
|
|
68
|
-
<html>
|
|
69
|
-
<head>
|
|
70
|
-
<title>Package Information</title>
|
|
71
|
-
</head>
|
|
72
|
-
<body>
|
|
73
|
-
<h1>Package Information</h1>
|
|
74
|
-
<p><strong>Current Version:</strong> ${JSON.stringify(packageJson.version, null, 2)}</p>
|
|
75
|
-
<p><strong>Latest Version:</strong> Failed to fetch latest package data, Erro:${err.message}</p>
|
|
76
|
-
</body>
|
|
77
|
-
</html>
|
|
78
|
-
`);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// Return package-lock.json data of mbkauthe from project the package is installed in
|
|
83
|
-
router.get("/mbkauthe/package-lock", (_, res) => {
|
|
84
|
-
console.log("Request for package-lock.json received");
|
|
32
|
+
async function getPackageLock() {
|
|
85
33
|
const packageLockPath = path.resolve(process.cwd(), "package-lock.json");
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
34
|
+
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
fs.readFile(packageLockPath, "utf8", (err, data) => {
|
|
37
|
+
if (err) {
|
|
38
|
+
console.error("Error reading package-lock.json:", err);
|
|
39
|
+
return reject({ success: false, message: "Failed to read package-lock.json" });
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const packageLock = JSON.parse(data);
|
|
43
|
+
const mbkautheData = {
|
|
44
|
+
name: 'mbkauthe',
|
|
45
|
+
version: packageLock.packages['node_modules/mbkauthe']?.version || packageJson.version,
|
|
46
|
+
resolved: packageLock.packages['node_modules/mbkauthe']?.resolved || '',
|
|
47
|
+
integrity: packageLock.packages['node_modules/mbkauthe']?.integrity || '',
|
|
48
|
+
license: packageLock.packages['node_modules/mbkauthe']?.license || packageJson.license,
|
|
49
|
+
dependencies: packageLock.packages['node_modules/mbkauthe']?.dependencies || {}
|
|
50
|
+
};
|
|
51
|
+
const rootDependency = packageLock.dependencies?.mbkauthe || {};
|
|
52
|
+
resolve({ mbkautheData, rootDependency });
|
|
53
|
+
} catch (parseError) {
|
|
54
|
+
console.error("Error parsing package-lock.json:", parseError);
|
|
55
|
+
reject("Error parsing package-lock.json");
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function formatJson(json) {
|
|
62
|
+
if (typeof json === 'string') {
|
|
91
63
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
version: packageLock.packages['node_modules/mbkauthe'].version,
|
|
96
|
-
resolved: packageLock.packages['node_modules/mbkauthe'].resolved,
|
|
97
|
-
integrity: packageLock.packages['node_modules/mbkauthe'].integrity,
|
|
98
|
-
license: packageLock.packages['node_modules/mbkauthe'].license,
|
|
99
|
-
dependencies: packageLock.packages['node_modules/mbkauthe'].dependencies
|
|
100
|
-
};
|
|
101
|
-
const rootDependency = packageLock.packages[''].dependencies.mbkauthe;
|
|
102
|
-
console.log('mbkauthe package data:', mbkautheData);
|
|
103
|
-
console.log('Root dependency version:', rootDependency);
|
|
104
|
-
res.status(200).json({ mbkautheData, rootDependency });
|
|
105
|
-
} catch (parseError) {
|
|
106
|
-
console.error("Error parsing package-lock.json:", parseError);
|
|
107
|
-
res.status(500).json({ success: false, message: "Failed to parse package-lock.json" });
|
|
64
|
+
json = JSON.parse(json);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return json;
|
|
108
67
|
}
|
|
109
|
-
}
|
|
110
|
-
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// First stringify with proper indentation
|
|
71
|
+
let jsonString = JSON.stringify(json, null, 2);
|
|
72
|
+
|
|
73
|
+
// Escape HTML special characters EXCEPT for our span tags
|
|
74
|
+
jsonString = jsonString
|
|
75
|
+
.replace(/&/g, '&')
|
|
76
|
+
.replace(/</g, '<')
|
|
77
|
+
.replace(/>/g, '>');
|
|
78
|
+
|
|
79
|
+
// Now apply syntax highlighting (after escaping)
|
|
80
|
+
jsonString = jsonString
|
|
81
|
+
// Highlight keys
|
|
82
|
+
.replace(/"([^"]+)":/g, '"<span style="color: #2b6cb0;">$1</span>":')
|
|
83
|
+
// Highlight string values
|
|
84
|
+
.replace(/:\s*"([^"]+)"/g, ': "<span style="color: #38a169;">$1</span>"')
|
|
85
|
+
// Highlight numbers
|
|
86
|
+
.replace(/: (\d+)/g, ': <span style="color: #dd6b20;">$1</span>')
|
|
87
|
+
// Highlight booleans and null
|
|
88
|
+
.replace(/: (true|false|null)/g, ': <span style="color: #805ad5;">$1</span>');
|
|
89
|
+
|
|
90
|
+
return jsonString;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
router.get(["/mbkauthe/info", "/mbkauthe/i"], async (_, res) => {
|
|
94
|
+
let pkgl = {};
|
|
95
|
+
let latestVersion;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
pkgl = await getPackageLock();
|
|
99
|
+
// latestVersion = await getLatestVersion();
|
|
100
|
+
latestVersion = "Under Development"; // Placeholder for the latest version
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.error("Error fetching package-lock.json:", err);
|
|
103
|
+
pkgl = { error: "Failed to fetch package-lock.json" };
|
|
104
|
+
}
|
|
111
105
|
|
|
112
|
-
// Return version number of mbkauthe
|
|
113
|
-
router.get(["/mbkauthe", "/mbkauthe/info", "/mbkauthe/i"], async (_, res) => {
|
|
114
106
|
try {
|
|
115
107
|
res.status(200).send(`
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
108
|
+
<html>
|
|
109
|
+
<head>
|
|
110
|
+
<title>Version and Configuration Information</title>
|
|
111
|
+
<style>
|
|
112
|
+
:root {
|
|
113
|
+
--bg-color: #121212;
|
|
114
|
+
--card-bg: #1e1e1e;
|
|
115
|
+
--text-color: #e0e0e0;
|
|
116
|
+
--text-secondary: #a0a0a0;
|
|
117
|
+
--primary: #bb86fc;
|
|
118
|
+
--primary-dark: #3700b3;
|
|
119
|
+
--secondary: #03dac6;
|
|
120
|
+
--border-color: #333;
|
|
121
|
+
--success: #4caf50;
|
|
122
|
+
--warning: #ff9800;
|
|
123
|
+
--error: #f44336;
|
|
124
|
+
--key-color: #bb86fc;
|
|
125
|
+
--string-color: #03dac6;
|
|
126
|
+
--number-color: #ff7043;
|
|
127
|
+
--boolean-color: #7986cb;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
body {
|
|
131
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
132
|
+
margin: 0;
|
|
133
|
+
padding: 20px;
|
|
134
|
+
background-color: var(--bg-color);
|
|
135
|
+
color: var(--text-color);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.container {
|
|
139
|
+
max-width: 1000px;
|
|
140
|
+
margin: 0 auto;
|
|
141
|
+
padding: 20px;
|
|
142
|
+
background: var(--card-bg);
|
|
143
|
+
border-radius: 8px;
|
|
144
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
h1 {
|
|
148
|
+
color: var(--primary);
|
|
149
|
+
text-align: center;
|
|
150
|
+
margin-bottom: 30px;
|
|
151
|
+
padding-bottom: 10px;
|
|
152
|
+
border-bottom: 1px solid var(--border-color);
|
|
153
|
+
font-weight: bold;
|
|
154
|
+
letter-spacing: 1px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.info-section {
|
|
158
|
+
margin-bottom: 25px;
|
|
159
|
+
padding: 20px;
|
|
160
|
+
border: 1px solid var(--border-color);
|
|
161
|
+
border-radius: 8px;
|
|
162
|
+
background-color: rgba(30, 30, 30, 0.7);
|
|
163
|
+
transition: all 0.3s ease;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.info-section:hover {
|
|
167
|
+
border-color: var(--primary);
|
|
168
|
+
box-shadow: 0 0 0 1px var(--primary);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.info-section h2 {
|
|
172
|
+
color: var(--primary);
|
|
173
|
+
border-bottom: 2px solid var(--primary-dark);
|
|
174
|
+
padding-bottom: 8px;
|
|
175
|
+
margin-top: 0;
|
|
176
|
+
margin-bottom: 15px;
|
|
177
|
+
font-size: 1.2em;
|
|
178
|
+
display: flex;
|
|
179
|
+
justify-content: space-between;
|
|
180
|
+
align-items: center;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.info-row {
|
|
184
|
+
display: flex;
|
|
185
|
+
margin-bottom: 10px;
|
|
186
|
+
padding-bottom: 10px;
|
|
187
|
+
border-bottom: 1px solid var(--border-color);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.info-label {
|
|
191
|
+
font-weight: 600;
|
|
192
|
+
color: var(--text-secondary);
|
|
193
|
+
min-width: 220px;
|
|
194
|
+
font-size: 0.95em;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.info-value {
|
|
198
|
+
flex: 1;
|
|
199
|
+
word-break: break-word;
|
|
200
|
+
color: var(--text-color);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.json-container {
|
|
204
|
+
background: #252525;
|
|
205
|
+
border: 1px solid var(--border-color);
|
|
206
|
+
border-radius: 6px;
|
|
207
|
+
padding: 12px;
|
|
208
|
+
margin-top: 10px;
|
|
209
|
+
max-height: 400px;
|
|
210
|
+
overflow: auto;
|
|
211
|
+
font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
|
|
212
|
+
font-size: 0.85em;
|
|
213
|
+
white-space: pre-wrap;
|
|
214
|
+
position: relative;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.json-container pre {
|
|
218
|
+
margin: 0;
|
|
219
|
+
font-family: inherit;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.json-container .key {
|
|
223
|
+
color: var(--key-color);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.json-container .string {
|
|
227
|
+
color: var(--string-color);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.json-container .number {
|
|
231
|
+
color: var(--number-color);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.json-container .boolean {
|
|
235
|
+
color: var(--boolean-color);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.json-container .null {
|
|
239
|
+
color: var(--boolean-color);
|
|
240
|
+
opacity: 0.7;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.version-status {
|
|
244
|
+
display: inline-block;
|
|
245
|
+
padding: 3px 10px;
|
|
246
|
+
border-radius: 12px;
|
|
247
|
+
font-size: 0.8em;
|
|
248
|
+
font-weight: 600;
|
|
249
|
+
margin-left: 10px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.version-up-to-date {
|
|
253
|
+
background: rgba(76, 175, 80, 0.2);
|
|
254
|
+
color: var(--success);
|
|
255
|
+
border: 1px solid var(--success);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.version-outdated {
|
|
259
|
+
background: rgba(244, 67, 54, 0.2);
|
|
260
|
+
color: var(--error);
|
|
261
|
+
border: 1px solid var(--error);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.version-fetch-error {
|
|
265
|
+
background: rgba(255, 152, 0, 0.2);
|
|
266
|
+
color: var(--warning);
|
|
267
|
+
border: 1px solid var(--warning);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.copy-btn {
|
|
271
|
+
background: var(--primary-dark);
|
|
272
|
+
color: white;
|
|
273
|
+
border: none;
|
|
274
|
+
padding: 5px 12px;
|
|
275
|
+
border-radius: 4px;
|
|
276
|
+
cursor: pointer;
|
|
277
|
+
font-size: 0.8em;
|
|
278
|
+
transition: all 0.2s ease;
|
|
279
|
+
display: flex;
|
|
280
|
+
align-items: center;
|
|
281
|
+
gap: 5px;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.copy-btn:hover {
|
|
285
|
+
background: var(--primary);
|
|
286
|
+
transform: translateY(-1px);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.copy-btn:active {
|
|
290
|
+
transform: translateY(0);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* Scrollbar styling */
|
|
294
|
+
::-webkit-scrollbar {
|
|
295
|
+
width: 8px;
|
|
296
|
+
height: 8px;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
::-webkit-scrollbar-track {
|
|
300
|
+
background: #2d2d2d;
|
|
301
|
+
border-radius: 4px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
::-webkit-scrollbar-thumb {
|
|
305
|
+
background: #555;
|
|
306
|
+
border-radius: 4px;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
::-webkit-scrollbar-thumb:hover {
|
|
310
|
+
background: var(--primary);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Tooltip for copy button */
|
|
314
|
+
.tooltip {
|
|
315
|
+
position: relative;
|
|
316
|
+
display: inline-block;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.tooltip .tooltiptext {
|
|
320
|
+
visibility: hidden;
|
|
321
|
+
width: 120px;
|
|
322
|
+
background-color: #333;
|
|
323
|
+
color: #fff;
|
|
324
|
+
text-align: center;
|
|
325
|
+
border-radius: 6px;
|
|
326
|
+
padding: 5px;
|
|
327
|
+
position: absolute;
|
|
328
|
+
z-index: 1;
|
|
329
|
+
bottom: 125%;
|
|
330
|
+
left: 50%;
|
|
331
|
+
margin-left: -60px;
|
|
332
|
+
opacity: 0;
|
|
333
|
+
transition: opacity 0.3s;
|
|
334
|
+
font-size: 0.8em;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.tooltip:hover .tooltiptext {
|
|
338
|
+
visibility: visible;
|
|
339
|
+
opacity: 1;
|
|
340
|
+
}
|
|
341
|
+
</style>
|
|
342
|
+
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
|
343
|
+
</head>
|
|
344
|
+
|
|
345
|
+
<body>
|
|
346
|
+
<div class="container">
|
|
347
|
+
<h1>Version and Configuration Dashboard</h1>
|
|
348
|
+
|
|
349
|
+
<div class="info-section">
|
|
350
|
+
<h2>Version Information</h2>
|
|
351
|
+
<div class="info-row">
|
|
352
|
+
<div class="info-label">Current Version:</div>
|
|
353
|
+
<div class="info-value">${packageJson.version}</div>
|
|
150
354
|
</div>
|
|
151
|
-
<div class="info-
|
|
355
|
+
<div class="info-row">
|
|
356
|
+
<div class="info-label">Latest Version:</div>
|
|
357
|
+
<div class="info-value">
|
|
358
|
+
${latestVersion || 'Could not fetch latest version'}
|
|
359
|
+
${latestVersion ? `
|
|
360
|
+
<span class="version-status ${packageJson.version === latestVersion ? 'version-up-to-date' : 'version-outdated'}">
|
|
361
|
+
${packageJson.version === latestVersion ? 'Up to date' : 'Update available'}
|
|
362
|
+
</span>
|
|
363
|
+
` : `
|
|
364
|
+
<span class="version-status version-fetch-error">
|
|
365
|
+
Fetch error
|
|
366
|
+
</span>
|
|
367
|
+
`}
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
<div class="info-section">
|
|
152
373
|
<h2>Configuration Information</h2>
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
<p><strong>COOKIE_EXPIRE_TIME:</strong> ${mbkautheVar.COOKIE_EXPIRE_TIME} Days</p>
|
|
157
|
-
<p><strong>IS_DEPLOYED:</strong> ${mbkautheVar.IS_DEPLOYED}</p>
|
|
158
|
-
<p><strong>DOMAIN:</strong> ${mbkautheVar.DOMAIN}</p>
|
|
374
|
+
<div class="info-row">
|
|
375
|
+
<div class="info-label">APP_NAME:</div>
|
|
376
|
+
<div class="info-value">${mbkautheVar.APP_NAME}</div>
|
|
159
377
|
</div>
|
|
160
|
-
<div class="info-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
<a href="/mbkauthe/package-lock">View mbkauthe version info from installed project package-lock.json</a>
|
|
164
|
-
<a href="/mbkauthe/version">View Current and Latest Package Version</a>
|
|
378
|
+
<div class="info-row">
|
|
379
|
+
<div class="info-label">RECAPTCHA_Enabled:</div>
|
|
380
|
+
<div class="info-value">${mbkautheVar.RECAPTCHA_Enabled}</div>
|
|
165
381
|
</div>
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
382
|
+
<div class="info-row">
|
|
383
|
+
<div class="info-label">MBKAUTH_TWO_FA_ENABLE:</div>
|
|
384
|
+
<div class="info-value">${mbkautheVar.MBKAUTH_TWO_FA_ENABLE}</div>
|
|
385
|
+
</div>
|
|
386
|
+
<div class="info-row">
|
|
387
|
+
<div class="info-label">COOKIE_EXPIRE_TIME:</div>
|
|
388
|
+
<div class="info-value">${mbkautheVar.COOKIE_EXPIRE_TIME} Days</div>
|
|
389
|
+
</div>
|
|
390
|
+
<div class="info-row">
|
|
391
|
+
<div class="info-label">IS_DEPLOYED:</div>
|
|
392
|
+
<div class="info-value">${mbkautheVar.IS_DEPLOYED}</div>
|
|
393
|
+
</div>
|
|
394
|
+
<div class="info-row">
|
|
395
|
+
<div class="info-label">DOMAIN:</div>
|
|
396
|
+
<div class="info-value">${mbkautheVar.DOMAIN}</div>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
|
|
400
|
+
<div class="info-section">
|
|
401
|
+
<h2>
|
|
402
|
+
Package Information
|
|
403
|
+
<button class="copy-btn tooltip" onclick="copyToClipboard('package-json')">
|
|
404
|
+
<span class="tooltiptext">Copy to clipboard</span>
|
|
405
|
+
Copy JSON
|
|
406
|
+
</button>
|
|
407
|
+
</h2>
|
|
408
|
+
<div id="package-json" class="json-container"><pre>${JSON.stringify(packageJson, null, 2)}</pre></div>
|
|
409
|
+
</div>
|
|
410
|
+
|
|
411
|
+
<div class="info-section">
|
|
412
|
+
<h2>
|
|
413
|
+
Package Lock
|
|
414
|
+
<button class="copy-btn tooltip" onclick="copyToClipboard('package-lock')">
|
|
415
|
+
<span class="tooltiptext">Copy to clipboard</span>
|
|
416
|
+
Copy JSON
|
|
417
|
+
</button>
|
|
418
|
+
</h2>
|
|
419
|
+
<div id="package-lock" class="json-container"><pre>${JSON.stringify(pkgl, null, 2)}</pre></div>
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
422
|
+
|
|
423
|
+
<script>
|
|
424
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
425
|
+
// Apply syntax highlighting to all JSON containers
|
|
426
|
+
const jsonContainers = document.querySelectorAll('.json-container pre');
|
|
427
|
+
jsonContainers.forEach(container => {
|
|
428
|
+
container.innerHTML = syntaxHighlight(container.textContent);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
function syntaxHighlight(json) {
|
|
433
|
+
if (typeof json !== 'string') {
|
|
434
|
+
json = JSON.stringify(json, null, 2);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Escape HTML
|
|
438
|
+
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
439
|
+
|
|
440
|
+
// Apply syntax highlighting
|
|
441
|
+
return json.replace(
|
|
442
|
+
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
|
|
443
|
+
function(match) {
|
|
444
|
+
let cls = 'number';
|
|
445
|
+
if (/^"/.test(match)) {
|
|
446
|
+
if (/:$/.test(match)) {
|
|
447
|
+
cls = 'key';
|
|
448
|
+
} else {
|
|
449
|
+
cls = 'string';
|
|
450
|
+
}
|
|
451
|
+
} else if (/true|false/.test(match)) {
|
|
452
|
+
cls = 'boolean';
|
|
453
|
+
} else if (/null/.test(match)) {
|
|
454
|
+
cls = 'null';
|
|
455
|
+
}
|
|
456
|
+
return '<span class="' + cls + '">' + match + '</span>';
|
|
457
|
+
}
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function copyToClipboard(elementId) {
|
|
462
|
+
const element = document.getElementById(elementId);
|
|
463
|
+
const text = element.textContent;
|
|
464
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
465
|
+
const btn = element.parentElement.querySelector('.copy-btn');
|
|
466
|
+
const originalText = btn.innerHTML;
|
|
467
|
+
btn.innerHTML = '<span class="tooltiptext">Copied!</span>✓ Copied';
|
|
468
|
+
setTimeout(() => {
|
|
469
|
+
btn.innerHTML = '<span class="tooltiptext">Copy to clipboard</span>' + originalText.replace('✓ Copied', 'Copy JSON');
|
|
470
|
+
}, 2000);
|
|
471
|
+
}).catch(err => {
|
|
472
|
+
console.error('Failed to copy text: ', err);
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
</script>
|
|
476
|
+
</body>
|
|
477
|
+
</html>
|
|
478
|
+
`);
|
|
169
479
|
} catch (err) {
|
|
170
480
|
console.error("Error fetching version information:", err);
|
|
171
481
|
res.status(500).send(`
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
482
|
+
<html>
|
|
483
|
+
<head>
|
|
484
|
+
<title>Error</title>
|
|
485
|
+
</head>
|
|
486
|
+
<body>
|
|
487
|
+
<h1>Error</h1>
|
|
488
|
+
<p>Failed to fetch version information. Please try again later.</p>
|
|
489
|
+
</body>
|
|
490
|
+
</html>
|
|
491
|
+
`);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
const DOCUMENTATION_TITLE = "Project Documentation";
|
|
495
|
+
const CACHE_TTL = 3600000; // 1 hour in milliseconds
|
|
496
|
+
|
|
497
|
+
// Cache for the rendered HTML
|
|
498
|
+
let cachedHtml = null;
|
|
499
|
+
let cacheTimestamp = 0;
|
|
500
|
+
|
|
501
|
+
router.get(["/mbkauthe/"], async (_, res) => {
|
|
502
|
+
try {
|
|
503
|
+
// Check cache first
|
|
504
|
+
const now = Date.now();
|
|
505
|
+
if (cachedHtml && (now - cacheTimestamp) < CACHE_TTL) {
|
|
506
|
+
return res.send(cachedHtml);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Read and process file
|
|
510
|
+
const readmePath = path.join(process.cwd(), "README.md");
|
|
511
|
+
const data = await fs.promises.readFile(readmePath, "utf8");
|
|
512
|
+
|
|
513
|
+
// Convert markdown to HTML
|
|
514
|
+
let html = marked(data, {
|
|
515
|
+
breaks: true,
|
|
516
|
+
gfm: true,
|
|
517
|
+
smartypants: true
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
// Process HTML with cheerio
|
|
521
|
+
const $ = cheerio.load(html);
|
|
522
|
+
|
|
523
|
+
// Add IDs to headers for anchor links
|
|
524
|
+
$('h1, h2, h3, h4, h5, h6').each(function() {
|
|
525
|
+
const id = $(this).text()
|
|
526
|
+
.toLowerCase()
|
|
527
|
+
.replace(/\s+/g, '-')
|
|
528
|
+
.replace(/[^\w-]+/g, '');
|
|
529
|
+
$(this).attr('id', id);
|
|
530
|
+
$(this).addClass('header-anchor');
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// Fix table of contents links and add icons
|
|
534
|
+
$('a[href^="#"]').each(function() {
|
|
535
|
+
const href = $(this).attr('href');
|
|
536
|
+
const id = href.substring(1)
|
|
537
|
+
.toLowerCase()
|
|
538
|
+
.replace(/\s+/g, '-')
|
|
539
|
+
.replace(/[^\w-]+/g, '');
|
|
540
|
+
$(this).attr('href', `#${id}`);
|
|
541
|
+
$(this).addClass('toc-link');
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
// Add copy buttons to code blocks
|
|
545
|
+
$('pre').each(function() {
|
|
546
|
+
const $pre = $(this);
|
|
547
|
+
const $button = $(`<button class="copy-button" aria-label="Copy code">📋</button>`);
|
|
548
|
+
$pre.prepend($button);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// Create the full HTML response
|
|
552
|
+
const htmlContent = generateFullHtml($.html());
|
|
553
|
+
|
|
554
|
+
// Update cache
|
|
555
|
+
cachedHtml = htmlContent;
|
|
556
|
+
cacheTimestamp = now;
|
|
557
|
+
|
|
558
|
+
res.send(htmlContent);
|
|
559
|
+
} catch (err) {
|
|
560
|
+
console.error("Error processing documentation:", err);
|
|
561
|
+
res.status(500).send(generateErrorHtml());
|
|
182
562
|
}
|
|
183
563
|
});
|
|
184
564
|
|
|
565
|
+
function generateFullHtml(contentHtml) {
|
|
566
|
+
return `<!DOCTYPE html>
|
|
567
|
+
<html lang="en">
|
|
568
|
+
<head>
|
|
569
|
+
<meta charset="UTF-8">
|
|
570
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
571
|
+
<meta name="description" content="Project documentation generated from README.md">
|
|
572
|
+
<title>${DOCUMENTATION_TITLE}</title>
|
|
573
|
+
<style>
|
|
574
|
+
:root {
|
|
575
|
+
--primary-color: #bb86fc;
|
|
576
|
+
--primary-dark: #9a67ea;
|
|
577
|
+
--secondary-color: #03dac6;
|
|
578
|
+
--secondary-dark: #018786;
|
|
579
|
+
--background-dark: #121212;
|
|
580
|
+
--background-darker: #1e1e1e;
|
|
581
|
+
--background-light: #2d2d2d;
|
|
582
|
+
--text-primary: #e0e0e0;
|
|
583
|
+
--text-secondary: #a0a0a0;
|
|
584
|
+
--error-color: #cf6679;
|
|
585
|
+
--success-color: #4caf50;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
body {
|
|
589
|
+
font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
590
|
+
line-height: 1.6;
|
|
591
|
+
margin: 0;
|
|
592
|
+
padding: 0;
|
|
593
|
+
background-color: var(--background-dark);
|
|
594
|
+
color: var(--text-primary);
|
|
595
|
+
max-width: 1200px;
|
|
596
|
+
margin: 0 auto;
|
|
597
|
+
padding: 2rem;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.header-anchor {
|
|
601
|
+
position: relative;
|
|
602
|
+
padding-left: 1.5rem;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.header-anchor::before {
|
|
606
|
+
content: "#";
|
|
607
|
+
position: absolute;
|
|
608
|
+
left: 0;
|
|
609
|
+
color: var(--text-secondary);
|
|
610
|
+
opacity: 0;
|
|
611
|
+
transition: opacity 0.2s;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.header-anchor:hover::before {
|
|
615
|
+
opacity: 1;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
pre {
|
|
619
|
+
position: relative;
|
|
620
|
+
background: var(--background-darker);
|
|
621
|
+
padding: 1.5rem;
|
|
622
|
+
border-radius: 8px;
|
|
623
|
+
overflow-x: auto;
|
|
624
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.copy-button {
|
|
628
|
+
position: absolute;
|
|
629
|
+
top: 0.5rem;
|
|
630
|
+
right: 0.5rem;
|
|
631
|
+
background: var(--background-light);
|
|
632
|
+
color: var(--text-primary);
|
|
633
|
+
border: none;
|
|
634
|
+
border-radius: 4px;
|
|
635
|
+
padding: 0.25rem 0.5rem;
|
|
636
|
+
cursor: pointer;
|
|
637
|
+
opacity: 0;
|
|
638
|
+
transition: opacity 0.2s, background 0.2s;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
pre:hover .copy-button {
|
|
642
|
+
opacity: 1;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.copy-button:hover {
|
|
646
|
+
background: var(--primary-color);
|
|
647
|
+
color: var(--background-dark);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.copy-button.copied {
|
|
651
|
+
background: var(--success-color);
|
|
652
|
+
color: white;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
code {
|
|
656
|
+
font-family: 'Fira Code', 'Courier New', monospace;
|
|
657
|
+
background: var(--background-darker);
|
|
658
|
+
padding: 0.2rem 0.4rem;
|
|
659
|
+
border-radius: 4px;
|
|
660
|
+
color: var(--secondary-color);
|
|
661
|
+
font-size: 0.9em;
|
|
662
|
+
word-wrap: break-word;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
h1, h2, h3, h4, h5, h6 {
|
|
666
|
+
color: var(--primary-color);
|
|
667
|
+
margin-top: 1.8em;
|
|
668
|
+
margin-bottom: 0.8em;
|
|
669
|
+
scroll-margin-top: 1em;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
h1 {
|
|
673
|
+
font-size: 2.4rem;
|
|
674
|
+
border-bottom: 2px solid var(--primary-color);
|
|
675
|
+
padding-bottom: 0.5rem;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
h2 { font-size: 2rem; }
|
|
679
|
+
h3 { font-size: 1.6rem; }
|
|
680
|
+
|
|
681
|
+
a {
|
|
682
|
+
color: var(--secondary-color);
|
|
683
|
+
text-decoration: none;
|
|
684
|
+
transition: color 0.2s;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
a:hover {
|
|
688
|
+
color: var(--primary-color);
|
|
689
|
+
text-decoration: underline;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.toc-link {
|
|
693
|
+
display: inline-block;
|
|
694
|
+
padding: 0.2rem 0;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.toc-link::before {
|
|
698
|
+
content: "→ ";
|
|
699
|
+
opacity: 0;
|
|
700
|
+
transition: opacity 0.2s;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.toc-link:hover::before {
|
|
704
|
+
opacity: 1;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
blockquote {
|
|
708
|
+
border-left: 4px solid var(--primary-color);
|
|
709
|
+
padding-left: 1.5rem;
|
|
710
|
+
margin-left: 0;
|
|
711
|
+
color: var(--text-secondary);
|
|
712
|
+
font-style: italic;
|
|
713
|
+
background: rgba(187, 134, 252, 0.05);
|
|
714
|
+
border-radius: 0 4px 4px 0;
|
|
715
|
+
padding: 1rem;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
table {
|
|
719
|
+
border-collapse: collapse;
|
|
720
|
+
width: 100%;
|
|
721
|
+
margin: 1.5rem 0;
|
|
722
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
th, td {
|
|
726
|
+
border: 1px solid #444;
|
|
727
|
+
padding: 0.75rem;
|
|
728
|
+
text-align: left;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
th {
|
|
732
|
+
background-color: var(--background-darker);
|
|
733
|
+
font-weight: 600;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
tr:nth-child(even) {
|
|
737
|
+
background-color: rgba(255, 255, 255, 0.05);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
tr:hover {
|
|
741
|
+
background-color: rgba(187, 134, 252, 0.1);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
img {
|
|
745
|
+
max-width: 100%;
|
|
746
|
+
height: auto;
|
|
747
|
+
border-radius: 8px;
|
|
748
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
hr {
|
|
752
|
+
border: none;
|
|
753
|
+
height: 1px;
|
|
754
|
+
background-color: #444;
|
|
755
|
+
margin: 2rem 0;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/* Dark mode toggle */
|
|
759
|
+
.theme-toggle {
|
|
760
|
+
position: fixed;
|
|
761
|
+
bottom: 1rem;
|
|
762
|
+
right: 1rem;
|
|
763
|
+
background: var(--background-light);
|
|
764
|
+
border: none;
|
|
765
|
+
border-radius: 50%;
|
|
766
|
+
width: 3rem;
|
|
767
|
+
height: 3rem;
|
|
768
|
+
display: flex;
|
|
769
|
+
align-items: center;
|
|
770
|
+
justify-content: center;
|
|
771
|
+
cursor: pointer;
|
|
772
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
773
|
+
z-index: 100;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/* Responsive design */
|
|
777
|
+
@media (max-width: 768px) {
|
|
778
|
+
body {
|
|
779
|
+
padding: 1rem;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
h1 {
|
|
783
|
+
font-size: 2rem;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
h2 {
|
|
787
|
+
font-size: 1.7rem;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
pre {
|
|
791
|
+
padding: 1rem;
|
|
792
|
+
font-size: 0.9em;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/* Print styles */
|
|
797
|
+
@media print {
|
|
798
|
+
body {
|
|
799
|
+
background-color: white;
|
|
800
|
+
color: black;
|
|
801
|
+
padding: 0;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
a {
|
|
805
|
+
color: #0066cc;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
pre, code {
|
|
809
|
+
background-color: #f5f5f5;
|
|
810
|
+
color: #333;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
</style>
|
|
814
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Fira+Code&display=swap" rel="stylesheet">
|
|
815
|
+
</head>
|
|
816
|
+
<body>
|
|
817
|
+
<a href="/mbkauthe/info/" class="toc-link">mbkauthe Info</a>
|
|
818
|
+
<main>
|
|
819
|
+
${contentHtml}
|
|
820
|
+
</main>
|
|
821
|
+
|
|
822
|
+
<button class="theme-toggle" aria-label="Toggle theme">🌓</button>
|
|
823
|
+
|
|
824
|
+
<script>
|
|
825
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
826
|
+
// Smooth scrolling for TOC links
|
|
827
|
+
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
828
|
+
anchor.addEventListener('click', function(e) {
|
|
829
|
+
e.preventDefault();
|
|
830
|
+
const targetId = this.getAttribute('href');
|
|
831
|
+
const targetElement = document.querySelector(targetId);
|
|
832
|
+
|
|
833
|
+
if (targetElement) {
|
|
834
|
+
targetElement.scrollIntoView({
|
|
835
|
+
behavior: 'smooth',
|
|
836
|
+
block: 'start'
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
// Update URL without page jump
|
|
840
|
+
history.pushState(null, null, targetId);
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
// Copy button functionality
|
|
846
|
+
document.querySelectorAll('.copy-button').forEach(button => {
|
|
847
|
+
button.addEventListener('click', function() {
|
|
848
|
+
const pre = this.parentElement;
|
|
849
|
+
const code = pre.querySelector('code') || pre;
|
|
850
|
+
const range = document.createRange();
|
|
851
|
+
range.selectNode(code);
|
|
852
|
+
window.getSelection().removeAllRanges();
|
|
853
|
+
window.getSelection().addRange(range);
|
|
854
|
+
|
|
855
|
+
try {
|
|
856
|
+
const successful = document.execCommand('copy');
|
|
857
|
+
if (successful) {
|
|
858
|
+
this.textContent = '✓ Copied!';
|
|
859
|
+
this.classList.add('copied');
|
|
860
|
+
setTimeout(() => {
|
|
861
|
+
this.textContent = '📋';
|
|
862
|
+
this.classList.remove('copied');
|
|
863
|
+
}, 2000);
|
|
864
|
+
}
|
|
865
|
+
} catch (err) {
|
|
866
|
+
console.error('Failed to copy:', err);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
window.getSelection().removeAllRanges();
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// Highlight current section in view
|
|
874
|
+
const observerOptions = {
|
|
875
|
+
root: null,
|
|
876
|
+
rootMargin: '0px',
|
|
877
|
+
threshold: 0.5
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
const observer = new IntersectionObserver(function(entries) {
|
|
881
|
+
entries.forEach(function(entry) {
|
|
882
|
+
const id = entry.target.getAttribute('id');
|
|
883
|
+
if (entry.isIntersecting) {
|
|
884
|
+
document.querySelectorAll('a[href="#' + id + '"]').forEach(function(link) {
|
|
885
|
+
link.style.fontWeight = '600';
|
|
886
|
+
link.style.color = 'var(--primary-color)';
|
|
887
|
+
});
|
|
888
|
+
} else {
|
|
889
|
+
document.querySelectorAll('a[href="#' + id + '"]').forEach(function(link) {
|
|
890
|
+
link.style.fontWeight = '';
|
|
891
|
+
link.style.color = '';
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
}, observerOptions);
|
|
896
|
+
|
|
897
|
+
document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(function(heading) {
|
|
898
|
+
if (heading.id) {
|
|
899
|
+
observer.observe(heading);
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// Theme toggle functionality
|
|
904
|
+
const themeToggle = document.querySelector('.theme-toggle');
|
|
905
|
+
themeToggle.addEventListener('click', function() {
|
|
906
|
+
document.body.classList.toggle('light-theme');
|
|
907
|
+
const isLight = document.body.classList.contains('light-theme');
|
|
908
|
+
this.textContent = isLight ? '🌙' : '🌓';
|
|
909
|
+
localStorage.setItem('themePreference', isLight ? 'light' : 'dark');
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
// Load saved theme preference
|
|
913
|
+
const savedTheme = localStorage.getItem('themePreference');
|
|
914
|
+
if (savedTheme === 'light') {
|
|
915
|
+
document.body.classList.add('light-theme');
|
|
916
|
+
themeToggle.textContent = '🌙';
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
</script>
|
|
920
|
+
</body>
|
|
921
|
+
</html>`;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function generateErrorHtml() {
|
|
925
|
+
return `<!DOCTYPE html>
|
|
926
|
+
<html lang="en">
|
|
927
|
+
<head>
|
|
928
|
+
<meta charset="UTF-8">
|
|
929
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
930
|
+
<title>Error Loading Documentation</title>
|
|
931
|
+
</head>
|
|
932
|
+
<body>
|
|
933
|
+
<h1>Error Loading Documentation</h1>
|
|
934
|
+
<p>Failed to load README.md file. Please try again later.</p>
|
|
935
|
+
<p>If the problem persists, contact your system administrator.</p>
|
|
936
|
+
<a href="/">Return to Home</a>
|
|
937
|
+
<div class="error-details">
|
|
938
|
+
Error: ${err.message || 'Unknown error'}
|
|
939
|
+
</div>
|
|
940
|
+
</body>
|
|
941
|
+
</html>`;
|
|
942
|
+
}
|
|
943
|
+
|
|
185
944
|
export default router;
|
package/lib/main.js
CHANGED
|
@@ -17,7 +17,6 @@ const mbkautheVar = JSON.parse(process.env.mbkautheVar);
|
|
|
17
17
|
|
|
18
18
|
const router = express.Router();
|
|
19
19
|
|
|
20
|
-
// Enable CORS for subdomains
|
|
21
20
|
router.use((req, res, next) => {
|
|
22
21
|
const origin = req.headers.origin;
|
|
23
22
|
if (origin && origin.endsWith(`.${mbkautheVar.DOMAIN}`)) {
|
|
@@ -33,7 +32,6 @@ router.use(express.json());
|
|
|
33
32
|
router.use(express.urlencoded({ extended: true }));
|
|
34
33
|
router.use(cookieParser());
|
|
35
34
|
|
|
36
|
-
// Add rate limiting for sensitive operations
|
|
37
35
|
const LoginLimit = rateLimit({
|
|
38
36
|
windowMs: 1 * 60 * 1000,
|
|
39
37
|
max: 8,
|
|
@@ -43,7 +41,6 @@ const LoginLimit = rateLimit({
|
|
|
43
41
|
}
|
|
44
42
|
});
|
|
45
43
|
|
|
46
|
-
// Configure session with proper domain settings for cross-subdomain sharing
|
|
47
44
|
const sessionConfig = {
|
|
48
45
|
store: new PgSession({
|
|
49
46
|
pool: dblogin,
|
|
@@ -53,12 +50,12 @@ const sessionConfig = {
|
|
|
53
50
|
secret: mbkautheVar.SESSION_SECRET_KEY,
|
|
54
51
|
resave: false,
|
|
55
52
|
saveUninitialized: false,
|
|
56
|
-
proxy: true,
|
|
53
|
+
proxy: true,
|
|
57
54
|
cookie: {
|
|
58
55
|
maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
|
|
59
56
|
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
60
57
|
httpOnly: true,
|
|
61
|
-
secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
|
|
58
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
|
|
62
59
|
sameSite: 'lax',
|
|
63
60
|
path: '/'
|
|
64
61
|
},
|
|
@@ -67,7 +64,6 @@ const sessionConfig = {
|
|
|
67
64
|
|
|
68
65
|
router.use(session(sessionConfig));
|
|
69
66
|
|
|
70
|
-
// Middleware to handle session restoration from sessionId cookie
|
|
71
67
|
router.use(async (req, res, next) => {
|
|
72
68
|
if (!req.session.user && req.cookies.sessionId) {
|
|
73
69
|
try {
|
|
@@ -90,7 +86,6 @@ router.use(async (req, res, next) => {
|
|
|
90
86
|
next();
|
|
91
87
|
});
|
|
92
88
|
|
|
93
|
-
// Set consistent cookie options for all cookies
|
|
94
89
|
const getCookieOptions = () => ({
|
|
95
90
|
maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
|
|
96
91
|
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
@@ -120,7 +115,6 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_
|
|
|
120
115
|
return res.status(500).json({ success: false, message: "Failed to terminate sessions" });
|
|
121
116
|
}
|
|
122
117
|
|
|
123
|
-
// Clear all cookies with proper domain
|
|
124
118
|
const cookieOptions = getCookieOptions();
|
|
125
119
|
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
126
120
|
res.clearCookie("sessionId", cookieOptions);
|
|
@@ -203,7 +197,6 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
|
|
|
203
197
|
return res.status(500).json({ success: false, errorCode: 605, message: `Internal Server Error` });
|
|
204
198
|
}
|
|
205
199
|
} else {
|
|
206
|
-
// Check if the password matches
|
|
207
200
|
if (user.Password !== password) {
|
|
208
201
|
console.log(`Incorrect password for username: ${username}`);
|
|
209
202
|
return res.status(401).json({ success: false, errorCode: 603, message: "Incorrect Username Or Password" });
|
|
@@ -286,10 +279,8 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
|
|
|
286
279
|
try {
|
|
287
280
|
const { id, username } = req.session.user;
|
|
288
281
|
|
|
289
|
-
// Clear the SessionId in the database first
|
|
290
282
|
await dblogin.query(`UPDATE "Users" SET "SessionId" = NULL WHERE "id" = $1`, [id]);
|
|
291
283
|
|
|
292
|
-
// Remove the session from the session table
|
|
293
284
|
if (req.sessionID) {
|
|
294
285
|
await dblogin.query('DELETE FROM "session" WHERE sid = $1', [req.sessionID]);
|
|
295
286
|
}
|
|
@@ -300,7 +291,6 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
|
|
|
300
291
|
return res.status(500).json({ success: false, message: "Logout failed" });
|
|
301
292
|
}
|
|
302
293
|
|
|
303
|
-
// Clear all cookies with proper domain
|
|
304
294
|
const cookieOptions = getCookieOptions();
|
|
305
295
|
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
306
296
|
res.clearCookie("sessionId", cookieOptions);
|
package/lib/pool.js
CHANGED
|
@@ -15,28 +15,27 @@ if (!mbkautheVar) {
|
|
|
15
15
|
}
|
|
16
16
|
const requiredKeys = ["APP_NAME", "RECAPTCHA_Enabled", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
|
|
17
17
|
requiredKeys.forEach(key => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (!mbkautheVar[key]) {
|
|
19
|
+
throw new Error(`mbkautheVar.${key} is required`);
|
|
20
|
+
}
|
|
21
21
|
});
|
|
22
22
|
if (mbkautheVar.RECAPTCHA_Enabled === "true") {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (mbkautheVar.RECAPTCHA_SECRET_KEY === undefined) {
|
|
24
|
+
throw new Error("mbkautheVar.RECAPTCHA_SECRET_KEY is required");
|
|
25
|
+
}
|
|
26
26
|
}
|
|
27
27
|
if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
|
|
29
|
+
if (isNaN(expireTime) || expireTime <= 0) {
|
|
30
|
+
throw new Error("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
|
|
31
|
+
}
|
|
32
32
|
}
|
|
33
33
|
if (mbkautheVar.BypassUsers !== undefined) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
if (!Array.isArray(mbkautheVar.BypassUsers)) {
|
|
35
|
+
throw new Error("mbkautheVar.BypassUsers must be a valid array");
|
|
36
|
+
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// PostgreSQL connection pool for pool
|
|
40
39
|
const poolConfig = {
|
|
41
40
|
connectionString: mbkautheVar.LOGIN_DB,
|
|
42
41
|
ssl: {
|
|
@@ -47,7 +46,6 @@ const poolConfig = {
|
|
|
47
46
|
|
|
48
47
|
export const dblogin = new Pool(poolConfig);
|
|
49
48
|
|
|
50
|
-
// Test connection for pool
|
|
51
49
|
(async () => {
|
|
52
50
|
try {
|
|
53
51
|
const client = await dblogin.connect();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { dblogin } from "./pool.js";
|
|
2
2
|
const mbkautheVar = JSON.parse(process.env.mbkautheVar);
|
|
3
3
|
|
|
4
|
-
// Get consistent cookie options
|
|
5
4
|
const getCookieOptions = () => ({
|
|
6
5
|
maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
|
|
7
6
|
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
@@ -12,7 +11,6 @@ const getCookieOptions = () => ({
|
|
|
12
11
|
});
|
|
13
12
|
|
|
14
13
|
async function validateSession(req, res, next) {
|
|
15
|
-
// First check if we have a session cookie
|
|
16
14
|
if (!req.session.user && req.cookies.sessionId) {
|
|
17
15
|
try {
|
|
18
16
|
const sessionId = req.cookies.sessionId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mbkauthe",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "MBKTechStudio's reusable authentication system for Node.js applications.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -27,12 +27,14 @@
|
|
|
27
27
|
"homepage": "https://github.com/MIbnEKhalid/mbkauthe#readme",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"bcrypt": "^5.1.1",
|
|
30
|
+
"cheerio": "^1.0.0",
|
|
30
31
|
"connect-pg-simple": "^10.0.0",
|
|
31
32
|
"cookie-parser": "^1.4.7",
|
|
32
33
|
"dotenv": "^16.4.7",
|
|
33
34
|
"express": "^5.1.0",
|
|
34
35
|
"express-rate-limit": "^7.5.0",
|
|
35
36
|
"express-session": "^1.18.1",
|
|
37
|
+
"marked": "^15.0.11",
|
|
36
38
|
"node-fetch": "^3.3.2",
|
|
37
39
|
"pg": "^8.14.1"
|
|
38
40
|
},
|