mbkauthe 1.1.7 → 1.1.9

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,5 +1,6 @@
1
1
  import express from "express"; // Add this line
2
2
  import router from "./lib/main.js";
3
+ import { getLatestVersion } from "./lib/info.js";
3
4
  import { engine } from "express-handlebars";
4
5
  import dotenv from "dotenv";
5
6
  import path from "path";
@@ -63,6 +64,14 @@ app.engine("handlebars", engine({
63
64
 
64
65
  app.set("view engine", "handlebars");
65
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
+
66
75
  export { validateSession, checkRolePermission, validateSessionAndRole, getUserData, authenticate, authapi } from "./lib/validateSessionAndRole.js";
67
76
  export { dblogin } from "./lib/pool.js";
68
77
  export default router;
package/lib/info.js CHANGED
@@ -18,6 +18,7 @@ const router = express.Router();
18
18
  router.get(["/mbkauthe/login"], (req, res) => {
19
19
  return res.render("loginmbkauthe", {
20
20
  layout: false,
21
+ customURL: mbkautheVar.loginRedirectURL || '/home',
21
22
  userLoggedIn: !!req.session?.user,
22
23
  UserName: req.session?.user?.username || ''
23
24
  });
@@ -104,8 +105,8 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], async (_, res) => {
104
105
 
105
106
  try {
106
107
  pkgl = await getPackageLock();
107
- // latestVersion = await getLatestVersion();
108
- latestVersion = "Under Development"; // Placeholder for the latest version
108
+ latestVersion = await getLatestVersion();
109
+ //latestVersion = "Under Development"; // Placeholder for the latest version
109
110
  } catch (err) {
110
111
  console.error("Error fetching package-lock.json:", err);
111
112
  pkgl = { error: "Failed to fetch package-lock.json" };
@@ -499,460 +500,6 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], async (_, res) => {
499
500
  `);
500
501
  }
501
502
  });
502
- const DOCUMENTATION_TITLE = "Project Documentation";
503
- const CACHE_TTL = 3600000; // 1 hour in milliseconds
504
-
505
- // Cache for the rendered HTML
506
- let cachedHtml = null;
507
- let cacheTimestamp = 0;
508
-
509
- router.get(["/mbkauthe/"], async (_, res) => {
510
- try {
511
- // Check cache first
512
- const now = Date.now();
513
- if (cachedHtml && (now - cacheTimestamp) < CACHE_TTL) {
514
- return res.send(cachedHtml);
515
- }
516
-
517
- // Read and process file
518
- let readmePath;
519
- if (process.env.test === "true") {
520
- readmePath = path.join(process.cwd(), "README.md");
521
- }
522
- else {
523
- readmePath = path.join(process.cwd(), "./node_modules/mbkauthe/README.md");
524
- }
525
- const data = await fs.promises.readFile(readmePath, "utf8");
526
-
527
- // Convert markdown to HTML
528
- let html = marked(data, {
529
- breaks: true,
530
- gfm: true,
531
- smartypants: true
532
- });
533
-
534
- // Process HTML with cheerio
535
- const $ = cheerio.load(html);
536
-
537
- // Add IDs to headers for anchor links
538
- $('h1, h2, h3, h4, h5, h6').each(function () {
539
- const id = $(this).text()
540
- .toLowerCase()
541
- .replace(/\s+/g, '-')
542
- .replace(/[^\w-]+/g, '');
543
- $(this).attr('id', id);
544
- $(this).addClass('header-anchor');
545
- });
546
-
547
- // Fix table of contents links and add icons
548
- $('a[href^="#"]').each(function () {
549
- const href = $(this).attr('href');
550
- const id = href.substring(1)
551
- .toLowerCase()
552
- .replace(/\s+/g, '-')
553
- .replace(/[^\w-]+/g, '');
554
- $(this).attr('href', `#${id}`);
555
- $(this).addClass('toc-link');
556
- });
557
-
558
- // Add copy buttons to code blocks
559
- $('pre').each(function () {
560
- const $pre = $(this);
561
- const $button = $(`<button class="copy-button" aria-label="Copy code">📋</button>`);
562
- $pre.prepend($button);
563
- });
564
-
565
- // Create the full HTML response
566
- const htmlContent = generateFullHtml($.html());
567
-
568
- // Update cache
569
- cachedHtml = htmlContent;
570
- cacheTimestamp = now;
571
-
572
- res.send(htmlContent);
573
- } catch (err) {
574
- console.error("Error processing documentation:", err);
575
- res.status(500).send(generateErrorHtml());
576
- }
577
- });
578
-
579
- function generateFullHtml(contentHtml) {
580
- return `<!DOCTYPE html>
581
- <html lang="en">
582
- <head>
583
- <meta charset="UTF-8">
584
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
585
- <meta name="description" content="Project documentation generated from README.md">
586
- <title>${DOCUMENTATION_TITLE}</title>
587
- <style>
588
- :root {
589
- --primary-color: #bb86fc;
590
- --primary-dark: #9a67ea;
591
- --secondary-color: #03dac6;
592
- --secondary-dark: #018786;
593
- --background-dark: #121212;
594
- --background-darker: #1e1e1e;
595
- --background-light: #2d2d2d;
596
- --text-primary: #e0e0e0;
597
- --text-secondary: #a0a0a0;
598
- --error-color: #cf6679;
599
- --success-color: #4caf50;
600
- }
601
-
602
- body {
603
- font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
604
- line-height: 1.6;
605
- margin: 0;
606
- padding: 0;
607
- background-color: var(--background-dark);
608
- color: var(--text-primary);
609
- max-width: 1200px;
610
- margin: 0 auto;
611
- padding: 2rem;
612
- }
613
-
614
- .header-anchor {
615
- position: relative;
616
- padding-left: 1.5rem;
617
- }
618
-
619
- .header-anchor::before {
620
- content: "#";
621
- position: absolute;
622
- left: 0;
623
- color: var(--text-secondary);
624
- opacity: 0;
625
- transition: opacity 0.2s;
626
- }
627
-
628
- .header-anchor:hover::before {
629
- opacity: 1;
630
- }
631
-
632
- pre {
633
- position: relative;
634
- background: var(--background-darker);
635
- padding: 1.5rem;
636
- border-radius: 8px;
637
- overflow-x: auto;
638
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
639
- }
640
-
641
- .copy-button {
642
- position: absolute;
643
- top: 0.5rem;
644
- right: 0.5rem;
645
- background: var(--background-light);
646
- color: var(--text-primary);
647
- border: none;
648
- border-radius: 4px;
649
- padding: 0.25rem 0.5rem;
650
- cursor: pointer;
651
- opacity: 0;
652
- transition: opacity 0.2s, background 0.2s;
653
- }
654
-
655
- pre:hover .copy-button {
656
- opacity: 1;
657
- }
658
-
659
- .copy-button:hover {
660
- background: var(--primary-color);
661
- color: var(--background-dark);
662
- }
663
-
664
- .copy-button.copied {
665
- background: var(--success-color);
666
- color: white;
667
- }
668
-
669
- code {
670
- font-family: 'Fira Code', 'Courier New', monospace;
671
- background: var(--background-darker);
672
- padding: 0.2rem 0.4rem;
673
- border-radius: 4px;
674
- color: var(--secondary-color);
675
- font-size: 0.9em;
676
- word-wrap: break-word;
677
- }
678
-
679
- h1, h2, h3, h4, h5, h6 {
680
- color: var(--primary-color);
681
- margin-top: 1.8em;
682
- margin-bottom: 0.8em;
683
- scroll-margin-top: 1em;
684
- }
685
-
686
- h1 {
687
- font-size: 2.4rem;
688
- border-bottom: 2px solid var(--primary-color);
689
- padding-bottom: 0.5rem;
690
- }
691
-
692
- h2 { font-size: 2rem; }
693
- h3 { font-size: 1.6rem; }
694
-
695
- a {
696
- color: var(--secondary-color);
697
- text-decoration: none;
698
- transition: color 0.2s;
699
- }
700
-
701
- a:hover {
702
- color: var(--primary-color);
703
- text-decoration: underline;
704
- }
705
-
706
- .toc-link {
707
- display: inline-block;
708
- padding: 0.2rem 0;
709
- }
710
-
711
- .toc-link::before {
712
- content: "→ ";
713
- opacity: 0;
714
- transition: opacity 0.2s;
715
- }
716
-
717
- .toc-link:hover::before {
718
- opacity: 1;
719
- }
720
-
721
- blockquote {
722
- border-left: 4px solid var(--primary-color);
723
- padding-left: 1.5rem;
724
- margin-left: 0;
725
- color: var(--text-secondary);
726
- font-style: italic;
727
- background: rgba(187, 134, 252, 0.05);
728
- border-radius: 0 4px 4px 0;
729
- padding: 1rem;
730
- }
731
-
732
- table {
733
- border-collapse: collapse;
734
- width: 100%;
735
- margin: 1.5rem 0;
736
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
737
- }
738
-
739
- th, td {
740
- border: 1px solid #444;
741
- padding: 0.75rem;
742
- text-align: left;
743
- }
744
-
745
- th {
746
- background-color: var(--background-darker);
747
- font-weight: 600;
748
- }
749
-
750
- tr:nth-child(even) {
751
- background-color: rgba(255, 255, 255, 0.05);
752
- }
753
-
754
- tr:hover {
755
- background-color: rgba(187, 134, 252, 0.1);
756
- }
757
-
758
- img {
759
- max-width: 100%;
760
- height: auto;
761
- border-radius: 8px;
762
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
763
- }
764
-
765
- hr {
766
- border: none;
767
- height: 1px;
768
- background-color: #444;
769
- margin: 2rem 0;
770
- }
771
-
772
- /* Dark mode toggle */
773
- .theme-toggle {
774
- position: fixed;
775
- bottom: 1rem;
776
- right: 1rem;
777
- background: var(--background-light);
778
- border: none;
779
- border-radius: 50%;
780
- width: 3rem;
781
- height: 3rem;
782
- display: flex;
783
- align-items: center;
784
- justify-content: center;
785
- cursor: pointer;
786
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
787
- z-index: 100;
788
- }
789
-
790
- /* Responsive design */
791
- @media (max-width: 768px) {
792
- body {
793
- padding: 1rem;
794
- }
795
-
796
- h1 {
797
- font-size: 2rem;
798
- }
799
-
800
- h2 {
801
- font-size: 1.7rem;
802
- }
803
-
804
- pre {
805
- padding: 1rem;
806
- font-size: 0.9em;
807
- }
808
- }
809
-
810
- /* Print styles */
811
- @media print {
812
- body {
813
- background-color: white;
814
- color: black;
815
- padding: 0;
816
- }
817
-
818
- a {
819
- color: #0066cc;
820
- }
821
-
822
- pre, code {
823
- background-color: #f5f5f5;
824
- color: #333;
825
- }
826
- }
827
- </style>
828
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Fira+Code&display=swap" rel="stylesheet">
829
- </head>
830
- <body>
831
- <a href="/mbkauthe/info/" class="toc-link">mbkauthe Info</a>
832
- <main>
833
- ${contentHtml}
834
- </main>
835
-
836
- <button class="theme-toggle" aria-label="Toggle theme">🌓</button>
837
-
838
- <script>
839
- document.addEventListener('DOMContentLoaded', function() {
840
- // Smooth scrolling for TOC links
841
- document.querySelectorAll('a[href^="#"]').forEach(anchor => {
842
- anchor.addEventListener('click', function(e) {
843
- e.preventDefault();
844
- const targetId = this.getAttribute('href');
845
- const targetElement = document.querySelector(targetId);
846
-
847
- if (targetElement) {
848
- targetElement.scrollIntoView({
849
- behavior: 'smooth',
850
- block: 'start'
851
- });
852
-
853
- // Update URL without page jump
854
- history.pushState(null, null, targetId);
855
- }
856
- });
857
- });
858
-
859
- // Copy button functionality
860
- document.querySelectorAll('.copy-button').forEach(button => {
861
- button.addEventListener('click', function() {
862
- const pre = this.parentElement;
863
- const code = pre.querySelector('code') || pre;
864
- const range = document.createRange();
865
- range.selectNode(code);
866
- window.getSelection().removeAllRanges();
867
- window.getSelection().addRange(range);
868
-
869
- try {
870
- const successful = document.execCommand('copy');
871
- if (successful) {
872
- this.textContent = '✓ Copied!';
873
- this.classList.add('copied');
874
- setTimeout(() => {
875
- this.textContent = '📋';
876
- this.classList.remove('copied');
877
- }, 2000);
878
- }
879
- } catch (err) {
880
- console.error('Failed to copy:', err);
881
- }
882
-
883
- window.getSelection().removeAllRanges();
884
- });
885
- });
886
-
887
- // Highlight current section in view
888
- const observerOptions = {
889
- root: null,
890
- rootMargin: '0px',
891
- threshold: 0.5
892
- };
893
-
894
- const observer = new IntersectionObserver(function(entries) {
895
- entries.forEach(function(entry) {
896
- const id = entry.target.getAttribute('id');
897
- if (entry.isIntersecting) {
898
- document.querySelectorAll('a[href="#' + id + '"]').forEach(function(link) {
899
- link.style.fontWeight = '600';
900
- link.style.color = 'var(--primary-color)';
901
- });
902
- } else {
903
- document.querySelectorAll('a[href="#' + id + '"]').forEach(function(link) {
904
- link.style.fontWeight = '';
905
- link.style.color = '';
906
- });
907
- }
908
- });
909
- }, observerOptions);
910
-
911
- document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(function(heading) {
912
- if (heading.id) {
913
- observer.observe(heading);
914
- }
915
- });
916
-
917
- // Theme toggle functionality
918
- const themeToggle = document.querySelector('.theme-toggle');
919
- themeToggle.addEventListener('click', function() {
920
- document.body.classList.toggle('light-theme');
921
- const isLight = document.body.classList.contains('light-theme');
922
- this.textContent = isLight ? '🌙' : '🌓';
923
- localStorage.setItem('themePreference', isLight ? 'light' : 'dark');
924
- });
925
-
926
- // Load saved theme preference
927
- const savedTheme = localStorage.getItem('themePreference');
928
- if (savedTheme === 'light') {
929
- document.body.classList.add('light-theme');
930
- themeToggle.textContent = '🌙';
931
- }
932
- });
933
- </script>
934
- </body>
935
- </html>`;
936
- }
937
-
938
- function generateErrorHtml() {
939
- return `<!DOCTYPE html>
940
- <html lang="en">
941
- <head>
942
- <meta charset="UTF-8">
943
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
944
- <title>Error Loading Documentation</title>
945
- </head>
946
- <body>
947
- <h1>Error Loading Documentation</h1>
948
- <p>Failed to load README.md file. Please try again later.</p>
949
- <p>If the problem persists, contact your system administrator.</p>
950
- <a href="/">Return to Home</a>
951
- <div class="error-details">
952
- Error: ${err.message || 'Unknown error'}
953
- </div>
954
- </body>
955
- </html>`;
956
- }
957
503
 
504
+ export { getLatestVersion };
958
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.7",
3
+ "version": "1.1.9",
4
4
  "description": "MBKTechStudio's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -39,6 +39,7 @@
39
39
  "node-fetch": "^3.3.2",
40
40
  "path": "^0.12.7",
41
41
  "pg": "^8.14.1",
42
+ "speakeasy": "^2.0.0",
42
43
  "url": "^0.11.4"
43
44
  },
44
45
  "devDependencies": {
@@ -1,257 +1,570 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en" dir="ltr">
2
+ <html lang="en">
3
+ {{> script/showmessage }}
3
4
 
4
5
  <head>
5
- <title>Login</title>
6
- <script src="/Assets/Scripts/logout.js"></script>
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
8
  <meta name="description"
8
9
  content="Log in to portal.mbktechstudio.com to access your resources and manage projects securely.">
9
10
  <meta name="keywords" content="MBK Tech Studio, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
10
- <meta property="og:title" content="Portal Login | MBK Tech STudio" />
11
+ <meta property="og:title" content="Login | MBK Tech Studio" />
11
12
  <meta property="og:image" content="https://www.mbktechstudio.com/Assets/Images/Icon/logo.png" />
12
13
  <meta property="og:url" content="https://portal.mbktechstudio.com/login">
13
- <script type="application/ld+json">
14
- { "@context": "https://schema.org", "@type": "Organization", "name": "MBK Tech Studio", "url":
15
- "https://portal.mbktechstudio.com/login", "logo": "https://www.mbktechstudio.com/Assets/Images/Icon/logo.png", "description":
16
- "MBK Tech Studio: Log in to portal.mbktechstudio.com to access your resources and manage projects securely." }
17
- </script>
14
+ <title>Login | MBK Tech Studio Portal</title>
15
+ <link rel="icon" type="image/x-icon" href="https://mbktechstudio.com/Assets/Images/Icon/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">
18
21
  <script src="https://www.google.com/recaptcha/api.js" async defer></script>
19
22
  <style>
20
- .info {
21
- font-family: 'Open Sans', sans-serif;
22
- font-size: 14px;
23
- color: #0c5460;
24
- background-color: #d1ecf1;
25
- border: 1px solid #ddd;
26
- border-radius: 8px;
27
- padding: 5px 15px;
28
- margin-top: 5px;
29
- font-weight: 500;
30
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
31
- line-height: 1.2;
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;
32
50
  }
33
51
 
34
- .info span {
35
- color: #007bff;
36
- /* Highlight for the info icon */
37
- margin-right: 8px;
52
+ * {
53
+ margin: 0;
54
+ padding: 0;
55
+ box-sizing: border-box;
56
+ font-family: 'Poppins', sans-serif;
38
57
  }
39
58
 
40
- section .button {
41
- margin: 0 0 2px !important;
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;
42
67
  }
43
68
 
44
- section .button button {
45
- margin: 0 !important;
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);
46
80
  }
47
81
 
48
- .back {
49
- top: 0;
50
- color: #fff;
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: '';
51
158
  position: absolute;
52
- margin-top: 10px;
53
- font-size: 20px;
54
- transition: color 0.3s ease;
159
+ bottom: -10px;
160
+ left: 50%;
161
+ transform: translateX(-50%);
162
+ width: 80px;
163
+ height: 4px;
164
+ background: var(--gradient);
165
+ border-radius: 2px;
55
166
  }
56
167
 
57
- .back:hover {
58
- color: rgb(185, 185, 185);
59
- transition: color 0.3s ease;
168
+ /* Form Elements */
169
+ .form-group {
170
+ position: relative;
171
+ margin-bottom: 2rem;
60
172
  }
61
173
 
62
- @media(max-width: 1200px) {
63
- .user-box label {
64
- left: 2% !important;
65
- }
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);
66
183
  }
67
184
 
68
- @media(max-width: 1200px) {
69
- .user-box label {
70
- left: 2% !important;
71
- }
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);
72
208
  }
73
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 */
74
283
  .recaptcha-container {
284
+ margin: 2rem 0;
75
285
  display: flex;
76
286
  justify-content: center;
77
- align-items: center;
78
- margin-top: 7px;
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
+ }
79
384
  }
80
385
  </style>
81
386
  </head>
82
- {{> header1 }}
83
387
 
84
388
  <body>
85
- <section class="about" id="about">
86
- <div class="content">
87
- <div class="portalbox">
88
- <div class="title">
89
- <span style="font-size:30px;">Login</span>
389
+ <header>
390
+ <nav>
391
+ <div class="navbar">
392
+ <a class="logo">
393
+ <img src="https://mbktechstudio.com/Assets/Images/Icon/dgicon.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>
90
420
  </div>
91
- <form id="loginForm" action="/login" method="POST">
92
- <div class="back" onclick="loadpage('/')">
93
- <i class="fa-solid fa-arrow-left"></i>
94
- </div>
95
- <div class="user-box">
96
- <input id="loginUsername" type="text" name="username" required pattern="^[a-z0-9.]+$"
97
- title="Username must contain lowercase letters, numbers, and periods only, no spaces, no special characters."
98
- oninput="this.value = this.value.toLowerCase().replace(/[^a-z0-9.]/g, '')" />
99
- <label>User Name</label>
100
- <span class="info-icon" onclick="usernameinfo()">
101
- <i class="fa fa-info-circle"></i>
102
- </span>
103
- </div>
104
- <div class="user-box">
105
- <input id="loginPassword" type="password" name="Password" required="" />
106
- <label>Password</label>
107
- <span class="password-toggle-icon">
108
- <i class="fa fa-eye"></i>
109
- </span>
110
- </div>
111
- <div class="user-box" id="tokenCon" style="display: none;">
112
- <input id="token" type="text" class="no-spinner" name="token" pattern="\d{6}"
113
- title="Token must be exactly 6 digits" maxlength="6" minlength="6" />
114
- <label>Token</label>
115
- <span class="info-icon" onclick="tokeninfo()">
116
- <i class="fa fa-info-circle"></i>
117
- </span>
118
- </div>
119
- <div class="wrap">
120
- <div class="button">
121
- <button style="font-size: 17px;" type="submit">Login</button>
122
- </div>
123
- </div>
124
- </form>
125
- {{#if userLoggedIn }}
126
- {{> alreadyloggedin UserName=UserName }}
127
- {{/if }}
128
- <p class="info maincssp">
129
- <span class="">
130
- <i class="fa fa-info-circle"></i>
131
- </span>By Logging In, You Are Automatically Agreeing To Our
132
- <a class="links" href="/info/Terms&Conditions">Use Of Terms & Conditions
133
- </a>
134
- </p>
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
+
135
436
  <div class="recaptcha-container">
136
437
  <div class="g-recaptcha" data-theme="dark" data-sitekey="6LfhaPgqAAAAAPgOw7r3rNOHKKfh5d7K3MMJeUHo">
137
438
  </div>
138
439
  </div>
139
- </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>
140
457
  </div>
141
458
  </section>
142
- </body>
143
- {{> footer }}
144
- <script>
145
- function usernameinfo() {
146
- showMessage(
147
- '<div style="">If you are a member of the Mbk Tech Studio team, your username is the first part of your email address (e.' +
148
- 'g., for abc.xyz@mbktechstudio.com, your username is abc.xyz). If you are a guest or have forgotten your username and pas' +
149
- 'sword, please fill contact form at <a class="links" href="https://www.mbktechstudio.com/Support">mbktechstudio.com/Suppo' +
150
- 'rt</a> or contact me directly.<div>',
151
- "What Is My Username?"
152
- );
153
- }
154
- function tokeninfo() {
155
- showMessage(
156
- '<div style="">The 2FA token or Auth token is required for SuperAdmin and NormalAdmin to log in. Each user has been given' +
157
- ' an Auth Code, which they can use to generate an Auth token using any Auth App.<div>',
158
- "What Is Token?"
159
- );
160
- }
161
-
162
- document.getElementById('loginForm').addEventListener('submit', function (event) {
163
- event.preventDefault();
164
- const username = document.getElementById('loginUsername').value.trim();
165
- const password = document.getElementById('loginPassword').value.trim();
166
- const token = document.getElementById('token').value.trim(); // 2FA token
167
- let recaptchaResponse;
168
-
169
- recaptchaResponse = grecaptcha.getResponse();
170
-
171
- if (!username || !password) {
172
- showMessage("Username and password cannot be empty", "Error");
173
- return;
174
- }
175
- const loginButton = document.querySelector('button[type="submit"]');
176
- loginButton.disabled = true;
177
- loginButton.innerText = "Logging in.....";
178
- fetch('/mbkauthe/api/login', {
179
- method: 'POST',
180
- headers: {
181
- 'Content-Type': 'application/json'
182
- },
183
- body: JSON.stringify({ username, password, token, recaptcha: recaptchaResponse || null })
184
- })
185
- .then(response => {
186
- loginButton.disabled = false;
187
- loginButton.innerText = "Login";
188
- return response.json().then(data => {
189
- if (response.ok) {
190
- return data;
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) : '{{customURL}}';
191
527
  } else {
192
- throw new Error(data.message || "An error occurred");
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
+ }
193
542
  }
194
- });
195
- })
196
- .then(data => {
197
- if (data.success) {
198
- sessionStorage.setItem('sessionId', data.sessionId);
199
- clearAllCookies();
200
- const redirectUrl = new URLSearchParams(window.location.search).get('redirect');
201
- loginButton.innerText = "Redirecting.....";
202
- window.location.href = redirectUrl ? decodeURIComponent(redirectUrl) : '/chatbot';
203
- } else {
543
+ })
544
+ .catch(error => {
204
545
  grecaptcha.reset();
205
- showMessage(data.message, "Error");
206
- document.getElementById('loginMessage').innerText = data.message;
207
- if (data.code === 401 && data.message === "Invalid 2FA code") {
208
- console.log("data.message");
209
- }
210
- }
211
- })
212
- .catch(error => {
213
- grecaptcha.reset();
214
- if (error.message === "Please Enter 2FA code") {
215
- showMessage("Please enter the 2FA code", "Error");
216
- const tokenCon = document.getElementById('tokenCon');
217
- const tokenInput = document.getElementById('token');
218
- tokenCon.style.display = "block";
219
- tokenInput.setAttribute('required', '');
220
- } else {
221
- console.error('Error:', error);
222
- showMessage(error.message, "Error");
223
546
  loginButton.disabled = false;
224
- loginButton.innerText = "Login";
225
- }
226
- });
227
- });
228
- document.addEventListener('DOMContentLoaded', function () {
229
- const loginButton = document.querySelector('button[type="submit"]');
230
- loginButton.innerText = "Login";
231
- });
232
- // Event listener for toggling password visibility
233
- document.addEventListener('DOMContentLoaded', function () { // Set the value of the input field with the page URL
234
- const passwordInput = document.getElementById('loginPassword');
235
- const toggleIcon = document.querySelector('.password-toggle-icon');
236
- toggleIcon.addEventListener('click', function () { // Toggle the type of the input field
237
- if (passwordInput.type === 'password') {
238
- passwordInput.type = 'text'; // Show password
239
- toggleIcon.innerHTML = '<i class="fa fa-eye-slash"></i>'; // Change icon to closed eye
240
- } else {
241
- passwordInput.type = 'password'; // Hide password
242
- toggleIcon.innerHTML = '<i class="fa fa-eye"></i>'; // Change icon to open eye
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;
243
565
  }
244
566
  });
245
- });
246
- const urlParams = new URLSearchParams(window.location.search);
247
- const usernameFromUrl = urlParams.get('username');
248
- const passwordFromUrl = urlParams.get('password');
249
- if (usernameFromUrl && !document.getElementById('loginUsername').value) {
250
- document.getElementById('loginUsername').value = usernameFromUrl;
251
- }
252
- if (passwordFromUrl && !document.getElementById('loginPassword').value) {
253
- document.getElementById('loginPassword').value = passwordFromUrl;
254
- }
255
- </script>
567
+ </script>
568
+ </body>
256
569
 
257
570
  </html>