From a93389f5c5ae870c44f4fe1e7e7c8614e7a10f63 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Thu, 26 Dec 2024 19:06:56 -0500 Subject: [PATCH 1/6] add simple verify email page --- .../internal/views/verify_email_page.templ | 88 +++++++ .../internal/views/verify_email_page_templ.go | 221 ++++++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 backend/internal/views/verify_email_page.templ create mode 100644 backend/internal/views/verify_email_page_templ.go diff --git a/backend/internal/views/verify_email_page.templ b/backend/internal/views/verify_email_page.templ new file mode 100644 index 00000000..ed71dda7 --- /dev/null +++ b/backend/internal/views/verify_email_page.templ @@ -0,0 +1,88 @@ +package views + +type VerifyEmailPageVariant string + +const ( + SuccessVerifyEmailPage VerifyEmailPageVariant = "success" + FailVerifyEmailPage VerifyEmailPageVariant = "fail" +) + +css main() { + background-color: #f8fafc; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +css card() { + padding: 2rem; + border-width: 1px; + border-radius: 0.25rem; + border-style: solid; + border-color: #cbd5e1; + box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + max-width: 28rem; +} + +templ VerifyEmailPage(variant VerifyEmailPageVariant, details string) { + + + + + + { getTitle(variant) } + + + +
+
+

{ getCardTitle(variant) }

+ if details == "" { +

{ getCardContent(variant) }

+ } else { +

{ details }

+ } +
+
+ + +} + +func getTitle(variant VerifyEmailPageVariant) string { + switch variant { + case SuccessVerifyEmailPage: + return "Email Verified" + case FailVerifyEmailPage: + return "Email Not Verified" + } + + return "Oops... Something went wrong" +} + +func getCardTitle(variant VerifyEmailPageVariant) string { + switch variant { + case SuccessVerifyEmailPage: + return "Your email has been verified." + case FailVerifyEmailPage: + return "Hmmm... something went wrong" + } + + return "Oops... Something went wrong" +} + +func getCardContent(variant VerifyEmailPageVariant) string { + switch variant { + case SuccessVerifyEmailPage: + return "You can close this window now." + case FailVerifyEmailPage: + return "Please try again by requesting a new verification email.\nIf the problem persists, please try again later." + } + + return "Oops... Something went wrong" +} diff --git a/backend/internal/views/verify_email_page_templ.go b/backend/internal/views/verify_email_page_templ.go new file mode 100644 index 00000000..9fa5b9e4 --- /dev/null +++ b/backend/internal/views/verify_email_page_templ.go @@ -0,0 +1,221 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.793 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +type VerifyEmailPageVariant string + +const ( + SuccessVerifyEmailPage VerifyEmailPageVariant = "success" + FailVerifyEmailPage VerifyEmailPageVariant = "fail" +) + +func main() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`background-color:#f8fafc;`) + templ_7745c5c3_CSSBuilder.WriteString(`height:100vh;`) + templ_7745c5c3_CSSBuilder.WriteString(`display:flex;`) + templ_7745c5c3_CSSBuilder.WriteString(`justify-content:center;`) + templ_7745c5c3_CSSBuilder.WriteString(`align-items:center;`) + templ_7745c5c3_CSSID := templ.CSSID(`main`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func card() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`padding:2rem;`) + templ_7745c5c3_CSSBuilder.WriteString(`border-width:1px;`) + templ_7745c5c3_CSSBuilder.WriteString(`border-radius:0.25rem;`) + templ_7745c5c3_CSSBuilder.WriteString(`border-style:solid;`) + templ_7745c5c3_CSSBuilder.WriteString(`border-color:#cbd5e1;`) + templ_7745c5c3_CSSBuilder.WriteString(`box-shadow:0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);`) + templ_7745c5c3_CSSBuilder.WriteString(`max-width:28rem;`) + templ_7745c5c3_CSSID := templ.CSSID(`card`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func VerifyEmailPage(variant VerifyEmailPageVariant, details string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 string + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(getTitle(variant)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 34, Col: 29} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var3 = []any{main()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 = []any{card()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(getCardTitle(variant)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 45, Col: 32} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if details == "" { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(getCardContent(variant)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 47, Col: 34} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(details) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 49, Col: 18} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func getTitle(variant VerifyEmailPageVariant) string { + switch variant { + case SuccessVerifyEmailPage: + return "Email Verified" + case FailVerifyEmailPage: + return "Email Not Verified" + } + + return "Oops... Something went wrong" +} + +func getCardTitle(variant VerifyEmailPageVariant) string { + switch variant { + case SuccessVerifyEmailPage: + return "Your email has been verified." + case FailVerifyEmailPage: + return "Hmmm... something went wrong" + } + + return "Oops... Something went wrong" +} + +func getCardContent(variant VerifyEmailPageVariant) string { + switch variant { + case SuccessVerifyEmailPage: + return "You can close this window now." + case FailVerifyEmailPage: + return "Please try again by requesting a new verification email.\nIf the problem persists, please try again later." + } + + return "Oops... Something went wrong" +} + +var _ = templruntime.GeneratedTemplate From aa28d30ccf8b33088a0bdd378e3809b032f468b3 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:35:07 -0500 Subject: [PATCH 2/6] update verify email page template --- .../internal/views/verify_email_page.templ | 83 ++++++- .../internal/views/verify_email_page_templ.go | 230 ++++++++++++++++-- 2 files changed, 283 insertions(+), 30 deletions(-) diff --git a/backend/internal/views/verify_email_page.templ b/backend/internal/views/verify_email_page.templ index ed71dda7..4dfa4334 100644 --- a/backend/internal/views/verify_email_page.templ +++ b/backend/internal/views/verify_email_page.templ @@ -15,8 +15,18 @@ css main() { align-items: center; } +css xIcon() { + width: 4rem; + color: #dc2626; +} + +css checkIcon() { + width: 4rem; + color: #22c55e; +} + css card() { - padding: 2rem; + padding: 1.5rem; border-width: 1px; border-radius: 0.25rem; border-style: solid; @@ -25,7 +35,32 @@ css card() { max-width: 28rem; } -templ VerifyEmailPage(variant VerifyEmailPageVariant, details string) { +css cardContent() { + display: flex; + flex-direction: column; + align-items: center; + color: #475569; +} + +css button() { + text-decoration: none; + color: white; + padding: 1rem 2rem; + background-color: black; + border-radius: 0.25rem; + max-width: 24rem; + display: inline-block; +} + +css cardFooter() { + display: flex; + justify-content: center; + align-items: center; + margin-top: 1rem; +} + +templ VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) { + @@ -36,6 +71,16 @@ templ VerifyEmailPage(variant VerifyEmailPageVariant, details string) { * { margin: 0; padding: 0; + font-size: 1rem; + line-height: 1.5rem; + } + h3 { + text-align: center; + font-size: 1.5rem; + line-height: 2rem; + } + .space-y > * + * { + margin-top: 0.5rem; } @@ -43,11 +88,27 @@ templ VerifyEmailPage(variant VerifyEmailPageVariant, details string) {

{ getCardTitle(variant) }

- if details == "" { -

{ getCardContent(variant) }

- } else { -

{ details }

- } +
+ if variant == SuccessVerifyEmailPage { + + + + } else { + + + + } + if details == "" { +

{ getCardContent(variant) }

+ } else { +

{ details }

+ } +
+
+ if variant == SuccessVerifyEmailPage { + Go to Dashboard + } +
@@ -57,7 +118,7 @@ templ VerifyEmailPage(variant VerifyEmailPageVariant, details string) { func getTitle(variant VerifyEmailPageVariant) string { switch variant { case SuccessVerifyEmailPage: - return "Email Verified" + return "Email Verified Successfully" case FailVerifyEmailPage: return "Email Not Verified" } @@ -68,9 +129,9 @@ func getTitle(variant VerifyEmailPageVariant) string { func getCardTitle(variant VerifyEmailPageVariant) string { switch variant { case SuccessVerifyEmailPage: - return "Your email has been verified." + return "Email Verified Successfully" case FailVerifyEmailPage: - return "Hmmm... something went wrong" + return "Failed to Verify Email" } return "Oops... Something went wrong" @@ -79,7 +140,7 @@ func getCardTitle(variant VerifyEmailPageVariant) string { func getCardContent(variant VerifyEmailPageVariant) string { switch variant { case SuccessVerifyEmailPage: - return "You can close this window now." + return "Thank you for verifying your email address. You can now close this window or click the button below to go open the dashboard." case FailVerifyEmailPage: return "Please try again by requesting a new verification email.\nIf the problem persists, please try again later." } diff --git a/backend/internal/views/verify_email_page_templ.go b/backend/internal/views/verify_email_page_templ.go index 9fa5b9e4..2dc63a36 100644 --- a/backend/internal/views/verify_email_page_templ.go +++ b/backend/internal/views/verify_email_page_templ.go @@ -29,9 +29,31 @@ func main() templ.CSSClass { } } +func xIcon() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`width:4rem;`) + templ_7745c5c3_CSSBuilder.WriteString(`color:#dc2626;`) + templ_7745c5c3_CSSID := templ.CSSID(`xIcon`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func checkIcon() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`width:4rem;`) + templ_7745c5c3_CSSBuilder.WriteString(`color:#22c55e;`) + templ_7745c5c3_CSSID := templ.CSSID(`checkIcon`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + func card() templ.CSSClass { templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() - templ_7745c5c3_CSSBuilder.WriteString(`padding:2rem;`) + templ_7745c5c3_CSSBuilder.WriteString(`padding:1.5rem;`) templ_7745c5c3_CSSBuilder.WriteString(`border-width:1px;`) templ_7745c5c3_CSSBuilder.WriteString(`border-radius:0.25rem;`) templ_7745c5c3_CSSBuilder.WriteString(`border-style:solid;`) @@ -45,7 +67,49 @@ func card() templ.CSSClass { } } -func VerifyEmailPage(variant VerifyEmailPageVariant, details string) templ.Component { +func cardContent() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`display:flex;`) + templ_7745c5c3_CSSBuilder.WriteString(`flex-direction:column;`) + templ_7745c5c3_CSSBuilder.WriteString(`align-items:center;`) + templ_7745c5c3_CSSBuilder.WriteString(`color:#475569;`) + templ_7745c5c3_CSSID := templ.CSSID(`cardContent`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func button() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`text-decoration:none;`) + templ_7745c5c3_CSSBuilder.WriteString(`color:white;`) + templ_7745c5c3_CSSBuilder.WriteString(`padding:1rem 2rem;`) + templ_7745c5c3_CSSBuilder.WriteString(`background-color:black;`) + templ_7745c5c3_CSSBuilder.WriteString(`border-radius:0.25rem;`) + templ_7745c5c3_CSSBuilder.WriteString(`max-width:24rem;`) + templ_7745c5c3_CSSBuilder.WriteString(`display:inline-block;`) + templ_7745c5c3_CSSID := templ.CSSID(`button`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func cardFooter() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`display:flex;`) + templ_7745c5c3_CSSBuilder.WriteString(`justify-content:center;`) + templ_7745c5c3_CSSBuilder.WriteString(`align-items:center;`) + templ_7745c5c3_CSSBuilder.WriteString(`margin-top:1rem;`) + templ_7745c5c3_CSSID := templ.CSSID(`cardFooter`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -66,20 +130,20 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, details string) templ.Compo templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\"><head><link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta charset=\"UTF-8\"><title>") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(getTitle(variant)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 34, Col: 29} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 69, Col: 29} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -130,7 +194,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, details string) templ.Compo var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(getCardTitle(variant)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 45, Col: 32} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 90, Col: 32} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -140,17 +204,86 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, details string) templ.Compo if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + var templ_7745c5c3_Var8 = []any{"space-y", cardContent()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if variant == SuccessVerifyEmailPage { + var templ_7745c5c3_Var10 = []any{checkIcon()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + var templ_7745c5c3_Var12 = []any{xIcon()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } if details == "" { _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var8 string - templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(getCardContent(variant)) + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(getCardContent(variant)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 47, Col: 34} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 102, Col: 35} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -163,12 +296,12 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, details string) templ.Compo if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var9 string - templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(details) + var templ_7745c5c3_Var15 string + templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(details) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 49, Col: 18} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 104, Col: 19} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -177,7 +310,66 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, details string) templ.Compo return templ_7745c5c3_Err } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var16 = []any{cardFooter()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var16...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if variant == SuccessVerifyEmailPage { + var templ_7745c5c3_Var18 = []any{button()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Go to Dashboard") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -188,7 +380,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, details string) templ.Compo func getTitle(variant VerifyEmailPageVariant) string { switch variant { case SuccessVerifyEmailPage: - return "Email Verified" + return "Email Verified Successfully" case FailVerifyEmailPage: return "Email Not Verified" } @@ -199,9 +391,9 @@ func getTitle(variant VerifyEmailPageVariant) string { func getCardTitle(variant VerifyEmailPageVariant) string { switch variant { case SuccessVerifyEmailPage: - return "Your email has been verified." + return "Email Verified Successfully" case FailVerifyEmailPage: - return "Hmmm... something went wrong" + return "Failed to Verify Email" } return "Oops... Something went wrong" @@ -210,7 +402,7 @@ func getCardTitle(variant VerifyEmailPageVariant) string { func getCardContent(variant VerifyEmailPageVariant) string { switch variant { case SuccessVerifyEmailPage: - return "You can close this window now." + return "Thank you for verifying your email address. You can now close this window or click the button below to go open the dashboard." case FailVerifyEmailPage: return "Please try again by requesting a new verification email.\nIf the problem persists, please try again later." } From 033e59569d2c7f52b2e89dce3f8fe5f53e715f3b Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:54:39 -0500 Subject: [PATCH 3/6] add internal error variant view --- backend/internal/views/verify_email_page.templ | 3 ++- backend/internal/views/verify_email_page_templ.go | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/internal/views/verify_email_page.templ b/backend/internal/views/verify_email_page.templ index 4dfa4334..396a4a9f 100644 --- a/backend/internal/views/verify_email_page.templ +++ b/backend/internal/views/verify_email_page.templ @@ -5,6 +5,7 @@ type VerifyEmailPageVariant string const ( SuccessVerifyEmailPage VerifyEmailPageVariant = "success" FailVerifyEmailPage VerifyEmailPageVariant = "fail" + InternalErrorEmailPage VerifyEmailPageVariant = "internal" ) css main() { @@ -105,7 +106,7 @@ templ VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string }
- if variant == SuccessVerifyEmailPage { + if variant != InternalErrorEmailPage { Go to Dashboard }
diff --git a/backend/internal/views/verify_email_page_templ.go b/backend/internal/views/verify_email_page_templ.go index 2dc63a36..e1dfc42f 100644 --- a/backend/internal/views/verify_email_page_templ.go +++ b/backend/internal/views/verify_email_page_templ.go @@ -13,6 +13,7 @@ type VerifyEmailPageVariant string const ( SuccessVerifyEmailPage VerifyEmailPageVariant = "success" FailVerifyEmailPage VerifyEmailPageVariant = "fail" + InternalErrorEmailPage VerifyEmailPageVariant = "internal" ) func main() templ.CSSClass { @@ -137,7 +138,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(getTitle(variant)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 69, Col: 29} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 70, Col: 29} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -194,7 +195,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(getCardTitle(variant)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 90, Col: 32} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 91, Col: 32} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -281,7 +282,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(getCardContent(variant)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 102, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 103, Col: 35} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { @@ -299,7 +300,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) var templ_7745c5c3_Var15 string templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(details) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 104, Col: 19} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 105, Col: 19} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { @@ -336,7 +337,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - if variant == SuccessVerifyEmailPage { + if variant != InternalErrorEmailPage { var templ_7745c5c3_Var18 = []any{button()} templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...) if templ_7745c5c3_Err != nil { From ad85713bd27d714287a16537a18d944b085f93fc Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:56:05 -0500 Subject: [PATCH 4/6] update verify email route to render a view instead of json as response --- backend/internal/v1/v1_auth/auth.go | 58 +++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/backend/internal/v1/v1_auth/auth.go b/backend/internal/v1/v1_auth/auth.go index 8175dd8a..873bae87 100644 --- a/backend/internal/v1/v1_auth/auth.go +++ b/backend/internal/v1/v1_auth/auth.go @@ -6,10 +6,12 @@ import ( "KonferCA/SPUR/internal/middleware" "KonferCA/SPUR/internal/service" "KonferCA/SPUR/internal/v1/v1_common" + "KonferCA/SPUR/internal/views" "context" "errors" "fmt" "net/http" + "os" "time" "github.com/labstack/echo/v4" @@ -215,52 +217,92 @@ func (h *Handler) handleVerifyEmail(c echo.Context) error { ctx, cancel := context.WithTimeout(c.Request().Context(), time.Minute) defer cancel() + viewUrl := fmt.Sprintf("%s/dashboard", os.Getenv("BACKEND_URL")) + tokenStr := c.QueryParam("token") if tokenStr == "" { - return v1_common.Fail(c, http.StatusBadRequest, "Missing required query parameter: 'token'", nil) + view := views.VerifyEmailPage(views.FailVerifyEmailPage, viewUrl, "Missing validation token. Please get a new link from the dashboard.") + if err := view.Render(c.Request().Context(), c.Response()); err != nil { + return v1_common.Fail(c, http.StatusBadRequest, "Failed to render verify email page", err) + } + return nil } claims, err := jwt.VerifyEmailToken(tokenStr) if err != nil { - return v1_common.Fail(c, http.StatusBadRequest, "Failed to verify email. Invalid or expired token.", err) + logger.Error(err, "Failed to verify email token") + view := views.VerifyEmailPage(views.FailVerifyEmailPage, viewUrl, "The verification link is invalid or expired. You can get a new link in the dashboard.") + if err := view.Render(c.Request().Context(), c.Response()); err != nil { + return v1_common.Fail(c, http.StatusBadRequest, "Failed to render verify email page", err) + } + return nil } token, err := db.New(h.server.GetDB()).GetVerifyEmailTokenByID(ctx, claims.ID) if err != nil { - return v1_common.Fail(c, http.StatusInternalServerError, "Failed to verify email.", err) + logger.Error(err, "Failed to get email token from the database.") + view := views.VerifyEmailPage(views.InternalErrorEmailPage, viewUrl, "") + if err := view.Render(c.Request().Context(), c.Response()); err != nil { + return v1_common.Fail(c, http.StatusBadRequest, "Failed to render verify email page", err) + } + return nil } // start transaction to make sure that both user email verified status and the email token are deleted tx, err := h.server.GetDB().Begin(ctx) if err != nil { - return v1_common.Fail(c, http.StatusInternalServerError, "Failed to verify email.", err) + logger.Error(err, "Failed to start transaction to update user and remove email token from database.") + view := views.VerifyEmailPage(views.InternalErrorEmailPage, viewUrl, "") + if err := view.Render(c.Request().Context(), c.Response()); err != nil { + return v1_common.Fail(c, http.StatusBadRequest, "Failed to render verify email page", err) + } + return nil } q := db.New(h.server.GetDB()).WithTx(tx) err = q.UpdateUserEmailVerifiedStatus(ctx, db.UpdateUserEmailVerifiedStatusParams{EmailVerified: true, ID: token.UserID}) if err != nil { + logger.Error(err, "Failed to update user in database.") if err := tx.Rollback(ctx); err != nil { logger.Error(err, "Failed to rollback") } - return v1_common.Fail(c, http.StatusInternalServerError, "Failed to verify email.", err) + view := views.VerifyEmailPage(views.InternalErrorEmailPage, viewUrl, "") + if err := view.Render(c.Request().Context(), c.Response()); err != nil { + return v1_common.Fail(c, http.StatusBadRequest, "Failed to render verify email page", err) + } + return nil } err = q.RemoveVerifyEmailTokenByID(ctx, token.ID) if err != nil { + logger.Error(err, "Failed to remove email token from database.") if err := tx.Rollback(ctx); err != nil { logger.Error(err, "Failed to rollback") } - return v1_common.Fail(c, http.StatusInternalServerError, "Failed to verify email.", err) + view := views.VerifyEmailPage(views.InternalErrorEmailPage, viewUrl, "") + if err := view.Render(c.Request().Context(), c.Response()); err != nil { + return v1_common.Fail(c, http.StatusBadRequest, "Failed to render verify email page", err) + } + return nil } err = tx.Commit(ctx) if err != nil { + logger.Error(err, "Failed to commit changes.") if err := tx.Rollback(ctx); err != nil { logger.Error(err, "Failed to rollback") } - return v1_common.Fail(c, http.StatusInternalServerError, "Failed to verify email.", err) + view := views.VerifyEmailPage(views.InternalErrorEmailPage, viewUrl, "") + if err := view.Render(c.Request().Context(), c.Response()); err != nil { + return v1_common.Fail(c, http.StatusBadRequest, "Failed to render verify email page", err) + } + return nil } - return c.JSON(http.StatusOK, EmailVerifiedStatusResponse{Verified: true}) + view := views.VerifyEmailPage(views.SuccessVerifyEmailPage, viewUrl, "") + if err := view.Render(c.Request().Context(), c.Response()); err != nil { + return v1_common.Fail(c, http.StatusBadRequest, "Failed to render verify email page", err) + } + return nil } /* From 4eed68512d6fce0f67bec2de0cbad29d18256d0e Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Fri, 27 Dec 2024 18:44:10 -0500 Subject: [PATCH 5/6] add data-testid to verify email page --- .../internal/views/verify_email_page.templ | 16 +++++++------- .../internal/views/verify_email_page_templ.go | 22 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/backend/internal/views/verify_email_page.templ b/backend/internal/views/verify_email_page.templ index 396a4a9f..4183715c 100644 --- a/backend/internal/views/verify_email_page.templ +++ b/backend/internal/views/verify_email_page.templ @@ -88,26 +88,26 @@ templ VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string
-

{ getCardTitle(variant) }

-
+

{ getCardTitle(variant) }

+
if variant == SuccessVerifyEmailPage { - + } else { - + } if details == "" { -

{ getCardContent(variant) }

+

{ getCardContent(variant) }

} else { -

{ details }

+

{ details }

}
-
+
if variant != InternalErrorEmailPage { - Go to Dashboard + Go to Dashboard }
diff --git a/backend/internal/views/verify_email_page_templ.go b/backend/internal/views/verify_email_page_templ.go index e1dfc42f..691fcf36 100644 --- a/backend/internal/views/verify_email_page_templ.go +++ b/backend/internal/views/verify_email_page_templ.go @@ -188,14 +188,14 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(getCardTitle(variant)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 91, Col: 32} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 91, Col: 57} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -223,7 +223,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-testid=\"card-content-container\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -246,7 +246,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-testid=\"check-icon\"> ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -269,20 +269,20 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-testid=\"x-icon\"> ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if details == "" { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(getCardContent(variant)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 103, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 103, Col: 62} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { @@ -293,14 +293,14 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) return templ_7745c5c3_Err } } else { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var15 string templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(details) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 105, Col: 19} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/verify_email_page.templ`, Line: 105, Col: 46} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { @@ -333,7 +333,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-testid=\"card-footer\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -343,7 +343,7 @@ func VerifyEmailPage(variant VerifyEmailPageVariant, url string, details string) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" Date: Fri, 27 Dec 2024 18:44:31 -0500 Subject: [PATCH 6/6] update verify email route tests --- backend/go.mod | 2 + backend/go.sum | 62 +++++++++++++++++++++++++++ backend/internal/tests/server_test.go | 54 ++++++++++++++--------- 3 files changed, 98 insertions(+), 20 deletions(-) diff --git a/backend/go.mod b/backend/go.mod index 9a53add6..aa749255 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -21,6 +21,8 @@ require ( ) require ( + github.com/PuerkitoBio/goquery v1.10.1 // indirect + github.com/andybalholm/cascadia v1.3.3 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.47 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect diff --git a/backend/go.sum b/backend/go.sum index 22cfb19f..18b224ab 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,5 +1,9 @@ +github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU= +github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY= github.com/a-h/templ v0.2.793 h1:Io+/ocnfGWYO4VHdR0zBbf39PQlnzVCVVD+wEEs6/qY= github.com/a-h/templ v0.2.793/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w= +github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= +github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= @@ -105,23 +109,81 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/backend/internal/tests/server_test.go b/backend/internal/tests/server_test.go index dcbfd919..26c93cfa 100644 --- a/backend/internal/tests/server_test.go +++ b/backend/internal/tests/server_test.go @@ -5,7 +5,6 @@ import ( "KonferCA/SPUR/internal/jwt" "KonferCA/SPUR/internal/server" "KonferCA/SPUR/internal/v1/v1_auth" - "KonferCA/SPUR/internal/v1/v1_common" "bytes" "context" @@ -18,6 +17,7 @@ import ( "testing" "time" + "github.com/PuerkitoBio/goquery" golangJWT "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" "github.com/labstack/echo/v4" @@ -400,30 +400,39 @@ func TestServer(t *testing.T) { s.Echo.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code) - resBodyBytes, err := io.ReadAll(rec.Body) - assert.Nil(t, err) - - var resBody map[string]any - err = json.Unmarshal(resBodyBytes, &resBody) - assert.Nil(t, err) - assert.Equal(t, resBody["verified"], true) + doc, err := goquery.NewDocumentFromReader(rec.Body) + assert.NoError(t, err) + title := doc.Find(`[data-testid="card-title"]`).Text() + assert.Equal(t, title, "Email Verified Successfully") + details := doc.Find(`[data-testid="card-details"]`).Text() + assert.Contains(t, details, "Thank you for verifying your email address") + icon := doc.Find(`[data-testid="check-icon"]`) + assert.Equal(t, 1, icon.Length()) + button := doc.Find(`[data-testid="go-to-dashboard"]`) + assert.Equal(t, 1, button.Length()) }) - t.Run("/auth/verify-email - 400 Bad Request - missing token query parameter", func(t *testing.T) { + t.Run("/auth/verify-email - missing token query parameter", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/api/v1/auth/verify-email", nil) rec := httptest.NewRecorder() s.Echo.ServeHTTP(rec, req) - var apiErr v1_common.APIError - err := json.NewDecoder(rec.Body).Decode(&apiErr) + assert.Equal(t, http.StatusOK, rec.Code) + + doc, err := goquery.NewDocumentFromReader(rec.Body) assert.NoError(t, err) - assert.Equal(t, http.StatusBadRequest, rec.Code) - assert.Equal(t, v1_common.ErrorTypeBadRequest, apiErr.Type) - assert.Equal(t, "Missing required query parameter: 'token'", apiErr.Message) + title := doc.Find(`[data-testid="card-title"]`).Text() + assert.Equal(t, title, "Failed to Verify Email") + details := doc.Find(`[data-testid="card-details"]`).Text() + assert.Contains(t, details, "Missing validation token") + icon := doc.Find(`[data-testid="x-icon"]`) + assert.Equal(t, 1, icon.Length()) + button := doc.Find(`[data-testid="go-to-dashboard"]`) + assert.Equal(t, 1, button.Length()) }) - t.Run("/auth/verify-email - 400 Bad Request - deny expired email token", func(t *testing.T) { + t.Run("/auth/verify-email - deny expired email token", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() userID, email, _, err := createTestUser(ctx, s) @@ -440,13 +449,18 @@ func TestServer(t *testing.T) { rec := httptest.NewRecorder() s.Echo.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code) - var apiErr v1_common.APIError - err = json.NewDecoder(rec.Body).Decode(&apiErr) + doc, err := goquery.NewDocumentFromReader(rec.Body) assert.NoError(t, err) - assert.Equal(t, http.StatusBadRequest, rec.Code) - assert.Equal(t, v1_common.ErrorTypeBadRequest, apiErr.Type) - assert.Equal(t, "Failed to verify email. Invalid or expired token.", apiErr.Message) + title := doc.Find(`[data-testid="card-title"]`).Text() + assert.Equal(t, title, "Failed to Verify Email") + details := doc.Find(`[data-testid="card-details"]`).Text() + assert.Contains(t, details, "The verification link is invalid or expired") + icon := doc.Find(`[data-testid="x-icon"]`) + assert.Equal(t, 1, icon.Length()) + button := doc.Find(`[data-testid="go-to-dashboard"]`) + assert.Equal(t, 1, button.Length()) }) t.Run("/api/v1/auth/logout - 200 OK - successfully logout", func(t *testing.T) {