From c873eaef3ea15d1280bb3d8ac615e341ecf4d69b Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Sun, 26 May 2024 19:30:04 -0700 Subject: [PATCH] Adding profile, trying to get footer to stick and some other random things --- apiclient/users.go | 6 ++++-- apiclient/util.go | 16 ++++++++++++++-- go.mod | 3 ++- go.sum | 6 ++++-- internal/handlers/handler.go | 11 ++++------- internal/handlers/users.go | 17 ++++++++++++++++- internal/handlers/util.go | 22 ++++++++++++++++------ internal/views/bulma/hero.templ | 10 ++++++++++ internal/views/bulma/section.templ | 11 +++++++++++ internal/views/home/index.templ | 3 ++- internal/views/layout/footer.templ | 16 ++++------------ internal/views/layout/header.templ | 7 +++++++ internal/views/layout/navbar.templ | 2 +- internal/views/layout/withTemplate.templ | 4 ++-- internal/views/users/profile.templ | 15 +++++++++++++++ 15 files changed, 112 insertions(+), 37 deletions(-) create mode 100644 internal/views/bulma/hero.templ create mode 100644 internal/views/bulma/section.templ create mode 100644 internal/views/users/profile.templ diff --git a/apiclient/users.go b/apiclient/users.go index 7510802..37ec219 100644 --- a/apiclient/users.go +++ b/apiclient/users.go @@ -15,6 +15,8 @@ const ( type Users interface { Login(username, password string) (domain.LoginResponse, error) SignUp(username, password string) (domain.BaseResponse, error) + RefreshJwtToken(username, refreshToken string) (domain.LoginResponse, error) + RefreshSessionToken(jwtToken string) (domain.BaseResponse, error) } type userClient struct { @@ -79,11 +81,11 @@ func (a userClient) RefreshJwtToken(username, refreshToken string) (domain.Login return bind, nil } -func (a userClient) refreshSessionToken() (domain.BaseResponse, error) { +func (a userClient) RefreshSessionToken(jwtToken string) (domain.BaseResponse, error) { endpoint := fmt.Sprintf("%s/%s/refresh/sessionToken", a.serverAddress, UserBaseRoute) var bind = domain.BaseResponse{} - err := PostUrl(a.client, endpoint, &bind) + err := PostUrlAuthorized(a.client, endpoint, jwtToken, &bind) if err != nil { return domain.BaseResponse{}, err } diff --git a/apiclient/util.go b/apiclient/util.go index 441e89e..4c7d212 100644 --- a/apiclient/util.go +++ b/apiclient/util.go @@ -3,10 +3,15 @@ package apiclient import ( "bytes" "encoding/json" + "fmt" "net/http" "net/url" ) +const ( + HeaderAuthorization = "Authorization" +) + func PostUrlForm(client http.Client, endpoint string, param url.Values, t any) error { payload := bytes.NewBufferString(param.Encode()) req, err := http.NewRequest(http.MethodPost, endpoint, payload) @@ -30,8 +35,15 @@ func PostUrlForm(client http.Client, endpoint string, param url.Values, t any) e return nil } -func PostUrl(client http.Client, endpoint string, t any) error { - response, err := http.Post(endpoint, ApplicationJson, nil) +func PostUrlAuthorized(client http.Client, endpoint, jwtToken string, t any) error { + req, err := http.NewRequest(http.MethodPost, endpoint, nil) + if err != nil { + return err + } + req.Header.Add(HeaderAuthorization, fmt.Sprintf("%s %s", "Bearer", jwtToken)) + + response, err := client.Do(req) + //response, err := http.Post(endpoint, ApplicationJson, nil) if err != nil { return err } diff --git a/go.mod b/go.mod index d372940..0dac382 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,9 @@ go 1.22.1 require ( git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240510021003-4e9a17209f02 github.com/a-h/templ v0.2.680 - github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang-jwt/jwt/v5 v5.2.1 github.com/joho/godotenv v1.5.1 + github.com/labstack/echo-jwt/v4 v4.2.0 github.com/labstack/echo/v4 v4.12.0 ) diff --git a/go.sum b/go.sum index 7666828..dc0ffa7 100644 --- a/go.sum +++ b/go.sum @@ -6,12 +6,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c= +github.com/labstack/echo-jwt/v4 v4.2.0/go.mod h1:MA2RqdXdEn4/uEglx0HcUOgQSyBaTh5JcaHIan3biwU= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= diff --git a/internal/handlers/handler.go b/internal/handlers/handler.go index 946979a..26f6c8c 100644 --- a/internal/handlers/handler.go +++ b/internal/handlers/handler.go @@ -44,17 +44,11 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient. api: apiClient, } - //jwtConfig := echojwt.Config{ - // NewClaimsFunc: func(c echo.Context) jwt.Claims { - // return new(JwtToken) - // }, - // SigningKey: []byte(configs.JwtSecret), - //} - router := echo.New() router.Pre(middleware.RemoveTrailingSlash()) router.Pre(middleware.Logger()) router.Pre(middleware.Recover()) + router.Use(middleware.Static("/internal/static")) router.GET("/", s.HomeIndex) router.GET("/about", s.HomeAbout) @@ -71,6 +65,9 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient. users.GET("/signup", s.UserSignUp) users.POST("/signup", s.UserAfterSignUp) + //users.Use(echojwt.WithConfig(jwtConfig)) + users.GET("/profile", s.UserProfile) + s.Router = router return s } diff --git a/internal/handlers/users.go b/internal/handlers/users.go index 2571e27..b12bebb 100644 --- a/internal/handlers/users.go +++ b/internal/handlers/users.go @@ -5,6 +5,7 @@ import ( "net/http" "git.jamestombleson.com/jtom38/newsbot-portal/internal/domain" + "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/users" "github.com/labstack/echo/v4" ) @@ -46,4 +47,18 @@ func (h *Handler) UserAfterSignUp(c echo.Context) error { return Render(c, http.StatusBadRequest, users.AfterLogin(msg, false)) } return Render(c, http.StatusOK, users.AfterSignUp("Registration Successful!", true)) -} \ No newline at end of file +} + +func (h *Handler) UserProfile(c echo.Context) error { + return Render(c, http.StatusOK, users.Profile()) +} + +func (h *Handler) ForceLogout(c echo.Context) error { + _, err := ValidateJwt(c, h.config.JwtSecret, h.config.ServerAddress) + if err != nil { + return Render(c, http.StatusOK, layout.Error(err)) + } + + h.api.Users.RefreshSessionToken(GetJwtToken(c)) + return nil +} diff --git a/internal/handlers/util.go b/internal/handlers/util.go index 91a8aff..0eaea40 100644 --- a/internal/handlers/util.go +++ b/internal/handlers/util.go @@ -8,7 +8,7 @@ import ( "git.jamestombleson.com/jtom38/newsbot-portal/internal/domain" "github.com/a-h/templ" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo/v4" ) @@ -23,11 +23,13 @@ func SetCookie(c echo.Context, key, value, path string) { } type jwtToken struct { - Exp time.Time `json:"exp"` - Iss string `json:"iss"` - Authorized bool `json:"authorized"` - UserName string `json:"username"` - Scopes []string `json:"scopes"` + Exp time.Time `json:"exp"` + Iss string `json:"iss"` + Authorized bool `json:"authorized"` + UserName string `json:"username"` + UserId int64 `json:"userId"` + Scopes []string `json:"scopes"` + SessionToken string `json:"sessionToken"` jwt.RegisteredClaims } @@ -63,6 +65,14 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error return *claims, nil } +func GetJwtToken(c echo.Context) string { + cookie, err := c.Cookie(domain.CookieToken) + if err != nil { + return "" + } + return cookie.Value +} + func Render(ctx echo.Context, statusCode int, t templ.Component) error { ctx.Response().Writer.WriteHeader(statusCode) ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML) diff --git a/internal/views/bulma/hero.templ b/internal/views/bulma/hero.templ new file mode 100644 index 0000000..ce5dd39 --- /dev/null +++ b/internal/views/bulma/hero.templ @@ -0,0 +1,10 @@ +package bulma + +templ Hero(title, subtitle string) { +
+
+

{ title }

+

{ subtitle }

+
+
+} diff --git a/internal/views/bulma/section.templ b/internal/views/bulma/section.templ new file mode 100644 index 0000000..1726976 --- /dev/null +++ b/internal/views/bulma/section.templ @@ -0,0 +1,11 @@ +package bulma + +templ Section(title, subtitle string) { +
+

Section

+

+ A simple container to divide your page into sections, like + the one you're currently reading. +

+
+} diff --git a/internal/views/home/index.templ b/internal/views/home/index.templ index 2e3b282..b90be24 100644 --- a/internal/views/home/index.templ +++ b/internal/views/home/index.templ @@ -1,10 +1,11 @@ package home import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" +import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/bulma" templ Index() { @layout.WithTemplate() { -

Welcome to Newsbot

+ @bulma.Hero("Welcome to Newsbot!", "Your new home for your news.")

diff --git a/internal/views/layout/footer.templ b/internal/views/layout/footer.templ index 167ff62..d8d8e8c 100644 --- a/internal/views/layout/footer.templ +++ b/internal/views/layout/footer.templ @@ -1,15 +1,7 @@ package layout templ footer() { -

-} \ No newline at end of file +
+ Bulma by Jeremy Thomas. +
+} diff --git a/internal/views/layout/header.templ b/internal/views/layout/header.templ index f7e5976..942e76f 100644 --- a/internal/views/layout/header.templ +++ b/internal/views/layout/header.templ @@ -2,6 +2,7 @@ package layout templ header() { + @@ -9,4 +10,10 @@ templ header() { + } \ No newline at end of file diff --git a/internal/views/layout/navbar.templ b/internal/views/layout/navbar.templ index b333da7..b77d84f 100644 --- a/internal/views/layout/navbar.templ +++ b/internal/views/layout/navbar.templ @@ -32,7 +32,7 @@ templ navBar() { { getUsername(ctx) } diff --git a/internal/views/layout/withTemplate.templ b/internal/views/layout/withTemplate.templ index f936ef8..28da166 100644 --- a/internal/views/layout/withTemplate.templ +++ b/internal/views/layout/withTemplate.templ @@ -4,15 +4,15 @@ templ WithTemplate() { - @header() + @header() @navBar()
{ children... } + @footer()
- @footer() } diff --git a/internal/views/users/profile.templ b/internal/views/users/profile.templ new file mode 100644 index 0000000..8edd35e --- /dev/null +++ b/internal/views/users/profile.templ @@ -0,0 +1,15 @@ +package users + +import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" +import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/bulma" + +templ Profile() { + @layout.WithTemplate() { + @bulma.Hero("Profile", "Here you can update your profile 😀") + + +

This will force all active sessions to stop working and require a new login.

+ } +}