websocket checkboxes

This commit is contained in:
JurajKubrican
2024-12-28 18:49:49 +01:00
parent 6aee06da49
commit 73054d4325
5 changed files with 144 additions and 30 deletions

20
go.mod
View File

@@ -1,13 +1,19 @@
module knet.sk module knet.sk
go 1.23.0 go 1.23.4
require github.com/labstack/echo/v4 v4.12.0 require (
github.com/aws/aws-sdk-go-v2 v1.32.4
github.com/aws/aws-sdk-go-v2/config v1.28.4
github.com/aws/aws-sdk-go-v2/service/s3 v1.67.0
github.com/labstack/echo/v4 v4.12.0
github.com/labstack/gommon v0.4.2
github.com/russross/blackfriday/v2 v2.1.0
golang.org/x/net v0.24.0
)
require ( require (
github.com/aws/aws-sdk-go-v2 v1.32.4 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect
github.com/aws/aws-sdk-go-v2/config v1.28.4 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.45 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.45 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.19 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.19 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 // indirect
@@ -18,20 +24,16 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.67.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.5 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.4 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.0 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.0 // indirect
github.com/aws/smithy-go v1.22.0 // indirect github.com/aws/smithy-go v1.22.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.22.0 // 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.19.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect

9
go.sum
View File

@@ -34,14 +34,10 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.0 h1:s7LRgBqhwLaxcocnAniBJp7gaAB+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.0/go.mod h1:9XEUty5v5UAsMiFOBJrNibZgwCeOma73jgGwwhgffa8= github.com/aws/aws-sdk-go-v2/service/sts v1.33.0/go.mod h1:9XEUty5v5UAsMiFOBJrNibZgwCeOma73jgGwwhgffa8=
github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
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=
@@ -53,7 +49,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -72,7 +69,5 @@ 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/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -4,10 +4,14 @@ import (
"html/template" "html/template"
"io" "io"
"net/http" "net/http"
"strconv"
"sync"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware" "github.com/labstack/echo/v4/middleware"
"github.com/labstack/gommon/log" "github.com/labstack/gommon/log"
"github.com/russross/blackfriday/v2"
"golang.org/x/net/websocket"
) )
type Templates struct { type Templates struct {
@@ -25,22 +29,42 @@ func NewTemplates() *Templates {
} }
type Page struct { type Page struct {
Boxes map[int]bool
} }
func newPage() Page { func newPage(boxes map[int]bool) Page {
return Page{} return Page{
Boxes: boxes,
}
} }
type Article struct { type Article struct {
Content string Content template.HTML
} }
func newArticle(content string) Article { func newArticle(content template.HTML) Article {
return Article{ return Article{
Content: content, Content: content,
} }
} }
var (
wsConnections = make(map[*websocket.Conn]bool)
wsMutex sync.Mutex
boxes = make(map[int]bool, 1000)
)
func broadcastMessage(message string) {
wsMutex.Lock()
defer wsMutex.Unlock()
for conn := range wsConnections {
if err := websocket.Message.Send(conn, message); err != nil {
conn.Close()
delete(wsConnections, conn)
}
}
}
func main() { func main() {
e := echo.New() e := echo.New()
e.Renderer = NewTemplates() e.Renderer = NewTemplates()
@@ -48,7 +72,11 @@ func main() {
e.Logger.SetLevel(log.DEBUG) e.Logger.SetLevel(log.DEBUG)
e.Use(middleware.Logger()) e.Use(middleware.Logger())
page := newPage() for i := 0; i < 1000; i++ {
boxes[i] = false
}
page := newPage(boxes)
e.Static("/images", "images") e.Static("/images", "images")
e.Static("/css", "css") e.Static("/css", "css")
@@ -57,16 +85,64 @@ func main() {
return c.Render(200, "index", page) return c.Render(200, "index", page)
}) })
e.GET("/we-are-here", func(c echo.Context) error { e.GET("/blog", func(c echo.Context) error {
// Log a test message // Log a test message
e.Logger.Info("Test log: /we-are-here endpoint called") e.Logger.Info("Test log: /we-are-here endpoint called")
msg := getNotes(c, e.Logger) msg := getNotes(c, e.Logger)
e.Logger.Info(msg) e.Logger.Info(msg)
article := newArticle(" content") htmlContent := blackfriday.Run([]byte(msg))
article := newArticle(template.HTML(string(htmlContent)))
return c.Render(http.StatusOK, "article", article) return c.Render(http.StatusOK, "article", article)
}) })
e.POST("/box/:id", updateBoxes)
e.GET("/ws", initWs)
e.Logger.Fatal(e.Start(":54321")) e.Logger.Fatal(e.Start(":54321"))
} }
func updateBoxes(c echo.Context) error {
id := c.Param("id")
checked := c.FormValue("checked") == "on"
index, err := strconv.Atoi(id)
if err != nil {
return c.String(http.StatusBadRequest, "Invalid ID")
}
boxes[index] = checked
message := strconv.Itoa(index)
if checked {
message += ":+"
} else {
message += ":-"
}
broadcastMessage(message)
return c.JSON(200, "[]")
}
func initWs(c echo.Context) error {
websocket.Handler(func(ws *websocket.Conn) {
wsMutex.Lock()
wsConnections[ws] = true
wsMutex.Unlock()
for {
var msg string
if err := websocket.Message.Receive(ws, &msg); err != nil {
wsMutex.Lock()
delete(wsConnections, ws)
wsMutex.Unlock()
ws.Close()
break
}
}
}).ServeHTTP(c.Response(), c.Request())
return nil
}

10
views/boxes.html Normal file
View File

@@ -0,0 +1,10 @@
{{block "boxes" .}}
{{range $index, $value := .}}
<input type="checkbox" id="box-{{$index}}" name="checked" {{if $value}}checked{{end}}
hx-post="/box/{{$index}}"
hx-vals='{"value": this.checked}'
hx-trigger="change"
hx-encoding="none"
>
{{end}}
{{end}}

View File

@@ -7,19 +7,50 @@
<title>Home</title> <title>Home</title>
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@1.9.5"></script> <script src="https://unpkg.com/htmx.org@1.9.5"></script>
<style>
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1;
}
</style>
</head> </head>
<body class="bg-gray-900 text-white font-sans"> <body class="bg-gray-900 text-white font-sans">
<main id="main" class="max-w-3xl mx-auto p-6 rounded-lg"> <main id="main" class="max-w-3xl mx-auto p-6 rounded-lg">
<section> <section>
<p class="mt-4"> <div class="mt-4">
Nothing to see here... {{template "boxes" .Boxes}}
</p> </div>
</section> </section>
</main> </main>
<footer class="bg-gray-800 text-center p-4">
<!-- link to github -->
<div class="mt-4">
<a href="https://github.com/JurajKubrican" class="text-blue-500">Github</a>
</div>
<!-- Link to linkedin -->
<div class="mt-4">
<a href="https://www.linkedin.com/in/juraj-kubri%C4%8Dan-614b3274/" class="text-blue-500">LinkedIn</a>
</div>
</footer>
</body> </body>
</html> </html>
{{end}}
<script>
const socket = new WebSocket("ws://" + window.location.host+"/ws");
socket.addEventListener("message", function(event) {
const parts = event.data.split(":");
document.getElementById("box-"+parts[0]).checked = parts[1] === "+";
console.log("box-"+parts[0])
});
</script>
{{end}}