Merge pull request #25 from msoedov/restore_session

Restore previous session
This commit is contained in:
Alex Miasoiedov 2018-01-18 20:25:25 -05:00 committed by GitHub
commit 39531f0825
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 60 deletions

1
.gitignore vendored
View file

@ -25,3 +25,4 @@ src/
slides/ slides/
main main
node_modules node_modules
hacker-slides

View file

@ -34,10 +34,10 @@ func Test(t *testing.T) {
g.Describe("App api", func() { g.Describe("App api", func() {
var cookie string 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", "/", "") w := client("GET", "/", "")
g.Assert(w.Code).Equal(200) g.Assert(w.Code).Equal(302)
cookie = w.HeaderMap.Get(Cookie) cookie = w.HeaderMap.Get(Cookie)
}) })

56
auth/auth.go Normal file
View file

@ -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))
}

25
files/query.go Normal file
View file

@ -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
}

81
main.go
View file

@ -1,65 +1,24 @@
package main package main
import ( import (
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strconv"
"strings" "strings"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
haikunator "github.com/atrox/haikunatorgo" haikunator "github.com/atrox/haikunatorgo"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/msoedov/hacker-slides/auth"
"github.com/msoedov/hacker-slides/files"
) )
const sessionHeader = "slide-session" const sessionHeader = "slide-session"
func Header(c *gin.Context, key string) string { func SlidePath(name string) string {
if values, _ := c.Request.Header[key]; len(values) > 0 { return fmt.Sprintf("slides/%s.md", name)
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 NewApp() *gin.Engine { func NewApp() *gin.Engine {
@ -68,22 +27,29 @@ func NewApp() *gin.Engine {
store := sessions.NewCookieStore([]byte("secret")) store := sessions.NewCookieStore([]byte("secret"))
r.Use(sessions.Sessions(sessionHeader, store)) r.Use(sessions.Sessions(sessionHeader, store))
r.Use(BasicAuth()) r.Use(auth.BasicAuth())
r.LoadHTMLGlob("templates/*.tmpl") r.LoadHTMLGlob("templates/*.tmpl")
r.Static("/static", "./static") r.Static("/static", "./static")
r.GET("/", func(c *gin.Context) { r.GET("/", func(c *gin.Context) {
isNew := c.Query("new")
fname := c.Param("name") latest := files.LatestFileIn("slides")
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"name": fname, "name": latest,
}).Info("Restore?") "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{ log.WithFields(log.Fields{
"path": path, "path": path,
}).Info("A new session") }).Info("A new session")
@ -91,7 +57,8 @@ func NewApp() *gin.Engine {
session.Set("name", path) session.Set("name", path)
session.Save() 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, "pubTo": path,
}) })
}) })
@ -176,7 +143,7 @@ func NewApp() *gin.Engine {
if strings.HasSuffix(name, ".md") { if strings.HasSuffix(name, ".md") {
name = name[0 : len(name)-3] name = name[0 : len(name)-3]
} }
path := fmt.Sprintf("slides/%s.md", name) path := SlidePath(name)
session := sessions.Default(c) session := sessions.Default(c)
session.Set("name", path) session.Set("name", path)
session.Save() session.Save()
@ -196,7 +163,7 @@ func NewApp() *gin.Engine {
if strings.HasSuffix(name, ".md") { if strings.HasSuffix(name, ".md") {
name = name[0 : len(name)-3] name = name[0 : len(name)-3]
} }
path := fmt.Sprintf("slides/%s.md", name) path := SlidePath(name)
session := sessions.Default(c) session := sessions.Default(c)
session.Set("name", path) session.Set("name", path)
session.Save() session.Save()

View file

@ -14,7 +14,8 @@
<body> <body>
<div id="edit-pane"> <div id="edit-pane">
<div id="controls"> <div id="controls">
<a href="/stash" target="_blank" onclick="save();"> Stash</a> | <a href="/?new=true" target="_blank" onclick="save();"> New</a> |
<a href="/stash" target="_blank" onclick="save();"> Stash</a> |
<a href="/published/{{ .pubTo}}" target="_blank" onclick="save();"> Present</a> | <a href="/published/{{ .pubTo}}" target="_blank" onclick="save();"> Present</a> |
<a href="/published/{{ .pubTo}}?print-pdf" target="_blank" onclick="save();"> Pdf</a> <a href="/published/{{ .pubTo}}?print-pdf" target="_blank" onclick="save();"> Pdf</a>
</div> </div>