Adding profile, trying to get footer to stick and some other random things
This commit is contained in:
parent
3796ed4d20
commit
c873eaef3e
@ -15,6 +15,8 @@ const (
|
|||||||
type Users interface {
|
type Users interface {
|
||||||
Login(username, password string) (domain.LoginResponse, error)
|
Login(username, password string) (domain.LoginResponse, error)
|
||||||
SignUp(username, password string) (domain.BaseResponse, error)
|
SignUp(username, password string) (domain.BaseResponse, error)
|
||||||
|
RefreshJwtToken(username, refreshToken string) (domain.LoginResponse, error)
|
||||||
|
RefreshSessionToken(jwtToken string) (domain.BaseResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type userClient struct {
|
type userClient struct {
|
||||||
@ -79,11 +81,11 @@ func (a userClient) RefreshJwtToken(username, refreshToken string) (domain.Login
|
|||||||
return bind, nil
|
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)
|
endpoint := fmt.Sprintf("%s/%s/refresh/sessionToken", a.serverAddress, UserBaseRoute)
|
||||||
|
|
||||||
var bind = domain.BaseResponse{}
|
var bind = domain.BaseResponse{}
|
||||||
err := PostUrl(a.client, endpoint, &bind)
|
err := PostUrlAuthorized(a.client, endpoint, jwtToken, &bind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.BaseResponse{}, err
|
return domain.BaseResponse{}, err
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,15 @@ package apiclient
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
HeaderAuthorization = "Authorization"
|
||||||
|
)
|
||||||
|
|
||||||
func PostUrlForm(client http.Client, endpoint string, param url.Values, t any) error {
|
func PostUrlForm(client http.Client, endpoint string, param url.Values, t any) error {
|
||||||
payload := bytes.NewBufferString(param.Encode())
|
payload := bytes.NewBufferString(param.Encode())
|
||||||
req, err := http.NewRequest(http.MethodPost, endpoint, payload)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostUrl(client http.Client, endpoint string, t any) error {
|
func PostUrlAuthorized(client http.Client, endpoint, jwtToken string, t any) error {
|
||||||
response, err := http.Post(endpoint, ApplicationJson, nil)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
3
go.mod
3
go.mod
@ -5,8 +5,9 @@ go 1.22.1
|
|||||||
require (
|
require (
|
||||||
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240510021003-4e9a17209f02
|
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240510021003-4e9a17209f02
|
||||||
github.com/a-h/templ v0.2.680
|
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/joho/godotenv v1.5.1
|
||||||
|
github.com/labstack/echo-jwt/v4 v4.2.0
|
||||||
github.com/labstack/echo/v4 v4.12.0
|
github.com/labstack/echo/v4 v4.12.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
6
go.sum
6
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/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 h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
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/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
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 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
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 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
||||||
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
|
@ -44,17 +44,11 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient.
|
|||||||
api: apiClient,
|
api: apiClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
//jwtConfig := echojwt.Config{
|
|
||||||
// NewClaimsFunc: func(c echo.Context) jwt.Claims {
|
|
||||||
// return new(JwtToken)
|
|
||||||
// },
|
|
||||||
// SigningKey: []byte(configs.JwtSecret),
|
|
||||||
//}
|
|
||||||
|
|
||||||
router := echo.New()
|
router := echo.New()
|
||||||
router.Pre(middleware.RemoveTrailingSlash())
|
router.Pre(middleware.RemoveTrailingSlash())
|
||||||
router.Pre(middleware.Logger())
|
router.Pre(middleware.Logger())
|
||||||
router.Pre(middleware.Recover())
|
router.Pre(middleware.Recover())
|
||||||
|
router.Use(middleware.Static("/internal/static"))
|
||||||
|
|
||||||
router.GET("/", s.HomeIndex)
|
router.GET("/", s.HomeIndex)
|
||||||
router.GET("/about", s.HomeAbout)
|
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.GET("/signup", s.UserSignUp)
|
||||||
users.POST("/signup", s.UserAfterSignUp)
|
users.POST("/signup", s.UserAfterSignUp)
|
||||||
|
|
||||||
|
//users.Use(echojwt.WithConfig(jwtConfig))
|
||||||
|
users.GET("/profile", s.UserProfile)
|
||||||
|
|
||||||
s.Router = router
|
s.Router = router
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
"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"
|
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/users"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
@ -47,3 +48,17 @@ func (h *Handler) UserAfterSignUp(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
return Render(c, http.StatusOK, users.AfterSignUp("Registration Successful!", true))
|
return Render(c, http.StatusOK, users.AfterSignUp("Registration Successful!", true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,11 +23,13 @@ func SetCookie(c echo.Context, key, value, path string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type jwtToken struct {
|
type jwtToken struct {
|
||||||
Exp time.Time `json:"exp"`
|
Exp time.Time `json:"exp"`
|
||||||
Iss string `json:"iss"`
|
Iss string `json:"iss"`
|
||||||
Authorized bool `json:"authorized"`
|
Authorized bool `json:"authorized"`
|
||||||
UserName string `json:"username"`
|
UserName string `json:"username"`
|
||||||
Scopes []string `json:"scopes"`
|
UserId int64 `json:"userId"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
SessionToken string `json:"sessionToken"`
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +65,14 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error
|
|||||||
return *claims, nil
|
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 {
|
func Render(ctx echo.Context, statusCode int, t templ.Component) error {
|
||||||
ctx.Response().Writer.WriteHeader(statusCode)
|
ctx.Response().Writer.WriteHeader(statusCode)
|
||||||
ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML)
|
ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML)
|
||||||
|
10
internal/views/bulma/hero.templ
Normal file
10
internal/views/bulma/hero.templ
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package bulma
|
||||||
|
|
||||||
|
templ Hero(title, subtitle string) {
|
||||||
|
<section class="hero">
|
||||||
|
<div class="hero-body">
|
||||||
|
<p class="title">{ title }</p>
|
||||||
|
<p class="subtitle">{ subtitle }</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
11
internal/views/bulma/section.templ
Normal file
11
internal/views/bulma/section.templ
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package bulma
|
||||||
|
|
||||||
|
templ Section(title, subtitle string) {
|
||||||
|
<section class="section">
|
||||||
|
<h1 class="title">Section</h1>
|
||||||
|
<h2 class="subtitle">
|
||||||
|
A simple container to divide your page into <strong>sections</strong>, like
|
||||||
|
the one you're currently reading.
|
||||||
|
</h2>
|
||||||
|
</section>
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
package home
|
package home
|
||||||
|
|
||||||
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
|
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
|
||||||
|
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/bulma"
|
||||||
|
|
||||||
templ Index() {
|
templ Index() {
|
||||||
@layout.WithTemplate() {
|
@layout.WithTemplate() {
|
||||||
<h1 class="title">Welcome to Newsbot</h1>
|
@bulma.Hero("Welcome to Newsbot!", "Your new home for your news.")
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<p>
|
<p>
|
||||||
|
@ -1,15 +1,7 @@
|
|||||||
package layout
|
package layout
|
||||||
|
|
||||||
templ footer() {
|
templ footer() {
|
||||||
<footer class="footer">
|
<div class="has-text-centered pin-botton">
|
||||||
<div class="content has-text-centered">
|
<strong>Bulma</strong> by <a href="https://jgthms.com">Jeremy Thomas</a>.
|
||||||
<p>
|
</div>
|
||||||
<strong>Bulma</strong> by <a href="https://jgthms.com">Jeremy Thomas</a>.
|
|
||||||
The source code is licensed
|
|
||||||
<a href="http://opensource.org/licenses/mit-license.php">MIT</a>. The
|
|
||||||
website content is licensed
|
|
||||||
<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY NC SA 4.0</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ package layout
|
|||||||
|
|
||||||
templ header() {
|
templ header() {
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.0/css/bulma.min.css"/>
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.0/css/bulma.min.css"/>
|
||||||
|
<link rel="stylesheet" href="/css/main.css"/>
|
||||||
<script src="https://unpkg.com/htmx.org@1.9.11" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0" crossorigin="anonymous"></script>
|
<script src="https://unpkg.com/htmx.org@1.9.11" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0" crossorigin="anonymous"></script>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta property="og:title" content=""/>
|
<meta property="og:title" content=""/>
|
||||||
@ -9,4 +10,10 @@ templ header() {
|
|||||||
<meta property="og:image" content=""/>
|
<meta property="og:image" content=""/>
|
||||||
<meta property="og:description" content=""/>
|
<meta property="og:description" content=""/>
|
||||||
<meta property="og:type" content=""/>
|
<meta property="og:type" content=""/>
|
||||||
|
<style type="text/css">
|
||||||
|
.pin-bottom {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
}
|
}
|
@ -32,7 +32,7 @@ templ navBar() {
|
|||||||
<a class="navbar-link">{ getUsername(ctx) }</a>
|
<a class="navbar-link">{ getUsername(ctx) }</a>
|
||||||
|
|
||||||
<div class="navbar-dropdown">
|
<div class="navbar-dropdown">
|
||||||
<a class="navbar-item">Profile</a>
|
<a class="navbar-item" href="/users/profile" >Profile</a>
|
||||||
<a class="navbar-item">Logout</a>
|
<a class="navbar-item">Logout</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,15 +4,15 @@ templ WithTemplate() {
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@header()
|
@header()
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@navBar()
|
@navBar()
|
||||||
<br/>
|
<br/>
|
||||||
<div class="container is-widescreen">
|
<div class="container is-widescreen">
|
||||||
{ children... }
|
{ children... }
|
||||||
|
@footer()
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
@footer()
|
|
||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
15
internal/views/users/profile.templ
Normal file
15
internal/views/users/profile.templ
Normal file
@ -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 😀")
|
||||||
|
|
||||||
|
<button type="button" class="button">
|
||||||
|
<a href="/users/forcelogout">Logout Everywhere</a>
|
||||||
|
Logout Everywhere</button>
|
||||||
|
<p class="subtitle">This will force all active sessions to stop working and require a new login.</p>
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user