first commit
This commit is contained in:
commit
527344b220
12
.idea/.gitignore
generated
vendored
Normal file
12
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module ron-pets
|
||||||
|
|
||||||
|
go 1.23.2
|
||||||
|
|
||||||
|
replace ron => ./../ron-gola
|
||||||
|
|
||||||
|
require ron v0.0.0-00010101000000-000000000000
|
||||||
|
|
||||||
|
require (
|
||||||
|
aidanwoods.dev/go-paseto v1.5.2 // indirect
|
||||||
|
aidanwoods.dev/go-result v0.1.0 // indirect
|
||||||
|
golang.org/x/crypto v0.29.0 // indirect
|
||||||
|
golang.org/x/sys v0.27.0 // indirect
|
||||||
|
)
|
||||||
8
go.sum
Normal file
8
go.sum
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
aidanwoods.dev/go-paseto v1.5.2 h1:9aKbCQQUeHCqis9Y6WPpJpM9MhEOEI5XBmfTkFMSF/o=
|
||||||
|
aidanwoods.dev/go-paseto v1.5.2/go.mod h1:7eEJZ98h2wFi5mavCcbKfv9h86oQwut4fLVeL/UBFnw=
|
||||||
|
aidanwoods.dev/go-result v0.1.0 h1:y/BMIRX6q3HwaorX1Wzrjo3WUdiYeyWbvGe18hKS3K8=
|
||||||
|
aidanwoods.dev/go-result v0.1.0/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM=
|
||||||
|
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||||
|
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||||
|
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||||
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
16
internal/config/config.go
Normal file
16
internal/config/config.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"aidanwoods.dev/go-paseto"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
Security Security
|
||||||
|
}
|
||||||
|
|
||||||
|
type Security struct {
|
||||||
|
AsymmetricKey paseto.V4AsymmetricSecretKey
|
||||||
|
PublicKey paseto.V4AsymmetricPublicKey
|
||||||
|
Duration time.Duration
|
||||||
|
}
|
||||||
55
internal/handlers/handlers.go
Normal file
55
internal/handlers/handlers.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"ron"
|
||||||
|
"ron-pets/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handlers struct {
|
||||||
|
app *config.App
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(app *config.App) *Handlers {
|
||||||
|
return &Handlers{
|
||||||
|
app: app,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hq *Handlers) HelloWorld(c *ron.Context) {
|
||||||
|
slog.Info("Dummy info message")
|
||||||
|
c.W.Write([]byte("hello world"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hq *Handlers) AnotherHelloWorld(c *ron.Context) {
|
||||||
|
c.W.Write([]byte("another hello world"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hq *Handlers) HelloWorldJSON(c *ron.Context) {
|
||||||
|
id := c.R.PathValue("id")
|
||||||
|
slog.Info("path value", "id", id)
|
||||||
|
|
||||||
|
c.JSON(200, ron.Data{"message": "hello world"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hq *Handlers) HelloWorldHTML(c *ron.Context) {
|
||||||
|
|
||||||
|
//pages := ron.Pages{
|
||||||
|
// TotalElements: len(elements),
|
||||||
|
// ElementsPerPage: 5,
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//pages.PaginationParams(c.R)
|
||||||
|
//elementsPaginated := pages.PaginateArray(elements)
|
||||||
|
//
|
||||||
|
//td := &ron.TemplateData{
|
||||||
|
// Data: ron.Data{"title": "hello world", "message": "hello world from html", "elements": elementsPaginated},
|
||||||
|
// Pages: pages,
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//c.HTML(200, "page.index.gohtml", td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hq *Handlers) ComponentHTML(c *ron.Context) {
|
||||||
|
c.HTML(200, "component.list.gohtml", nil)
|
||||||
|
}
|
||||||
82
internal/handlers/token.go
Normal file
82
internal/handlers/token.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"aidanwoods.dev/go-paseto"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"ron"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserPayload struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hq *Handlers) CreateToken(c *ron.Context) {
|
||||||
|
token := paseto.NewToken()
|
||||||
|
token.Set("userPayload", UserPayload{User: "pedro", Role: "admin"})
|
||||||
|
token.SetExpiration(time.Now().Add(hq.app.Security.Duration))
|
||||||
|
signed := token.V4Sign(hq.app.Security.AsymmetricKey, nil)
|
||||||
|
|
||||||
|
cookie := http.Cookie{
|
||||||
|
Name: "token",
|
||||||
|
Value: signed,
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: 3600,
|
||||||
|
HttpOnly: true,
|
||||||
|
Secure: true,
|
||||||
|
SameSite: http.SameSiteLaxMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
http.SetCookie(c.W, &cookie)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, ron.Data{"token": signed})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hq *Handlers) ValidateTokenAuthorization(c *ron.Context) {
|
||||||
|
signed := c.R.Header.Get("Authorization")
|
||||||
|
split := strings.Split(signed, "Bearer ")
|
||||||
|
slog.Info("signed", "signed", split[1])
|
||||||
|
parser := paseto.NewParser()
|
||||||
|
token, err := parser.ParseV4Public(hq.app.Security.PublicKey, split[1], nil)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("error", "err", err)
|
||||||
|
c.JSON(http.StatusUnauthorized, ron.Data{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var userPayload UserPayload
|
||||||
|
token.Get("userPayload", &userPayload)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, ron.Data{
|
||||||
|
"authorized": true,
|
||||||
|
"payload": userPayload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hq *Handlers) ValidateTokenCookie(c *ron.Context) {
|
||||||
|
cookie, err := c.R.Cookie("token")
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("error", "err", err)
|
||||||
|
c.JSON(http.StatusUnauthorized, ron.Data{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parser := paseto.NewParser()
|
||||||
|
token, err := parser.ParseV4Public(hq.app.Security.PublicKey, cookie.Value, nil)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("error", "err", err)
|
||||||
|
c.JSON(http.StatusUnauthorized, ron.Data{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var userPayload UserPayload
|
||||||
|
token.Get("userPayload", &userPayload)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, ron.Data{
|
||||||
|
"authorized": true,
|
||||||
|
"payload": userPayload,
|
||||||
|
})
|
||||||
|
}
|
||||||
94
main.go
Normal file
94
main.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"aidanwoods.dev/go-paseto"
|
||||||
|
"log/slog"
|
||||||
|
"ron"
|
||||||
|
"ron-pets/internal/config"
|
||||||
|
"ron-pets/internal/handlers"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SomethingElements struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
var elements = []SomethingElements{
|
||||||
|
{"element 1", "description 1"},
|
||||||
|
{"element 2", "description 2"},
|
||||||
|
{"element 3", "description 3"},
|
||||||
|
{"element 4", "description 4"},
|
||||||
|
{"element 5", "description 5"},
|
||||||
|
{"element 6", "description 6"},
|
||||||
|
{"element 7", "description 7"},
|
||||||
|
{"element 8", "description 8"},
|
||||||
|
{"element 9", "description 9"},
|
||||||
|
{"element 10", "description 10"},
|
||||||
|
{"element 11", "description 11"},
|
||||||
|
{"element 12", "description 12"},
|
||||||
|
{"element 13", "description 13"},
|
||||||
|
{"element 14", "description 14"},
|
||||||
|
{"element 15", "description 15"},
|
||||||
|
{"element 16", "description 16"},
|
||||||
|
{"element 17", "description 17"},
|
||||||
|
{"element 18", "description 18"},
|
||||||
|
{"element 19", "description 19"},
|
||||||
|
{"element 20", "description 20"},
|
||||||
|
{"element 21", "description 21"},
|
||||||
|
{"element 22", "description 22"},
|
||||||
|
{"element 23", "description 23"},
|
||||||
|
{"element 24", "description 24"},
|
||||||
|
{"element 25", "description 25"},
|
||||||
|
{"element 26", "description 26"},
|
||||||
|
{"element 27", "description 27"},
|
||||||
|
{"element 28", "description 28"},
|
||||||
|
{"element 29", "description 29"},
|
||||||
|
{"element 30", "description 30"},
|
||||||
|
{"element 31", "description 31"},
|
||||||
|
{"element 32", "description 32"},
|
||||||
|
{"element 33", "description 33"},
|
||||||
|
{"element 34", "description 34"},
|
||||||
|
{"element 35", "description 35"},
|
||||||
|
{"element 36", "description 36"},
|
||||||
|
{"element 37", "description 37"},
|
||||||
|
{"element 38", "description 38"},
|
||||||
|
{"element 39", "description 39"},
|
||||||
|
{"element 40", "description 40"},
|
||||||
|
{"element 41", "description 41"},
|
||||||
|
{"element 42", "description 42"},
|
||||||
|
{"element 43", "description 43"},
|
||||||
|
{"element 44", "description 44"},
|
||||||
|
{"element 45", "description 45"},
|
||||||
|
{"element 46", "description 46"},
|
||||||
|
{"element 47", "description 47"},
|
||||||
|
{"element 48", "description 48"},
|
||||||
|
{"element 49", "description 49"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var app *config.App
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := ron.New(func(e *ron.Engine) {
|
||||||
|
e.LogLevel = slog.LevelDebug
|
||||||
|
})
|
||||||
|
|
||||||
|
asymmetricKey, _ := paseto.NewV4AsymmetricSecretKeyFromHex("c3e9d207e752bd506a89e4ab09210f4b0100ddd31d3c815c0ab671f6885e9eae4def9afa5e982684329746a0718ea0a534fd9ce64813efee08c89ad6700a045d")
|
||||||
|
app = &config.App{
|
||||||
|
Security: config.Security{
|
||||||
|
AsymmetricKey: asymmetricKey,
|
||||||
|
PublicKey: asymmetricKey.Public(),
|
||||||
|
Duration: time.Second * 20,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("asymmetric key", "key", asymmetricKey.ExportHex())
|
||||||
|
|
||||||
|
h := handlers.New(app)
|
||||||
|
router(h, r)
|
||||||
|
|
||||||
|
err := r.Run(":8080")
|
||||||
|
if err != nil {
|
||||||
|
slog.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
20
middleware.go
Normal file
20
middleware.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func someMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
slog.Info("triggered middleware")
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func anotherMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
slog.Info("triggered another middleware")
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
17
router.go
Normal file
17
router.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ron"
|
||||||
|
"ron-pets/internal/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func router(h *handlers.Handlers, r *ron.Engine) {
|
||||||
|
htmlRender := ron.NewHTMLRender()
|
||||||
|
r.Render = htmlRender
|
||||||
|
|
||||||
|
r.Static("static", "static")
|
||||||
|
|
||||||
|
r.GET("/create", h.CreateToken)
|
||||||
|
r.GET("/validate", h.ValidateTokenAuthorization)
|
||||||
|
r.GET("/cookie", h.ValidateTokenCookie)
|
||||||
|
}
|
||||||
BIN
static/img/dummy.png
Normal file
BIN
static/img/dummy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
4
templates/component.list.gohtml
Normal file
4
templates/component.list.gohtml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<ul>
|
||||||
|
<li>elem1</li>
|
||||||
|
<li>elem2</li>
|
||||||
|
</ul>
|
||||||
52
templates/page.index.gohtml
Normal file
52
templates/page.index.gohtml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ .Data.title }}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<img src="/static/img/dummy.png">
|
||||||
|
|
||||||
|
{{ .Data.message }}
|
||||||
|
|
||||||
|
{{ range .Data.elements }}
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
{{ .Name }}
|
||||||
|
{{ .Description }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if not .Pages.HasPrevious }}
|
||||||
|
primera página
|
||||||
|
{{ else }}
|
||||||
|
<a href="html?page={{ .Pages.First }}">primera</a>
|
||||||
|
<a href="html?page={{ .Pages.Previous }}">anterior</a>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ range .Pages.PageRange 5 }}
|
||||||
|
{{ if .Active }}
|
||||||
|
<strong>{{ .Number }}</strong>
|
||||||
|
{{ else }}
|
||||||
|
<a href="html?page={{ .Number }}">{{ .Number }}</a>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if not .Pages.HasNext }}
|
||||||
|
última página
|
||||||
|
{{ else }}
|
||||||
|
<a href="html?page={{ .Pages.Next }}">siguiente</a>
|
||||||
|
<a href="html?page={{ .Pages.Last }}">última</a>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
Reference in New Issue
Block a user