Added logout functionality and client error message for 500 status codes
This commit is contained in:
@@ -48,7 +48,6 @@ func HandleLoginRequest(
|
||||
config *config.Config,
|
||||
logger *zerolog.Logger,
|
||||
conn *sql.DB,
|
||||
secretKey string,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
62
handlers/logout.go
Normal file
62
handlers/logout.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"projectreshoot/config"
|
||||
"projectreshoot/cookies"
|
||||
"projectreshoot/jwt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// Retrieve and revoke the user's tokens
|
||||
func revokeTokens(
|
||||
config *config.Config,
|
||||
conn *sql.DB,
|
||||
r *http.Request,
|
||||
) error {
|
||||
// get the tokens from the cookies
|
||||
atStr, rtStr := cookies.GetTokenStrings(r)
|
||||
aT, err := jwt.ParseAccessToken(config, conn, atStr)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "jwt.ParseAccessToken")
|
||||
}
|
||||
rT, err := jwt.ParseRefreshToken(config, conn, rtStr)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "jwt.ParseRefreshToken")
|
||||
}
|
||||
// revoke the refresh token first as the access token expires quicker
|
||||
// only matters if there is an error revoking the tokens
|
||||
err = jwt.RevokeToken(conn, rT)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "jwt.RevokeToken")
|
||||
}
|
||||
err = jwt.RevokeToken(conn, aT)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "jwt.RevokeToken")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle a logout request
|
||||
func HandleLogout(
|
||||
config *config.Config,
|
||||
logger *zerolog.Logger,
|
||||
conn *sql.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
err := revokeTokens(config, conn, r)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Error occured on user logout")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
cookies.DeleteCookie(w, "access", "/")
|
||||
cookies.DeleteCookie(w, "refresh", "/")
|
||||
w.Header().Set("HX-Redirect", "/login")
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -36,8 +36,9 @@ func addRoutes(
|
||||
config,
|
||||
logger,
|
||||
conn,
|
||||
config.SecretKey,
|
||||
))
|
||||
|
||||
// Logout
|
||||
mux.Handle("POST /logout", handlers.HandleLogout(config, logger, conn))
|
||||
// Profile page
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Ubuntu+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap");
|
||||
@import "tailwindcss";
|
||||
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
@theme inline {
|
||||
--color-rosewater: var(--rosewater);
|
||||
--color-flamingo: var(--flamingo);
|
||||
--color-pink: var(--pink);
|
||||
--color-mauve: var(--mauve);
|
||||
--color-red: var(--red);
|
||||
--color-dark-red: var(--dark-red);
|
||||
--color-maroon: var(--maroon);
|
||||
--color-peach: var(--peach);
|
||||
--color-yellow: var(--yellow);
|
||||
@@ -35,6 +39,7 @@
|
||||
--pink: hsl(316, 73%, 69%);
|
||||
--mauve: hsl(266, 85%, 58%);
|
||||
--red: hsl(347, 87%, 44%);
|
||||
--dark-red: hsl(343, 50%, 82%);
|
||||
--maroon: hsl(355, 76%, 59%);
|
||||
--peach: hsl(22, 99%, 52%);
|
||||
--yellow: hsl(35, 77%, 49%);
|
||||
@@ -64,6 +69,7 @@
|
||||
--pink: hsl(316, 72%, 86%);
|
||||
--mauve: hsl(267, 84%, 81%);
|
||||
--red: hsl(343, 81%, 75%);
|
||||
--dark-red: hsl(316, 19%, 27%);
|
||||
--maroon: hsl(350, 65%, 77%);
|
||||
--peach: hsl(23, 92%, 75%);
|
||||
--yellow: hsl(41, 86%, 83%);
|
||||
|
||||
63
view/component/errorPopup.templ
Normal file
63
view/component/errorPopup.templ
Normal file
@@ -0,0 +1,63 @@
|
||||
package component
|
||||
|
||||
templ ErrorPopup() {
|
||||
<div
|
||||
x-cloak
|
||||
x-show="showError"
|
||||
class="absolute w-82 left-0 right-0 mt-20 mr-5 ml-auto"
|
||||
x-transition:enter="transform translate-x-[100%] opacity-0 duration-200"
|
||||
x-transition:enter-start="opacity-0 translate-x-[100%]"
|
||||
x-transition:enter-end="opacity-100 translate-x-0"
|
||||
x-transition:leave="opacity-0 duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-x-0"
|
||||
x-transition:leave-end="opacity-0 translate-x-[100%]"
|
||||
>
|
||||
<div
|
||||
role="alert"
|
||||
class="rounded-sm bg-dark-red p-4"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<div class="flex items-center gap-2 text-red w-fit">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-5"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355
|
||||
12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309
|
||||
0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75
|
||||
0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0
|
||||
01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
<strong class="block font-medium">Something went wrong </strong>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 text-subtext0 hover:cursor-pointer"
|
||||
@click="showError=false"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-red">
|
||||
An error occured on the server. Please try again later,
|
||||
or contact an administrator
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -40,7 +40,9 @@ templ Footer() {
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293 3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 010 1.414z"
|
||||
d="M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293
|
||||
3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4
|
||||
4a1 1 0 010 1.414z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
@@ -14,7 +14,11 @@ templ LoginForm(loginError string) {
|
||||
} else {
|
||||
errCreds = "false"
|
||||
}
|
||||
xdata := fmt.Sprintf("{credentialError: %s, errorMessage: '%s'}", errCreds, loginError)
|
||||
xdata := fmt.Sprintf(
|
||||
"{credentialError: %s, errorMessage: '%s'}",
|
||||
errCreds,
|
||||
loginError,
|
||||
)
|
||||
}}
|
||||
<form
|
||||
hx-post="/login"
|
||||
@@ -57,7 +61,12 @@ templ LoginForm(loginError string) {
|
||||
viewBox="0 0 16 16"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0 0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path>
|
||||
<path
|
||||
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8
|
||||
4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0
|
||||
0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1
|
||||
1 0 1 0 0 2 1 1 0 0 0 0-2z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,7 +110,12 @@ templ LoginForm(loginError string) {
|
||||
viewBox="0 0 16 16"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0 0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path>
|
||||
<path
|
||||
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8
|
||||
4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0
|
||||
0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1
|
||||
1 0 1 0 0 2 1 1 0 0 0 0-2z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -72,6 +72,7 @@ templ navRight() {
|
||||
rounded-lg px-4 py-2 text-md text-red
|
||||
hover:bg-red/25 hover:cursor-pointer"
|
||||
role="menuitem"
|
||||
@click="isActive=false"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
|
||||
@@ -2,6 +2,7 @@ package layout
|
||||
|
||||
import "projectreshoot/view/component/nav"
|
||||
import "projectreshoot/view/component/footer"
|
||||
import "projectreshoot/view/component"
|
||||
|
||||
// Global page layout. Includes HTML document settings, header tags
|
||||
// navbar and footer
|
||||
@@ -37,11 +38,22 @@ templ Global() {
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.x.x/dist/cdn.min.js"></script>
|
||||
<script src="https://unpkg.com/alpinejs" defer></script>
|
||||
<script>
|
||||
// uncomment this line to enable logging of htmx events
|
||||
//htmx.logAll();
|
||||
</script>
|
||||
</head>
|
||||
<body
|
||||
class="bg-base text-text ubuntu-mono-regular"
|
||||
class="bg-base text-text ubuntu-mono-regular overflow-x-hidden"
|
||||
x-data="{ showError: false }"
|
||||
x-on:htmx:error="if ($event.detail.errorInfo.error.includes('Code 500'))
|
||||
showError = true; setTimeout(() => showError = false, 6000)"
|
||||
>
|
||||
@component.ErrorPopup()
|
||||
<div
|
||||
id="main-content"
|
||||
class="flex flex-col h-screen justify-between"
|
||||
>
|
||||
<div id="main-content" class="flex flex-col h-screen justify-between">
|
||||
@nav.Navbar()
|
||||
<div id="page-content" class="mb-auto">
|
||||
{ children... }
|
||||
|
||||
Reference in New Issue
Block a user