main.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package main
  2. import (
  3. "encoding/base64"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "strconv"
  9. "strings"
  10. log "github.com/Sirupsen/logrus"
  11. haikunator "github.com/atrox/haikunatorgo"
  12. "github.com/gin-contrib/sessions"
  13. "github.com/gin-gonic/gin"
  14. )
  15. const sessionHeader = "slide-session"
  16. func Header(c *gin.Context, key string) string {
  17. if values, _ := c.Request.Header[key]; len(values) > 0 {
  18. return values[0]
  19. }
  20. return ""
  21. }
  22. func BasicAuth() gin.HandlerFunc {
  23. realm := "Authorization Required"
  24. realm = "Basic realm=" + strconv.Quote(realm)
  25. user := os.Getenv("USER")
  26. password := os.Getenv("PASSWORD")
  27. enabled := isEnabled(user, password)
  28. if enabled {
  29. log.Warn("Auth mode enabled")
  30. log.Warn(fmt.Sprintf("Visit http://%s:%s@0.0.0.0:8080", user, password))
  31. }
  32. return func(c *gin.Context) {
  33. header := Header(c, "Authorization")
  34. if enabled && header != authorizationHeader(user, password) {
  35. // Credentials doesn't match, we return 401 and abort handlers chain.
  36. c.Header("WWW-Authenticate", realm)
  37. c.AbortWithStatus(401)
  38. return
  39. }
  40. c.Next()
  41. }
  42. }
  43. func isEnabled(user, password string) bool {
  44. switch {
  45. case user == "":
  46. return false
  47. case password == "":
  48. return false
  49. default:
  50. return true
  51. }
  52. }
  53. func authorizationHeader(user, password string) string {
  54. base := user + ":" + password
  55. return "Basic " + base64.StdEncoding.EncodeToString([]byte(base))
  56. }
  57. func NewApp() *gin.Engine {
  58. r := gin.Default()
  59. store := sessions.NewCookieStore([]byte("secret"))
  60. r.Use(sessions.Sessions(sessionHeader, store))
  61. r.Use(BasicAuth())
  62. r.LoadHTMLGlob("templates/*.tmpl")
  63. r.Static("/static", "./static")
  64. r.GET("/", func(c *gin.Context) {
  65. fname := c.Param("name")
  66. log.WithFields(log.Fields{
  67. "name": fname,
  68. }).Info("Restore?")
  69. haikunator := haikunator.New()
  70. haikunator.TokenLength = 0
  71. name := haikunator.Haikunate()
  72. path := fmt.Sprintf("slides/%s.md", name)
  73. log.WithFields(log.Fields{
  74. "path": path,
  75. }).Info("A new session")
  76. session := sessions.Default(c)
  77. session.Set("name", path)
  78. session.Save()
  79. c.HTML(200, "index.tmpl", gin.H{
  80. "pubTo": path,
  81. })
  82. })
  83. mustHaveSession := func(c *gin.Context) (string, error) {
  84. session := sessions.Default(c)
  85. val := session.Get("name")
  86. emptySession := errors.New("Emtpy session")
  87. if val == nil {
  88. c.String(400, "No context")
  89. return "", emptySession
  90. }
  91. log.WithFields(log.Fields{
  92. "path": val,
  93. }).Info("Got session")
  94. path, ok := val.(string)
  95. if !ok {
  96. c.String(400, "No context")
  97. return "", emptySession
  98. }
  99. return path, nil
  100. }
  101. r.GET("/slides.md", func(c *gin.Context) {
  102. path, err := mustHaveSession(c)
  103. if err != nil {
  104. return
  105. }
  106. if _, err := os.Stat(path); err != nil {
  107. // copy sample markdown file to the path
  108. body, err := ioutil.ReadFile("initial-slides.md")
  109. if err != nil {
  110. panic(err)
  111. }
  112. ioutil.WriteFile(path, body, 0644)
  113. c.String(200, string(body))
  114. return
  115. }
  116. body, err := ioutil.ReadFile(path)
  117. if err != nil {
  118. panic(err)
  119. }
  120. c.String(200, string(body))
  121. })
  122. r.PUT("/slides.md", func(c *gin.Context) {
  123. path, err := mustHaveSession(c)
  124. if err != nil {
  125. return
  126. }
  127. body, _ := ioutil.ReadAll(c.Request.Body)
  128. ioutil.WriteFile(path, body, 0644)
  129. log.WithFields(log.Fields{
  130. "size": len(body),
  131. "file": path,
  132. }).Info("Wrote to file")
  133. c.String(200, "")
  134. })
  135. r.GET("/stash", func(c *gin.Context) {
  136. files, err := ioutil.ReadDir("slides")
  137. if err != nil {
  138. log.Fatal(err)
  139. }
  140. var stash []string
  141. for _, file := range files {
  142. stash = append(stash, file.Name())
  143. }
  144. c.HTML(200, "stash.tmpl", gin.H{
  145. "stash": stash,
  146. })
  147. })
  148. r.GET("/stash/edit/:name", func(c *gin.Context) {
  149. name := c.Param("name")
  150. log.WithFields(log.Fields{
  151. "name": name,
  152. }).Info("Restore session?")
  153. if strings.HasSuffix(name, ".md") {
  154. name = name[0 : len(name)-3]
  155. }
  156. path := fmt.Sprintf("slides/%s.md", name)
  157. session := sessions.Default(c)
  158. session.Set("name", path)
  159. session.Save()
  160. c.HTML(200, "index.tmpl", gin.H{
  161. "pubTo": path,
  162. })
  163. })
  164. r.GET("/published/slides/:name", func(c *gin.Context) {
  165. name := c.Param("name")
  166. log.WithFields(log.Fields{
  167. "name": name,
  168. }).Info("Published")
  169. if strings.HasSuffix(name, ".md") {
  170. name = name[0 : len(name)-3]
  171. }
  172. path := fmt.Sprintf("slides/%s.md", name)
  173. session := sessions.Default(c)
  174. session.Set("name", path)
  175. session.Save()
  176. c.HTML(200, "slides.tmpl", gin.H{
  177. "pubTo": path,
  178. })
  179. })
  180. return r
  181. }
  182. func main() {
  183. r := NewApp()
  184. port := "8080"
  185. if len(os.Args) > 1 {
  186. port = os.Args[1]
  187. } else {
  188. envPort := os.Getenv("PORT")
  189. if len(envPort) > 0 {
  190. port = envPort
  191. }
  192. }
  193. log.Info("Started http://0.0.0.0:8080")
  194. r.Run(fmt.Sprintf(":%s", port))
  195. }