mbkauthe 1.1.1 → 1.1.3

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/.env.example CHANGED
@@ -8,7 +8,8 @@ mbkautheVar='{
8
8
  "LOGIN_DB": "postgres://username:password@host:port/database",
9
9
  "MBKAUTH_TWO_FA_ENABLE": "false",
10
10
  "COOKIE_EXPIRE_TIME": 2,
11
- "DOMAIN": "yourdomain.com"
11
+ "DOMAIN": "yourdomain.com",
12
+ "layout": false
12
13
  }'
13
14
 
14
15
  # See env.md for more details
package/README.md CHANGED
@@ -94,7 +94,8 @@ mbkautheVar='{
94
94
  "LOGIN_DB": "postgres://username:password@host:port/database",
95
95
  "MBKAUTH_TWO_FA_ENABLE": "false",
96
96
  "COOKIE_EXPIRE_TIME": 2,
97
- "DOMAIN": "yourdomain.com"
97
+ "DOMAIN": "yourdomain.com",
98
+ "layout": false
98
99
  }'
99
100
  ```
100
101
 
package/env.md CHANGED
@@ -70,4 +70,6 @@ MBKAUTH_TWO_FA_ENABLE=false
70
70
  ```properties
71
71
  COOKIE_EXPIRE_TIME=5
72
72
  ```
73
- > Cookie expiration time in days. Default is `2 days`.
73
+ > Cookie expiration time in days. Default is `2 days`.
74
+
75
+ "layout": false
package/index.js CHANGED
@@ -36,6 +36,7 @@ if (mbkautheVar.BypassUsers !== undefined) {
36
36
  }
37
37
 
38
38
  if (process.env.test === "true") {
39
+ console.log("Test mode is enabled. Starting server in test mode.");
39
40
  const app = express();
40
41
  const port = 3000;
41
42
  app.use(router);
package/lib/info.js CHANGED
@@ -3,6 +3,8 @@ import fetch from 'node-fetch';
3
3
  import { createRequire } from "module";
4
4
  import fs from "fs";
5
5
  import path from "path";
6
+ import { marked } from "marked";
7
+ import * as cheerio from 'cheerio';
6
8
 
7
9
  const require = createRequire(import.meta.url);
8
10
  const packageJson = require("../package.json");
@@ -13,173 +15,936 @@ const mbkautheVar = JSON.parse(process.env.mbkautheVar);
13
15
 
14
16
  const router = express.Router();
15
17
 
16
- // Return package.json data of mbkauthe
17
- router.get("/mbkauthe/package", async (_, res) => {
18
+ async function getLatestVersion() {
18
19
  try {
19
- const response = await fetch("https://mbkauthe.mbktechstudio.com/mbkauthe/package");
20
- const latestPackageData = await response.json();
21
- res.status(200).send(`
22
- <html>
23
- <head>
24
- <title>Package Information</title>
25
- </head>
26
- <body>
27
- <h1>Package Information</h1>
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
- `);
46
- }
47
- });
48
-
49
- // Return version number of mbkauthe
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
- `);
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;
79
29
  }
80
- });
30
+ }
81
31
 
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
- fs.readFile(packageLockPath, "utf8", (err, data) => {
87
- if (err) {
88
- console.error("Error reading package-lock.json:", err);
89
- return res.status(500).json({ success: false, message: "Failed to read package-lock.json" });
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
- const packageLock = JSON.parse(data);
93
- const mbkautheData = {
94
- name: 'mbkauthe',
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, '&amp;')
76
+ .replace(/</g, '&lt;')
77
+ .replace(/>/g, '&gt;');
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
- <html>
117
- <head>
118
- <title>Version and Configuration Information</title>
119
- <style>
120
- body {
121
- font-family: Arial, sans-serif;
122
- line-height: 1.6;
123
- margin: 20px;
124
- }
125
- h1 {
126
- color: #333;
127
- }
128
- p {
129
- margin: 5px 0;
130
- }
131
- a {
132
- display: block;
133
- margin: 10px 0;
134
- color: #007BFF;
135
- text-decoration: none;
136
- }
137
- a:hover {
138
- text-decoration: underline;
139
- }
140
- .info-section {
141
- margin-bottom: 20px;
142
- }
143
- </style>
144
- </head>
145
- <body>
146
- <h1>Version and Configuration Information</h1>
147
- <div class="info-section">
148
- <h2>Current Version</h2>
149
- <p><strong>Version:</strong> ${JSON.stringify(packageJson.version, null, 2)}</p>
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>
354
+ </div>
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>
150
369
  </div>
151
- <div class="info-section">
370
+ </div>
371
+
372
+ <div class="info-section">
152
373
  <h2>Configuration Information</h2>
153
- <p><strong>APP_NAME:</strong> ${mbkautheVar.APP_NAME}</p>
154
- <p><strong>RECAPTCHA_Enabled:</strong> ${mbkautheVar.RECAPTCHA_Enabled}</p>
155
- <p><strong>MBKAUTH_TWO_FA_ENABLE:</strong> ${mbkautheVar.MBKAUTH_TWO_FA_ENABLE}</p>
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>
377
+ </div>
378
+ <div class="info-row">
379
+ <div class="info-label">RECAPTCHA_Enabled:</div>
380
+ <div class="info-value">${mbkautheVar.RECAPTCHA_Enabled}</div>
381
+ </div>
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>
159
385
  </div>
160
- <div class="info-section">
161
- <h2>Useful Links</h2>
162
- <a href="/mbkauthe/package">View mbkauthe package.json</a>
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>
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>
165
389
  </div>
166
- </body>
167
- </html>
168
- `);
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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
- <html>
173
- <head>
174
- <title>Error</title>
175
- </head>
176
- <body>
177
- <h1>Error</h1>
178
- <p>Failed to fetch version information. Please try again later.</p>
179
- </body>
180
- </html>
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
+ `);
182
492
  }
183
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
+ let readmePath;
511
+ if (process.env.test === "true") {
512
+ readmePath = path.join(process.cwd(), "README.md");
513
+ }
514
+ else {
515
+ readmePath = path.join(process.cwd(), "./node_modules/mbkauthe/README.md");
516
+ }
517
+ const data = await fs.promises.readFile(readmePath, "utf8");
518
+
519
+ // Convert markdown to HTML
520
+ let html = marked(data, {
521
+ breaks: true,
522
+ gfm: true,
523
+ smartypants: true
524
+ });
525
+
526
+ // Process HTML with cheerio
527
+ const $ = cheerio.load(html);
528
+
529
+ // Add IDs to headers for anchor links
530
+ $('h1, h2, h3, h4, h5, h6').each(function () {
531
+ const id = $(this).text()
532
+ .toLowerCase()
533
+ .replace(/\s+/g, '-')
534
+ .replace(/[^\w-]+/g, '');
535
+ $(this).attr('id', id);
536
+ $(this).addClass('header-anchor');
537
+ });
538
+
539
+ // Fix table of contents links and add icons
540
+ $('a[href^="#"]').each(function () {
541
+ const href = $(this).attr('href');
542
+ const id = href.substring(1)
543
+ .toLowerCase()
544
+ .replace(/\s+/g, '-')
545
+ .replace(/[^\w-]+/g, '');
546
+ $(this).attr('href', `#${id}`);
547
+ $(this).addClass('toc-link');
548
+ });
549
+
550
+ // Add copy buttons to code blocks
551
+ $('pre').each(function () {
552
+ const $pre = $(this);
553
+ const $button = $(`<button class="copy-button" aria-label="Copy code">📋</button>`);
554
+ $pre.prepend($button);
555
+ });
556
+
557
+ // Create the full HTML response
558
+ const htmlContent = generateFullHtml($.html());
559
+
560
+ // Update cache
561
+ cachedHtml = htmlContent;
562
+ cacheTimestamp = now;
563
+
564
+ res.send(htmlContent);
565
+ } catch (err) {
566
+ console.error("Error processing documentation:", err);
567
+ res.status(500).send(generateErrorHtml());
568
+ }
569
+ });
570
+
571
+ function generateFullHtml(contentHtml) {
572
+ return `<!DOCTYPE html>
573
+ <html lang="en">
574
+ <head>
575
+ <meta charset="UTF-8">
576
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
577
+ <meta name="description" content="Project documentation generated from README.md">
578
+ <title>${DOCUMENTATION_TITLE}</title>
579
+ <style>
580
+ :root {
581
+ --primary-color: #bb86fc;
582
+ --primary-dark: #9a67ea;
583
+ --secondary-color: #03dac6;
584
+ --secondary-dark: #018786;
585
+ --background-dark: #121212;
586
+ --background-darker: #1e1e1e;
587
+ --background-light: #2d2d2d;
588
+ --text-primary: #e0e0e0;
589
+ --text-secondary: #a0a0a0;
590
+ --error-color: #cf6679;
591
+ --success-color: #4caf50;
592
+ }
593
+
594
+ body {
595
+ font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
596
+ line-height: 1.6;
597
+ margin: 0;
598
+ padding: 0;
599
+ background-color: var(--background-dark);
600
+ color: var(--text-primary);
601
+ max-width: 1200px;
602
+ margin: 0 auto;
603
+ padding: 2rem;
604
+ }
605
+
606
+ .header-anchor {
607
+ position: relative;
608
+ padding-left: 1.5rem;
609
+ }
610
+
611
+ .header-anchor::before {
612
+ content: "#";
613
+ position: absolute;
614
+ left: 0;
615
+ color: var(--text-secondary);
616
+ opacity: 0;
617
+ transition: opacity 0.2s;
618
+ }
619
+
620
+ .header-anchor:hover::before {
621
+ opacity: 1;
622
+ }
623
+
624
+ pre {
625
+ position: relative;
626
+ background: var(--background-darker);
627
+ padding: 1.5rem;
628
+ border-radius: 8px;
629
+ overflow-x: auto;
630
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
631
+ }
632
+
633
+ .copy-button {
634
+ position: absolute;
635
+ top: 0.5rem;
636
+ right: 0.5rem;
637
+ background: var(--background-light);
638
+ color: var(--text-primary);
639
+ border: none;
640
+ border-radius: 4px;
641
+ padding: 0.25rem 0.5rem;
642
+ cursor: pointer;
643
+ opacity: 0;
644
+ transition: opacity 0.2s, background 0.2s;
645
+ }
646
+
647
+ pre:hover .copy-button {
648
+ opacity: 1;
649
+ }
650
+
651
+ .copy-button:hover {
652
+ background: var(--primary-color);
653
+ color: var(--background-dark);
654
+ }
655
+
656
+ .copy-button.copied {
657
+ background: var(--success-color);
658
+ color: white;
659
+ }
660
+
661
+ code {
662
+ font-family: 'Fira Code', 'Courier New', monospace;
663
+ background: var(--background-darker);
664
+ padding: 0.2rem 0.4rem;
665
+ border-radius: 4px;
666
+ color: var(--secondary-color);
667
+ font-size: 0.9em;
668
+ word-wrap: break-word;
669
+ }
670
+
671
+ h1, h2, h3, h4, h5, h6 {
672
+ color: var(--primary-color);
673
+ margin-top: 1.8em;
674
+ margin-bottom: 0.8em;
675
+ scroll-margin-top: 1em;
676
+ }
677
+
678
+ h1 {
679
+ font-size: 2.4rem;
680
+ border-bottom: 2px solid var(--primary-color);
681
+ padding-bottom: 0.5rem;
682
+ }
683
+
684
+ h2 { font-size: 2rem; }
685
+ h3 { font-size: 1.6rem; }
686
+
687
+ a {
688
+ color: var(--secondary-color);
689
+ text-decoration: none;
690
+ transition: color 0.2s;
691
+ }
692
+
693
+ a:hover {
694
+ color: var(--primary-color);
695
+ text-decoration: underline;
696
+ }
697
+
698
+ .toc-link {
699
+ display: inline-block;
700
+ padding: 0.2rem 0;
701
+ }
702
+
703
+ .toc-link::before {
704
+ content: "→ ";
705
+ opacity: 0;
706
+ transition: opacity 0.2s;
707
+ }
708
+
709
+ .toc-link:hover::before {
710
+ opacity: 1;
711
+ }
712
+
713
+ blockquote {
714
+ border-left: 4px solid var(--primary-color);
715
+ padding-left: 1.5rem;
716
+ margin-left: 0;
717
+ color: var(--text-secondary);
718
+ font-style: italic;
719
+ background: rgba(187, 134, 252, 0.05);
720
+ border-radius: 0 4px 4px 0;
721
+ padding: 1rem;
722
+ }
723
+
724
+ table {
725
+ border-collapse: collapse;
726
+ width: 100%;
727
+ margin: 1.5rem 0;
728
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
729
+ }
730
+
731
+ th, td {
732
+ border: 1px solid #444;
733
+ padding: 0.75rem;
734
+ text-align: left;
735
+ }
736
+
737
+ th {
738
+ background-color: var(--background-darker);
739
+ font-weight: 600;
740
+ }
741
+
742
+ tr:nth-child(even) {
743
+ background-color: rgba(255, 255, 255, 0.05);
744
+ }
745
+
746
+ tr:hover {
747
+ background-color: rgba(187, 134, 252, 0.1);
748
+ }
749
+
750
+ img {
751
+ max-width: 100%;
752
+ height: auto;
753
+ border-radius: 8px;
754
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
755
+ }
756
+
757
+ hr {
758
+ border: none;
759
+ height: 1px;
760
+ background-color: #444;
761
+ margin: 2rem 0;
762
+ }
763
+
764
+ /* Dark mode toggle */
765
+ .theme-toggle {
766
+ position: fixed;
767
+ bottom: 1rem;
768
+ right: 1rem;
769
+ background: var(--background-light);
770
+ border: none;
771
+ border-radius: 50%;
772
+ width: 3rem;
773
+ height: 3rem;
774
+ display: flex;
775
+ align-items: center;
776
+ justify-content: center;
777
+ cursor: pointer;
778
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
779
+ z-index: 100;
780
+ }
781
+
782
+ /* Responsive design */
783
+ @media (max-width: 768px) {
784
+ body {
785
+ padding: 1rem;
786
+ }
787
+
788
+ h1 {
789
+ font-size: 2rem;
790
+ }
791
+
792
+ h2 {
793
+ font-size: 1.7rem;
794
+ }
795
+
796
+ pre {
797
+ padding: 1rem;
798
+ font-size: 0.9em;
799
+ }
800
+ }
801
+
802
+ /* Print styles */
803
+ @media print {
804
+ body {
805
+ background-color: white;
806
+ color: black;
807
+ padding: 0;
808
+ }
809
+
810
+ a {
811
+ color: #0066cc;
812
+ }
813
+
814
+ pre, code {
815
+ background-color: #f5f5f5;
816
+ color: #333;
817
+ }
818
+ }
819
+ </style>
820
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Fira+Code&display=swap" rel="stylesheet">
821
+ </head>
822
+ <body>
823
+ <a href="/mbkauthe/info/" class="toc-link">mbkauthe Info</a>
824
+ <main>
825
+ ${contentHtml}
826
+ </main>
827
+
828
+ <button class="theme-toggle" aria-label="Toggle theme">🌓</button>
829
+
830
+ <script>
831
+ document.addEventListener('DOMContentLoaded', function() {
832
+ // Smooth scrolling for TOC links
833
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
834
+ anchor.addEventListener('click', function(e) {
835
+ e.preventDefault();
836
+ const targetId = this.getAttribute('href');
837
+ const targetElement = document.querySelector(targetId);
838
+
839
+ if (targetElement) {
840
+ targetElement.scrollIntoView({
841
+ behavior: 'smooth',
842
+ block: 'start'
843
+ });
844
+
845
+ // Update URL without page jump
846
+ history.pushState(null, null, targetId);
847
+ }
848
+ });
849
+ });
850
+
851
+ // Copy button functionality
852
+ document.querySelectorAll('.copy-button').forEach(button => {
853
+ button.addEventListener('click', function() {
854
+ const pre = this.parentElement;
855
+ const code = pre.querySelector('code') || pre;
856
+ const range = document.createRange();
857
+ range.selectNode(code);
858
+ window.getSelection().removeAllRanges();
859
+ window.getSelection().addRange(range);
860
+
861
+ try {
862
+ const successful = document.execCommand('copy');
863
+ if (successful) {
864
+ this.textContent = '✓ Copied!';
865
+ this.classList.add('copied');
866
+ setTimeout(() => {
867
+ this.textContent = '📋';
868
+ this.classList.remove('copied');
869
+ }, 2000);
870
+ }
871
+ } catch (err) {
872
+ console.error('Failed to copy:', err);
873
+ }
874
+
875
+ window.getSelection().removeAllRanges();
876
+ });
877
+ });
878
+
879
+ // Highlight current section in view
880
+ const observerOptions = {
881
+ root: null,
882
+ rootMargin: '0px',
883
+ threshold: 0.5
884
+ };
885
+
886
+ const observer = new IntersectionObserver(function(entries) {
887
+ entries.forEach(function(entry) {
888
+ const id = entry.target.getAttribute('id');
889
+ if (entry.isIntersecting) {
890
+ document.querySelectorAll('a[href="#' + id + '"]').forEach(function(link) {
891
+ link.style.fontWeight = '600';
892
+ link.style.color = 'var(--primary-color)';
893
+ });
894
+ } else {
895
+ document.querySelectorAll('a[href="#' + id + '"]').forEach(function(link) {
896
+ link.style.fontWeight = '';
897
+ link.style.color = '';
898
+ });
899
+ }
900
+ });
901
+ }, observerOptions);
902
+
903
+ document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(function(heading) {
904
+ if (heading.id) {
905
+ observer.observe(heading);
906
+ }
907
+ });
908
+
909
+ // Theme toggle functionality
910
+ const themeToggle = document.querySelector('.theme-toggle');
911
+ themeToggle.addEventListener('click', function() {
912
+ document.body.classList.toggle('light-theme');
913
+ const isLight = document.body.classList.contains('light-theme');
914
+ this.textContent = isLight ? '🌙' : '🌓';
915
+ localStorage.setItem('themePreference', isLight ? 'light' : 'dark');
916
+ });
917
+
918
+ // Load saved theme preference
919
+ const savedTheme = localStorage.getItem('themePreference');
920
+ if (savedTheme === 'light') {
921
+ document.body.classList.add('light-theme');
922
+ themeToggle.textContent = '🌙';
923
+ }
924
+ });
925
+ </script>
926
+ </body>
927
+ </html>`;
928
+ }
929
+
930
+ function generateErrorHtml() {
931
+ return `<!DOCTYPE html>
932
+ <html lang="en">
933
+ <head>
934
+ <meta charset="UTF-8">
935
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
936
+ <title>Error Loading Documentation</title>
937
+ </head>
938
+ <body>
939
+ <h1>Error Loading Documentation</h1>
940
+ <p>Failed to load README.md file. Please try again later.</p>
941
+ <p>If the problem persists, contact your system administrator.</p>
942
+ <a href="/">Return to Home</a>
943
+ <div class="error-details">
944
+ Error: ${err.message || 'Unknown error'}
945
+ </div>
946
+ </body>
947
+ </html>`;
948
+ }
184
949
 
185
950
  export default router;
@@ -36,6 +36,7 @@ async function validateSession(req, res, next) {
36
36
  console.log("User not authenticated");
37
37
  console.log(req.session.user);
38
38
  return res.render("templates/Error/NotLoggedIn.handlebars", {
39
+ layout: mbkautheVar.layout === true ? true : false,
39
40
  currentUrl: req.originalUrl,
40
41
  });
41
42
  }
@@ -54,6 +55,7 @@ async function validateSession(req, res, next) {
54
55
  res.clearCookie("sessionId", cookieOptions);
55
56
  res.clearCookie("username", cookieOptions);
56
57
  return res.render("templates/Error/SessionExpire.handlebars", {
58
+ layout: mbkautheVar.layout === true ? true : false,
57
59
  currentUrl: req.originalUrl,
58
60
  });
59
61
  }
@@ -66,6 +68,7 @@ async function validateSession(req, res, next) {
66
68
  res.clearCookie("sessionId", cookieOptions);
67
69
  res.clearCookie("username", cookieOptions);
68
70
  return res.render("templates/Error/AccountInactive.handlebars", {
71
+ layout: mbkautheVar.layout === true ? true : false,
69
72
  currentUrl: req.originalUrl,
70
73
  });
71
74
  }
@@ -80,6 +83,7 @@ async function validateSession(req, res, next) {
80
83
  res.clearCookie("sessionId", cookieOptions);
81
84
  res.clearCookie("username", cookieOptions);
82
85
  return res.render("templates/Error/Error.handlebars", {
86
+ layout: mbkautheVar.layout === true ? true : false,
83
87
  error: `You Are Not Authorized To Use The Application \"${mbkautheVar.APP_NAME}\"`,
84
88
  });
85
89
  }
@@ -99,6 +103,7 @@ const checkRolePermission = (requiredRole) => {
99
103
  console.log("User not authenticated");
100
104
  console.log(req.session);
101
105
  return res.render("templates/Error/NotLoggedIn.handlebars", {
106
+ layout: mbkautheVar.layout === true ? true : false,
102
107
  currentUrl: req.originalUrl,
103
108
  });
104
109
  }
@@ -119,6 +124,7 @@ const checkRolePermission = (requiredRole) => {
119
124
  const userRole = result.rows[0].Role;
120
125
  if (userRole !== requiredRole) {
121
126
  return res.render("templates/Error/AccessDenied.handlebars", {
127
+ layout: mbkautheVar.layout === true ? true : false,
122
128
  currentRole: userRole,
123
129
  requiredRole: requiredRole,
124
130
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
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
  },