diff --git a/.gitignore b/.gitignore index 4fae73f..1479763 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ src/ slides/ main node_modules +hacker-slides diff --git a/app_test.go b/app_test.go index c6edb76..1a85d7d 100644 --- a/app_test.go +++ b/app_test.go @@ -34,10 +34,10 @@ func Test(t *testing.T) { g.Describe("App api", func() { var cookie string - g.It("Should return 200 on / ", func() { + g.It("Should return 302 on / to redirect to file name ", func() { w := client("GET", "/", "") - g.Assert(w.Code).Equal(200) + g.Assert(w.Code).Equal(302) cookie = w.HeaderMap.Get(Cookie) }) diff --git a/auth/auth.go b/auth/auth.go new file mode 100644 index 0000000..c384a40 --- /dev/null +++ b/auth/auth.go @@ -0,0 +1,56 @@ +package auth + +import ( + "encoding/base64" + "fmt" + "os" + "strconv" + + log "github.com/Sirupsen/logrus" + "github.com/gin-gonic/gin" +) + +func Header(c *gin.Context, key string) string { + if values, _ := c.Request.Header[key]; len(values) > 0 { + return values[0] + } + return "" +} + +func BasicAuth() gin.HandlerFunc { + realm := "Authorization Required" + realm = "Basic realm=" + strconv.Quote(realm) + user := os.Getenv("USER") + password := os.Getenv("PASSWORD") + enabled := isEnabled(user, password) + if enabled { + log.Warn("Auth mode enabled") + log.Warn(fmt.Sprintf("Visit http://%s:%s@0.0.0.0:8080", user, password)) + } + return func(c *gin.Context) { + header := Header(c, "Authorization") + if enabled && header != authorizationHeader(user, password) { + // Credentials doesn't match, we return 401 and abort handlers chain. + c.Header("WWW-Authenticate", realm) + c.AbortWithStatus(401) + return + } + c.Next() + } +} + +func isEnabled(user, password string) bool { + switch { + case user == "": + return false + case password == "": + return false + default: + return true + } +} + +func authorizationHeader(user, password string) string { + base := user + ":" + password + return "Basic " + base64.StdEncoding.EncodeToString([]byte(base)) +} diff --git a/files/query.go b/files/query.go new file mode 100644 index 0000000..060474f --- /dev/null +++ b/files/query.go @@ -0,0 +1,25 @@ +package files + +import ( + "io/ioutil" + "time" +) + +var epoch = time.Unix(1494505756, 0) + +func LatestFileIn(path string) (latest string) { + files, err := ioutil.ReadDir(path) + if err != nil { + return "" + } + latestTime := epoch + for _, f := range files { + path := f.Name() + pathModifiedAt := f.ModTime() + if pathModifiedAt.After(latestTime) { + latestTime = pathModifiedAt + latest = path + } + } + return +} diff --git a/main.go b/main.go index 39707ac..1f6ca11 100644 --- a/main.go +++ b/main.go @@ -1,65 +1,24 @@ package main import ( - "encoding/base64" "errors" "fmt" "io/ioutil" "os" - "strconv" "strings" log "github.com/Sirupsen/logrus" haikunator "github.com/atrox/haikunatorgo" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + "github.com/msoedov/hacker-slides/auth" + "github.com/msoedov/hacker-slides/files" ) const sessionHeader = "slide-session" -func Header(c *gin.Context, key string) string { - if values, _ := c.Request.Header[key]; len(values) > 0 { - return values[0] - } - return "" -} - -func BasicAuth() gin.HandlerFunc { - realm := "Authorization Required" - realm = "Basic realm=" + strconv.Quote(realm) - user := os.Getenv("USER") - password := os.Getenv("PASSWORD") - enabled := isEnabled(user, password) - if enabled { - log.Warn("Auth mode enabled") - log.Warn(fmt.Sprintf("Visit http://%s:%s@0.0.0.0:8080", user, password)) - } - return func(c *gin.Context) { - header := Header(c, "Authorization") - if enabled && header != authorizationHeader(user, password) { - // Credentials doesn't match, we return 401 and abort handlers chain. - c.Header("WWW-Authenticate", realm) - c.AbortWithStatus(401) - return - } - c.Next() - } -} - -func isEnabled(user, password string) bool { - switch { - case user == "": - return false - case password == "": - return false - default: - return true - } -} - -func authorizationHeader(user, password string) string { - base := user + ":" + password - return "Basic " + base64.StdEncoding.EncodeToString([]byte(base)) +func SlidePath(name string) string { + return fmt.Sprintf("slides/%s.md", name) } func NewApp() *gin.Engine { @@ -68,22 +27,29 @@ func NewApp() *gin.Engine { store := sessions.NewCookieStore([]byte("secret")) r.Use(sessions.Sessions(sessionHeader, store)) - r.Use(BasicAuth()) + r.Use(auth.BasicAuth()) r.LoadHTMLGlob("templates/*.tmpl") r.Static("/static", "./static") r.GET("/", func(c *gin.Context) { - - fname := c.Param("name") + isNew := c.Query("new") + latest := files.LatestFileIn("slides") log.WithFields(log.Fields{ - "name": fname, - }).Info("Restore?") + "name": latest, + "isNew": isNew, + }).Info("Restoring latest point") + + var path, name string + if latest == "" || isNew != "" { + haikunator := haikunator.New() + haikunator.TokenLength = 0 + name = haikunator.Haikunate() + } else { + name = strings.Replace(latest, ".md", "", 1) + } + path = SlidePath(name) - haikunator := haikunator.New() - haikunator.TokenLength = 0 - name := haikunator.Haikunate() - path := fmt.Sprintf("slides/%s.md", name) log.WithFields(log.Fields{ "path": path, }).Info("A new session") @@ -91,7 +57,8 @@ func NewApp() *gin.Engine { session.Set("name", path) session.Save() - c.HTML(200, "index.tmpl", gin.H{ + c.Writer.Header().Set("Location", fmt.Sprintf("/stash/edit/%s", name)) + c.HTML(302, "index.tmpl", gin.H{ "pubTo": path, }) }) @@ -176,7 +143,7 @@ func NewApp() *gin.Engine { if strings.HasSuffix(name, ".md") { name = name[0 : len(name)-3] } - path := fmt.Sprintf("slides/%s.md", name) + path := SlidePath(name) session := sessions.Default(c) session.Set("name", path) session.Save() @@ -196,7 +163,7 @@ func NewApp() *gin.Engine { if strings.HasSuffix(name, ".md") { name = name[0 : len(name)-3] } - path := fmt.Sprintf("slides/%s.md", name) + path := SlidePath(name) session := sessions.Default(c) session.Set("name", path) session.Save() diff --git a/templates/index.tmpl b/templates/index.tmpl index d607b1f..8da3312 100644 --- a/templates/index.tmpl +++ b/templates/index.tmpl @@ -14,7 +14,8 @@