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/
main
node_modules
hacker-slides

View file

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

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
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()

View file

@ -14,7 +14,8 @@
<body>
<div id="edit-pane">
<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}}?print-pdf" target="_blank" onclick="save();"> Pdf</a>
</div>