A simple TicTacToe app with Golang backend and WebSockets gluing it all together.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
2.6 KiB

  1. package main
  2. import (
  3. "errors"
  4. "log"
  5. "net/http"
  6. "os"
  7. Cache "tictactoe-api/cache"
  8. "time"
  9. "github.com/gin-contrib/multitemplate"
  10. "github.com/gin-gonic/autotls"
  11. "github.com/gin-gonic/gin"
  12. )
  13. func setupRender() multitemplate.Renderer {
  14. r := multitemplate.NewRenderer()
  15. r.AddFromFiles("index", "dist/index.html")
  16. r.AddFromFiles("join", "dist/join.html")
  17. return r
  18. }
  19. func main() {
  20. hub := newHub()
  21. go hub.run()
  22. cache := Cache.NewCache(time.Minute*10, time.Minute)
  23. router := gin.Default()
  24. router.HTMLRender = setupRender()
  25. router.Static("/static", "dist")
  26. router.GET("/favicon.ico", func(c *gin.Context) {
  27. c.File("dist/favicon.ico")
  28. })
  29. router.GET("/ping", func(c *gin.Context) {
  30. c.String(http.StatusOK, "pong")
  31. })
  32. router.Any("/ws", func(c *gin.Context) {
  33. serveWs(hub, c.Writer, c.Request)
  34. })
  35. //
  36. //
  37. //
  38. router.GET("/join", func(c *gin.Context) {
  39. c.HTML(http.StatusOK, "join", gin.H{
  40. "title": "Play Game",
  41. })
  42. })
  43. router.GET("/game", func(c *gin.Context) {
  44. c.HTML(http.StatusOK, "index", gin.H{
  45. "title": "Start New Game",
  46. })
  47. })
  48. router.POST("/game", func(c *gin.Context) {
  49. var series Cache.Series
  50. err := c.BindJSON(&series)
  51. if err != nil {
  52. log.Printf("Error Binding Request %s\n", err.Error())
  53. }
  54. if len(series.ID) == 0 {
  55. err = errors.New("id is required")
  56. }
  57. if err != nil {
  58. c.JSON(http.StatusBadRequest, gin.H{
  59. "error": err.Error(),
  60. })
  61. return
  62. }
  63. cache.Set(series.ID, series, 0)
  64. c.JSON(http.StatusOK, series)
  65. })
  66. router.GET("/game/:seriesID", func(c *gin.Context) {
  67. seriesID := c.Params.ByName("seriesID")
  68. series, found := cache.Get(seriesID)
  69. if !found {
  70. c.Status(http.StatusNotFound)
  71. return
  72. }
  73. c.JSON(http.StatusOK, series)
  74. })
  75. router.POST("/game/:seriesID", func(c *gin.Context) {
  76. seriesID := c.Params.ByName("seriesID")
  77. series, found := cache.Get(seriesID)
  78. if !found {
  79. c.Status(http.StatusNotFound)
  80. return
  81. }
  82. var updateSeries Cache.Series
  83. err := c.BindJSON(&updateSeries)
  84. if err != nil {
  85. log.Printf("Error: %s\n", err)
  86. }
  87. if updateSeries.Turn != nil {
  88. series.Turn = updateSeries.Turn
  89. }
  90. if (Cache.Matrix{}) != updateSeries.Matrix {
  91. series.Matrix = updateSeries.Matrix
  92. }
  93. // if updateGame.Draw != nil {
  94. // game.Draw = updateGame.Draw
  95. // }
  96. // if updateGame.Winner != nil {
  97. // game.Winner = updateGame.Winner
  98. // }
  99. if updateSeries.LogTally != nil {
  100. series.Tally = append(series.Tally[1:], updateSeries.LogTally)
  101. }
  102. cache.Set(seriesID, series, 0)
  103. c.JSON(http.StatusOK, series)
  104. })
  105. //
  106. //
  107. //
  108. if os.Getenv("GIN_MODE") == "release" {
  109. log.Fatal(autotls.Run(router, "tictactoe.l3vi.co"))
  110. } else {
  111. log.Fatal(router.Run(":80"))
  112. }
  113. }