features/cookies-maybe #2
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,6 +2,9 @@
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
|
||||
*.env
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
|
6
Justfile
6
Justfile
@ -1,4 +1,8 @@
|
||||
|
||||
# Runs the 'templ generate` every time a file is updated and reloads the debugger
|
||||
debug:
|
||||
templ generate --watch --proxy="http://localhost:3000" --cmd="go run ."
|
||||
templ generate --watch --proxy="http://localhost:3000" --cmd="go run cmd/main.go"
|
||||
|
||||
# Generates templ files
|
||||
gen:
|
||||
templ generate
|
||||
|
@ -7,6 +7,7 @@ const (
|
||||
|
||||
type ApiClient struct {
|
||||
Auth Auth
|
||||
Demo DemoApiClient
|
||||
|
||||
ServerAddress string
|
||||
}
|
||||
@ -14,5 +15,6 @@ type ApiClient struct {
|
||||
func New(serverAddress string) ApiClient {
|
||||
return ApiClient{
|
||||
Auth: newAuthClient(serverAddress),
|
||||
Demo: newDemoClient(serverAddress),
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,14 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
)
|
||||
|
||||
type Auth interface {
|
||||
Register(username, password string) error
|
||||
Login(username, password string) error
|
||||
Login(username, password string) (LoginResponse, error)
|
||||
}
|
||||
|
||||
type AuthClient struct {
|
||||
@ -42,7 +43,7 @@ func (a AuthClient) Register(username, password string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
//defer resp.Body.Close()
|
||||
|
||||
content, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
@ -58,31 +59,25 @@ func (a AuthClient) Register(username, password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AuthClient) Login(username, password string) error {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/auth/register", a.serverAddress)
|
||||
req, err := http.NewRequest(http.MethodPost, endpoint, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
type LoginResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Token string `json:"token"`
|
||||
Type string `json:"type"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
req.Header.Set(HeaderContentType, MIMEApplicationForm)
|
||||
req.Form.Add("username", username)
|
||||
req.Form.Add("password", password)
|
||||
resp, err := a.client.Do(req)
|
||||
func (a AuthClient) Login(username, password string) (LoginResponse, error) {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/auth/login", a.serverAddress)
|
||||
|
||||
param := url.Values{}
|
||||
param.Set("username", username)
|
||||
param.Set("password", password)
|
||||
|
||||
var bind = LoginResponse{}
|
||||
err := PostUrlForm(a.client, endpoint, param, &bind)
|
||||
if err != nil {
|
||||
return err
|
||||
return LoginResponse{}, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
content, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var bind = domain.ErrorResponse{}
|
||||
err = json.Unmarshal(content, &bind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return bind, nil
|
||||
}
|
47
client/demo.go
Normal file
47
client/demo.go
Normal file
@ -0,0 +1,47 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type DemoApiClient struct {
|
||||
serverAddress string
|
||||
client http.Client
|
||||
}
|
||||
|
||||
func newDemoClient(serverAddress string) DemoApiClient {
|
||||
return DemoApiClient{
|
||||
serverAddress: serverAddress,
|
||||
client: http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
type HelloBodyParam struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// This is an example route to demo passing a body in
|
||||
func (d DemoApiClient) Hello() error {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/demo/hello", d.serverAddress)
|
||||
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := d.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//defer resp.Body.Close()
|
||||
|
||||
content, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println(string(content))
|
||||
|
||||
return nil
|
||||
}
|
31
client/util.go
Normal file
31
client/util.go
Normal file
@ -0,0 +1,31 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set(HeaderContentType, MIMEApplicationForm)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
err = decoder.Decode(&t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
31
cmd/main.go
Normal file
31
cmd/main.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"templ-test/client"
|
||||
"templ-test/handlers"
|
||||
"templ-test/services"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/labstack/echo-contrib/session"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := services.NewEnvConfig()
|
||||
|
||||
// connect to api server
|
||||
apiClient := client.New(cfg.ApiServerUri)
|
||||
|
||||
e := echo.New()
|
||||
e.Use(session.Middleware(sessions.NewCookieStore([]byte(cfg.CookieSecret))))
|
||||
e.Pre(middleware.Logger())
|
||||
|
||||
handler := e.Group("")
|
||||
portalClient := handlers.NewHandlerClient(apiClient, cfg)
|
||||
portalClient.Register(*handler)
|
||||
|
||||
fmt.Println("Listening on :1324")
|
||||
e.Logger.Fatal(e.Start(":1324"))
|
||||
}
|
@ -3,22 +3,46 @@ package handlers
|
||||
import (
|
||||
"net/http"
|
||||
"templ-test/views"
|
||||
"templ-test/views/auth"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func (h *Handlers) AuthLogin(c echo.Context) error {
|
||||
return Render(c, http.StatusOK, views.AuthLogin())
|
||||
return Render(c, http.StatusOK, auth.AuthLogin())
|
||||
}
|
||||
|
||||
func (h *Handlers) AuthLoginPost(c echo.Context) error {
|
||||
// check the form data
|
||||
//user := c.FormValue("email")
|
||||
//password := c.FormValue("password")
|
||||
user := c.FormValue("username")
|
||||
password := c.FormValue("password")
|
||||
|
||||
// send request to the API
|
||||
//h.api.
|
||||
resp, err := h.api.Auth.Login(user, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cookie := new(http.Cookie)
|
||||
cookie.Name = CookieToken
|
||||
cookie.Value = resp.Token
|
||||
c.SetCookie(cookie)
|
||||
|
||||
cookie = new(http.Cookie)
|
||||
cookie.Name = CookieRefreshToken
|
||||
cookie.Value = resp.RefreshToken
|
||||
c.SetCookie(cookie)
|
||||
|
||||
cookie = new(http.Cookie)
|
||||
cookie.Name = CookieUser
|
||||
cookie.Value = user
|
||||
c.SetCookie(cookie)
|
||||
|
||||
// render
|
||||
return nil
|
||||
return Render(c, http.StatusOK, views.Home())
|
||||
}
|
||||
|
||||
func (h *Handlers) AuthShowCookies(c echo.Context) error {
|
||||
cookies := GetCookieValues(c)
|
||||
return Render(c, http.StatusOK, auth.ShowCookie(cookies))
|
||||
}
|
||||
|
@ -2,35 +2,42 @@ package handlers
|
||||
|
||||
import (
|
||||
"templ-test/client"
|
||||
"templ-test/models"
|
||||
"templ-test/services"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/labstack/echo-contrib/session"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
CookieToken = "token"
|
||||
CookieRefreshToken = "refresh"
|
||||
CookieUser = "user"
|
||||
)
|
||||
|
||||
type Handlers struct {
|
||||
Server *echo.Echo
|
||||
api client.ApiClient
|
||||
cfg services.EnvConfig
|
||||
}
|
||||
|
||||
func NewHandlerClient(api client.ApiClient, cfg services.EnvConfig) *Handlers {
|
||||
h := Handlers{
|
||||
api: api,
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
e := echo.New()
|
||||
e.Use(session.Middleware(sessions.NewCookieStore([]byte(cfg.CookieSecret))))
|
||||
e.GET("/", h.HomeHandler)
|
||||
e.GET("/list", h.ListHandler)
|
||||
return &h
|
||||
}
|
||||
|
||||
auth := e.Group("/auth")
|
||||
func (h *Handlers) Register(group echo.Group) {
|
||||
group.GET("/", h.HomeHandler)
|
||||
group.GET("/list", h.ListHandler)
|
||||
|
||||
auth := group.Group("/auth")
|
||||
auth.GET("/login", h.AuthLogin)
|
||||
auth.POST("/login", h.AuthLoginPost)
|
||||
|
||||
h.Server = e
|
||||
return &h
|
||||
auth.GET("/cookie", h.AuthShowCookies)
|
||||
}
|
||||
|
||||
func Render(ctx echo.Context, statusCode int, t templ.Component) error {
|
||||
@ -38,3 +45,24 @@ func Render(ctx echo.Context, statusCode int, t templ.Component) error {
|
||||
ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML)
|
||||
return t.Render(ctx.Request().Context(), ctx.Response().Writer)
|
||||
}
|
||||
|
||||
func GetCookieValues(ctx echo.Context) models.AllCookies {
|
||||
m := models.AllCookies{}
|
||||
|
||||
token, err := ctx.Cookie(CookieToken)
|
||||
if err == nil {
|
||||
m.Token = token.Value
|
||||
}
|
||||
|
||||
user, err := ctx.Cookie(CookieUser)
|
||||
if err == nil {
|
||||
m.Username = user.Value
|
||||
}
|
||||
|
||||
refresh, err := ctx.Cookie(CookieRefreshToken)
|
||||
if err == nil {
|
||||
m.RefreshToken = refresh.Value
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
19
main.go
19
main.go
@ -1,19 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"templ-test/client"
|
||||
"templ-test/handlers"
|
||||
"templ-test/services"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := services.NewEnvConfig()
|
||||
|
||||
// connect to api server
|
||||
apiClient := client.New(cfg.ApiServerUri)
|
||||
handler := handlers.NewHandlerClient(apiClient, cfg)
|
||||
|
||||
fmt.Println("Listening on :3000")
|
||||
handler.Server.Start(":3000")
|
||||
}
|
11
models/auth.go
Normal file
11
models/auth.go
Normal file
@ -0,0 +1,11 @@
|
||||
package models
|
||||
|
||||
type AllCookies struct {
|
||||
Username string
|
||||
Token string
|
||||
RefreshToken string
|
||||
}
|
||||
|
||||
type ShowCookie struct {
|
||||
AllCookies
|
||||
}
|
@ -13,10 +13,13 @@ type EnvConfig struct {
|
||||
}
|
||||
|
||||
func NewEnvConfig() EnvConfig {
|
||||
err := godotenv.Load()
|
||||
_, err := os.Stat(".env")
|
||||
if err == nil {
|
||||
err = godotenv.Load()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
return EnvConfig{
|
||||
ApiServerUri: os.Getenv("ApiServerUri"),
|
||||
|
@ -1,22 +0,0 @@
|
||||
package views
|
||||
|
||||
templ AuthLogin() {
|
||||
@WithLayout("Login", true) {
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label for="exampleInputEmail1" class="form-label">Email address</label>
|
||||
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp"/>
|
||||
<div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="exampleInputPassword1" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="exampleInputPassword1"/>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="exampleCheck1"/>
|
||||
<label class="form-check-label" for="exampleCheck1">Check me out</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" hx-post="/auth/" >Submit</button>
|
||||
</form>
|
||||
}
|
||||
}
|
13
views/auth/cookie.templ
Normal file
13
views/auth/cookie.templ
Normal file
@ -0,0 +1,13 @@
|
||||
package auth
|
||||
|
||||
import "templ-test/views"
|
||||
import "templ-test/models"
|
||||
|
||||
templ ShowCookie(m models.AllCookies) {
|
||||
@views.WithLayout("Cookie Explorer", true) {
|
||||
<h2>These are stored as cookies</h2>
|
||||
<p>Username: { m.Username }</p>
|
||||
<p>JWT Token: { m.Token }</p>
|
||||
<p>RefreshToken: { m.RefreshToken }</p>
|
||||
}
|
||||
}
|
23
views/auth/login.templ
Normal file
23
views/auth/login.templ
Normal file
@ -0,0 +1,23 @@
|
||||
package auth
|
||||
|
||||
import "templ-test/views"
|
||||
|
||||
templ AuthLogin() {
|
||||
@views.WithLayout("Login", true) {
|
||||
<form hx-post="/auth/login">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" name="username" class="form-control" id="username"/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="exampleInputPassword1" class="form-label">Password</label>
|
||||
<input type="password" name="password" class="form-control" id="exampleInputPassword1"/>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="exampleCheck1"/>
|
||||
<label class="form-check-label" for="exampleCheck1">Check me out</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user