mbkauthe 1.1.6 → 1.1.8

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 CHANGED
@@ -1,9 +1,17 @@
1
1
  import express from "express"; // Add this line
2
2
  import router from "./lib/main.js";
3
-
3
+ import { getLatestVersion } from "./lib/info.js";
4
+ import { engine } from "express-handlebars";
4
5
  import dotenv from "dotenv";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+
5
9
  dotenv.config();
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
6
13
  let mbkautheVar;
14
+
7
15
  try {
8
16
  mbkautheVar = JSON.parse(process.env.mbkautheVar);
9
17
  } catch (error) {
@@ -35,9 +43,9 @@ if (mbkautheVar.BypassUsers !== undefined) {
35
43
  }
36
44
  }
37
45
 
46
+ const app = express();
38
47
  if (process.env.test === "true") {
39
48
  console.log("Test mode is enabled. Starting server in test mode.");
40
- const app = express();
41
49
  const port = 3000;
42
50
  app.use(router);
43
51
  app.listen(port, () => {
@@ -45,6 +53,25 @@ if (process.env.test === "true") {
45
53
  });
46
54
  }
47
55
 
56
+ app.set("views", path.join(__dirname, "node_modules/mbkauthe/views"));
57
+
58
+ app.engine("handlebars", engine({
59
+ defaultLayout: false,
60
+ partialsDir: [
61
+ path.join(__dirname, "node_modules/mbkauthe/views"),
62
+ ],
63
+ }));
64
+
65
+ app.set("view engine", "handlebars");
66
+
67
+ import { createRequire } from "module";
68
+ const require = createRequire(import.meta.url);
69
+ const packageJson = require("./package.json");
70
+ const latestVersion = await getLatestVersion();
71
+ if(latestVersion !== packageJson.version) {
72
+ console.warn(`Warning: The current version (${packageJson.version}) is not the latest version (${latestVersion}). Please update mbkauthe.`);
73
+ }
74
+
48
75
  export { validateSession, checkRolePermission, validateSessionAndRole, getUserData, authenticate, authapi } from "./lib/validateSessionAndRole.js";
49
76
  export { dblogin } from "./lib/pool.js";
50
77
  export default router;
package/lib/info.js CHANGED
@@ -15,6 +15,15 @@ const mbkautheVar = JSON.parse(process.env.mbkautheVar);
15
15
 
16
16
  const router = express.Router();
17
17
 
18
+ router.get(["/mbkauthe/login"], (req, res) => {
19
+ return res.render("loginmbkauthe", {
20
+ layout: false,
21
+ customURL: mbkautheVar.loginRedirectURL || '/home',
22
+ userLoggedIn: !!req.session?.user,
23
+ UserName: req.session?.user?.username || ''
24
+ });
25
+ });
26
+
18
27
  async function getLatestVersion() {
19
28
  try {
20
29
  const response = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkauthe/main/package.json');
@@ -96,8 +105,8 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], async (_, res) => {
96
105
 
97
106
  try {
98
107
  pkgl = await getPackageLock();
99
- // latestVersion = await getLatestVersion();
100
- latestVersion = "Under Development"; // Placeholder for the latest version
108
+ latestVersion = await getLatestVersion();
109
+ //latestVersion = "Under Development"; // Placeholder for the latest version
101
110
  } catch (err) {
102
111
  console.error("Error fetching package-lock.json:", err);
103
112
  pkgl = { error: "Failed to fetch package-lock.json" };
@@ -491,460 +500,6 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], async (_, res) => {
491
500
  `);
492
501
  }
493
502
  });
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
- }
949
503
 
504
+ export { getLatestVersion };
950
505
  export default router;
package/lib/main.js CHANGED
@@ -10,6 +10,7 @@ import cookieParser from "cookie-parser";
10
10
  import bcrypt from 'bcrypt';
11
11
  import rateLimit from 'express-rate-limit';
12
12
  import mbkautheinfo from "./info.js";
13
+ import speakeasy from "speakeasy";
13
14
 
14
15
  import dotenv from "dotenv";
15
16
  dotenv.config();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "description": "MBKTechStudio's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -32,11 +32,15 @@
32
32
  "cookie-parser": "^1.4.7",
33
33
  "dotenv": "^16.4.7",
34
34
  "express": "^5.1.0",
35
+ "express-handlebars": "^8.0.1",
35
36
  "express-rate-limit": "^7.5.0",
36
37
  "express-session": "^1.18.1",
37
38
  "marked": "^15.0.11",
38
39
  "node-fetch": "^3.3.2",
39
- "pg": "^8.14.1"
40
+ "path": "^0.12.7",
41
+ "pg": "^8.14.1",
42
+ "speakeasy": "^2.0.0",
43
+ "url": "^0.11.4"
40
44
  },
41
45
  "devDependencies": {
42
46
  "nodemon": "^2.0.22"
@@ -0,0 +1,570 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ {{> script/showmessage }}
4
+
5
+ <head>
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <meta name="description"
9
+ content="Log in to portal.mbktechstudio.com to access your resources and manage projects securely.">
10
+ <meta name="keywords" content="MBK Tech Studio, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
11
+ <meta property="og:title" content="Portal Login | MBK Tech Studio" />
12
+ <meta property="og:image" content="https://www.mbktechstudio.com/Assets/Images/Icon/logo.png" />
13
+ <meta property="og:url" content="https://portal.mbktechstudio.com/login">
14
+ <title>Login | MBK Tech Studio Portal</title>
15
+ <link rel="icon" type="image/x-icon" href="/Assets/Images/dgicon.svg">
16
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
17
+ <link rel="preconnect" href="https://fonts.googleapis.com">
18
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
19
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
20
+ rel="stylesheet">
21
+ <script src="https://www.google.com/recaptcha/api.js" async defer></script>
22
+ <style>
23
+ :root {
24
+ --primary: #4361ee;
25
+ --primary-dark: #3a0ca3;
26
+ --primary-light: rgba(67, 97, 238, 0.1);
27
+ --secondary: #f72585;
28
+ --secondary-light: rgba(247, 37, 133, 0.1);
29
+ --dark: #121212;
30
+ --dark-light: #1e1e1e;
31
+ --darker: #0a0a0a;
32
+ --light: #f8f9fa;
33
+ --lighter: #ffffff;
34
+ --gray: #cccccc;
35
+ --gray-dark: #888888;
36
+ --success: #4cc9f0;
37
+ --warning: #f8961e;
38
+ --danger: #ef233c;
39
+ --gradient: linear-gradient(135deg, var(--primary), var(--secondary));
40
+ --glass: rgba(30, 30, 30, 0.5);
41
+ --glass-border: rgba(255, 255, 255, 0.1);
42
+ --transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.1);
43
+ --shadow-sm: 0 4px 6px rgba(0, 0, 0, 0.1);
44
+ --shadow-md: 0 8px 30px rgba(0, 0, 0, 0.2);
45
+ --shadow-lg: 0 15px 40px rgba(0, 0, 0, 0.3);
46
+ --radius-sm: 8px;
47
+ --radius-md: 12px;
48
+ --radius-lg: 16px;
49
+ --radius-xl: 24px;
50
+ }
51
+
52
+ * {
53
+ margin: 0;
54
+ padding: 0;
55
+ box-sizing: border-box;
56
+ font-family: 'Poppins', sans-serif;
57
+ }
58
+
59
+ body {
60
+ background: var(--dark);
61
+ color: var(--light);
62
+ line-height: 1.6;
63
+ overflow-x: hidden;
64
+ min-height: 100vh;
65
+ display: flex;
66
+ flex-direction: column;
67
+ }
68
+
69
+ /* Header - Consistent with index */
70
+ header {
71
+ position: fixed;
72
+ top: 0;
73
+ left: 0;
74
+ width: 100%;
75
+ z-index: 1000;
76
+ transition: var(--transition);
77
+ background: linear-gradient(to bottom, rgba(10, 10, 10, 0.9), rgba(10, 10, 10, 0.7));
78
+ backdrop-filter: blur(10px);
79
+ border-bottom: 1px solid var(--glass-border);
80
+ }
81
+
82
+ nav {
83
+ padding: 15px 5%;
84
+ max-width: 1400px;
85
+ margin: 0 auto;
86
+ }
87
+
88
+ .navbar {
89
+ display: flex;
90
+ justify-content: space-between;
91
+ align-items: center;
92
+ }
93
+
94
+ .logo {
95
+ display: flex;
96
+ align-items: center;
97
+ gap: 10px;
98
+ }
99
+
100
+ .logo img {
101
+ height: 30px;
102
+ width: auto;
103
+ transition: var(--transition);
104
+ }
105
+
106
+ .logo:hover img {
107
+ transform: rotate(15deg);
108
+ }
109
+
110
+ .logo-text {
111
+ font-size: 1.8rem;
112
+ font-weight: 700;
113
+ background: var(--gradient);
114
+ -webkit-background-clip: text;
115
+ background-clip: text;
116
+ color: transparent;
117
+ }
118
+
119
+ /* Login Container */
120
+ .login-container {
121
+ flex: 1;
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ padding: 120px 5% 80px;
126
+ position: relative;
127
+ overflow: hidden;
128
+ background: radial-gradient(circle at 70% 20%, rgba(67, 97, 238, 0.15), transparent 60%);
129
+ }
130
+
131
+ .login-box {
132
+ background: var(--dark-light);
133
+ border-radius: var(--radius-xl);
134
+ padding: 3rem;
135
+ width: 100%;
136
+ max-width: 500px;
137
+ box-shadow: var(--shadow-lg);
138
+ border: 1px solid var(--glass-border);
139
+ position: relative;
140
+ z-index: 2;
141
+ transition: var(--transition);
142
+ }
143
+
144
+ .login-box:hover {
145
+ box-shadow: var(--shadow-lg);
146
+ }
147
+
148
+ .login-title {
149
+ text-align: center;
150
+ margin-bottom: 2rem;
151
+ font-size: 2rem;
152
+ position: relative;
153
+ color: var(--lighter);
154
+ }
155
+
156
+ .login-title::after {
157
+ content: '';
158
+ position: absolute;
159
+ bottom: -10px;
160
+ left: 50%;
161
+ transform: translateX(-50%);
162
+ width: 80px;
163
+ height: 4px;
164
+ background: var(--gradient);
165
+ border-radius: 2px;
166
+ }
167
+
168
+ /* Form Elements */
169
+ .form-group {
170
+ position: relative;
171
+ margin-bottom: 2rem;
172
+ }
173
+
174
+ .form-input {
175
+ width: 100%;
176
+ padding: 15px 20px;
177
+ background: var(--darker);
178
+ border: 2px solid var(--glass-border);
179
+ border-radius: var(--radius-sm);
180
+ color: var(--light);
181
+ font-size: 1rem;
182
+ transition: var(--transition);
183
+ }
184
+
185
+ .form-input:focus {
186
+ outline: none;
187
+ border-color: var(--primary);
188
+ box-shadow: 0 0 0 3px var(--primary-light);
189
+ }
190
+
191
+ .form-label {
192
+ position: absolute;
193
+ top: 15px;
194
+ left: 20px;
195
+ color: var(--gray);
196
+ transition: var(--transition);
197
+ pointer-events: none;
198
+ }
199
+
200
+ .form-input:focus+.form-label,
201
+ .form-input:not(:placeholder-shown)+.form-label {
202
+ top: -10px;
203
+ left: 15px;
204
+ font-size: 0.8rem;
205
+ background: var(--dark-light);
206
+ padding: 0 5px;
207
+ color: var(--primary);
208
+ }
209
+
210
+ .input-icon {
211
+ position: absolute;
212
+ right: 20px;
213
+ top: 50%;
214
+ transform: translateY(-50%);
215
+ color: var(--gray);
216
+ cursor: pointer;
217
+ transition: var(--transition);
218
+ }
219
+
220
+ .input-icon:hover {
221
+ color: var(--primary);
222
+ }
223
+
224
+ .btn-login {
225
+ width: 100%;
226
+ padding: 15px;
227
+ border-radius: var(--radius-sm);
228
+ background: var(--primary);
229
+ color: white;
230
+ font-weight: 600;
231
+ font-size: 1rem;
232
+ border: none;
233
+ cursor: pointer;
234
+ transition: var(--transition);
235
+ box-shadow: var(--shadow-sm);
236
+ margin-top: 1rem;
237
+ }
238
+
239
+ .btn-login:hover {
240
+ background: var(--primary-dark);
241
+ transform: translateY(-3px);
242
+ box-shadow: var(--shadow-md);
243
+ }
244
+
245
+ .btn-login:disabled {
246
+ background: var(--gray-dark);
247
+ cursor: not-allowed;
248
+ transform: none;
249
+ box-shadow: none;
250
+ }
251
+
252
+ /* Additional Links */
253
+ .login-links {
254
+ display: flex;
255
+ justify-content: space-between;
256
+ margin-top: 1.5rem;
257
+ font-size: 0.9rem;
258
+ }
259
+
260
+ .login-link {
261
+ color: var(--gray);
262
+ transition: var(--transition);
263
+ }
264
+
265
+ .login-link:hover {
266
+ color: var(--primary);
267
+ }
268
+
269
+ /* Terms Info */
270
+ .terms-info {
271
+ margin-top: 2rem;
272
+ font-size: 0.8rem;
273
+ color: var(--gray);
274
+ text-align: center;
275
+ }
276
+
277
+ .terms-link {
278
+ color: var(--primary);
279
+ font-weight: 500;
280
+ }
281
+
282
+ /* Recaptcha */
283
+ .recaptcha-container {
284
+ margin: 2rem 0;
285
+ display: flex;
286
+ justify-content: center;
287
+ }
288
+
289
+ /* 2FA Token Container */
290
+ .token-container {
291
+ animation: fadeInUp 0.4s ease-out;
292
+ }
293
+
294
+ .token-container.disable {
295
+ display: none;
296
+ }
297
+
298
+ .token-container.enable {
299
+ display: block;
300
+ }
301
+
302
+ /* Floating Elements */
303
+ .ai-element {
304
+ position: absolute;
305
+ opacity: 0.1;
306
+ z-index: 1;
307
+ animation: float 6s ease-in-out infinite;
308
+ }
309
+
310
+ .ai-element:nth-child(1) {
311
+ top: 20%;
312
+ left: 10%;
313
+ font-size: 5rem;
314
+ animation-delay: 0s;
315
+ }
316
+
317
+ .ai-element:nth-child(2) {
318
+ top: 60%;
319
+ left: 80%;
320
+ font-size: 4rem;
321
+ animation-delay: 1s;
322
+ }
323
+
324
+ .ai-element:nth-child(3) {
325
+ top: 30%;
326
+ left: 70%;
327
+ font-size: 3rem;
328
+ animation-delay: 2s;
329
+ }
330
+
331
+ .ai-element:nth-child(4) {
332
+ top: 80%;
333
+ left: 20%;
334
+ font-size: 6rem;
335
+ animation-delay: 3s;
336
+ }
337
+
338
+ @keyframes float {
339
+
340
+ 0%,
341
+ 100% {
342
+ transform: translateY(0) rotate(0deg);
343
+ }
344
+
345
+ 50% {
346
+ transform: translateY(-20px) rotate(5deg);
347
+ }
348
+ }
349
+
350
+ @keyframes fadeInUp {
351
+ from {
352
+ opacity: 0;
353
+ transform: translateY(20px);
354
+ }
355
+
356
+ to {
357
+ opacity: 1;
358
+ transform: translateY(0);
359
+ }
360
+ }
361
+
362
+ /* Responsive Styles */
363
+ @media (max-width: 768px) {
364
+ .login-box {
365
+ padding: 2rem;
366
+ }
367
+
368
+ .login-title {
369
+ font-size: 1.8rem;
370
+ }
371
+ }
372
+
373
+ @media (max-width: 576px) {
374
+ .login-box {
375
+ padding: 1.5rem;
376
+ border-radius: var(--radius-lg);
377
+ }
378
+
379
+ .login-links {
380
+ flex-direction: column;
381
+ gap: 0.5rem;
382
+ align-items: center;
383
+ }
384
+ }
385
+ </style>
386
+ </head>
387
+
388
+ <body>
389
+ <header>
390
+ <nav>
391
+ <div class="navbar">
392
+ <a class="logo">
393
+ <img src="/Assets/Images/dg.svg" alt="MBK Tech Studio Logo">
394
+ <span class="logo-text">MBK Authe</span>
395
+ </a>
396
+ </div>
397
+ </nav>
398
+ </header>
399
+
400
+ <section class="login-container">
401
+ <!-- Floating AI elements -->
402
+ <i class="fas fa-robot ai-element"></i>
403
+ <i class="fas fa-microchip ai-element"></i>
404
+ <i class="fas fa-user-astronaut ai-element"></i>
405
+ <i class="fas fa-satellite-dish ai-element"></i>
406
+ <i class="fas fa-cloud ai-element"></i>
407
+ <i class="fas fa-shield-alt ai-element"></i>
408
+
409
+ <div class="login-box">
410
+ <h1 class="login-title">Login</h1>
411
+
412
+ <form id="loginForm" method="POST">
413
+ <div class="form-group">
414
+ <input id="loginUsername" class="form-input" type="text" name="username" placeholder=" " required
415
+ pattern="^[a-z0-9.]+$"
416
+ title="Username must contain lowercase letters, numbers, and periods only, no spaces, no special characters."
417
+ oninput="this.value = this.value.toLowerCase().replace(/[^a-z0-9.]/g, '')" />
418
+ <label class="form-label">Username</label>
419
+ <i class="fas fa-info-circle input-icon" onclick="usernameinfo()"></i>
420
+ </div>
421
+
422
+ <div class="form-group">
423
+ <input id="loginPassword" class="form-input" type="password" name="Password" placeholder=" "
424
+ required />
425
+ <label class="form-label">Password</label>
426
+ <i class="fas fa-eye input-icon" id="togglePassword"></i>
427
+ </div>
428
+
429
+ <div class="form-group token-container disable" id="tokenCon">
430
+ <input id="token" class="form-input" type="text" name="token" placeholder=" " pattern="\d{6}"
431
+ title="Token must be exactly 6 digits" maxlength="6" minlength="6" />
432
+ <label class="form-label">2FA Token</label>
433
+ <i class="fas fa-info-circle input-icon" onclick="tokeninfo()"></i>
434
+ </div>
435
+
436
+ <div class="recaptcha-container">
437
+ <div class="g-recaptcha" data-theme="dark" data-sitekey="6LfhaPgqAAAAAPgOw7r3rNOHKKfh5d7K3MMJeUHo">
438
+ </div>
439
+ </div>
440
+
441
+ <button type="submit" class="btn-login" id="loginButton">
442
+ <span id="loginButtonText">Login</span>
443
+ </button>
444
+
445
+ <div class="login-links">
446
+ <a onclick="fpass()" class="login-link">Forgot Password?</a>
447
+ <a href="https://www.mbktechstudio.com/Support" target="_blank" class="login-link">Need Help?</a>
448
+ </div>
449
+
450
+ <p class="terms-info">
451
+ By logging in, you agree to our
452
+ <a href="/info/Terms&Conditions" class="terms-link">Terms & Conditions</a>
453
+ and
454
+ <a href="/info/PrivacyPolicy" class="terms-link">Privacy Policy</a>.
455
+ </p>
456
+ </form>
457
+ </div>
458
+ </section>
459
+
460
+ <script>
461
+
462
+
463
+ // Toggle password visibility
464
+ const togglePassword = document.getElementById('togglePassword');
465
+ const passwordInput = document.getElementById('loginPassword');
466
+
467
+ togglePassword.addEventListener('click', function () {
468
+ const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
469
+ passwordInput.setAttribute('type', type);
470
+ togglePassword.classList.toggle('fa-eye');
471
+ togglePassword.classList.toggle('fa-eye-slash');
472
+ });
473
+
474
+ function fpass() {
475
+ showMessage(`If you have forgotten your password, please contact support at <a href="https://www.mbktechstudio.com/Support" target="_blank">https://www.mbktechstudio.com/Support</a> to reset it.`, `Forgot Password`);
476
+ }
477
+
478
+ // Info dialogs
479
+ function usernameinfo() {
480
+ showMessage(`If you are a member of the MBK Tech Studio team, your username is the first part of your email address (e.g., for abc.xyz@mbktechstudio.com, your username is abc.xyz). If you are a guest or have forgotten your username and password, please contact support.`, `What is my username?`);
481
+ }
482
+
483
+ function tokeninfo() {
484
+ showMessage(`The 2FA token is a 6-digit code generated by your authenticator app (such as Google Authenticator, Microsoft Authenticator, or Authy). Enter the code shown in your app to complete login. If you have not set up 2FA or are having trouble, please contact support.`, `What is the 2FA token?`);
485
+ }
486
+
487
+ // Form submission
488
+ document.getElementById('loginForm').addEventListener('submit', function (event) {
489
+ event.preventDefault();
490
+
491
+ const username = document.getElementById('loginUsername').value.trim();
492
+ const password = document.getElementById('loginPassword').value.trim();
493
+ const token = document.getElementById('token') ? document.getElementById('token').value.trim() : '';
494
+ const recaptchaResponse = grecaptcha.getResponse();
495
+ const loginButton = document.getElementById('loginButton');
496
+ const loginButtonText = document.getElementById('loginButtonText');
497
+
498
+ if (!username || !password) {
499
+ showMessage('Username and password are required', 'Login Error');
500
+ return;
501
+ }
502
+
503
+ loginButton.disabled = true;
504
+ loginButtonText.textContent = 'Authenticating...';
505
+
506
+ fetch('/mbkauthe/api/login', {
507
+ method: 'POST',
508
+ headers: {
509
+ 'Content-Type': 'application/json'
510
+ },
511
+ body: JSON.stringify({
512
+ username,
513
+ password,
514
+ token,
515
+ recaptcha: recaptchaResponse
516
+ })
517
+ })
518
+ .then(response => response.json())
519
+ .then(data => {
520
+ if (data.success) {
521
+ loginButtonText.textContent = 'Success! Redirecting...';
522
+ sessionStorage.setItem('sessionId', data.sessionId);
523
+
524
+ // Redirect to the appropriate page
525
+ const redirectUrl = new URLSearchParams(window.location.search).get('redirect');
526
+ window.location.href = redirectUrl ? decodeURIComponent(redirectUrl) : '/chatbot';
527
+ } else {
528
+ // Handle errors
529
+ grecaptcha.reset();
530
+ loginButton.disabled = false;
531
+ loginButtonText.textContent = 'Login';
532
+
533
+ if (data.message === "Please Enter 2FA code") {
534
+ console.log('2FA required');
535
+ document.getElementById('tokenCon').classList.remove('disable');
536
+ document.getElementById('tokenCon').classList.add('enable');
537
+ document.getElementById('tokenCon').style.animation = 'fadeInUp 0.4s ease-out';
538
+ showMessage('Please enter your 2FA token', '2FA Required');
539
+ } else {
540
+ showMessage(data.message || 'Login failed. Please try again.', 'Login Error');
541
+ }
542
+ }
543
+ })
544
+ .catch(error => {
545
+ grecaptcha.reset();
546
+ loginButton.disabled = false;
547
+ loginButtonText.textContent = 'Login';
548
+ console.error('Error:', error);
549
+ showMessage('An error occurred. Please try again.', 'Login Error');
550
+ });
551
+ });
552
+
553
+ // Check for URL parameters
554
+ document.addEventListener('DOMContentLoaded', function () {
555
+ const urlParams = new URLSearchParams(window.location.search);
556
+ const usernameFromUrl = urlParams.get('username');
557
+ const passwordFromUrl = urlParams.get('password');
558
+
559
+ if (usernameFromUrl) {
560
+ document.getElementById('loginUsername').value = usernameFromUrl;
561
+ }
562
+
563
+ if (passwordFromUrl) {
564
+ document.getElementById('loginPassword').value = passwordFromUrl;
565
+ }
566
+ });
567
+ </script>
568
+ </body>
569
+
570
+ </html>