main.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "sort"
  8. "strings"
  9. log "github.com/Sirupsen/logrus"
  10. haikunator "github.com/atrox/haikunatorgo"
  11. "github.com/gin-contrib/sessions"
  12. "github.com/gin-gonic/gin"
  13. cache "github.com/hashicorp/golang-lru"
  14. "github.com/msoedov/hacker-slides/auth"
  15. "github.com/msoedov/hacker-slides/files"
  16. )
  17. const sessionHeader = "slide-session"
  18. func SlidePath(name string) string {
  19. return fmt.Sprintf("slides/%s.md", name)
  20. }
  21. func NewApp() *gin.Engine {
  22. r := gin.Default()
  23. store := sessions.NewCookieStore([]byte("secret"))
  24. arc, err := cache.NewARC(10)
  25. if err != nil {
  26. log.Fatalf("Failied to allocate cache %#v", err)
  27. }
  28. r.Use(sessions.Sessions(sessionHeader, store))
  29. r.Use(auth.BasicAuth())
  30. r.LoadHTMLGlob("templates/*.tmpl")
  31. r.Static("/static", "./static")
  32. r.Static("/images", "./slides/images")
  33. r.GET("/", func(c *gin.Context) {
  34. isNew := c.Query("new")
  35. latest := files.LatestFileIn("slides")
  36. log.WithFields(log.Fields{
  37. "name": latest,
  38. "isNew": isNew,
  39. }).Info("Restoring latest point")
  40. var path, name string
  41. if latest == "" || isNew != "" {
  42. haikunator := haikunator.New()
  43. haikunator.TokenLength = 0
  44. name = haikunator.Haikunate()
  45. } else {
  46. name = strings.Replace(latest, ".md", "", 1)
  47. }
  48. path = SlidePath(name)
  49. log.WithFields(log.Fields{
  50. "path": path,
  51. }).Info("A new session")
  52. session := sessions.Default(c)
  53. session.Set("name", path)
  54. session.Save()
  55. c.Writer.Header().Set("Location", fmt.Sprintf("/stash/edit/%s", name))
  56. c.HTML(302, "index.tmpl", gin.H{
  57. "pubTo": path,
  58. })
  59. })
  60. mustHaveSession := func(c *gin.Context) (string, error) {
  61. session := sessions.Default(c)
  62. val := session.Get("name")
  63. emptySession := errors.New("Emtpy session")
  64. if val == nil {
  65. c.String(400, "No context")
  66. return "", emptySession
  67. }
  68. log.WithFields(log.Fields{
  69. "path": val,
  70. }).Info("Got session")
  71. path, ok := val.(string)
  72. if !ok {
  73. c.String(400, "No context")
  74. return "", emptySession
  75. }
  76. return path, nil
  77. }
  78. r.GET("/slides.md", func(c *gin.Context) {
  79. path, err := mustHaveSession(c)
  80. if err != nil {
  81. return
  82. }
  83. if _, err := os.Stat(path); err != nil {
  84. // copy sample markdown file to the path
  85. body, err := ioutil.ReadFile("initial-slides.md")
  86. if err != nil {
  87. panic(err)
  88. }
  89. ioutil.WriteFile(path, body, 0644)
  90. c.String(200, string(body))
  91. return
  92. }
  93. var slide string
  94. cached, ok := arc.Get(path)
  95. if ok {
  96. slide = string(cached.([]byte))
  97. } else {
  98. body, err := ioutil.ReadFile(path)
  99. if err != nil {
  100. log.Errorf("Failied to read file %#v", err)
  101. c.Abort()
  102. return
  103. }
  104. slide = string(body)
  105. }
  106. c.String(200, slide)
  107. })
  108. r.PUT("/slides.md", func(c *gin.Context) {
  109. path, err := mustHaveSession(c)
  110. if err != nil {
  111. return
  112. }
  113. body, _ := ioutil.ReadAll(c.Request.Body)
  114. arc.Add(path, body)
  115. go ioutil.WriteFile(path, body, 0644)
  116. log.WithFields(log.Fields{
  117. "size": len(body),
  118. "file": path,
  119. }).Info("Async wrote to file")
  120. c.String(200, "")
  121. })
  122. r.GET("/stash", func(c *gin.Context) {
  123. files, err := ioutil.ReadDir("slides")
  124. if err != nil {
  125. log.Fatal(err)
  126. }
  127. sort.Slice(files, func(i, j int) bool {
  128. return files[i].ModTime().Unix() > files[j].ModTime().Unix()
  129. })
  130. var stash []string
  131. for _, file := range files {
  132. if file.IsDir() {
  133. continue
  134. }
  135. stash = append(stash, file.Name())
  136. }
  137. c.HTML(200, "stash.tmpl", gin.H{
  138. "stash": stash,
  139. })
  140. })
  141. r.GET("/stash/edit/:name", func(c *gin.Context) {
  142. name := c.Param("name")
  143. log.WithFields(log.Fields{
  144. "name": name,
  145. }).Info("Restore session?")
  146. if strings.HasSuffix(name, ".md") {
  147. name = name[0 : len(name)-3]
  148. }
  149. path := SlidePath(name)
  150. session := sessions.Default(c)
  151. session.Set("name", path)
  152. session.Save()
  153. c.HTML(200, "index.tmpl", gin.H{
  154. "pubTo": path,
  155. })
  156. })
  157. r.GET("/published/slides/:name", func(c *gin.Context) {
  158. name := c.Param("name")
  159. log.WithFields(log.Fields{
  160. "name": name,
  161. }).Info("Published")
  162. if strings.HasSuffix(name, ".md") {
  163. name = name[0 : len(name)-3]
  164. }
  165. path := SlidePath(name)
  166. session := sessions.Default(c)
  167. session.Set("name", path)
  168. session.Save()
  169. c.HTML(200, "slides.tmpl", gin.H{
  170. "pubTo": path,
  171. })
  172. })
  173. return r
  174. }
  175. func main() {
  176. r := NewApp()
  177. port := "8080"
  178. if len(os.Args) > 1 {
  179. port = os.Args[1]
  180. } else {
  181. envPort := os.Getenv("PORT")
  182. if len(envPort) > 0 {
  183. port = envPort
  184. }
  185. }
  186. log.Info("Started http://0.0.0.0:8080")
  187. r.Run(fmt.Sprintf(":%s", port))
  188. }