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 }