sqlite
This commit is contained in:
1
data/.gitignore
vendored
Normal file
1
data/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
data.db
|
||||||
1
go.mod
1
go.mod
@@ -5,6 +5,7 @@ go 1.23.4
|
|||||||
require (
|
require (
|
||||||
github.com/labstack/echo/v4 v4.12.0
|
github.com/labstack/echo/v4 v4.12.0
|
||||||
github.com/labstack/gommon v0.4.2
|
github.com/labstack/gommon v0.4.2
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.24
|
||||||
golang.org/x/net v0.24.0
|
golang.org/x/net v0.24.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -11,6 +11,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
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/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
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/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
|||||||
73
src/box-db.go
Normal file
73
src/box-db.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-sqlite3" // Import the SQLite driver
|
||||||
|
)
|
||||||
|
|
||||||
|
func initDb() *sql.DB {
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite3", "./data/data.db")
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a table
|
||||||
|
sqlStmt := `
|
||||||
|
CREATE TABLE IF NOT EXISTS boxes (id INTEGER PRIMARY KEY, value BOOLEAN);
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err = db.Exec(sqlStmt)
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Fatalf("%q: %s\n", err, sqlStmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBoxes() map[int]bool {
|
||||||
|
|
||||||
|
rows, err := db.Query("SELECT id, value FROM boxes")
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
boxes := make(map[int]bool, 1000)
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
boxes[i] = false
|
||||||
|
}
|
||||||
|
for rows.Next() {
|
||||||
|
var id int
|
||||||
|
var value bool
|
||||||
|
err = rows.Scan(&id, &value)
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Fatal(err)
|
||||||
|
}
|
||||||
|
boxes[id] = value
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
return boxes
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBox(id int, value bool) {
|
||||||
|
e.Logger.Info("START INSERT")
|
||||||
|
|
||||||
|
stmt, err := db.Prepare("INSERT OR REPLACE INTO boxes(id, value) VALUES(?, ?)")
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Logger.Info("PREPARED")
|
||||||
|
|
||||||
|
res, err := stmt.Exec(id, value)
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Fatal(err)
|
||||||
|
}
|
||||||
|
stmt.Close()
|
||||||
|
|
||||||
|
e.Logger.Info("INSERTED")
|
||||||
|
|
||||||
|
e.Logger.Info(res)
|
||||||
|
}
|
||||||
102
src/boxes.go
Normal file
102
src/boxes.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/labstack/gommon/log"
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
wsConnections = make(map[*websocket.Conn]bool)
|
||||||
|
wsMutex sync.Mutex
|
||||||
|
db = initDb()
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleWsError(err error, conn *websocket.Conn) {
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to handle websocket: %v", err)
|
||||||
|
conn.Close()
|
||||||
|
wsMutex.Lock()
|
||||||
|
defer wsMutex.Unlock()
|
||||||
|
delete(wsConnections, conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func broadcastMessage(message string) {
|
||||||
|
wsMutex.Lock()
|
||||||
|
defer wsMutex.Unlock()
|
||||||
|
|
||||||
|
for conn := range wsConnections {
|
||||||
|
err := websocket.Message.Send(conn, message)
|
||||||
|
handleWsError(err, conn)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMessage(index int, checked bool) string {
|
||||||
|
message := "u:" + strconv.Itoa(index)
|
||||||
|
if checked {
|
||||||
|
message += ":+"
|
||||||
|
} else {
|
||||||
|
message += ":-"
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMessage(msg string, ws *websocket.Conn) error {
|
||||||
|
if strings.Contains(msg, "u:") {
|
||||||
|
parts := strings.Split(msg, ":")
|
||||||
|
index, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
checked := parts[2] == "+"
|
||||||
|
|
||||||
|
message := formatMessage(index, checked)
|
||||||
|
broadcastMessage(message)
|
||||||
|
|
||||||
|
setBox(index, checked)
|
||||||
|
}
|
||||||
|
if strings.Contains(msg, "ping") {
|
||||||
|
len := len(wsConnections)
|
||||||
|
websocket.Message.Send(ws, "pong-"+strconv.Itoa(len))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initWs(c echo.Context) error {
|
||||||
|
websocket.Handler(func(ws *websocket.Conn) {
|
||||||
|
wsMutex.Lock()
|
||||||
|
wsConnections[ws] = true
|
||||||
|
wsMutex.Unlock()
|
||||||
|
|
||||||
|
boxes := getBoxes()
|
||||||
|
|
||||||
|
initialState := "i:"
|
||||||
|
for index, checked := range boxes {
|
||||||
|
if checked {
|
||||||
|
initialState += strconv.Itoa(index) + ";"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := websocket.Message.Send(ws, initialState)
|
||||||
|
handleWsError(err, ws)
|
||||||
|
|
||||||
|
for {
|
||||||
|
var msg string
|
||||||
|
err := websocket.Message.Receive(ws, &msg)
|
||||||
|
handleWsError(err, ws)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
handleMessage(msg, ws)
|
||||||
|
log.Infof("Received message: %s", msg)
|
||||||
|
}
|
||||||
|
}).ServeHTTP(c.Response(), c.Request())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
96
src/main.go
96
src/main.go
@@ -3,14 +3,10 @@ package main
|
|||||||
import (
|
import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"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"
|
||||||
"golang.org/x/net/websocket"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Templates struct {
|
type Templates struct {
|
||||||
@@ -38,113 +34,27 @@ func newPage(boxes map[int]bool) Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
wsConnections = make(map[*websocket.Conn]bool)
|
|
||||||
wsMutex sync.Mutex
|
|
||||||
boxes = make(map[int]bool, 1000)
|
|
||||||
e = echo.New()
|
e = echo.New()
|
||||||
)
|
)
|
||||||
|
|
||||||
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.Renderer = NewTemplates()
|
e.Renderer = NewTemplates()
|
||||||
|
|
||||||
e.Logger.SetLevel(log.DEBUG)
|
e.Logger.SetLevel(log.DEBUG)
|
||||||
e.Use(middleware.Logger())
|
e.Use(middleware.Logger())
|
||||||
|
|
||||||
for i := 0; i < 1000; i++ {
|
boxes := getBoxes()
|
||||||
boxes[i] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
page := newPage(boxes)
|
|
||||||
|
|
||||||
e.Static("/images", "images")
|
e.Static("/images", "images")
|
||||||
e.Static("/css", "css")
|
e.Static("/css", "css")
|
||||||
|
|
||||||
e.GET("/", func(c echo.Context) error {
|
e.GET("/", func(c echo.Context) error {
|
||||||
return c.Render(200, "index", page)
|
return c.Render(200, "index", newPage(boxes))
|
||||||
})
|
})
|
||||||
|
|
||||||
e.GET("/ws", initWs)
|
e.GET("/ws", initWs)
|
||||||
|
|
||||||
e.Logger.Fatal(e.Start(":54321"))
|
e.Logger.Fatal(e.Start(":54321"))
|
||||||
}
|
|
||||||
|
|
||||||
func sendUpdate(index int, checked bool) {
|
defer db.Close()
|
||||||
message := formatMessage(index, checked)
|
|
||||||
broadcastMessage(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatMessage(index int, checked bool) string {
|
|
||||||
message := "u:" + strconv.Itoa(index)
|
|
||||||
if checked {
|
|
||||||
message += ":+"
|
|
||||||
} else {
|
|
||||||
message += ":-"
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMessage(msg string, ws *websocket.Conn) error {
|
|
||||||
if strings.Contains(msg, "u:") {
|
|
||||||
parts := strings.Split(msg, ":")
|
|
||||||
index, err := strconv.Atoi(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
checked := parts[2] == "+"
|
|
||||||
|
|
||||||
boxes[index] = checked
|
|
||||||
|
|
||||||
sendUpdate(index, checked)
|
|
||||||
}
|
|
||||||
if strings.Contains(msg, "ping") {
|
|
||||||
len := len(wsConnections)
|
|
||||||
websocket.Message.Send(ws, "pong-"+strconv.Itoa(len))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initWs(c echo.Context) error {
|
|
||||||
websocket.Handler(func(ws *websocket.Conn) {
|
|
||||||
wsMutex.Lock()
|
|
||||||
wsConnections[ws] = true
|
|
||||||
wsMutex.Unlock()
|
|
||||||
|
|
||||||
initialState := "i:"
|
|
||||||
for index, checked := range boxes {
|
|
||||||
if checked {
|
|
||||||
initialState += strconv.Itoa(index) + ";"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := websocket.Message.Send(ws, initialState); err != nil {
|
|
||||||
log.Errorf("Failed to send initial data: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
var msg string
|
|
||||||
if err := websocket.Message.Receive(ws, &msg); err != nil {
|
|
||||||
wsMutex.Lock()
|
|
||||||
delete(wsConnections, ws)
|
|
||||||
wsMutex.Unlock()
|
|
||||||
ws.Close()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
handleMessage(msg, ws)
|
|
||||||
log.Infof("Received message: %s", msg)
|
|
||||||
}
|
|
||||||
}).ServeHTTP(c.Response(), c.Request())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user