Compare commits

...

13 Commits

22 changed files with 168 additions and 51 deletions

15
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch file",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "cmd/main.go"
}
]
}

View File

@ -1,6 +1,13 @@
{ {
"editor.formatOnSave": true,
"[templ]": {
"editor.defaultFormatter": "a-h.templ"
},
"files.exclude": { "files.exclude": {
"**/*_templ.go": true, "**/*_templ.go": true,
"**/*_templ.txt": true "**/*_templ.txt": true
},
"emmet.includeLanguages": {
"templ": "html"
} }
} }

9
domain/consts.go Normal file
View File

@ -0,0 +1,9 @@
package domain
const (
CookieToken = "token"
CookieRefreshToken = "refresh"
CookieUser = "user"
CookieSettingsDarkMode = "darkmode"
)

View File

@ -3,6 +3,7 @@ package handlers
import ( import (
"log" "log"
"net/http" "net/http"
"templ-test/domain"
"templ-test/views/auth" "templ-test/views/auth"
"templ-test/views/home" "templ-test/views/home"
@ -25,22 +26,22 @@ func (h *Handlers) AuthLoginPost(c echo.Context) error {
} }
cookie := new(http.Cookie) cookie := new(http.Cookie)
cookie.Name = CookieToken cookie.Name = domain.CookieToken
cookie.Value = resp.Token cookie.Value = resp.Token
c.SetCookie(cookie) c.SetCookie(cookie)
cookie = new(http.Cookie) cookie = new(http.Cookie)
cookie.Name = CookieRefreshToken cookie.Name = domain.CookieRefreshToken
cookie.Value = resp.RefreshToken cookie.Value = resp.RefreshToken
c.SetCookie(cookie) c.SetCookie(cookie)
cookie = new(http.Cookie) cookie = new(http.Cookie)
cookie.Name = CookieUser cookie.Name = domain.CookieUser
cookie.Value = user cookie.Value = user
c.SetCookie(cookie) c.SetCookie(cookie)
// render // render
return Render(c, http.StatusOK, home.Home()) return Render(c, http.StatusOK, auth.LoginPost())
} }
func (h *Handlers) AuthShowCookies(c echo.Context) error { func (h *Handlers) AuthShowCookies(c echo.Context) error {

View File

@ -1,8 +1,10 @@
package handlers package handlers
import ( import (
"context"
"errors" "errors"
"templ-test/client" "templ-test/client"
"templ-test/domain"
"templ-test/models" "templ-test/models"
"templ-test/services" "templ-test/services"
"time" "time"
@ -13,12 +15,6 @@ import (
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
) )
const (
CookieToken = "token"
CookieRefreshToken = "refresh"
CookieUser = "user"
)
type Handlers struct { type Handlers struct {
api client.ApiClient api client.ApiClient
cfg services.EnvConfig cfg services.EnvConfig
@ -48,7 +44,16 @@ func (h *Handlers) Register(group echo.Group) {
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)
return t.Render(ctx.Request().Context(), ctx.Response().Writer)
// 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
darkMode, err := ctx.Cookie(domain.CookieSettingsDarkMode)
if err == nil {
request = context.WithValue(request, domain.CookieSettingsDarkMode, darkMode.Value)
}
return t.Render(request, ctx.Response().Writer)
} }
type jwtToken struct { type jwtToken struct {
@ -76,7 +81,7 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error
if !token.Valid { if !token.Valid {
return jwtToken{}, errors.New("invalid jwt token") return jwtToken{}, errors.New("invalid jwt token")
} }
claims := token.Claims.(*jwtToken) claims := token.Claims.(*jwtToken)
if !claims.Exp.After(time.Now()) { if !claims.Exp.After(time.Now()) {
return jwtToken{}, errors.New("the jwt token has expired") return jwtToken{}, errors.New("the jwt token has expired")
@ -91,20 +96,25 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error
func GetCookieValues(ctx echo.Context) models.AllCookies { func GetCookieValues(ctx echo.Context) models.AllCookies {
m := models.AllCookies{} m := models.AllCookies{}
token, err := ctx.Cookie(CookieToken) token, err := ctx.Cookie(domain.CookieToken)
if err == nil { if err == nil {
m.Token = token.Value m.Token = token.Value
} }
user, err := ctx.Cookie(CookieUser) user, err := ctx.Cookie(domain.CookieUser)
if err == nil { if err == nil {
m.Username = user.Value m.Username = user.Value
} }
refresh, err := ctx.Cookie(CookieRefreshToken) refresh, err := ctx.Cookie(domain.CookieRefreshToken)
if err == nil { if err == nil {
m.RefreshToken = refresh.Value m.RefreshToken = refresh.Value
} }
darkMode, err := ctx.Cookie(domain.CookieSettingsDarkMode)
if err == nil {
m.DarkMode = darkMode.Value
}
return m return m
} }

View File

@ -2,6 +2,8 @@ package handlers
import ( import (
"net/http" "net/http"
"templ-test/domain"
"templ-test/models"
"templ-test/views/home" "templ-test/views/home"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@ -12,15 +14,25 @@ func (h *Handlers) HomeHandler(c echo.Context) error {
} }
func (h *Handlers) Settings(c echo.Context) error { func (h *Handlers) Settings(c echo.Context) error {
return Render(c, http.StatusOK, home.UserSettings()) m := models.SettingsViewModel{}
darkMode, err := c.Cookie(domain.CookieSettingsDarkMode)
if err == nil {
m.DarkMode = darkMode.Value
}
return Render(c, http.StatusOK, home.UserSettings(m))
} }
func (h *Handlers) SettingsPost(c echo.Context) error { func (h *Handlers) SettingsPost(c echo.Context) error {
// take in the updated values from he user and write the cookies... tbd // take in the updated values from he user and write the cookies... tbd
useDarkMode := c.FormValue(domain.CookieSettingsDarkMode)
if useDarkMode != "" {
darkModeCookie := new(http.Cookie)
darkModeCookie.Name = domain.CookieSettingsDarkMode
darkModeCookie.Value = useDarkMode
return Render(c, http.StatusOK, home.UserSettings()) c.SetCookie(darkModeCookie)
}
return Render(c, http.StatusOK, home.SettingsUpdated())
} }
//func (h *Handlers) ListHandler(c echo.Context) error {
// return Render(c, http.StatusOK, views.List())
//}

View File

@ -4,6 +4,7 @@ type AllCookies struct {
Username string Username string
Token string Token string
RefreshToken string RefreshToken string
DarkMode string
} }
type ShowCookie struct { type ShowCookie struct {

5
models/home.go Normal file
View File

@ -0,0 +1,5 @@
package models
type SettingsViewModel struct {
DarkMode string
}

View File

@ -4,10 +4,13 @@ import "templ-test/models"
import "templ-test/views/layout" import "templ-test/views/layout"
templ ShowCookie(m models.AllCookies) { templ ShowCookie(m models.AllCookies) {
@layout.Testing("Cookie Explorer") { @layout.WithLayout("Cookie Explorer") {
<h2>These are stored as cookies</h2> <h2>These are stored as cookies</h2>
<h3>JWT Values</h3>
<p>Username: { m.Username }</p> <p>Username: { m.Username }</p>
<p>JWT Token: { m.Token }</p> <p>JWT Token: { m.Token }</p>
<p>RefreshToken: { m.RefreshToken }</p> <p>RefreshToken: { m.RefreshToken }</p>
<h3>User Settings</h3>
<p>DarkMode: { m.DarkMode }</p>
} }
} }

View File

@ -3,7 +3,7 @@ package auth
import "templ-test/views/layout" import "templ-test/views/layout"
templ AuthLogin() { templ AuthLogin() {
@layout.WithLayout("Login", true) { @layout.WithLayout("Login") {
<form hx-post="/auth/login"> <form hx-post="/auth/login">
<div class="mb-3"> <div class="mb-3">
<label for="username" class="form-label">Username</label> <label for="username" class="form-label">Username</label>

View File

@ -0,0 +1,7 @@
package auth
import "templ-test/views/components/bootstrap"
templ LoginPost() {
@bootstrap.BootstrapAlert("Login successful!", bootstrap.VariantSuccess)
}

View File

@ -17,7 +17,3 @@ templ BootstrapAlert(message, variant string) {
{ message } { message }
</div> </div>
} }
templ BootstrapButton(message, variant string) {
<button type="button" class={ getButtonVariant(variant) }>{ message }</button>
}

View File

@ -0,0 +1,10 @@
package bootstrap
const (
ButtonTypeSubmit = "submit"
ButtonTypeDefault = "button"
)
templ BootstrapButton(message, variant, buttonType string) {
<button type={ buttonType } class={ getButtonVariant(variant) }>{ message }</button>
}

View File

@ -0,0 +1,5 @@
package bootstrap
templ Label(cssClass, name, cssForId, message string) {
<label class={ cssClass } name={ name } for={ cssForId }>{ message }</label>
}

View File

@ -0,0 +1,13 @@
package bootstrap
const (
SwitchTypeCheckbox = "checkbox"
)
templ Switch(name, switchType, cssId string, enabled bool) {
if enabled == true {
<input class="form-check-input" name={ name } type={ switchType } role="switch" id={ cssId } checked/>
} else {
<input class="form-check-input" name={ name } type={ switchType } role="switch" id={ cssId }/>
}
}

View File

@ -3,7 +3,7 @@ package home
import "templ-test/views/layout" import "templ-test/views/layout"
templ Error(message error) { templ Error(message error) {
@layout.Testing("Error") { @layout.WithLayout("Error") {
<h1>Oops... :(</h1> <h1>Oops... :(</h1>
<h3>{ message.Error() } </h3> <h3>{ message.Error() } </h3>
} }

View File

@ -4,14 +4,14 @@ import "templ-test/views/components/bootstrap"
import "templ-test/views/layout" import "templ-test/views/layout"
templ Home() { templ Home() {
@layout.WithLayout("Home", true) { @layout.WithLayout("Home") {
<p> <p>
this should be above the alert this should be above the alert
</p> </p>
@bootstrap.BootstrapAlert("Testing!", bootstrap.VariantDark) @bootstrap.BootstrapAlert("Testing!", bootstrap.VariantDark)
<p>you should now see this under the Alert </p> <p>you should now see this under the Alert </p>
@bootstrap.BootstrapButton("I am in danger", bootstrap.VariantDanger) @bootstrap.BootstrapButton("I am in danger", bootstrap.VariantDanger, bootstrap.ButtonTypeDefault)
@bootstrap.BootstrapButton("I am the darkness", bootstrap.VariantDark) @bootstrap.BootstrapButton("I am the darkness", bootstrap.VariantDark, bootstrap.ButtonTypeDefault)
} }
} }

View File

@ -1,9 +1,33 @@
package home package home
import "templ-test/views/layout" import "templ-test/views/layout"
import "templ-test/views/components/bootstrap"
import "templ-test/models"
import "templ-test/domain"
templ UserSettings() { const (
@layout.Testing("Settings") { test = "t"
<h2>This is not ready yet</h2> )
templ UserSettings(model models.SettingsViewModel) {
@layout.WithLayout("Settings") {
<form hx-post="/settings">
<div class="mb-3">
if useDarkMode(model.DarkMode) {
@bootstrap.Switch("darkmode", bootstrap.SwitchTypeCheckbox, test, true)
} else {
@bootstrap.Switch("darkmode", bootstrap.SwitchTypeCheckbox, "flexSwitchCheckDefault", true)
}
@bootstrap.Label("form-check-label", domain.CookieSettingsDarkMode, "flexSwitchCheckDefault", "Use Dark Mode")
</div>
@bootstrap.BootstrapButton("Submit", bootstrap.VariantInfo, bootstrap.ButtonTypeSubmit)
</form>
} }
} }
func useDarkMode(value string) bool {
if value == "on" {
return true
}
return false
}

View File

@ -0,0 +1,5 @@
package home
templ SettingsUpdated() {
<p>You are now free to move about the cabin. Thank you for saving with us!</p>
}

View File

@ -1,20 +1,8 @@
package layout package layout
templ WithLayout(pageName string, useDarkMode bool) { templ WithLayout(pageName string) {
<html> <!doctype html>
@getHtmlHead() <html lang="en" data-bs-theme={ useLightOrDarkTheme(ctx) }>
<body>
@bootstrapNavBar()
@getBodyHeader(pageName)
<div class="container-fluid">
{ children... }
</div>
</body>
</html>
}
templ Testing(pageName string) {
<html>
@getHtmlHead() @getHtmlHead()
<body> <body>
@bootstrapNavBar() @bootstrapNavBar()

View File

@ -1,7 +1,7 @@
package layout package layout
templ bootstrapNavBar() { templ bootstrapNavBar() {
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme={ useLightOrDarkTheme(ctx)}> <nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a> <a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">

View File

@ -2,8 +2,14 @@ package layout
import ( import (
"context" "context"
"templ-test/domain"
) )
func useLightOrDarkTheme(ctx context.Context) string { func useLightOrDarkTheme(ctx context.Context) string {
return "dark" value := ctx.Value(domain.CookieSettingsDarkMode)
if value == "on" {
return "dark"
} else {
return "light"
}
} }