websocket checkboxes
This commit is contained in:
20
go.mod
20
go.mod
@@ -1,13 +1,19 @@
|
||||
module knet.sk
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require github.com/labstack/echo/v4 v4.12.0
|
||||
go 1.23.4
|
||||
|
||||
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 (
|
||||
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/config v1.28.4 // 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/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/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/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/ssooidc v1.28.4 // 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/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-isatty v0.0.20 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
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/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
|
||||
9
go.sum
9
go.sum
@@ -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/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
|
||||
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/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/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/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
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/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
90
src/main.go
90
src/main.go
@@ -4,10 +4,14 @@ import (
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/labstack/gommon/log"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
type Templates struct {
|
||||
@@ -25,22 +29,42 @@ func NewTemplates() *Templates {
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Boxes map[int]bool
|
||||
}
|
||||
|
||||
func newPage() Page {
|
||||
return Page{}
|
||||
func newPage(boxes map[int]bool) Page {
|
||||
return Page{
|
||||
Boxes: boxes,
|
||||
}
|
||||
}
|
||||
|
||||
type Article struct {
|
||||
Content string
|
||||
Content template.HTML
|
||||
}
|
||||
|
||||
func newArticle(content string) Article {
|
||||
func newArticle(content template.HTML) Article {
|
||||
return Article{
|
||||
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() {
|
||||
e := echo.New()
|
||||
e.Renderer = NewTemplates()
|
||||
@@ -48,7 +72,11 @@ func main() {
|
||||
e.Logger.SetLevel(log.DEBUG)
|
||||
e.Use(middleware.Logger())
|
||||
|
||||
page := newPage()
|
||||
for i := 0; i < 1000; i++ {
|
||||
boxes[i] = false
|
||||
}
|
||||
|
||||
page := newPage(boxes)
|
||||
|
||||
e.Static("/images", "images")
|
||||
e.Static("/css", "css")
|
||||
@@ -57,16 +85,64 @@ func main() {
|
||||
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
|
||||
e.Logger.Info("Test log: /we-are-here endpoint called")
|
||||
|
||||
msg := getNotes(c, e.Logger)
|
||||
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)
|
||||
})
|
||||
|
||||
e.POST("/box/:id", updateBoxes)
|
||||
e.GET("/ws", initWs)
|
||||
|
||||
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
10
views/boxes.html
Normal 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}}
|
||||
@@ -7,19 +7,50 @@
|
||||
<title>Home</title>
|
||||
<script src="https://cdn.tailwindcss.com"></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>
|
||||
<body class="bg-gray-900 text-white font-sans">
|
||||
|
||||
|
||||
<main id="main" class="max-w-3xl mx-auto p-6 rounded-lg">
|
||||
<section>
|
||||
<p class="mt-4">
|
||||
Nothing to see here...
|
||||
</p>
|
||||
<div class="mt-4">
|
||||
{{template "boxes" .Boxes}}
|
||||
</div>
|
||||
</section>
|
||||
</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>
|
||||
</html>
|
||||
|
||||
<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}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user