package main
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 {
templates *template.Template
}
func (t *Templates) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
func NewTemplates() *Templates {
return &Templates{
templates: template.Must(template.ParseGlob("views/*.html")),
}
}
type Page struct {
Boxes map[int]bool
}
func newPage(boxes map[int]bool) Page {
return Page{
Boxes: boxes,
}
}
type Article struct {
Content template.HTML
}
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()
e.Logger.SetLevel(log.DEBUG)
e.Use(middleware.Logger())
for i := 0; i < 1000; i++ {
boxes[i] = false
}
page := newPage(boxes)
e.Static("/images", "images")
e.Static("/css", "css")
e.GET("/", func(c echo.Context) error {
return c.Render(200, "index", page)
})
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)
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
}