aaspai-authx 0.1.4 → 0.1.6
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/dist/express/index.cjs +492 -41
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.js +495 -44
- package/dist/express/index.js.map +1 -1
- package/dist/index.cjs +492 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +495 -44
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +492 -41
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.js +495 -44
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/nest/index.cjs
CHANGED
|
@@ -57,8 +57,8 @@ function loadConfig() {
|
|
|
57
57
|
cookies: {
|
|
58
58
|
domain: process.env.COOKIE_DOMAIN,
|
|
59
59
|
secure: (process.env.COOKIE_SECURE || "true") === "true",
|
|
60
|
-
accessTtlMs: 24 * 60 * 60 * 1e3,
|
|
61
|
-
refreshTtlMs:
|
|
60
|
+
accessTtlMs: 7 * 24 * 60 * 60 * 1e3,
|
|
61
|
+
refreshTtlMs: 30 * 24 * 60 * 60 * 1e3
|
|
62
62
|
},
|
|
63
63
|
oidc: {
|
|
64
64
|
jwtSecret: process.env.JWT_SECRET
|
|
@@ -558,7 +558,7 @@ var AuthAdminService = class {
|
|
|
558
558
|
}
|
|
559
559
|
async updateUserPassword(userId, newPassword) {
|
|
560
560
|
const hashed = await import_bcrypt.default.hash(newPassword, 10);
|
|
561
|
-
await OrgUser.findOneAndUpdate({ id: userId }, {
|
|
561
|
+
await OrgUser.findOneAndUpdate({ id: userId }, { passwordHash: hashed });
|
|
562
562
|
}
|
|
563
563
|
// -------------------------------------------------------------------
|
|
564
564
|
// ADMIN TOKEN (self-issued JWT)
|
|
@@ -573,11 +573,11 @@ var AuthAdminService = class {
|
|
|
573
573
|
system: true
|
|
574
574
|
};
|
|
575
575
|
const accessToken = import_jsonwebtoken2.default.sign(payload, process.env.JWT_SECRET, {
|
|
576
|
-
expiresIn: "
|
|
576
|
+
expiresIn: "1d"
|
|
577
577
|
});
|
|
578
578
|
this.token = {
|
|
579
579
|
accessToken,
|
|
580
|
-
exp: now +
|
|
580
|
+
exp: now + 84800
|
|
581
581
|
};
|
|
582
582
|
return this.token.accessToken;
|
|
583
583
|
}
|
|
@@ -602,7 +602,7 @@ var EmailService = class {
|
|
|
602
602
|
}
|
|
603
603
|
});
|
|
604
604
|
}
|
|
605
|
-
sign(payload, ttlSec = 60 * 60 * 24) {
|
|
605
|
+
sign(payload, ttlSec = 60 * 60 * 24 * 30) {
|
|
606
606
|
return import_jsonwebtoken3.default.sign(payload, process.env.EMAIL_JWT_SECRET, {
|
|
607
607
|
expiresIn: ttlSec
|
|
608
608
|
});
|
|
@@ -610,11 +610,10 @@ var EmailService = class {
|
|
|
610
610
|
verify(token) {
|
|
611
611
|
return import_jsonwebtoken3.default.verify(token, process.env.EMAIL_JWT_SECRET);
|
|
612
612
|
}
|
|
613
|
-
async send(to, subject, html) {
|
|
614
|
-
console.log("[EmailService] Attempting to send:", { to, subject });
|
|
613
|
+
async send(to, subject, html, from) {
|
|
615
614
|
try {
|
|
616
615
|
const info = await this.transporter.sendMail({
|
|
617
|
-
from: process.env.EMAIL_FROM,
|
|
616
|
+
from: from ? `${from} ` + process.env.EMAIL_FROM : process.env.EMAIL_FROM,
|
|
618
617
|
to,
|
|
619
618
|
subject,
|
|
620
619
|
html
|
|
@@ -639,18 +638,6 @@ var EmailService = class {
|
|
|
639
638
|
}
|
|
640
639
|
}
|
|
641
640
|
canSend(lastEmailSent) {
|
|
642
|
-
console.log(
|
|
643
|
-
process.env.EMAIL_PASSWORD,
|
|
644
|
-
"pssword",
|
|
645
|
-
process.env.EMAIL_USER,
|
|
646
|
-
"user",
|
|
647
|
-
process.env.EMAIL_SECURE,
|
|
648
|
-
"secure",
|
|
649
|
-
process.env.EMAIL_PORT,
|
|
650
|
-
"porat",
|
|
651
|
-
process.env.EMAIL_HOST,
|
|
652
|
-
"hosat"
|
|
653
|
-
);
|
|
654
641
|
const now = Date.now();
|
|
655
642
|
const windowStart = now - this.WINDOW_MINUTES * 60 * 1e3;
|
|
656
643
|
const emailsInWindow = (lastEmailSent || []).map((d) => new Date(d)).filter((d) => d.getTime() >= windowStart);
|
|
@@ -664,6 +651,386 @@ var EmailService = class {
|
|
|
664
651
|
}
|
|
665
652
|
};
|
|
666
653
|
|
|
654
|
+
// src/templates/email.templates.ts
|
|
655
|
+
var colors = {
|
|
656
|
+
background: "#0a0a0a",
|
|
657
|
+
cardBackground: "#111111",
|
|
658
|
+
cardBorder: "#1a1a1a",
|
|
659
|
+
accent: "#ffffff",
|
|
660
|
+
accentMuted: "rgba(255, 255, 255, 0.9)",
|
|
661
|
+
textPrimary: "#ffffff",
|
|
662
|
+
textSecondary: "rgba(255, 255, 255, 0.7)",
|
|
663
|
+
textMuted: "rgba(255, 255, 255, 0.5)",
|
|
664
|
+
divider: "rgba(255, 255, 255, 0.1)",
|
|
665
|
+
subtle: "#161616",
|
|
666
|
+
highlight: "rgba(255, 255, 255, 0.05)"
|
|
667
|
+
};
|
|
668
|
+
var styles = {
|
|
669
|
+
wrapper: `
|
|
670
|
+
margin: 0;
|
|
671
|
+
padding: 40px 20px;
|
|
672
|
+
background-color: ${colors.background};
|
|
673
|
+
background-image:
|
|
674
|
+
radial-gradient(ellipse at top, rgba(255,255,255,0.03) 0%, transparent 50%),
|
|
675
|
+
radial-gradient(ellipse at bottom, rgba(255,255,255,0.02) 0%, transparent 50%);
|
|
676
|
+
min-height: 100vh;
|
|
677
|
+
`,
|
|
678
|
+
container: `
|
|
679
|
+
max-width: 520px;
|
|
680
|
+
margin: 0 auto;
|
|
681
|
+
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
682
|
+
color: ${colors.textPrimary};
|
|
683
|
+
line-height: 1.7;
|
|
684
|
+
`,
|
|
685
|
+
card: `
|
|
686
|
+
background-color: ${colors.cardBackground};
|
|
687
|
+
border: 1px solid ${colors.cardBorder};
|
|
688
|
+
border-radius: 16px;
|
|
689
|
+
overflow: hidden;
|
|
690
|
+
box-shadow:
|
|
691
|
+
0 0 0 1px rgba(255,255,255,0.05),
|
|
692
|
+
0 20px 50px -20px rgba(0,0,0,0.5),
|
|
693
|
+
0 30px 60px -30px rgba(0,0,0,0.3);
|
|
694
|
+
`,
|
|
695
|
+
header: `
|
|
696
|
+
padding: 48px 40px 32px;
|
|
697
|
+
text-align: center;
|
|
698
|
+
border-bottom: 1px solid ${colors.divider};
|
|
699
|
+
`,
|
|
700
|
+
iconWrapper: `
|
|
701
|
+
width: 64px;
|
|
702
|
+
height: 64px;
|
|
703
|
+
margin: 0 auto 24px;
|
|
704
|
+
background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
|
|
705
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
706
|
+
border-radius: 16px;
|
|
707
|
+
display: flex;
|
|
708
|
+
align-items: center;
|
|
709
|
+
justify-content: center;
|
|
710
|
+
font-size: 28px;
|
|
711
|
+
`,
|
|
712
|
+
headerTitle: `
|
|
713
|
+
color: ${colors.textPrimary};
|
|
714
|
+
margin: 0;
|
|
715
|
+
font-size: 24px;
|
|
716
|
+
font-weight: 600;
|
|
717
|
+
letter-spacing: -0.5px;
|
|
718
|
+
`,
|
|
719
|
+
headerSubtitle: `
|
|
720
|
+
color: ${colors.textMuted};
|
|
721
|
+
margin: 8px 0 0;
|
|
722
|
+
font-size: 14px;
|
|
723
|
+
font-weight: 400;
|
|
724
|
+
`,
|
|
725
|
+
body: `
|
|
726
|
+
padding: 40px;
|
|
727
|
+
`,
|
|
728
|
+
greeting: `
|
|
729
|
+
margin: 0 0 24px;
|
|
730
|
+
color: ${colors.textPrimary};
|
|
731
|
+
font-size: 18px;
|
|
732
|
+
font-weight: 500;
|
|
733
|
+
`,
|
|
734
|
+
paragraph: `
|
|
735
|
+
margin: 0 0 20px;
|
|
736
|
+
color: ${colors.textSecondary};
|
|
737
|
+
font-size: 15px;
|
|
738
|
+
line-height: 1.7;
|
|
739
|
+
`,
|
|
740
|
+
buttonWrapper: `
|
|
741
|
+
text-align: center;
|
|
742
|
+
margin: 32px 0;
|
|
743
|
+
`,
|
|
744
|
+
button: `
|
|
745
|
+
display: inline-block;
|
|
746
|
+
background-color: ${colors.accent};
|
|
747
|
+
color: #000000 !important;
|
|
748
|
+
text-decoration: none;
|
|
749
|
+
padding: 14px 36px;
|
|
750
|
+
border-radius: 8px;
|
|
751
|
+
font-weight: 600;
|
|
752
|
+
font-size: 14px;
|
|
753
|
+
letter-spacing: 0.3px;
|
|
754
|
+
transition: all 0.2s ease;
|
|
755
|
+
`,
|
|
756
|
+
secondaryButton: `
|
|
757
|
+
display: inline-block;
|
|
758
|
+
background-color: transparent;
|
|
759
|
+
color: ${colors.textPrimary} !important;
|
|
760
|
+
text-decoration: none;
|
|
761
|
+
padding: 12px 28px;
|
|
762
|
+
border-radius: 8px;
|
|
763
|
+
font-weight: 500;
|
|
764
|
+
font-size: 14px;
|
|
765
|
+
border: 1px solid ${colors.divider};
|
|
766
|
+
`,
|
|
767
|
+
infoCard: `
|
|
768
|
+
background-color: ${colors.subtle};
|
|
769
|
+
border: 1px solid ${colors.divider};
|
|
770
|
+
border-radius: 12px;
|
|
771
|
+
padding: 20px 24px;
|
|
772
|
+
margin: 28px 0;
|
|
773
|
+
`,
|
|
774
|
+
infoCardTitle: `
|
|
775
|
+
margin: 0 0 12px;
|
|
776
|
+
color: ${colors.textPrimary};
|
|
777
|
+
font-size: 13px;
|
|
778
|
+
font-weight: 600;
|
|
779
|
+
text-transform: uppercase;
|
|
780
|
+
letter-spacing: 0.5px;
|
|
781
|
+
`,
|
|
782
|
+
infoCardText: `
|
|
783
|
+
margin: 0;
|
|
784
|
+
color: ${colors.textSecondary};
|
|
785
|
+
font-size: 14px;
|
|
786
|
+
line-height: 1.6;
|
|
787
|
+
`,
|
|
788
|
+
warningCard: `
|
|
789
|
+
background: linear-gradient(135deg, rgba(255,180,0,0.1) 0%, rgba(255,140,0,0.05) 100%);
|
|
790
|
+
border: 1px solid rgba(255,180,0,0.2);
|
|
791
|
+
border-radius: 12px;
|
|
792
|
+
padding: 20px 24px;
|
|
793
|
+
margin: 28px 0;
|
|
794
|
+
`,
|
|
795
|
+
warningCardTitle: `
|
|
796
|
+
margin: 0 0 12px;
|
|
797
|
+
color: #ffc107;
|
|
798
|
+
font-size: 13px;
|
|
799
|
+
font-weight: 600;
|
|
800
|
+
text-transform: uppercase;
|
|
801
|
+
letter-spacing: 0.5px;
|
|
802
|
+
`,
|
|
803
|
+
warningCardText: `
|
|
804
|
+
margin: 0;
|
|
805
|
+
color: rgba(255,255,255,0.7);
|
|
806
|
+
font-size: 14px;
|
|
807
|
+
line-height: 1.6;
|
|
808
|
+
`,
|
|
809
|
+
successCard: `
|
|
810
|
+
background: linear-gradient(135deg, rgba(0,255,150,0.1) 0%, rgba(0,200,100,0.05) 100%);
|
|
811
|
+
border: 1px solid rgba(0,255,150,0.2);
|
|
812
|
+
border-radius: 12px;
|
|
813
|
+
padding: 20px 24px;
|
|
814
|
+
margin: 28px 0;
|
|
815
|
+
`,
|
|
816
|
+
successCardTitle: `
|
|
817
|
+
margin: 0 0 12px;
|
|
818
|
+
color: #00ff96;
|
|
819
|
+
font-size: 13px;
|
|
820
|
+
font-weight: 600;
|
|
821
|
+
text-transform: uppercase;
|
|
822
|
+
letter-spacing: 0.5px;
|
|
823
|
+
`,
|
|
824
|
+
linkSection: `
|
|
825
|
+
margin: 32px 0;
|
|
826
|
+
padding: 20px;
|
|
827
|
+
background-color: ${colors.subtle};
|
|
828
|
+
border-radius: 8px;
|
|
829
|
+
border: 1px solid ${colors.divider};
|
|
830
|
+
`,
|
|
831
|
+
linkLabel: `
|
|
832
|
+
margin: 0 0 8px;
|
|
833
|
+
color: ${colors.textMuted};
|
|
834
|
+
font-size: 12px;
|
|
835
|
+
text-transform: uppercase;
|
|
836
|
+
letter-spacing: 0.5px;
|
|
837
|
+
`,
|
|
838
|
+
linkText: `
|
|
839
|
+
word-break: break-all;
|
|
840
|
+
color: ${colors.textSecondary};
|
|
841
|
+
font-size: 13px;
|
|
842
|
+
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
|
|
843
|
+
margin: 0;
|
|
844
|
+
`,
|
|
845
|
+
divider: `
|
|
846
|
+
border: none;
|
|
847
|
+
border-top: 1px solid ${colors.divider};
|
|
848
|
+
margin: 32px 0;
|
|
849
|
+
`,
|
|
850
|
+
footer: `
|
|
851
|
+
padding: 24px 40px 32px;
|
|
852
|
+
text-align: center;
|
|
853
|
+
border-top: 1px solid ${colors.divider};
|
|
854
|
+
`,
|
|
855
|
+
footerText: `
|
|
856
|
+
margin: 0;
|
|
857
|
+
color: ${colors.textMuted};
|
|
858
|
+
font-size: 12px;
|
|
859
|
+
line-height: 1.8;
|
|
860
|
+
`,
|
|
861
|
+
footerLink: `
|
|
862
|
+
color: ${colors.textSecondary};
|
|
863
|
+
text-decoration: none;
|
|
864
|
+
`,
|
|
865
|
+
badge: `
|
|
866
|
+
display: inline-block;
|
|
867
|
+
background-color: rgba(255,255,255,0.1);
|
|
868
|
+
color: ${colors.textSecondary};
|
|
869
|
+
padding: 4px 12px;
|
|
870
|
+
border-radius: 20px;
|
|
871
|
+
font-size: 12px;
|
|
872
|
+
font-weight: 500;
|
|
873
|
+
letter-spacing: 0.3px;
|
|
874
|
+
`,
|
|
875
|
+
listItem: `
|
|
876
|
+
color: ${colors.textSecondary};
|
|
877
|
+
font-size: 14px;
|
|
878
|
+
margin: 8px 0;
|
|
879
|
+
padding-left: 8px;
|
|
880
|
+
`,
|
|
881
|
+
metaRow: `
|
|
882
|
+
display: flex;
|
|
883
|
+
justify-content: space-between;
|
|
884
|
+
padding: 12px 0;
|
|
885
|
+
border-bottom: 1px solid ${colors.divider};
|
|
886
|
+
`,
|
|
887
|
+
metaLabel: `
|
|
888
|
+
color: ${colors.textMuted};
|
|
889
|
+
font-size: 13px;
|
|
890
|
+
`,
|
|
891
|
+
metaValue: `
|
|
892
|
+
color: ${colors.textPrimary};
|
|
893
|
+
font-size: 13px;
|
|
894
|
+
font-weight: 500;
|
|
895
|
+
`
|
|
896
|
+
};
|
|
897
|
+
function buildVerificationEmailTemplate(data) {
|
|
898
|
+
const { firstName, verificationUrl, expiresIn } = data;
|
|
899
|
+
return `
|
|
900
|
+
<!DOCTYPE html>
|
|
901
|
+
<html lang="en">
|
|
902
|
+
<head>
|
|
903
|
+
<meta charset="UTF-8">
|
|
904
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
905
|
+
<meta name="color-scheme" content="dark">
|
|
906
|
+
<meta name="supported-color-schemes" content="dark">
|
|
907
|
+
<title>Verify Your Email</title>
|
|
908
|
+
<!--[if mso]>
|
|
909
|
+
<style type="text/css">
|
|
910
|
+
body, table, td {font-family: Arial, Helvetica, sans-serif !important;}
|
|
911
|
+
</style>
|
|
912
|
+
<![endif]-->
|
|
913
|
+
</head>
|
|
914
|
+
<body style="${styles.wrapper}">
|
|
915
|
+
<div style="${styles.container}">
|
|
916
|
+
<div style="${styles.card}">
|
|
917
|
+
<!-- Header -->
|
|
918
|
+
<div style="${styles.header}">
|
|
919
|
+
<div style="${styles.iconWrapper}">
|
|
920
|
+
\u2709\uFE0F
|
|
921
|
+
</div>
|
|
922
|
+
<h1 style="${styles.headerTitle}">Verify your email</h1>
|
|
923
|
+
<p style="${styles.headerSubtitle}">One quick step to get started</p>
|
|
924
|
+
</div>
|
|
925
|
+
|
|
926
|
+
<!-- Body -->
|
|
927
|
+
<div style="${styles.body}">
|
|
928
|
+
<p style="${styles.greeting}">Hi ${firstName},</p>
|
|
929
|
+
|
|
930
|
+
<p style="${styles.paragraph}">
|
|
931
|
+
Thanks for signing up. To complete your registration and unlock all features,
|
|
932
|
+
please verify your email address by clicking the button below.
|
|
933
|
+
</p>
|
|
934
|
+
|
|
935
|
+
<div style="${styles.buttonWrapper}">
|
|
936
|
+
<a href="${verificationUrl}" style="${styles.button}" target="_blank">
|
|
937
|
+
Verify Email Address
|
|
938
|
+
</a>
|
|
939
|
+
</div>
|
|
940
|
+
|
|
941
|
+
<div style="${styles.infoCard}">
|
|
942
|
+
<p style="${styles.infoCardTitle}">\u23F1 Time Sensitive</p>
|
|
943
|
+
<p style="${styles.infoCardText}">
|
|
944
|
+
This verification link will expire in <strong>${expiresIn}</strong>.
|
|
945
|
+
If you didn't create an account, you can safely ignore this email.
|
|
946
|
+
</p>
|
|
947
|
+
</div>
|
|
948
|
+
</div>
|
|
949
|
+
|
|
950
|
+
<!-- Footer -->
|
|
951
|
+
<div style="${styles.footer}">
|
|
952
|
+
<p style="${styles.footerText}">
|
|
953
|
+
This is an automated message \u2014 please do not reply.<br>
|
|
954
|
+
\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} All rights reserved.
|
|
955
|
+
</p>
|
|
956
|
+
</div>
|
|
957
|
+
</div>
|
|
958
|
+
</div>
|
|
959
|
+
</body>
|
|
960
|
+
</html>
|
|
961
|
+
`;
|
|
962
|
+
}
|
|
963
|
+
function buildResetPasswordEmailTemplate(data) {
|
|
964
|
+
const { firstName, resetUrl, expiresIn } = data;
|
|
965
|
+
return `
|
|
966
|
+
<!DOCTYPE html>
|
|
967
|
+
<html lang="en">
|
|
968
|
+
<head>
|
|
969
|
+
<meta charset="UTF-8">
|
|
970
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
971
|
+
<meta name="color-scheme" content="dark">
|
|
972
|
+
<meta name="supported-color-schemes" content="dark">
|
|
973
|
+
<title>Reset Your Password</title>
|
|
974
|
+
</head>
|
|
975
|
+
<body style="${styles.wrapper}">
|
|
976
|
+
<div style="${styles.container}">
|
|
977
|
+
<div style="${styles.card}">
|
|
978
|
+
<!-- Header -->
|
|
979
|
+
<div style="${styles.header}">
|
|
980
|
+
<div style="${styles.iconWrapper}">
|
|
981
|
+
\u{1F510}
|
|
982
|
+
</div>
|
|
983
|
+
<h1 style="${styles.headerTitle}">Reset your password</h1>
|
|
984
|
+
<p style="${styles.headerSubtitle}">We received a reset request</p>
|
|
985
|
+
</div>
|
|
986
|
+
|
|
987
|
+
<!-- Body -->
|
|
988
|
+
<div style="${styles.body}">
|
|
989
|
+
<p style="${styles.greeting}">Hi ${firstName},</p>
|
|
990
|
+
|
|
991
|
+
<p style="${styles.paragraph}">
|
|
992
|
+
We received a request to reset the password for your account.
|
|
993
|
+
Click the button below to create a new password.
|
|
994
|
+
</p>
|
|
995
|
+
|
|
996
|
+
<div style="${styles.buttonWrapper}">
|
|
997
|
+
<a href="${resetUrl}" style="${styles.button}" target="_blank">
|
|
998
|
+
Reset Password
|
|
999
|
+
</a>
|
|
1000
|
+
</div>
|
|
1001
|
+
|
|
1002
|
+
<div style="${styles.warningCard}">
|
|
1003
|
+
<p style="${styles.warningCardTitle}">\u26A0\uFE0F Security Notice</p>
|
|
1004
|
+
<p style="${styles.warningCardText}">
|
|
1005
|
+
\u2022 This link expires in <strong>${expiresIn}</strong><br>
|
|
1006
|
+
\u2022 This link can only be used once<br>
|
|
1007
|
+
\u2022 If you didn't request this, ignore this email
|
|
1008
|
+
</p>
|
|
1009
|
+
</div>
|
|
1010
|
+
|
|
1011
|
+
<hr style="${styles.divider}" />
|
|
1012
|
+
|
|
1013
|
+
<p style="${styles.paragraph}; font-size: 13px; color: ${colors.textMuted};">
|
|
1014
|
+
<strong>Didn't request this?</strong><br>
|
|
1015
|
+
Your password remains unchanged. If you're concerned about your account
|
|
1016
|
+
security, please contact our support team immediately.
|
|
1017
|
+
</p>
|
|
1018
|
+
</div>
|
|
1019
|
+
|
|
1020
|
+
<!-- Footer -->
|
|
1021
|
+
<div style="${styles.footer}">
|
|
1022
|
+
<p style="${styles.footerText}">
|
|
1023
|
+
This is an automated message \u2014 please do not reply.<br>
|
|
1024
|
+
\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} All rights reserved.
|
|
1025
|
+
</p>
|
|
1026
|
+
</div>
|
|
1027
|
+
</div>
|
|
1028
|
+
</div>
|
|
1029
|
+
</body>
|
|
1030
|
+
</html>
|
|
1031
|
+
`;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
667
1034
|
// src/express/auth.routes.ts
|
|
668
1035
|
function createAuthRouter(options = {}) {
|
|
669
1036
|
const googleClientId = process.env.GOOGLE_CLIENT_ID;
|
|
@@ -685,7 +1052,7 @@ function createAuthRouter(options = {}) {
|
|
|
685
1052
|
// default: secure in prod
|
|
686
1053
|
domain: options.cookie?.domain ?? void 0,
|
|
687
1054
|
path: options.cookie?.path ?? "/",
|
|
688
|
-
maxAgeMs: options.cookie?.maxAgeMs ?? 24 * 60 * 60 * 1e3
|
|
1055
|
+
maxAgeMs: options.cookie?.maxAgeMs ?? 30 * 24 * 60 * 60 * 1e3
|
|
689
1056
|
};
|
|
690
1057
|
r.use(import_express.default.json());
|
|
691
1058
|
r.use(import_express.default.urlencoded({ extended: true }));
|
|
@@ -742,6 +1109,7 @@ function createAuthRouter(options = {}) {
|
|
|
742
1109
|
projectId,
|
|
743
1110
|
metadata
|
|
744
1111
|
} = req.body || {};
|
|
1112
|
+
const COMPANY_NAME = process.env.COMPANY_NAME;
|
|
745
1113
|
try {
|
|
746
1114
|
const kcUser = await authAdmin.createUserInRealm({
|
|
747
1115
|
username: emailAddress,
|
|
@@ -770,10 +1138,21 @@ function createAuthRouter(options = {}) {
|
|
|
770
1138
|
emailService: email,
|
|
771
1139
|
user,
|
|
772
1140
|
subject: "Verify your email",
|
|
773
|
-
html: buildVerificationTemplate(
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
)
|
|
1141
|
+
// html: buildVerificationTemplate(
|
|
1142
|
+
// email.sign({ userId: kcUser.id, email: kcUser.email }),
|
|
1143
|
+
// options,
|
|
1144
|
+
// ),
|
|
1145
|
+
html: buildVerificationEmailTemplate({
|
|
1146
|
+
firstName: user.firstName,
|
|
1147
|
+
verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(
|
|
1148
|
+
{
|
|
1149
|
+
userId: user.id,
|
|
1150
|
+
email: user.email
|
|
1151
|
+
}
|
|
1152
|
+
)}`,
|
|
1153
|
+
expiresIn: "1 hour"
|
|
1154
|
+
}),
|
|
1155
|
+
from: COMPANY_NAME
|
|
777
1156
|
});
|
|
778
1157
|
if (emailResult.rateLimited) {
|
|
779
1158
|
return res.status(429).json({
|
|
@@ -838,6 +1217,7 @@ function createAuthRouter(options = {}) {
|
|
|
838
1217
|
"/resend-verification-email",
|
|
839
1218
|
validateResendEmail,
|
|
840
1219
|
async (req, res) => {
|
|
1220
|
+
const COMPANY_NAME = process.env.COMPANY_NAME;
|
|
841
1221
|
const user = await OrgUser.findOne({ email: req.body.email });
|
|
842
1222
|
if (!user)
|
|
843
1223
|
return res.status(404).json({ ok: false, error: "User not found" });
|
|
@@ -853,7 +1233,18 @@ function createAuthRouter(options = {}) {
|
|
|
853
1233
|
emailService: email,
|
|
854
1234
|
user,
|
|
855
1235
|
subject: "Verify your email",
|
|
856
|
-
html: buildVerificationTemplate(token, options)
|
|
1236
|
+
// html: buildVerificationTemplate(token, options),
|
|
1237
|
+
html: buildVerificationEmailTemplate({
|
|
1238
|
+
firstName: user.firstName,
|
|
1239
|
+
verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(
|
|
1240
|
+
{
|
|
1241
|
+
userId: user.id,
|
|
1242
|
+
email: user.email
|
|
1243
|
+
}
|
|
1244
|
+
)}`,
|
|
1245
|
+
expiresIn: "1 hour"
|
|
1246
|
+
}),
|
|
1247
|
+
from: COMPANY_NAME
|
|
857
1248
|
});
|
|
858
1249
|
if (resendResult.rateLimited) {
|
|
859
1250
|
return res.status(429).json({
|
|
@@ -866,6 +1257,7 @@ function createAuthRouter(options = {}) {
|
|
|
866
1257
|
}
|
|
867
1258
|
);
|
|
868
1259
|
r.post("/forgot-password", validateResendEmail, async (req, res) => {
|
|
1260
|
+
const COMPANY_NAME = process.env.COMPANY_NAME;
|
|
869
1261
|
const user = await OrgUser.findOne({ email: req.body.email });
|
|
870
1262
|
if (!user)
|
|
871
1263
|
return res.status(404).json({ ok: false, error: "User not found" });
|
|
@@ -882,7 +1274,18 @@ function createAuthRouter(options = {}) {
|
|
|
882
1274
|
emailService: email,
|
|
883
1275
|
user,
|
|
884
1276
|
subject: "Reset password",
|
|
885
|
-
html: buildResetTemplate(resetToken, options)
|
|
1277
|
+
// html: buildResetTemplate(resetToken, options),
|
|
1278
|
+
html: buildResetPasswordEmailTemplate({
|
|
1279
|
+
firstName: user.firstName,
|
|
1280
|
+
resetUrl: `${getFrontendBaseUrl(options)}/reset-password?token=${email.sign(
|
|
1281
|
+
{
|
|
1282
|
+
userId: user.id,
|
|
1283
|
+
email: user.email
|
|
1284
|
+
}
|
|
1285
|
+
)}`,
|
|
1286
|
+
expiresIn: "1 hour"
|
|
1287
|
+
}),
|
|
1288
|
+
from: COMPANY_NAME
|
|
886
1289
|
});
|
|
887
1290
|
if (resetResult.rateLimited) {
|
|
888
1291
|
return res.status(429).json({
|
|
@@ -895,9 +1298,16 @@ function createAuthRouter(options = {}) {
|
|
|
895
1298
|
});
|
|
896
1299
|
r.post("/reset-password", validateResetPassword, async (req, res) => {
|
|
897
1300
|
const { token, newPassword } = req.body || {};
|
|
1301
|
+
if (!token || !newPassword) {
|
|
1302
|
+
return res.status(400).json({
|
|
1303
|
+
ok: false,
|
|
1304
|
+
error: "Token and new password are required",
|
|
1305
|
+
code: "MISSING_FIELDS"
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
898
1308
|
try {
|
|
899
1309
|
const payload = email.verify(token);
|
|
900
|
-
const user = await OrgUser.findOne({
|
|
1310
|
+
const user = await OrgUser.findOne({ id: payload.userId });
|
|
901
1311
|
if (!user) {
|
|
902
1312
|
return res.status(404).json({ ok: false, error: "User not found" });
|
|
903
1313
|
}
|
|
@@ -1265,8 +1675,6 @@ function setAuthCookies(res, tokens, cookie) {
|
|
|
1265
1675
|
if (cookie.domain) {
|
|
1266
1676
|
base.domain = cookie.domain;
|
|
1267
1677
|
}
|
|
1268
|
-
console.log(cookie, "cookie");
|
|
1269
|
-
console.log(base, "base");
|
|
1270
1678
|
if (tokens?.access_token) {
|
|
1271
1679
|
res.cookie("access_token", tokens.access_token, base);
|
|
1272
1680
|
}
|
|
@@ -1290,12 +1698,6 @@ function respondWithKeycloakError(res, err, fallback, status = 400) {
|
|
|
1290
1698
|
const description = err?.response?.data?.error_description || err?.response?.data?.errorMessage || err?.message || fallback;
|
|
1291
1699
|
return res.status(status).json({ ok: false, error: description });
|
|
1292
1700
|
}
|
|
1293
|
-
function buildVerificationTemplate(token, options) {
|
|
1294
|
-
return `<a href="${getFrontendBaseUrl(options)}/auth/verify-email?token=${token}">Verify</a>`;
|
|
1295
|
-
}
|
|
1296
|
-
function buildResetTemplate(token, options) {
|
|
1297
|
-
return `<a href="${getFrontendBaseUrl(options)}/auth/reset-password?token=${token}">Reset</a>`;
|
|
1298
|
-
}
|
|
1299
1701
|
function getFrontendBaseUrl(options) {
|
|
1300
1702
|
if (options.frontendBaseUrl)
|
|
1301
1703
|
return options.frontendBaseUrl.replace(/\/$/, "");
|
|
@@ -1307,13 +1709,14 @@ async function sendRateLimitedEmail({
|
|
|
1307
1709
|
emailService,
|
|
1308
1710
|
user,
|
|
1309
1711
|
subject,
|
|
1310
|
-
html
|
|
1712
|
+
html,
|
|
1713
|
+
from
|
|
1311
1714
|
}) {
|
|
1312
1715
|
const can = emailService.canSend(user?.lastEmailSent || []);
|
|
1313
1716
|
if (!can.ok) {
|
|
1314
1717
|
return { rateLimited: true, waitMs: can.waitMs };
|
|
1315
1718
|
}
|
|
1316
|
-
await emailService.send(user.email, subject, html);
|
|
1719
|
+
await emailService.send(user.email, subject, html, from);
|
|
1317
1720
|
user.lastEmailSent = [...user.lastEmailSent || [], /* @__PURE__ */ new Date()];
|
|
1318
1721
|
await user.save();
|
|
1319
1722
|
return { rateLimited: false };
|
|
@@ -1334,7 +1737,7 @@ function generateTokens(user) {
|
|
|
1334
1737
|
type: "user"
|
|
1335
1738
|
};
|
|
1336
1739
|
const accessToken = import_jsonwebtoken4.default.sign(accessPayload, process.env.JWT_SECRET, {
|
|
1337
|
-
expiresIn: "
|
|
1740
|
+
expiresIn: "1d"
|
|
1338
1741
|
});
|
|
1339
1742
|
const refreshToken = import_jsonwebtoken4.default.sign(
|
|
1340
1743
|
{ sub: user._id.toString() },
|
|
@@ -1370,13 +1773,61 @@ function createDashboardRouter(options) {
|
|
|
1370
1773
|
}
|
|
1371
1774
|
|
|
1372
1775
|
// src/express/email.routes.ts
|
|
1373
|
-
var import_express3 = require("express");
|
|
1776
|
+
var import_express3 = __toESM(require("express"), 1);
|
|
1374
1777
|
function createEmailRouter(options) {
|
|
1375
1778
|
const r = (0, import_express3.Router)();
|
|
1779
|
+
const emailService = new EmailService();
|
|
1780
|
+
r.use(import_express3.default.json());
|
|
1781
|
+
r.use(import_express3.default.urlencoded({ extended: true }));
|
|
1376
1782
|
r.get(
|
|
1377
1783
|
"/verify",
|
|
1378
1784
|
(req, res) => res.json({ ok: true, token: req.query.token })
|
|
1379
1785
|
);
|
|
1786
|
+
r.post("/send", async (req, res) => {
|
|
1787
|
+
try {
|
|
1788
|
+
const { userId, to, subject, html, from } = req.body ?? {};
|
|
1789
|
+
if (!to || !subject || !html) {
|
|
1790
|
+
return res.status(400).json({
|
|
1791
|
+
ok: false,
|
|
1792
|
+
error: "BAD_REQUEST",
|
|
1793
|
+
message: "`to`, `subject`, and `html` are required."
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
if (userId) {
|
|
1797
|
+
const user = await OrgUser.findOne({ id: userId }).lean();
|
|
1798
|
+
if (!user) {
|
|
1799
|
+
return res.status(404).json({
|
|
1800
|
+
ok: false,
|
|
1801
|
+
error: "NOT_FOUND",
|
|
1802
|
+
message: "User not found."
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
const can = emailService.canSend(user?.lastEmailSent || []);
|
|
1806
|
+
if (!can.ok) {
|
|
1807
|
+
return res.status(429).json({
|
|
1808
|
+
ok: false,
|
|
1809
|
+
error: can.reason,
|
|
1810
|
+
waitMs: can.waitMs,
|
|
1811
|
+
message: "Too many emails sent recently. Please retry later."
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
await emailService.send(to, subject, html, from);
|
|
1816
|
+
if (userId) {
|
|
1817
|
+
await OrgUser.updateOne(
|
|
1818
|
+
{ id: userId },
|
|
1819
|
+
{ $push: { lastEmailSent: /* @__PURE__ */ new Date() } }
|
|
1820
|
+
);
|
|
1821
|
+
}
|
|
1822
|
+
return res.json({ ok: true });
|
|
1823
|
+
} catch (err) {
|
|
1824
|
+
return res.status(500).json({
|
|
1825
|
+
ok: false,
|
|
1826
|
+
error: "INTERNAL",
|
|
1827
|
+
message: err?.message ?? "Error"
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
});
|
|
1380
1831
|
return r;
|
|
1381
1832
|
}
|
|
1382
1833
|
|