features/token-things #2
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,7 +10,7 @@
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
tmp/
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/domain"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -16,6 +17,7 @@ type Users interface {
|
||||
Login(username, password string) (domain.LoginResponse, error)
|
||||
SignUp(username, password string) (domain.BaseResponse, error)
|
||||
RefreshJwtToken(username, refreshToken string) (domain.LoginResponse, error)
|
||||
RefreshJwtTokenFromContext(ctx echo.Context) (domain.LoginResponse, error)
|
||||
RefreshSessionToken(jwtToken string) (domain.BaseResponse, error)
|
||||
}
|
||||
|
||||
@ -81,6 +83,23 @@ func (a userClient) RefreshJwtToken(username, refreshToken string) (domain.Login
|
||||
return bind, nil
|
||||
}
|
||||
|
||||
|
||||
func (a userClient) RefreshJwtTokenFromContext(ctx echo.Context) (domain.LoginResponse, error) {
|
||||
resp := domain.LoginResponse{}
|
||||
|
||||
username, err := ctx.Cookie("newsbot.user")
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
refreshToken, err := ctx.Cookie("newsbot.refreshToken")
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return a.RefreshJwtToken(username.Value, refreshToken.Value)
|
||||
}
|
||||
|
||||
func (a userClient) RefreshSessionToken(jwtToken string) (domain.BaseResponse, error) {
|
||||
endpoint := fmt.Sprintf("%s/%s/refresh/sessionToken", a.serverAddress, UserBaseRoute)
|
||||
|
||||
|
@ -13,7 +13,7 @@ func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
cfg := config.New()
|
||||
apiClient := apiclient.New(cfg.ServerAddress)
|
||||
apiClient := apiclient.New(cfg.ApiServerAddress)
|
||||
server := handlers.NewServer(ctx, cfg, apiClient)
|
||||
|
||||
fmt.Println("The server is online and waiting for requests.")
|
||||
|
4
go.mod
4
go.mod
@ -4,7 +4,7 @@ go 1.22.1
|
||||
|
||||
require (
|
||||
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240603002809-9237369e5a76
|
||||
github.com/a-h/templ v0.2.680
|
||||
github.com/a-h/templ v0.2.747
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/labstack/echo/v4 v4.12.0
|
||||
@ -19,7 +19,7 @@ require (
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
)
|
||||
|
8
go.sum
8
go.sum
@ -1,7 +1,7 @@
|
||||
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240603002809-9237369e5a76 h1:B9t5fcfVerMjqnXXPUmYwdmUk76EoEL8x9IRehqg2c4=
|
||||
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240603002809-9237369e5a76/go.mod h1:A3UdJyQ/IEy3utEwJiC4nbi0ohfgrUNRLTei2iZhLLA=
|
||||
github.com/a-h/templ v0.2.680 h1:TflYFucxp5rmOxAXB9Xy3+QHTk8s8xG9+nCT/cLzjeE=
|
||||
github.com/a-h/templ v0.2.680/go.mod h1:NQGQOycaPKBxRB14DmAaeIpcGC1AOBPJEMO4ozS7m90=
|
||||
github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg=
|
||||
github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4=
|
||||
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=
|
||||
@ -35,8 +35,8 @@ golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
type Configs struct {
|
||||
ServerAddress string
|
||||
JwtSecret string
|
||||
ApiServerAddress string
|
||||
}
|
||||
|
||||
func New() Configs {
|
||||
@ -22,6 +23,7 @@ func getEnvConfig() Configs {
|
||||
return Configs{
|
||||
ServerAddress: os.Getenv("ServerAddress"),
|
||||
JwtSecret: os.Getenv("JwtSecret"),
|
||||
ApiServerAddress: os.Getenv("ApiServerAddress"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,38 +3,32 @@ package handlers
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
||||
apidomain "git.jamestombleson.com/jtom38/newsbot-api/domain"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/articles"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func (h *Handler) ArticlesList(c echo.Context) error {
|
||||
_, err := ValidateJwt(c, h.config.JwtSecret, h.config.ServerAddress)
|
||||
err := HasValidScope(c, apidomain.ScopeArticleRead)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusOK, layout.Error(err))
|
||||
return RenderError(c, err)
|
||||
}
|
||||
|
||||
userToken, err := c.Cookie(domain.CookieToken)
|
||||
resp, err := h.api.Articles.List(GetJwtToken(c), 0)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusBadRequest, layout.Error(err))
|
||||
}
|
||||
|
||||
resp, err := h.api.Articles.List(userToken.Value, 0)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusBadRequest, layout.Error(err))
|
||||
return RenderError(c, err)
|
||||
}
|
||||
|
||||
vm := models.ListArticlesViewModel{}
|
||||
|
||||
for _, article := range resp.Payload {
|
||||
source, err := h.api.Sources.GetById(userToken.Value, article.SourceID)
|
||||
source, err := h.api.Sources.GetById(GetJwtToken(c), article.SourceID)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusBadRequest, layout.Error(err))
|
||||
return RenderError(c, err)
|
||||
}
|
||||
|
||||
item := models.ListArticleSourceModel {
|
||||
item := models.ListArticleSourceModel{
|
||||
Article: article,
|
||||
Source: source.Payload[0],
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient.
|
||||
router.Pre(middleware.Logger())
|
||||
router.Pre(middleware.Recover())
|
||||
router.Use(middleware.Static("/internal/static"))
|
||||
//router.Use(RefreshJwtMiddleware(apiClient))
|
||||
|
||||
router.GET("/", s.HomeIndex)
|
||||
router.GET("/about", s.HomeAbout)
|
||||
@ -57,57 +58,23 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient.
|
||||
debug.GET("/cookies", s.DebugCookies)
|
||||
|
||||
articles := router.Group("/articles")
|
||||
//articles.Use(ValidateJwtMiddleware(configs.JwtSecret))
|
||||
articles.GET("", s.ArticlesList)
|
||||
|
||||
sources := router.Group("/sources")
|
||||
//sources.Use(ValidateJwtMiddleware(configs.JwtSecret))
|
||||
sources.GET("", s.ListAllSources)
|
||||
sources.GET("/add", s.AddSource)
|
||||
|
||||
users := router.Group("/users")
|
||||
users.GET("/login", s.UserLogin)
|
||||
users.POST("/login", s.UserAfterLogin)
|
||||
users.GET("/signup", s.UserSignUp)
|
||||
users.POST("/signup", s.UserAfterSignUp)
|
||||
users.Use(ValidateJwtMiddleware(configs.JwtSecret))
|
||||
users.GET("/logout", s.UsersLogout)
|
||||
users.GET("/profile", s.UserProfile)
|
||||
|
||||
s.Router = router
|
||||
return s
|
||||
}
|
||||
|
||||
// If the token is not valid then an json error will be returned.
|
||||
// If the token has the wrong scope, a json error will be returned.
|
||||
// If the token passes all the checks, it is valid and is returned back to the caller.
|
||||
//func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtToken, error) {
|
||||
// token, err := s.getJwtTokenFromContext(c)
|
||||
// if err != nil {
|
||||
// s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized)
|
||||
// }
|
||||
//
|
||||
// err = token.hasExpired()
|
||||
// if err != nil {
|
||||
// return JwtToken{}, errors.New(ErrJwtExpired)
|
||||
// //s.WriteMessage(c, ErrJwtExpired, http.StatusUnauthorized)
|
||||
// }
|
||||
//
|
||||
// err = token.hasScope(requiredScope)
|
||||
// if err != nil {
|
||||
// return JwtToken{}, errors.New(ErrJwtScopeMissing)
|
||||
// //s.WriteMessage(c, ErrJwtScopeMissing, http.StatusUnauthorized)
|
||||
// }
|
||||
//
|
||||
// if token.Iss != s.config.ServerAddress {
|
||||
// return JwtToken{}, errors.New(ErrJwtInvalidIssuer)
|
||||
// //s.WriteMessage(c, ErrJwtInvalidIssuer, http.StatusUnauthorized)
|
||||
// }
|
||||
//
|
||||
// return token, nil
|
||||
//}
|
||||
|
||||
//func (s *Handler) GetUserIdFromJwtToken(c echo.Context) int64 {
|
||||
// token, err := s.getJwtTokenFromContext(c)
|
||||
// if err != nil {
|
||||
// s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized)
|
||||
// }
|
||||
//
|
||||
// return token.GetUserId()
|
||||
//}
|
||||
|
64
internal/handlers/middleware.go
Normal file
64
internal/handlers/middleware.go
Normal file
@ -0,0 +1,64 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/apiclient"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func ValidateJwtMiddleware(jwtSecret string) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
cookie, err := c.Cookie(domain.CookieToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cookie.Value == "" {
|
||||
return echo.NewHTTPError(401, "Authorization token is missing.")
|
||||
}
|
||||
|
||||
token, err := jwt.ParseWithClaims(cookie.Value, &jwtToken{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(jwtSecret), nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return echo.NewHTTPError(401, "Invalid authorization token.")
|
||||
//return errors.New("invalid jwt token")
|
||||
}
|
||||
|
||||
claims := token.Claims.(*jwtToken)
|
||||
if !claims.Exp.After(time.Now()) {
|
||||
return echo.NewHTTPError(401, "Your Authorization token has expired.")
|
||||
//return errors.New("the jwt token has expired")
|
||||
}
|
||||
//if claims.Iss != issuer {
|
||||
// return jwtToken{}, errors.New("the issuer was invalid")
|
||||
//}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RefreshJwtMiddleware(api apiclient.ApiClient) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
resp, err := api.Users.RefreshJwtTokenFromContext(c)
|
||||
if err != nil {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
SetCookie(c, domain.CookieToken, resp.Token, "/")
|
||||
SetCookie(c, domain.CookieRefreshToken, resp.RefreshToken, "/")
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
@ -3,27 +3,22 @@ package handlers
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/sources"
|
||||
|
||||
apidomain "git.jamestombleson.com/jtom38/newsbot-api/domain"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func (h *Handler) ListAllSources(c echo.Context) error {
|
||||
_, err := ValidateJwt(c, h.config.JwtSecret, h.config.ServerAddress)
|
||||
err := HasValidScope(c, apidomain.ScopeSourceRead)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusOK, layout.Error(err))
|
||||
return RenderError(c, err)
|
||||
}
|
||||
|
||||
userToken, err := c.Cookie(domain.CookieToken)
|
||||
resp, err := h.api.Sources.ListAll(GetJwtToken(c), 0)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusBadRequest, layout.Error(err))
|
||||
}
|
||||
|
||||
resp, err := h.api.Sources.ListAll(userToken.Value, 0)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusOK, layout.Error(err))
|
||||
return RenderError(c, err)
|
||||
}
|
||||
|
||||
return Render(c, http.StatusOK, sources.ListAll(models.ListAllSourcesViewModel{
|
||||
@ -32,3 +27,12 @@ func (h *Handler) ListAllSources(c echo.Context) error {
|
||||
Message: resp.Message,
|
||||
}))
|
||||
}
|
||||
|
||||
func (h *Handler) AddSource(c echo.Context) error {
|
||||
err := HasValidScope(c, apidomain.ScopeSourceCreate)
|
||||
if err != nil {
|
||||
return RenderError(c, err)
|
||||
}
|
||||
|
||||
return Render(c, http.StatusOK, sources.Add(models.AddSourcePayloadModel{}))
|
||||
}
|
||||
|
@ -5,7 +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/models"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/users"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
@ -20,7 +20,10 @@ func (h *Handler) UserAfterLogin(c echo.Context) error {
|
||||
|
||||
resp, err := h.api.Users.Login(user, password)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusBadRequest, users.AfterLogin(err.Error(), false))
|
||||
return Render(c, http.StatusBadRequest, users.AfterLogin(models.AfterLoginViewModel{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
}))
|
||||
}
|
||||
|
||||
if user == "" {
|
||||
@ -31,7 +34,11 @@ func (h *Handler) UserAfterLogin(c echo.Context) error {
|
||||
SetCookie(c, domain.CookieRefreshToken, resp.RefreshToken, "/")
|
||||
SetCookie(c, domain.CookieUser, user, "/")
|
||||
|
||||
return Render(c, http.StatusOK, users.AfterLogin("Login Successful!", true))
|
||||
vm := models.AfterLoginViewModel{
|
||||
Success: true,
|
||||
Message: "Login Successful!",
|
||||
}
|
||||
return Render(c, http.StatusOK, users.AfterLogin(vm))
|
||||
}
|
||||
|
||||
func (h *Handler) UserSignUp(c echo.Context) error {
|
||||
@ -44,11 +51,17 @@ func (h *Handler) UserAfterSignUp(c echo.Context) error {
|
||||
|
||||
resp, err := h.api.Users.SignUp(user, password)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusBadRequest, users.AfterLogin(err.Error(), false))
|
||||
return Render(c, http.StatusBadRequest, users.AfterLogin(models.AfterLoginViewModel{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
}))
|
||||
}
|
||||
if resp.Message != "OK" {
|
||||
msg := fmt.Sprintf("Failed to create account. Message: %s", resp.Message)
|
||||
return Render(c, http.StatusBadRequest, users.AfterLogin(msg, false))
|
||||
return Render(c, http.StatusBadRequest, users.AfterLogin(models.AfterLoginViewModel{
|
||||
Message: msg,
|
||||
Success: false,
|
||||
}))
|
||||
}
|
||||
return Render(c, http.StatusOK, users.AfterSignUp("Registration Successful!", true))
|
||||
}
|
||||
@ -58,9 +71,9 @@ func (h *Handler) UserProfile(c echo.Context) error {
|
||||
}
|
||||
|
||||
func (h *Handler) ForceLogout(c echo.Context) error {
|
||||
_, err := ValidateJwt(c, h.config.JwtSecret, h.config.ServerAddress)
|
||||
err := IsLoggedIn(c)
|
||||
if err != nil {
|
||||
return Render(c, http.StatusOK, layout.Error(err))
|
||||
return RenderError(c, err)
|
||||
}
|
||||
|
||||
h.api.Users.RefreshSessionToken(GetJwtToken(c))
|
||||
|
@ -4,9 +4,12 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/config"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
|
||||
"github.com/a-h/templ"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/labstack/echo/v4"
|
||||
@ -33,7 +36,31 @@ type jwtToken struct {
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error) {
|
||||
func IsLoggedIn(ctx echo.Context) error {
|
||||
_, err := GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func HasValidScope(ctx echo.Context, scope string) error {
|
||||
token, err := GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userScopes := strings.Join(token.Scopes, ",")
|
||||
if strings.Contains(userScopes, scope) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("required permission is missing")
|
||||
}
|
||||
|
||||
func GetUserInfo(ctx echo.Context) (jwtToken, error) {
|
||||
cfg := config.New()
|
||||
cookie, err := ctx.Cookie(domain.CookieToken)
|
||||
if err != nil {
|
||||
return jwtToken{}, err
|
||||
@ -44,7 +71,7 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error
|
||||
}
|
||||
|
||||
token, err := jwt.ParseWithClaims(cookie.Value, &jwtToken{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(sharedSecret), nil
|
||||
return []byte(cfg.JwtSecret), nil
|
||||
})
|
||||
if err != nil {
|
||||
return jwtToken{}, err
|
||||
@ -58,9 +85,6 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error
|
||||
if !claims.Exp.After(time.Now()) {
|
||||
return jwtToken{}, errors.New("the jwt token has expired")
|
||||
}
|
||||
//if claims.Iss != issuer {
|
||||
// return jwtToken{}, errors.New("the issuer was invalid")
|
||||
//}
|
||||
|
||||
return *claims, nil
|
||||
}
|
||||
@ -90,3 +114,22 @@ func Render(ctx echo.Context, statusCode int, t templ.Component) error {
|
||||
|
||||
return t.Render(request, ctx.Response().Writer)
|
||||
}
|
||||
|
||||
func RenderError(ctx echo.Context, err error) error {
|
||||
var t templ.Component = layout.Error(err)
|
||||
ctx.Response().Writer.WriteHeader(200)
|
||||
ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML)
|
||||
|
||||
// take the request context and make it a var
|
||||
request := ctx.Request().Context()
|
||||
|
||||
//Check to see if we the echo context has the cookie we are looking for, if so, create a new context based on what we had and add the value
|
||||
username, err := ctx.Cookie(domain.CookieUser)
|
||||
if err == nil {
|
||||
request = context.WithValue(request, domain.UserNameContext, username.Value)
|
||||
} else {
|
||||
request = context.WithValue(request, domain.UserNameContext, "")
|
||||
}
|
||||
|
||||
return t.Render(request, ctx.Response().Writer)
|
||||
}
|
||||
|
@ -7,3 +7,7 @@ type ListAllSourcesViewModel struct {
|
||||
Message string
|
||||
Items []domain.SourceDto
|
||||
}
|
||||
|
||||
type AddSourcePayloadModel struct {
|
||||
|
||||
}
|
6
internal/models/users.go
Normal file
6
internal/models/users.go
Normal file
@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
type AfterLoginViewModel struct {
|
||||
Message string
|
||||
Success bool
|
||||
}
|
10
internal/views/sources/add.templ
Normal file
10
internal/views/sources/add.templ
Normal file
@ -0,0 +1,10 @@
|
||||
package sources
|
||||
|
||||
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
|
||||
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
|
||||
|
||||
templ Add(model models.AddSourcePayloadModel) {
|
||||
@layout.WithTemplate() {
|
||||
<form hx-post="/sources/add"></form>
|
||||
}
|
||||
}
|
@ -1,15 +1,17 @@
|
||||
package users
|
||||
|
||||
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
|
||||
|
||||
// This is returned after the user logs into the application.
|
||||
// It just returns a partial view because it will overlap with the existing template.
|
||||
templ AfterLogin(message string, success bool) {
|
||||
if success {
|
||||
templ AfterLogin(vm models.AfterLoginViewModel) {
|
||||
if vm.Success {
|
||||
<div class="notification is-success">
|
||||
{ message }
|
||||
{ vm.Message }
|
||||
</div>
|
||||
} else {
|
||||
<div class="notification is-error">
|
||||
{ message }
|
||||
{ vm.Message }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user