mirror of https://github.com/jlelse/GoBlog
Refactor and test authentication middleware
This commit is contained in:
parent
6429d64b0a
commit
c2dada5d50
|
@ -7,17 +7,22 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pquerna/otp/totp"
|
"github.com/pquerna/otp/totp"
|
||||||
"go.goblog.app/app/pkgs/contenttype"
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const loggedInKey contextKey = "loggedIn"
|
||||||
|
|
||||||
|
// Check if credentials are correct
|
||||||
func (a *goBlog) checkCredentials(username, password, totpPasscode string) bool {
|
func (a *goBlog) checkCredentials(username, password, totpPasscode string) bool {
|
||||||
return username == a.cfg.User.Nick &&
|
return username == a.cfg.User.Nick &&
|
||||||
password == a.cfg.User.Password &&
|
password == a.cfg.User.Password &&
|
||||||
(a.cfg.User.TOTP == "" || totp.Validate(totpPasscode, a.cfg.User.TOTP))
|
(a.cfg.User.TOTP == "" || totp.Validate(totpPasscode, a.cfg.User.TOTP))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if app passwords are correct
|
||||||
func (a *goBlog) checkAppPasswords(username, password string) bool {
|
func (a *goBlog) checkAppPasswords(username, password string) bool {
|
||||||
for _, apw := range a.cfg.User.AppPasswords {
|
for _, apw := range a.cfg.User.AppPasswords {
|
||||||
if apw.Username == username && apw.Password == password {
|
if apw.Username == username && apw.Password == password {
|
||||||
|
@ -27,31 +32,29 @@ func (a *goBlog) checkAppPasswords(username, password string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) jwtKey() []byte {
|
// Check if cookie is known and logged in
|
||||||
return []byte(a.cfg.Server.JWTSecret)
|
func (a *goBlog) checkLoginCookie(r *http.Request) bool {
|
||||||
|
ses, err := a.loginSessions.Get(r, "l")
|
||||||
|
if err == nil && ses != nil {
|
||||||
|
if login, ok := ses.Values["login"]; ok && login.(bool) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Middleware to force login
|
||||||
func (a *goBlog) authMiddleware(next http.Handler) http.Handler {
|
func (a *goBlog) authMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// 1. Check if already logged in
|
// Check if already logged in
|
||||||
if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn {
|
if a.isLoggedIn(r) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 2. Check BasicAuth (just for app passwords)
|
// Show login form
|
||||||
if username, password, ok := r.BasicAuth(); ok && a.checkAppPasswords(username, password) {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 3. Check login cookie
|
|
||||||
if a.checkLoginCookie(r) {
|
|
||||||
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), loggedInKey, true)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 4. Show login form
|
|
||||||
w.Header().Set("Cache-Control", "no-store,max-age=0")
|
w.Header().Set("Cache-Control", "no-store,max-age=0")
|
||||||
h, _ := json.Marshal(r.Header.Clone())
|
h, _ := json.Marshal(r.Header)
|
||||||
b, _ := io.ReadAll(io.LimitReader(r.Body, 2000000)) // Only allow 20 Megabyte
|
b, _ := io.ReadAll(io.LimitReader(r.Body, 20*1000*1000)) // Only allow 20 MB
|
||||||
_ = r.Body.Close()
|
_ = r.Body.Close()
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
// Maybe it's a form
|
// Maybe it's a form
|
||||||
|
@ -69,28 +72,7 @@ func (a *goBlog) authMiddleware(next http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const loggedInKey contextKey = "loggedIn"
|
// Middleware to check if the request is a login request
|
||||||
|
|
||||||
func (a *goBlog) checkLoggedIn(next http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
||||||
if a.checkLoginCookie(r) {
|
|
||||||
next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), loggedInKey, true)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
next.ServeHTTP(rw, r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *goBlog) checkLoginCookie(r *http.Request) bool {
|
|
||||||
ses, err := a.loginSessions.Get(r, "l")
|
|
||||||
if err == nil && ses != nil {
|
|
||||||
if login, ok := ses.Values["login"]; ok && login.(bool) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *goBlog) checkIsLogin(next http.Handler) http.Handler {
|
func (a *goBlog) checkIsLogin(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
if !a.checkLogin(rw, r) {
|
if !a.checkLogin(rw, r) {
|
||||||
|
@ -99,11 +81,12 @@ func (a *goBlog) checkIsLogin(next http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks login and returns true if it already served an error
|
||||||
func (a *goBlog) checkLogin(w http.ResponseWriter, r *http.Request) bool {
|
func (a *goBlog) checkLogin(w http.ResponseWriter, r *http.Request) bool {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if r.Header.Get(contentType) != contenttype.WWWForm {
|
if !strings.Contains(r.Header.Get(contentType), contenttype.WWWForm) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if r.FormValue("loginaction") != "login" {
|
if r.FormValue("loginaction") != "login" {
|
||||||
|
@ -131,22 +114,49 @@ func (a *goBlog) checkLogin(w http.ResponseWriter, r *http.Request) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
ses.Values["login"] = true
|
ses.Values["login"] = true
|
||||||
cookie, err := a.loginSessions.SaveGetCookie(r, w, ses)
|
err = a.loginSessions.Save(r, w, ses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
req.AddCookie(cookie)
|
|
||||||
// Serve original request
|
// Serve original request
|
||||||
|
setLoggedIn(req)
|
||||||
a.d.ServeHTTP(w, req)
|
a.d.ServeHTTP(w, req)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *goBlog) isLoggedIn(r *http.Request) bool {
|
||||||
|
// Check if context key already set
|
||||||
|
if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Check app passwords
|
||||||
|
if username, password, ok := r.BasicAuth(); ok && a.checkAppPasswords(username, password) {
|
||||||
|
setLoggedIn(r)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Check session cookie
|
||||||
|
if a.checkLoginCookie(r) {
|
||||||
|
setLoggedIn(r)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Not logged in
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set request context value
|
||||||
|
func setLoggedIn(r *http.Request) {
|
||||||
|
newRequest := r.WithContext(context.WithValue(r.Context(), loggedInKey, true))
|
||||||
|
(*r) = *newRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerFunc to redirect to home after login
|
||||||
// Need to set auth middleware!
|
// Need to set auth middleware!
|
||||||
func serveLogin(w http.ResponseWriter, r *http.Request) {
|
func serveLogin(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandlerFunc to delete login session and cookie
|
||||||
func (a *goBlog) serveLogout(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveLogout(w http.ResponseWriter, r *http.Request) {
|
||||||
if ses, err := a.loginSessions.Get(r, "l"); err == nil && ses != nil {
|
if ses, err := a.loginSessions.Get(r, "l"); err == nil && ses != nil {
|
||||||
_ = a.loginSessions.Delete(r, w, ses)
|
_ = a.loginSessions.Delete(r, w, ses)
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/justinas/alice"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_authMiddleware(t *testing.T) {
|
||||||
|
app := &goBlog{
|
||||||
|
cfg: &config{
|
||||||
|
Db: &configDb{
|
||||||
|
File: filepath.Join(t.TempDir(), "test.db"),
|
||||||
|
},
|
||||||
|
Server: &configServer{
|
||||||
|
PublicAddress: "https://example.com",
|
||||||
|
},
|
||||||
|
Blogs: map[string]*configBlog{
|
||||||
|
"en": {
|
||||||
|
Lang: "en",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultBlog: "en",
|
||||||
|
User: &configUser{
|
||||||
|
Nick: "test",
|
||||||
|
Password: "pass",
|
||||||
|
AppPasswords: []*configAppPassword{
|
||||||
|
{
|
||||||
|
Username: "app1",
|
||||||
|
Password: "pass1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = app.initDatabase(false)
|
||||||
|
app.initSessions()
|
||||||
|
_ = app.initTemplateStrings()
|
||||||
|
_ = app.initRendering()
|
||||||
|
|
||||||
|
app.d = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
_, _ = rw.Write([]byte("ABC Test"))
|
||||||
|
if app.isLoggedIn(r) {
|
||||||
|
_, _ = rw.Write([]byte("Logged in"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
h := alice.New(app.checkIsLogin, app.authMiddleware).Then(app.d)
|
||||||
|
|
||||||
|
t.Run("Login required", func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/abc", nil)
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
h.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
res := rec.Result()
|
||||||
|
resBody, _ := io.ReadAll(res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
|
resString := string(resBody)
|
||||||
|
|
||||||
|
// Login form
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
assert.Contains(t, res.Header.Get("Content-Type"), contenttype.HTML)
|
||||||
|
assert.Contains(t, resString, "name=loginmethod value=POST")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Login with app password", func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/abc", nil)
|
||||||
|
|
||||||
|
req.SetBasicAuth("app1", "pass1")
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
h.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
res := rec.Result()
|
||||||
|
resBody, _ := io.ReadAll(res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
|
resString := string(resBody)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
assert.Contains(t, resString, "ABC Test")
|
||||||
|
assert.Contains(t, resString, "Logged in")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Login with wrong app password", func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/abc", nil)
|
||||||
|
|
||||||
|
req.SetBasicAuth("app1", "wrong")
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
h.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
res := rec.Result()
|
||||||
|
resBody, _ := io.ReadAll(res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
|
resString := string(resBody)
|
||||||
|
|
||||||
|
// Login form
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
assert.Contains(t, res.Header.Get("Content-Type"), contenttype.HTML)
|
||||||
|
assert.Contains(t, resString, "name=loginmethod value=POST")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Login with form", func(t *testing.T) {
|
||||||
|
// First request
|
||||||
|
|
||||||
|
data := url.Values{}
|
||||||
|
data.Add("loginaction", "login")
|
||||||
|
data.Add("loginmethod", "GET")
|
||||||
|
data.Add("username", "test")
|
||||||
|
data.Add("password", "pass")
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/abc", strings.NewReader(data.Encode()))
|
||||||
|
req.Header.Add("Content-Type", contenttype.WWWForm)
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
h.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
res := rec.Result()
|
||||||
|
resBody, _ := io.ReadAll(res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
|
resString := string(resBody)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
assert.Contains(t, resString, "ABC Test")
|
||||||
|
assert.Contains(t, resString, "Logged in")
|
||||||
|
|
||||||
|
// Second request using cookie
|
||||||
|
|
||||||
|
require.Len(t, res.Cookies(), 1)
|
||||||
|
|
||||||
|
req = httptest.NewRequest(http.MethodPost, "/abc", nil)
|
||||||
|
req.AddCookie(res.Cookies()[0])
|
||||||
|
|
||||||
|
rec = httptest.NewRecorder()
|
||||||
|
|
||||||
|
h.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
res = rec.Result()
|
||||||
|
resBody, _ = io.ReadAll(res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
|
resString = string(resBody)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
assert.Contains(t, resString, "ABC Test")
|
||||||
|
assert.Contains(t, resString, "Logged in")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Login with wrong credentials", func(t *testing.T) {
|
||||||
|
data := url.Values{}
|
||||||
|
data.Add("loginaction", "login")
|
||||||
|
data.Add("loginmethod", "GET")
|
||||||
|
data.Add("username", "test")
|
||||||
|
data.Add("password", "wrong")
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/abc", strings.NewReader(data.Encode()))
|
||||||
|
req.Header.Add("Content-Type", contenttype.WWWForm)
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
h.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
res := rec.Result()
|
||||||
|
resBody, _ := io.ReadAll(res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
|
resString := string(resBody)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusUnauthorized, res.StatusCode)
|
||||||
|
assert.Contains(t, res.Header.Get("Content-Type"), contenttype.HTML)
|
||||||
|
assert.Contains(t, resString, "Incorrect credentials")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_setLoggedIn(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/abc", nil)
|
||||||
|
setLoggedIn(req)
|
||||||
|
loggedIn, ok := req.Context().Value(loggedInKey).(bool)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.True(t, loggedIn)
|
||||||
|
}
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kaorimatz/go-opml"
|
"github.com/kaorimatz/go-opml"
|
||||||
servertiming "github.com/mitchellh/go-server-timing"
|
|
||||||
"github.com/thoas/go-funk"
|
"github.com/thoas/go-funk"
|
||||||
"go.goblog.app/app/pkgs/contenttype"
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
)
|
)
|
||||||
|
@ -20,18 +19,16 @@ const defaultBlogrollPath = "/blogroll"
|
||||||
|
|
||||||
func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) {
|
||||||
blog := r.Context().Value(blogContextKey).(string)
|
blog := r.Context().Value(blogContextKey).(string)
|
||||||
t := servertiming.FromContext(r.Context()).NewMetric("bg").Start()
|
|
||||||
outlines, err, _ := a.blogrollCacheGroup.Do(blog, func() (interface{}, error) {
|
outlines, err, _ := a.blogrollCacheGroup.Do(blog, func() (interface{}, error) {
|
||||||
return a.getBlogrollOutlines(blog)
|
return a.getBlogrollOutlines(blog)
|
||||||
})
|
})
|
||||||
t.Stop()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get outlines: %v", err)
|
log.Printf("Failed to get outlines: %v", err)
|
||||||
a.serveError(w, r, "", http.StatusInternalServerError)
|
a.serveError(w, r, "", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if a.cfg.Cache != nil && a.cfg.Cache.Enable {
|
if a.cfg.Cache != nil && a.cfg.Cache.Enable {
|
||||||
setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration))
|
a.setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration))
|
||||||
}
|
}
|
||||||
c := a.cfg.Blogs[blog].Blogroll
|
c := a.cfg.Blogs[blog].Blogroll
|
||||||
can := a.getRelativePath(blog, defaultIfEmpty(c.Path, defaultBlogrollPath))
|
can := a.getRelativePath(blog, defaultIfEmpty(c.Path, defaultBlogrollPath))
|
||||||
|
@ -58,7 +55,7 @@ func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if a.cfg.Cache != nil && a.cfg.Cache.Enable {
|
if a.cfg.Cache != nil && a.cfg.Cache.Enable {
|
||||||
setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration))
|
a.setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration))
|
||||||
}
|
}
|
||||||
w.Header().Set(contentType, contenttype.XMLUTF8)
|
w.Header().Set(contentType, contenttype.XMLUTF8)
|
||||||
var opmlBytes bytes.Buffer
|
var opmlBytes bytes.Buffer
|
||||||
|
|
|
@ -5,8 +5,6 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
servertiming "github.com/mitchellh/go-server-timing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultBlogStatsPath = "/statistics"
|
const defaultBlogStatsPath = "/statistics"
|
||||||
|
@ -35,11 +33,9 @@ func (a *goBlog) serveBlogStats(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func (a *goBlog) serveBlogStatsTable(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveBlogStatsTable(w http.ResponseWriter, r *http.Request) {
|
||||||
blog := r.Context().Value(blogContextKey).(string)
|
blog := r.Context().Value(blogContextKey).(string)
|
||||||
t := servertiming.FromContext(r.Context()).NewMetric("bs").Start()
|
|
||||||
data, err, _ := a.blogStatsCacheGroup.Do(blog, func() (interface{}, error) {
|
data, err, _ := a.blogStatsCacheGroup.Do(blog, func() (interface{}, error) {
|
||||||
return a.db.getBlogStats(blog)
|
return a.db.getBlogStats(blog)
|
||||||
})
|
})
|
||||||
t.Stop()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
10
cache.go
10
cache.go
|
@ -40,7 +40,8 @@ func (a *goBlog) initCache() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cache) cacheMiddleware(next http.Handler) http.Handler {
|
func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler {
|
||||||
|
c := a.cache
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if c.c == nil {
|
if c.c == nil {
|
||||||
// No cache configured
|
// No cache configured
|
||||||
|
@ -56,7 +57,7 @@ func (c *cache) cacheMiddleware(next http.Handler) http.Handler {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn {
|
if a.isLoggedIn(r) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -179,7 +180,6 @@ func (c *cache) getCache(key string, next http.Handler, r *http.Request) (item *
|
||||||
result.Header.Del("Accept-Ranges")
|
result.Header.Del("Accept-Ranges")
|
||||||
result.Header.Del("ETag")
|
result.Header.Del("ETag")
|
||||||
result.Header.Del("Last-Modified")
|
result.Header.Del("Last-Modified")
|
||||||
result.Header.Del("Server-Timing")
|
|
||||||
// Create cache item
|
// Create cache item
|
||||||
item = &cacheItem{
|
item = &cacheItem{
|
||||||
expiration: exp,
|
expiration: exp,
|
||||||
|
@ -205,8 +205,8 @@ func (c *cache) purge() {
|
||||||
c.c.Clear()
|
c.c.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setInternalCacheExpirationHeader(w http.ResponseWriter, r *http.Request, expiration int) {
|
func (a *goBlog) setInternalCacheExpirationHeader(w http.ResponseWriter, r *http.Request, expiration int) {
|
||||||
if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn {
|
if a.isLoggedIn(r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set(cacheInternalExpirationHeader, strconv.Itoa(expiration))
|
w.Header().Set(cacheInternalExpirationHeader, strconv.Itoa(expiration))
|
||||||
|
|
26
captcha.go
26
captcha.go
|
@ -2,28 +2,38 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/dchest/captcha"
|
"github.com/dchest/captcha"
|
||||||
"go.goblog.app/app/pkgs/contenttype"
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const captchaSolvedKey contextKey = "captchaSolved"
|
||||||
|
|
||||||
func (a *goBlog) captchaMiddleware(next http.Handler) http.Handler {
|
func (a *goBlog) captchaMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// 1. Check Cookie
|
// Check if captcha already solved
|
||||||
|
if solved, ok := r.Context().Value(captchaSolvedKey).(bool); ok && solved {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check Cookie
|
||||||
ses, err := a.captchaSessions.Get(r, "c")
|
ses, err := a.captchaSessions.Get(r, "c")
|
||||||
if err == nil && ses != nil {
|
if err == nil && ses != nil {
|
||||||
if captcha, ok := ses.Values["captcha"]; ok && captcha.(bool) {
|
if captcha, ok := ses.Values["captcha"]; ok && captcha.(bool) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), captchaSolvedKey, true)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2. Show Captcha
|
// Show Captcha
|
||||||
h, _ := json.Marshal(r.Header.Clone())
|
w.Header().Set("Cache-Control", "no-store,max-age=0")
|
||||||
b, _ := io.ReadAll(io.LimitReader(r.Body, 2000000)) // Only allow 20 Megabyte
|
h, _ := json.Marshal(r.Header)
|
||||||
|
b, _ := io.ReadAll(io.LimitReader(r.Body, 20*1000*1000)) // Only allow 20 MB
|
||||||
_ = r.Body.Close()
|
_ = r.Body.Close()
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
// Maybe it's a form
|
// Maybe it's a form
|
||||||
|
@ -53,7 +63,7 @@ func (a *goBlog) checkCaptcha(w http.ResponseWriter, r *http.Request) bool {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if r.Header.Get(contentType) != contenttype.WWWForm {
|
if !strings.Contains(r.Header.Get(contentType), contenttype.WWWForm) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if r.FormValue("captchaaction") != "captcha" {
|
if r.FormValue("captchaaction") != "captcha" {
|
||||||
|
@ -77,12 +87,12 @@ func (a *goBlog) checkCaptcha(w http.ResponseWriter, r *http.Request) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
ses.Values["captcha"] = true
|
ses.Values["captcha"] = true
|
||||||
cookie, err := a.captchaSessions.SaveGetCookie(r, w, ses)
|
err = a.captchaSessions.Save(r, w, ses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
req.AddCookie(cookie)
|
req = req.WithContext(context.WithValue(req.Context(), captchaSolvedKey, true))
|
||||||
}
|
}
|
||||||
// Serve original request
|
// Serve original request
|
||||||
a.d.ServeHTTP(w, req)
|
a.d.ServeHTTP(w, req)
|
||||||
|
|
|
@ -8,9 +8,9 @@ func (a *goBlog) serveCustomPage(w http.ResponseWriter, r *http.Request) {
|
||||||
page := r.Context().Value(customPageContextKey).(*configCustomPage)
|
page := r.Context().Value(customPageContextKey).(*configCustomPage)
|
||||||
if a.cfg.Cache != nil && a.cfg.Cache.Enable && page.Cache {
|
if a.cfg.Cache != nil && a.cfg.Cache.Enable && page.Cache {
|
||||||
if page.CacheExpiration != 0 {
|
if page.CacheExpiration != 0 {
|
||||||
setInternalCacheExpirationHeader(w, r, page.CacheExpiration)
|
a.setInternalCacheExpirationHeader(w, r, page.CacheExpiration)
|
||||||
} else {
|
} else {
|
||||||
setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration))
|
a.setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a.render(w, r, page.Template, &renderData{
|
a.render(w, r, page.Template, &renderData{
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
create index sessions_exp on sessions (expires);
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"embed"
|
"embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
@ -82,7 +81,6 @@ var leafletFiles embed.FS
|
||||||
func (a *goBlog) serveLeaflet(basePath string) http.HandlerFunc {
|
func (a *goBlog) serveLeaflet(basePath string) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
fileName := strings.TrimPrefix(r.URL.Path, basePath)
|
fileName := strings.TrimPrefix(r.URL.Path, basePath)
|
||||||
log.Println(basePath, fileName)
|
|
||||||
fb, err := leafletFiles.ReadFile(fileName)
|
fb, err := leafletFiles.ReadFile(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serve404(w, r)
|
a.serve404(w, r)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -20,7 +20,6 @@ require (
|
||||||
github.com/go-chi/chi/v5 v5.0.3
|
github.com/go-chi/chi/v5 v5.0.3
|
||||||
github.com/go-fed/httpsig v1.1.0
|
github.com/go-fed/httpsig v1.1.0
|
||||||
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
||||||
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f // indirect
|
|
||||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect
|
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe // indirect
|
github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe // indirect
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
|
@ -38,7 +37,6 @@ require (
|
||||||
github.com/lopezator/migrator v0.3.0
|
github.com/lopezator/migrator v0.3.0
|
||||||
github.com/mattn/go-sqlite3 v1.14.8
|
github.com/mattn/go-sqlite3 v1.14.8
|
||||||
github.com/microcosm-cc/bluemonday v1.0.15
|
github.com/microcosm-cc/bluemonday v1.0.15
|
||||||
github.com/mitchellh/go-server-timing v1.0.1
|
|
||||||
github.com/paulmach/go.geojson v1.4.0
|
github.com/paulmach/go.geojson v1.4.0
|
||||||
github.com/pquerna/otp v1.3.0
|
github.com/pquerna/otp v1.3.0
|
||||||
github.com/schollz/sqlite3dump v1.3.0
|
github.com/schollz/sqlite3dump v1.3.0
|
||||||
|
|
36
go.sum
36
go.sum
|
@ -1,4 +1,3 @@
|
||||||
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
@ -68,7 +67,6 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
|
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
|
||||||
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
|
||||||
github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 h1:t8KYCwSKsOEZBFELI4Pn/phbp38iJ1RRAkDFNin1aak=
|
github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 h1:t8KYCwSKsOEZBFELI4Pn/phbp38iJ1RRAkDFNin1aak=
|
||||||
github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
@ -108,14 +106,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/felixge/httpsnoop v1.0.0/go.mod h1:3+D9sFq0ahK/JeJPhCBUV1xlf4/eIYrUQaxulT0VzX8=
|
|
||||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
|
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
|
||||||
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
|
||||||
github.com/gchaincl/sqlhooks/v2 v2.0.1 h1:j9WZAq1Tx/xngDfEdsgUww+o9iY3rLOYGYhYWcFxdmI=
|
github.com/gchaincl/sqlhooks/v2 v2.0.1 h1:j9WZAq1Tx/xngDfEdsgUww+o9iY3rLOYGYhYWcFxdmI=
|
||||||
github.com/gchaincl/sqlhooks/v2 v2.0.1/go.mod h1:Qv7HXjGB9TehamVK52yW5H+a0RRhprIj3ESTcWOG2jw=
|
github.com/gchaincl/sqlhooks/v2 v2.0.1/go.mod h1:Qv7HXjGB9TehamVK52yW5H+a0RRhprIj3ESTcWOG2jw=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
@ -129,19 +124,14 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
|
|
||||||
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg=
|
|
||||||
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU=
|
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU=
|
||||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
@ -167,10 +157,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
@ -200,7 +188,6 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe
|
||||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
@ -214,7 +201,6 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
@ -231,7 +217,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
@ -240,7 +225,6 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jlaffaye/ftp v0.0.0-20210307004419-5d4190119067 h1:P2S26PMwXl8+ZGuOG3C69LG4be5vHafUayZm9VPw3tU=
|
github.com/jlaffaye/ftp v0.0.0-20210307004419-5d4190119067 h1:P2S26PMwXl8+ZGuOG3C69LG4be5vHafUayZm9VPw3tU=
|
||||||
|
@ -262,7 +246,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
@ -283,13 +266,10 @@ github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lopezator/migrator v0.3.0 h1:VW/rR+J8NYwPdkBxjrFdjwejpgvP59LbmANJxXuNbuk=
|
github.com/lopezator/migrator v0.3.0 h1:VW/rR+J8NYwPdkBxjrFdjwejpgvP59LbmANJxXuNbuk=
|
||||||
github.com/lopezator/migrator v0.3.0/go.mod h1:bpVAVPkWSvTw8ya2Pk7E/KiNAyDWNImgivQY79o8/8I=
|
github.com/lopezator/migrator v0.3.0/go.mod h1:bpVAVPkWSvTw8ya2Pk7E/KiNAyDWNImgivQY79o8/8I=
|
||||||
github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
|
||||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
@ -302,13 +282,10 @@ github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3Zpq
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-server-timing v1.0.1 h1:f00/aIe8T3MrnLhQHu3tSWvnwc5GV/p5eutuu3hF/tE=
|
|
||||||
github.com/mitchellh/go-server-timing v1.0.1/go.mod h1:Mo6GKi9FSLwWFAMn3bqVPWe20y5ri5QGQuO9D9MCOxk=
|
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
@ -319,7 +296,6 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/paulmach/go.geojson v1.4.0 h1:5x5moCkCtDo5x8af62P9IOAYGQcYHtxz2QJ3x1DoCgY=
|
github.com/paulmach/go.geojson v1.4.0 h1:5x5moCkCtDo5x8af62P9IOAYGQcYHtxz2QJ3x1DoCgY=
|
||||||
github.com/paulmach/go.geojson v1.4.0/go.mod h1:YaKx1hKpWF+T2oj2lFJPsW/t1Q5e1jQI61eoQSTwpIs=
|
github.com/paulmach/go.geojson v1.4.0/go.mod h1:YaKx1hKpWF+T2oj2lFJPsW/t1Q5e1jQI61eoQSTwpIs=
|
||||||
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
|
||||||
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
@ -349,19 +325,14 @@ github.com/snabb/diagio v1.0.0 h1:kovhQ1rDXoEbmpf/T5N2sUp2iOdxEg+TcqzbYVHV2V0=
|
||||||
github.com/snabb/diagio v1.0.0/go.mod h1:ZyGaWFhfBVqstGUw6laYetzeTwZ2xxVPqTALx1QQa1w=
|
github.com/snabb/diagio v1.0.0/go.mod h1:ZyGaWFhfBVqstGUw6laYetzeTwZ2xxVPqTALx1QQa1w=
|
||||||
github.com/snabb/sitemap v1.0.0 h1:7vJeNPAaaj7fQSRS3WYuJHzUjdnhLdSLLpvVtnhbzC0=
|
github.com/snabb/sitemap v1.0.0 h1:7vJeNPAaaj7fQSRS3WYuJHzUjdnhLdSLLpvVtnhbzC0=
|
||||||
github.com/snabb/sitemap v1.0.0/go.mod h1:Id8uz1+WYdiNmSjEi4BIvL5UwNPYLsTHzRbjmDwNDzA=
|
github.com/snabb/sitemap v1.0.0/go.mod h1:Id8uz1+WYdiNmSjEi4BIvL5UwNPYLsTHzRbjmDwNDzA=
|
||||||
github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
|
||||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
|
||||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
|
||||||
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
|
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
|
||||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
@ -496,7 +467,6 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds=
|
golang.org/x/net v0.0.0-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds=
|
||||||
golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -509,7 +479,6 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
|
||||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -579,7 +548,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -638,7 +606,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
@ -668,7 +635,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
@ -710,7 +676,6 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
|
||||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||||
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -745,7 +710,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
|
64
http.go
64
http.go
|
@ -17,7 +17,6 @@ import (
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/justinas/alice"
|
"github.com/justinas/alice"
|
||||||
servertiming "github.com/mitchellh/go-server-timing"
|
|
||||||
"golang.org/x/crypto/acme"
|
"golang.org/x/crypto/acme"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -38,17 +37,15 @@ func (a *goBlog) startServer() (err error) {
|
||||||
}
|
}
|
||||||
a.d = fixHTTPHandler(router)
|
a.d = fixHTTPHandler(router)
|
||||||
// Set basic middlewares
|
// Set basic middlewares
|
||||||
var finalHandler http.Handler = a.d
|
h := alice.New()
|
||||||
if a.cfg.Server.PublicHTTPS || a.cfg.Server.SecurityHeaders {
|
|
||||||
finalHandler = a.securityHeaders(finalHandler)
|
|
||||||
}
|
|
||||||
finalHandler = servertiming.Middleware(finalHandler, nil)
|
|
||||||
finalHandler = middleware.Heartbeat("/ping")(finalHandler)
|
|
||||||
finalHandler = middleware.Compress(flate.DefaultCompression)(finalHandler)
|
|
||||||
finalHandler = middleware.Recoverer(finalHandler)
|
|
||||||
if a.cfg.Server.Logging {
|
if a.cfg.Server.Logging {
|
||||||
finalHandler = a.logMiddleware(finalHandler)
|
h = h.Append(a.logMiddleware)
|
||||||
}
|
}
|
||||||
|
h = h.Append(middleware.Recoverer, middleware.Compress(flate.DefaultCompression), middleware.Heartbeat("/ping"))
|
||||||
|
if a.cfg.Server.PublicHTTPS || a.cfg.Server.SecurityHeaders {
|
||||||
|
h = h.Append(a.securityHeaders)
|
||||||
|
}
|
||||||
|
finalHandler := h.Then(a.d)
|
||||||
// Start Onion service
|
// Start Onion service
|
||||||
if a.cfg.Server.Tor {
|
if a.cfg.Server.Tor {
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -154,10 +151,9 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
r.Use(noIndexHeader)
|
r.Use(noIndexHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login middleware etc.
|
// Login and captcha middleware
|
||||||
r.Use(a.checkIsLogin)
|
r.Use(a.checkIsLogin)
|
||||||
r.Use(a.checkIsCaptcha)
|
r.Use(a.checkIsCaptcha)
|
||||||
r.Use(a.checkLoggedIn)
|
|
||||||
|
|
||||||
// Logout
|
// Logout
|
||||||
r.With(a.authMiddleware).Get("/login", serveLogin)
|
r.With(a.authMiddleware).Get("/login", serveLogin)
|
||||||
|
@ -187,7 +183,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
r.Post("/{blog}/inbox", a.apHandleInbox)
|
r.Post("/{blog}/inbox", a.apHandleInbox)
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(a.cache.cacheMiddleware)
|
r.Use(a.cacheMiddleware)
|
||||||
r.Get("/.well-known/webfinger", a.apHandleWebfinger)
|
r.Get("/.well-known/webfinger", a.apHandleWebfinger)
|
||||||
r.Get("/.well-known/host-meta", handleWellKnownHostMeta)
|
r.Get("/.well-known/host-meta", handleWellKnownHostMeta)
|
||||||
r.Get("/.well-known/nodeinfo", a.serveNodeInfoDiscover)
|
r.Get("/.well-known/nodeinfo", a.serveNodeInfoDiscover)
|
||||||
|
@ -236,7 +232,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
r.Handle("/captcha/*", captcha.Server(500, 250))
|
r.Handle("/captcha/*", captcha.Server(500, 250))
|
||||||
|
|
||||||
// Short paths
|
// Short paths
|
||||||
r.With(privateModeHandler...).With(a.cache.cacheMiddleware).Get("/s/{id:[0-9a-fA-F]+}", a.redirectToLongPath)
|
r.With(privateModeHandler...).With(a.cacheMiddleware).Get("/s/{id:[0-9a-fA-F]+}", a.redirectToLongPath)
|
||||||
|
|
||||||
for blog, blogConfig := range a.cfg.Blogs {
|
for blog, blogConfig := range a.cfg.Blogs {
|
||||||
sbm := middleware.WithValue(blogContextKey, blog)
|
sbm := middleware.WithValue(blogContextKey, blog)
|
||||||
|
@ -244,7 +240,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
// Sections
|
// Sections
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(a.cache.cacheMiddleware, sbm)
|
r.Use(a.cacheMiddleware, sbm)
|
||||||
for _, section := range blogConfig.Sections {
|
for _, section := range blogConfig.Sections {
|
||||||
if section.Name != "" {
|
if section.Name != "" {
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
|
@ -264,7 +260,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
// Taxonomies
|
// Taxonomies
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(a.cache.cacheMiddleware, sbm)
|
r.Use(a.cacheMiddleware, sbm)
|
||||||
for _, taxonomy := range blogConfig.Taxonomies {
|
for _, taxonomy := range blogConfig.Taxonomies {
|
||||||
if taxonomy.Name != "" {
|
if taxonomy.Name != "" {
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
|
@ -285,7 +281,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
photoPath := blogConfig.getRelativePath(defaultIfEmpty(pc.Path, defaultPhotosPath))
|
photoPath := blogConfig.getRelativePath(defaultIfEmpty(pc.Path, defaultPhotosPath))
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(a.cache.cacheMiddleware, sbm, middleware.WithValue(indexConfigKey, &indexConfig{
|
r.Use(a.cacheMiddleware, sbm, middleware.WithValue(indexConfigKey, &indexConfig{
|
||||||
path: photoPath,
|
path: photoPath,
|
||||||
parameter: pc.Parameter,
|
parameter: pc.Parameter,
|
||||||
title: pc.Title,
|
title: pc.Title,
|
||||||
|
@ -308,7 +304,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
))
|
))
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(a.cache.cacheMiddleware)
|
r.Use(a.cacheMiddleware)
|
||||||
r.Get("/", a.serveSearch)
|
r.Get("/", a.serveSearch)
|
||||||
r.Post("/", a.serveSearch)
|
r.Post("/", a.serveSearch)
|
||||||
searchResultPath := "/" + searchPlaceholder
|
searchResultPath := "/" + searchPlaceholder
|
||||||
|
@ -316,7 +312,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
r.Get(searchResultPath+feedPath, a.serveSearchResult)
|
r.Get(searchResultPath+feedPath, a.serveSearchResult)
|
||||||
r.Get(searchResultPath+paginationPath, a.serveSearchResult)
|
r.Get(searchResultPath+paginationPath, a.serveSearchResult)
|
||||||
})
|
})
|
||||||
r.With(a.cache.cacheMiddleware).Get("/opensearch.xml", a.serveOpenSearch)
|
r.With(a.cacheMiddleware).Get("/opensearch.xml", a.serveOpenSearch)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +321,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
statsPath := blogConfig.getRelativePath(defaultIfEmpty(bsc.Path, defaultBlogStatsPath))
|
statsPath := blogConfig.getRelativePath(defaultIfEmpty(bsc.Path, defaultBlogStatsPath))
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(a.cache.cacheMiddleware, sbm)
|
r.Use(a.cacheMiddleware, sbm)
|
||||||
r.Get(statsPath, a.serveBlogStats)
|
r.Get(statsPath, a.serveBlogStats)
|
||||||
r.Get(statsPath+".table.html", a.serveBlogStatsTable)
|
r.Get(statsPath+".table.html", a.serveBlogStatsTable)
|
||||||
})
|
})
|
||||||
|
@ -334,7 +330,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
// Date archives
|
// Date archives
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(a.cache.cacheMiddleware, sbm)
|
r.Use(a.cacheMiddleware, sbm)
|
||||||
|
|
||||||
yearRegex := `/{year:x|\d\d\d\d}`
|
yearRegex := `/{year:x|\d\d\d\d}`
|
||||||
monthRegex := `/{month:x|\d\d}`
|
monthRegex := `/{month:x|\d\d}`
|
||||||
|
@ -361,9 +357,9 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(sbm)
|
r.Use(sbm)
|
||||||
r.With(a.checkActivityStreamsRequest, a.cache.cacheMiddleware).Get(blogConfig.getRelativePath(""), a.serveHome)
|
r.With(a.checkActivityStreamsRequest, a.cacheMiddleware).Get(blogConfig.getRelativePath(""), a.serveHome)
|
||||||
r.With(a.cache.cacheMiddleware).Get(blogConfig.getRelativePath("")+feedPath, a.serveHome)
|
r.With(a.cacheMiddleware).Get(blogConfig.getRelativePath("")+feedPath, a.serveHome)
|
||||||
r.With(a.cache.cacheMiddleware).Get(blogConfig.getRelativePath(paginationPath), a.serveHome)
|
r.With(a.cacheMiddleware).Get(blogConfig.getRelativePath(paginationPath), a.serveHome)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +367,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
for _, cp := range blogConfig.CustomPages {
|
for _, cp := range blogConfig.CustomPages {
|
||||||
scp := middleware.WithValue(customPageContextKey, cp)
|
scp := middleware.WithValue(customPageContextKey, cp)
|
||||||
if cp.Cache {
|
if cp.Cache {
|
||||||
r.With(privateModeHandler...).With(a.cache.cacheMiddleware, sbm, scp).Get(cp.Path, a.serveCustomPage)
|
r.With(privateModeHandler...).With(a.cacheMiddleware, sbm, scp).Get(cp.Path, a.serveCustomPage)
|
||||||
} else {
|
} else {
|
||||||
r.With(privateModeHandler...).With(sbm, scp).Get(cp.Path, a.serveCustomPage)
|
r.With(privateModeHandler...).With(sbm, scp).Get(cp.Path, a.serveCustomPage)
|
||||||
}
|
}
|
||||||
|
@ -407,7 +403,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
r.Route(commentsPath, func(r chi.Router) {
|
r.Route(commentsPath, func(r chi.Router) {
|
||||||
r.Use(sbm, middleware.WithValue(pathContextKey, commentsPath))
|
r.Use(sbm, middleware.WithValue(pathContextKey, commentsPath))
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.With(a.cache.cacheMiddleware, noIndexHeader).Get("/{id:[0-9]+}", a.serveComment)
|
r.With(a.cacheMiddleware, noIndexHeader).Get("/{id:[0-9]+}", a.serveComment)
|
||||||
r.With(a.captchaMiddleware).Post("/", a.createComment)
|
r.With(a.captchaMiddleware).Post("/", a.createComment)
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
// Admin
|
// Admin
|
||||||
|
@ -424,7 +420,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
brPath := blogConfig.getRelativePath(defaultIfEmpty(brConfig.Path, defaultBlogrollPath))
|
brPath := blogConfig.getRelativePath(defaultIfEmpty(brConfig.Path, defaultBlogrollPath))
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(a.cache.cacheMiddleware, sbm)
|
r.Use(a.cacheMiddleware, sbm)
|
||||||
r.Get(brPath, a.serveBlogroll)
|
r.Get(brPath, a.serveBlogroll)
|
||||||
r.Get(brPath+".opml", a.serveBlogrollExport)
|
r.Get(brPath+".opml", a.serveBlogrollExport)
|
||||||
})
|
})
|
||||||
|
@ -436,7 +432,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
r.Route(mapPath, func(r chi.Router) {
|
r.Route(mapPath, func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(a.cache.cacheMiddleware, sbm)
|
r.Use(a.cacheMiddleware, sbm)
|
||||||
r.Get("/", a.serveGeoMap)
|
r.Get("/", a.serveGeoMap)
|
||||||
r.HandleFunc("/leaflet/*", a.serveLeaflet(mapPath+"/"))
|
r.HandleFunc("/leaflet/*", a.serveLeaflet(mapPath+"/"))
|
||||||
})
|
})
|
||||||
|
@ -449,7 +445,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
contactPath := blogConfig.getRelativePath(defaultIfEmpty(cc.Path, defaultContactPath))
|
contactPath := blogConfig.getRelativePath(defaultIfEmpty(cc.Path, defaultContactPath))
|
||||||
r.Route(contactPath, func(r chi.Router) {
|
r.Route(contactPath, func(r chi.Router) {
|
||||||
r.Use(privateModeHandler...)
|
r.Use(privateModeHandler...)
|
||||||
r.Use(a.cache.cacheMiddleware, sbm)
|
r.Use(a.cacheMiddleware, sbm)
|
||||||
r.Get("/", a.serveContactForm)
|
r.Get("/", a.serveContactForm)
|
||||||
r.With(a.captchaMiddleware).Post("/", a.sendContactSubmission)
|
r.With(a.captchaMiddleware).Post("/", a.sendContactSubmission)
|
||||||
})
|
})
|
||||||
|
@ -458,7 +454,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sitemap
|
// Sitemap
|
||||||
r.With(privateModeHandler...).With(a.cache.cacheMiddleware).Get(sitemapPath, a.serveSitemap)
|
r.With(privateModeHandler...).With(a.cacheMiddleware).Get(sitemapPath, a.serveSitemap)
|
||||||
|
|
||||||
// Robots.txt - doesn't need cache, because it's too simple
|
// Robots.txt - doesn't need cache, because it's too simple
|
||||||
if !privateMode {
|
if !privateMode {
|
||||||
|
@ -517,7 +513,7 @@ func (a *goBlog) servePostsAliasesRedirects(pmh ...func(http.Handler) http.Handl
|
||||||
// Is post, check status
|
// Is post, check status
|
||||||
switch postStatus(value) {
|
switch postStatus(value) {
|
||||||
case statusPublished, statusUnlisted:
|
case statusPublished, statusUnlisted:
|
||||||
alicePrivate.Append(a.checkActivityStreamsRequest, a.cache.cacheMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
alicePrivate.Append(a.checkActivityStreamsRequest, a.cacheMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
case statusDraft, statusPrivate:
|
case statusDraft, statusPrivate:
|
||||||
alice.New(a.authMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
alice.New(a.authMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
||||||
|
@ -525,20 +521,20 @@ func (a *goBlog) servePostsAliasesRedirects(pmh ...func(http.Handler) http.Handl
|
||||||
}
|
}
|
||||||
case "alias":
|
case "alias":
|
||||||
// Is alias, redirect
|
// Is alias, redirect
|
||||||
alicePrivate.Append(a.cache.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
alicePrivate.Append(a.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, value, http.StatusFound)
|
http.Redirect(w, r, value, http.StatusFound)
|
||||||
}).ServeHTTP(w, r)
|
}).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
case "deleted":
|
case "deleted":
|
||||||
// Is deleted, serve 410
|
// Is deleted, serve 410
|
||||||
alicePrivate.Append(a.cache.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
alicePrivate.Append(a.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
a.serve410(w, r)
|
a.serve410(w, r)
|
||||||
}).ServeHTTP(w, r)
|
}).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No post, check regex redirects or serve 404 error
|
// No post, check regex redirects or serve 404 error
|
||||||
alice.New(a.cache.cacheMiddleware, a.checkRegexRedirects).ThenFunc(a.serve404).ServeHTTP(w, r)
|
alice.New(a.cacheMiddleware, a.checkRegexRedirects).ThenFunc(a.serve404).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
posts.go
5
posts.go
|
@ -13,7 +13,6 @@ import (
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
servertiming "github.com/mitchellh/go-server-timing"
|
|
||||||
"github.com/vcraescu/go-paginator"
|
"github.com/vcraescu/go-paginator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,9 +45,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *goBlog) servePost(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) servePost(w http.ResponseWriter, r *http.Request) {
|
||||||
t := servertiming.FromContext(r.Context()).NewMetric("gp").Start()
|
|
||||||
p, err := a.db.getPost(r.URL.Path)
|
p, err := a.db.getPost(r.URL.Path)
|
||||||
t.Stop()
|
|
||||||
if err == errPostNotFound {
|
if err == errPostNotFound {
|
||||||
a.serve404(w, r)
|
a.serve404(w, r)
|
||||||
return
|
return
|
||||||
|
@ -253,9 +250,7 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
}, db: a.db}, a.cfg.Blogs[blog].Pagination)
|
}, db: a.db}, a.cfg.Blogs[blog].Pagination)
|
||||||
p.SetPage(pageNo)
|
p.SetPage(pageNo)
|
||||||
var posts []*post
|
var posts []*post
|
||||||
t := servertiming.FromContext(r.Context()).NewMetric("gp").Start()
|
|
||||||
err := p.Results(&posts)
|
err := p.Results(&posts)
|
||||||
t.Stop()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
servertiming "github.com/mitchellh/go-server-timing"
|
|
||||||
"go.goblog.app/app/pkgs/contenttype"
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -115,9 +114,6 @@ func (a *goBlog) render(w http.ResponseWriter, r *http.Request, template string,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, statusCode int, template string, data *renderData) {
|
func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, statusCode int, template string, data *renderData) {
|
||||||
// Server timing
|
|
||||||
t := servertiming.FromContext(r.Context()).NewMetric("r").Start()
|
|
||||||
defer t.Stop()
|
|
||||||
// Check render data
|
// Check render data
|
||||||
a.checkRenderData(r, data)
|
a.checkRenderData(r, data)
|
||||||
// Set content type
|
// Set content type
|
||||||
|
@ -165,7 +161,7 @@ func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {
|
||||||
data.TorUsed = true
|
data.TorUsed = true
|
||||||
}
|
}
|
||||||
// Check login
|
// Check login
|
||||||
if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn {
|
if a.isLoggedIn(r) {
|
||||||
data.LoggedIn = true
|
data.LoggedIn = true
|
||||||
}
|
}
|
||||||
// Check if comments enabled
|
// Check if comments enabled
|
||||||
|
|
34
sessions.go
34
sessions.go
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -31,7 +30,7 @@ func (a *goBlog) initSessions() {
|
||||||
deleteExpiredSessions()
|
deleteExpiredSessions()
|
||||||
a.hourlyHooks = append(a.hourlyHooks, deleteExpiredSessions)
|
a.hourlyHooks = append(a.hourlyHooks, deleteExpiredSessions)
|
||||||
a.loginSessions = &dbSessionStore{
|
a.loginSessions = &dbSessionStore{
|
||||||
codecs: securecookie.CodecsFromPairs(a.jwtKey()),
|
codecs: securecookie.CodecsFromPairs([]byte(a.cfg.Server.JWTSecret)),
|
||||||
options: &sessions.Options{
|
options: &sessions.Options{
|
||||||
Secure: a.httpsConfigured(),
|
Secure: a.httpsConfigured(),
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
@ -42,7 +41,7 @@ func (a *goBlog) initSessions() {
|
||||||
db: a.db,
|
db: a.db,
|
||||||
}
|
}
|
||||||
a.captchaSessions = &dbSessionStore{
|
a.captchaSessions = &dbSessionStore{
|
||||||
codecs: securecookie.CodecsFromPairs(a.jwtKey()),
|
codecs: securecookie.CodecsFromPairs([]byte(a.cfg.Server.JWTSecret)),
|
||||||
options: &sessions.Options{
|
options: &sessions.Options{
|
||||||
Secure: a.httpsConfigured(),
|
Secure: a.httpsConfigured(),
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
@ -77,25 +76,19 @@ func (s *dbSessionStore) New(r *http.Request, name string) (session *sessions.Se
|
||||||
return session, err
|
return session, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dbSessionStore) Save(r *http.Request, w http.ResponseWriter, ss *sessions.Session) error {
|
func (s *dbSessionStore) Save(r *http.Request, w http.ResponseWriter, ss *sessions.Session) (err error) {
|
||||||
_, err := s.SaveGetCookie(r, w, ss)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *dbSessionStore) SaveGetCookie(r *http.Request, w http.ResponseWriter, ss *sessions.Session) (cookie *http.Cookie, err error) {
|
|
||||||
if ss.ID == "" {
|
if ss.ID == "" {
|
||||||
if err = s.insert(ss); err != nil {
|
if err = s.insert(ss); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
} else if err = s.save(ss); err != nil {
|
} else if err = s.save(ss); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
if encoded, err := securecookie.EncodeMulti(ss.Name(), ss.ID, s.codecs...); err != nil {
|
if encoded, err := securecookie.EncodeMulti(ss.Name(), ss.ID, s.codecs...); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
} else {
|
} else {
|
||||||
cookie = sessions.NewCookie(ss.Name(), encoded, ss.Options)
|
http.SetCookie(w, sessions.NewCookie(ss.Name(), encoded, ss.Options))
|
||||||
http.SetCookie(w, cookie)
|
return nil
|
||||||
return cookie, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,22 +106,17 @@ func (s *dbSessionStore) Delete(r *http.Request, w http.ResponseWriter, session
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dbSessionStore) load(session *sessions.Session) (err error) {
|
func (s *dbSessionStore) load(session *sessions.Session) (err error) {
|
||||||
row, err := s.db.queryRow("select data, created, modified, expires from sessions where id = @id", sql.Named("id", session.ID))
|
row, err := s.db.queryRow("select data, created, modified, expires from sessions where id = @id and expires > @now", sql.Named("id", session.ID), sql.Named("now", utcNowString()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var data, createdStr, modifiedStr, expiresStr string
|
var data, createdStr, modifiedStr, expiresStr string
|
||||||
if err = row.Scan(&data, &createdStr, &modifiedStr, &expiresStr); err == sql.ErrNoRows {
|
if err = row.Scan(&data, &createdStr, &modifiedStr, &expiresStr); err != nil {
|
||||||
return nil
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
created, _ := dateparse.ParseLocal(createdStr)
|
created, _ := dateparse.ParseLocal(createdStr)
|
||||||
modified, _ := dateparse.ParseLocal(modifiedStr)
|
modified, _ := dateparse.ParseLocal(modifiedStr)
|
||||||
expires, _ := dateparse.ParseLocal(expiresStr)
|
expires, _ := dateparse.ParseLocal(expiresStr)
|
||||||
if expires.Before(time.Now()) {
|
|
||||||
return errors.New("session expired")
|
|
||||||
}
|
|
||||||
if err = securecookie.DecodeMulti(session.Name(), data, &session.Values, s.codecs...); err != nil {
|
if err = securecookie.DecodeMulti(session.Name(), data, &session.Values, s.codecs...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -149,7 +137,7 @@ func (s *dbSessionStore) insert(session *sessions.Session) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res, err := s.db.exec("insert into sessions(data, created, modified, expires) values(@data, @created, @modified, @expires)",
|
res, err := s.db.exec("insert or replace into sessions(data, created, modified, expires) values(@data, @created, @modified, @expires)",
|
||||||
sql.Named("data", encoded), sql.Named("created", created.Format(time.RFC3339)), sql.Named("modified", modified.Format(time.RFC3339)), sql.Named("expires", expires.Format(time.RFC3339)))
|
sql.Named("data", encoded), sql.Named("created", created.Format(time.RFC3339)), sql.Named("modified", modified.Format(time.RFC3339)), sql.Named("expires", expires.Format(time.RFC3339)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -86,7 +85,8 @@ func (a *goBlog) verifyMention(m *mention) error {
|
||||||
// Server not yet started
|
// Server not yet started
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
a.d.ServeHTTP(rec, req.WithContext(context.WithValue(req.Context(), loggedInKey, true)))
|
setLoggedIn(req)
|
||||||
|
a.d.ServeHTTP(rec, req)
|
||||||
resp = rec.Result()
|
resp = rec.Result()
|
||||||
} else {
|
} else {
|
||||||
req.Header.Set(userAgent, appUserAgent)
|
req.Header.Set(userAgent, appUserAgent)
|
||||||
|
|
Loading…
Reference in New Issue