diff --git a/authentication_test.go b/authentication_test.go
index a932e2e..2e26f11 100644
--- a/authentication_test.go
+++ b/authentication_test.go
@@ -5,7 +5,6 @@ import (
"net/http"
"net/http/httptest"
"net/url"
- "path/filepath"
"strings"
"testing"
@@ -17,32 +16,20 @@ import (
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",
- },
- },
+ cfg: createDefaultTestConfig(t),
+ }
+ app.cfg.User = &configUser{
+ Nick: "test",
+ Password: "pass",
+ AppPasswords: []*configAppPassword{
+ {
+ Username: "app1",
+ Password: "pass1",
},
},
}
+ _ = app.initConfig()
_ = app.initDatabase(false)
app.initComponents(false)
diff --git a/captcha_test.go b/captcha_test.go
index 5cd855f..e911276 100644
--- a/captcha_test.go
+++ b/captcha_test.go
@@ -1,11 +1,9 @@
package main
import (
- "context"
"io"
"net/http"
"net/http/httptest"
- "path/filepath"
"testing"
"github.com/stretchr/testify/assert"
@@ -14,23 +12,10 @@ import (
func Test_captchaMiddleware(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{},
- },
+ cfg: createDefaultTestConfig(t),
}
+ _ = app.initConfig()
_ = app.initDatabase(false)
app.initComponents(false)
@@ -39,11 +24,9 @@ func Test_captchaMiddleware(t *testing.T) {
}))
t.Run("Default", func(t *testing.T) {
- req := httptest.NewRequest(http.MethodPost, "/abc", nil)
-
rec := httptest.NewRecorder()
- h.ServeHTTP(rec, req.WithContext(context.WithValue(req.Context(), blogKey, "en")))
+ h.ServeHTTP(rec, reqWithDefaultBlog(httptest.NewRequest(http.MethodPost, "/abc", nil)))
res := rec.Result()
resBody, _ := io.ReadAll(res.Body)
diff --git a/config.go b/config.go
index e1b820a..ae4c767 100644
--- a/config.go
+++ b/config.go
@@ -27,6 +27,7 @@ type config struct {
EasterEgg *configEasterEgg `mapstructure:"easterEgg"`
Debug bool `mapstructure:"debug"`
MapTiles *configMapTiles `mapstructure:"mapTiles"`
+ initialized bool
}
type configServer struct {
@@ -42,7 +43,6 @@ type configServer struct {
Tor bool `mapstructure:"tor"`
SecurityHeaders bool `mapstructure:"securityHeaders"`
CSPDomains []string `mapstructure:"cspDomains"`
- JWTSecret string `mapstructure:"jwtSecret"`
publicHostname string
shortPublicHostname string
mediaHostname string
@@ -287,86 +287,83 @@ type configMapTiles struct {
MaxZoom int `mapstructure:"maxZoom"`
}
-func (a *goBlog) initConfig(file string) error {
- log.Println("Initialize configuration...")
+func (a *goBlog) loadConfigFile(file string) error {
+ // Use viper to load the config file
+ v := viper.New()
if file != "" {
// Use config file from the flag
- viper.SetConfigFile(file)
+ v.SetConfigFile(file)
} else {
- viper.SetConfigName("config")
- viper.AddConfigPath("./config/")
+ // Search in default locations
+ v.SetConfigName("config")
+ v.AddConfigPath("./config/")
}
- err := viper.ReadInConfig()
- if err != nil {
+ // Read config
+ if err := v.ReadInConfig(); err != nil {
return err
}
- // Defaults
- viper.SetDefault("server.logging", false)
- viper.SetDefault("server.logFile", "data/access.log")
- viper.SetDefault("server.port", 8080)
- viper.SetDefault("server.publicAddress", "http://localhost:8080")
- viper.SetDefault("server.publicHttps", false)
- viper.SetDefault("database.file", "data/db.sqlite")
- viper.SetDefault("cache.enable", true)
- viper.SetDefault("cache.expiration", 600)
- viper.SetDefault("user.nick", "admin")
- viper.SetDefault("user.password", "secret")
- viper.SetDefault("hooks.shell", "/bin/bash")
- viper.SetDefault("micropub.categoryParam", "tags")
- viper.SetDefault("micropub.replyParam", "replylink")
- viper.SetDefault("micropub.replyTitleParam", "replytitle")
- viper.SetDefault("micropub.likeParam", "likelink")
- viper.SetDefault("micropub.likeTitleParam", "liketitle")
- viper.SetDefault("micropub.bookmarkParam", "link")
- viper.SetDefault("micropub.audioParam", "audio")
- viper.SetDefault("micropub.photoParam", "images")
- viper.SetDefault("micropub.photoDescriptionParam", "imagealts")
- viper.SetDefault("micropub.locationParam", "location")
- viper.SetDefault("activityPub.tagsTaxonomies", []string{"tags"})
// Unmarshal config
- a.cfg = &config{}
- err = viper.Unmarshal(a.cfg)
- if err != nil {
- return err
+ a.cfg = createDefaultConfig()
+ return v.Unmarshal(a.cfg)
+}
+
+func (a *goBlog) initConfig() error {
+ if a.cfg == nil {
+ a.cfg = createDefaultConfig()
+ }
+ if a.cfg.initialized {
+ return nil
}
// Check config
+ // Parse addresses and hostnames
+ if a.cfg.Server.PublicAddress == "" {
+ return errors.New("no public address configured")
+ }
publicURL, err := url.Parse(a.cfg.Server.PublicAddress)
if err != nil {
- return err
+ return errors.New("Invalid public address: " + err.Error())
}
a.cfg.Server.publicHostname = publicURL.Hostname()
if sa := a.cfg.Server.ShortPublicAddress; sa != "" {
shortPublicURL, err := url.Parse(sa)
if err != nil {
- return err
+ return errors.New("Invalid short public address: " + err.Error())
}
a.cfg.Server.shortPublicHostname = shortPublicURL.Hostname()
}
if ma := a.cfg.Server.MediaAddress; ma != "" {
mediaUrl, err := url.Parse(ma)
if err != nil {
- return err
+ return errors.New("Invalid media address: " + err.Error())
}
a.cfg.Server.mediaHostname = mediaUrl.Hostname()
}
- if a.cfg.Server.JWTSecret == "" {
- return errors.New("no JWT secret configured")
- }
- if len(a.cfg.Blogs) == 0 {
- return errors.New("no blog configured")
- }
- if len(a.cfg.DefaultBlog) == 0 || a.cfg.Blogs[a.cfg.DefaultBlog] == nil {
- return errors.New("no default blog or default blog not present")
- }
- if a.cfg.Micropub.MediaStorage != nil {
- if a.cfg.Micropub.MediaStorage.MediaURL == "" ||
- a.cfg.Micropub.MediaStorage.BunnyStorageKey == "" ||
- a.cfg.Micropub.MediaStorage.BunnyStorageName == "" {
- a.cfg.Micropub.MediaStorage.BunnyStorageKey = ""
- a.cfg.Micropub.MediaStorage.BunnyStorageName = ""
+ // Check if any blog is configured
+ if a.cfg.Blogs == nil || len(a.cfg.Blogs) == 0 {
+ a.cfg.Blogs = map[string]*configBlog{
+ "default": createDefaultBlog(),
}
- a.cfg.Micropub.MediaStorage.MediaURL = strings.TrimSuffix(a.cfg.Micropub.MediaStorage.MediaURL, "/")
}
+ // Check if default blog is set
+ if a.cfg.DefaultBlog == "" {
+ if len(a.cfg.Blogs) == 1 {
+ // Set default blog to the only blog that is configured
+ for k := range a.cfg.Blogs {
+ a.cfg.DefaultBlog = k
+ }
+ } else {
+ return errors.New("no default blog configured")
+ }
+ }
+ // Check if default blog exists
+ if a.cfg.Blogs[a.cfg.DefaultBlog] == nil {
+ return errors.New("default blog does not exist")
+ }
+ // Check media storage config
+ if ms := a.cfg.Micropub.MediaStorage; ms != nil && ms.MediaURL != "" {
+ ms.MediaURL = strings.TrimSuffix(ms.MediaURL, "/")
+ }
+ // Check if webmention receiving is disabled
if wm := a.cfg.Webmention; wm != nil && wm.DisableReceiving {
// Disable comments for all blogs
for _, b := range a.cfg.Blogs {
@@ -380,10 +377,65 @@ func (a *goBlog) initConfig(file string) error {
br.Enabled = false
}
}
+ // Log success
+ a.cfg.initialized = true
log.Println("Initialized configuration")
return nil
}
+func createDefaultConfig() *config {
+ return &config{
+ Server: &configServer{
+ Port: 8080,
+ PublicAddress: "http://localhost:8080",
+ },
+ Db: &configDb{
+ File: "data/db.sqlite",
+ },
+ Cache: &configCache{
+ Enable: true,
+ Expiration: 600,
+ },
+ User: &configUser{
+ Nick: "admin",
+ Password: "secret",
+ },
+ Hooks: &configHooks{
+ Shell: "/bin/bash",
+ },
+ Micropub: &configMicropub{
+ CategoryParam: "tags",
+ ReplyParam: "replylink",
+ ReplyTitleParam: "replytitle",
+ LikeParam: "likelink",
+ LikeTitleParam: "liketitle",
+ BookmarkParam: "link",
+ AudioParam: "audio",
+ PhotoParam: "images",
+ PhotoDescriptionParam: "imagealts",
+ LocationParam: "location",
+ },
+ ActivityPub: &configActivityPub{
+ TagsTaxonomies: []string{"tags"},
+ },
+ }
+}
+
+func createDefaultBlog() *configBlog {
+ return &configBlog{
+ Path: "/",
+ Lang: "en",
+ Title: "My Blog",
+ Description: "Welcome to my blog.",
+ Sections: map[string]*configSection{
+ "posts": {
+ Title: "Posts",
+ },
+ },
+ DefaultSection: "posts",
+ }
+}
+
func (a *goBlog) httpsConfigured(checkAddress bool) bool {
return a.cfg.Server.PublicHTTPS ||
a.cfg.Server.TailscaleHTTPS ||
diff --git a/config_test.go b/config_test.go
new file mode 100644
index 0000000..310206f
--- /dev/null
+++ b/config_test.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+ "context"
+ "net/http"
+ "path/filepath"
+ "testing"
+)
+
+func createDefaultTestConfig(t *testing.T) *config {
+ c := createDefaultConfig()
+ c.Db.File = filepath.Join(t.TempDir(), "blog.db")
+ return c
+}
+
+func reqWithDefaultBlog(req *http.Request) *http.Request {
+ return req.WithContext(context.WithValue(req.Context(), blogKey, "default"))
+}
diff --git a/dbmigrations/00026.sql b/dbmigrations/00026.sql
new file mode 100644
index 0000000..652f4e9
--- /dev/null
+++ b/dbmigrations/00026.sql
@@ -0,0 +1,3 @@
+drop table sessions;
+create table sessions (id text primary key, data blob, created text default '', modified text default '', expires text default '');
+create index sessions_exp on sessions (expires);
\ No newline at end of file
diff --git a/example-config.yml b/example-config.yml
index a2be07c..395c1c9 100644
--- a/example-config.yml
+++ b/example-config.yml
@@ -23,8 +23,6 @@ server:
securityHeaders: true # Set security HTTP headers (to always use HTTPS etc.)
cspDomains: # Specify additional domains to allow embedded content with enabled securityHeaders
- media.example.com
- # Cookies
- jwtSecret: changeThisWeakSecret # secret to use for cookies (login and captcha)
# Tor
tor: true # Publish onion service, requires Tor to be installed and available in path
# Tailscale (see https://tailscale.com)
diff --git a/go.mod b/go.mod
index f73286d..27bff41 100644
--- a/go.mod
+++ b/go.mod
@@ -20,10 +20,9 @@ require (
github.com/emersion/go-smtp v0.15.0
github.com/go-chi/chi/v5 v5.0.7
github.com/go-fed/httpsig v1.1.0
- github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.0
+ github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
- github.com/gorilla/securecookie v1.1.1
github.com/gorilla/sessions v1.2.1
github.com/gorilla/websocket v1.4.2
github.com/hacdias/indieauth v1.7.1
@@ -44,7 +43,7 @@ require (
github.com/spf13/cast v1.4.1
github.com/spf13/viper v1.10.0
github.com/stretchr/testify v1.7.0
- github.com/tdewolff/minify/v2 v2.9.22
+ github.com/tdewolff/minify/v2 v2.9.23
github.com/thoas/go-funk v0.9.1
github.com/tkrajina/gpxgo v1.1.2
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
@@ -87,6 +86,7 @@ require (
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/gorilla/css v1.0.0 // indirect
+ github.com/gorilla/securecookie v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/insomniacslk/dhcp v0.0.0-20210621130208-1cac67f12b1e // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
@@ -112,7 +112,7 @@ require (
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
github.com/tcnksm/go-httpstat v0.2.0 // indirect
- github.com/tdewolff/parse/v2 v2.5.21 // indirect
+ github.com/tdewolff/parse/v2 v2.5.24 // indirect
github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
github.com/vishvananda/netlink v1.1.1-0.20211101163509-b10eb8fe5cf6 // indirect
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
diff --git a/go.sum b/go.sum
index a3a6793..3fc561f 100644
--- a/go.sum
+++ b/go.sum
@@ -139,8 +139,8 @@ github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
-github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.0 h1:BtndtqqCQfPsL2uMkYmduOip1+dPcSmh40l82mBUPKk=
-github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.0/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
+github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
+github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
@@ -387,10 +387,10 @@ github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQ
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
-github.com/tdewolff/minify/v2 v2.9.22 h1:PlmaAakaJHdMMdTTwjjsuSwIxKqWPTlvjTj6a/g/ILU=
-github.com/tdewolff/minify/v2 v2.9.22/go.mod h1:dNlaFdXaIxgSXh3UFASqjTY0/xjpDkkCsYHA1NCGnmQ=
-github.com/tdewolff/parse/v2 v2.5.21 h1:s/OLsVxxmQUlbFtPODDVHA836qchgmoxjEsk/cUZl48=
-github.com/tdewolff/parse/v2 v2.5.21/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
+github.com/tdewolff/minify/v2 v2.9.23 h1:UrLltJpnJPm7/fYFP3Ue/GD5tHufx2z7ERQihACLkmg=
+github.com/tdewolff/minify/v2 v2.9.23/go.mod h1:4o1Mw4T3RLV0CHUny7OEnntezuwoj/FNst4QzrNxIts=
+github.com/tdewolff/parse/v2 v2.5.24 h1:sJPG5Viy2lq9NBbnK4KpWEA+17RNZz8EOXVqErHKHgs=
+github.com/tdewolff/parse/v2 v2.5.24/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
diff --git a/httpClient_test.go b/httpClient_test.go
index 9373363..ef667b5 100644
--- a/httpClient_test.go
+++ b/httpClient_test.go
@@ -29,7 +29,7 @@ func newFakeHttpClient() *fakeHttpClient {
}
// Copy result status code and body
rw.WriteHeader(fc.res.StatusCode)
- io.Copy(rw, rec.Body)
+ _, _ = io.Copy(rw, rec.Body)
}
}),
},
diff --git a/main.go b/main.go
index ccefbde..b1e42b2 100644
--- a/main.go
+++ b/main.go
@@ -57,7 +57,11 @@ func main() {
}
// Initialize config
- if err = app.initConfig(*configfile); err != nil {
+ if err = app.loadConfigFile(*configfile); err != nil {
+ app.logErrAndQuit("Failed to load config file:", err.Error())
+ return
+ }
+ if err = app.initConfig(); err != nil {
app.logErrAndQuit("Failed to init config:", err.Error())
return
}
diff --git a/sessions.go b/sessions.go
index cdebfc9..f00c137 100644
--- a/sessions.go
+++ b/sessions.go
@@ -1,14 +1,16 @@
package main
import (
+ "bytes"
"database/sql"
- "fmt"
+ "encoding/gob"
"log"
"net/http"
+ "strings"
"time"
"github.com/araddon/dateparse"
- "github.com/gorilla/securecookie"
+ "github.com/google/uuid"
"github.com/gorilla/sessions"
)
@@ -30,7 +32,6 @@ func (a *goBlog) initSessions() {
deleteExpiredSessions()
a.hourlyHooks = append(a.hourlyHooks, deleteExpiredSessions)
a.loginSessions = &dbSessionStore{
- codecs: securecookie.CodecsFromPairs([]byte(a.cfg.Server.JWTSecret)),
options: &sessions.Options{
Secure: a.httpsConfigured(true),
HttpOnly: true,
@@ -41,7 +42,6 @@ func (a *goBlog) initSessions() {
db: a.db,
}
a.captchaSessions = &dbSessionStore{
- codecs: securecookie.CodecsFromPairs([]byte(a.cfg.Server.JWTSecret)),
options: &sessions.Options{
Secure: a.httpsConfigured(true),
HttpOnly: true,
@@ -55,7 +55,6 @@ func (a *goBlog) initSessions() {
type dbSessionStore struct {
options *sessions.Options
- codecs []securecookie.Codec
db *database
}
@@ -67,29 +66,33 @@ func (s *dbSessionStore) New(r *http.Request, name string) (session *sessions.Se
session = sessions.NewSession(s, name)
opts := *s.options
session.Options = &opts
- session.IsNew = true
- if cook, errCookie := r.Cookie(name); errCookie == nil {
- if err = securecookie.DecodeMulti(name, cook.Value, &session.ID, s.codecs...); err == nil {
- session.IsNew = s.load(session) == nil
+ if c, cErr := r.Cookie(name); cErr == nil && strings.HasPrefix(c.Value, session.Name()+"-") {
+ // Has cookie, load from database
+ session.ID = c.Value
+ if s.load(session) != nil {
+ // Failed to load session from database, delete the ID (= new session)
+ session.ID = ""
}
}
+ // If no ID, the session is new
+ session.IsNew = session.ID == ""
return session, err
}
func (s *dbSessionStore) Save(r *http.Request, w http.ResponseWriter, ss *sessions.Session) (err error) {
if ss.ID == "" {
+ // Is new session, save it to database
if err = s.insert(ss); err != nil {
return err
}
- } else if err = s.save(ss); err != nil {
- return err
- }
- if encoded, err := securecookie.EncodeMulti(ss.Name(), ss.ID, s.codecs...); err != nil {
- return err
} else {
- http.SetCookie(w, sessions.NewCookie(ss.Name(), encoded, ss.Options))
- return nil
+ // Update existing session
+ if err = s.save(ss); err != nil {
+ return err
+ }
}
+ http.SetCookie(w, sessions.NewCookie(ss.Name(), ss.ID, ss.Options))
+ return nil
}
func (s *dbSessionStore) Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
@@ -106,15 +109,20 @@ func (s *dbSessionStore) Delete(r *http.Request, w http.ResponseWriter, session
}
func (s *dbSessionStore) load(session *sessions.Session) (err error) {
- 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()))
+ 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 {
return err
}
- var data, createdStr, modifiedStr, expiresStr string
+ var createdStr, modifiedStr, expiresStr string
+ var data []byte
if err = row.Scan(&data, &createdStr, &modifiedStr, &expiresStr); err != nil {
return err
}
- if err = securecookie.DecodeMulti(session.Name(), data, &session.Values, s.codecs...); err != nil {
+ if err = gob.NewDecoder(bytes.NewReader(data)).Decode(&session.Values); err != nil {
return err
}
session.Values[sessionCreatedOn] = timeNoErr(dateparse.ParseLocal(createdStr))
@@ -124,44 +132,44 @@ func (s *dbSessionStore) load(session *sessions.Session) (err error) {
}
func (s *dbSessionStore) insert(session *sessions.Session) (err error) {
- created := time.Now().UTC()
- modified := time.Now().UTC()
- expires := time.Now().UTC().Add(time.Second * time.Duration(session.Options.MaxAge))
- delete(session.Values, sessionCreatedOn)
- delete(session.Values, sessionExpiresOn)
- delete(session.Values, sessionModifiedOn)
- encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.codecs...)
- if err != nil {
+ deleteSessionValuesNotNeededForDb(session)
+ var encoded bytes.Buffer
+ if err := gob.NewEncoder(&encoded).Encode(session.Values); err != nil {
return err
}
- 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)))
- if err != nil {
- return err
- }
- lastInserted, err := res.LastInsertId()
- if err != nil {
- return err
- }
- session.ID = fmt.Sprintf("%d", lastInserted)
- return nil
+ session.ID = session.Name() + "-" + uuid.NewString()
+ created, modified := utcNowString(), utcNowString()
+ expires := time.Now().UTC().Add(time.Second * time.Duration(session.Options.MaxAge)).Format(time.RFC3339)
+ _, err = s.db.exec(
+ "insert or replace into sessions(id, data, created, modified, expires) values(@id, @data, @created, @modified, @expires)",
+ sql.Named("id", session.ID),
+ sql.Named("data", encoded.Bytes()),
+ sql.Named("created", created),
+ sql.Named("modified", modified),
+ sql.Named("expires", expires),
+ )
+ return err
}
func (s *dbSessionStore) save(session *sessions.Session) (err error) {
if session.IsNew {
return s.insert(session)
}
- delete(session.Values, sessionCreatedOn)
- delete(session.Values, sessionExpiresOn)
- delete(session.Values, sessionModifiedOn)
- encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.codecs...)
- if err != nil {
+ deleteSessionValuesNotNeededForDb(session)
+ var encoded bytes.Buffer
+ if err = gob.NewEncoder(&encoded).Encode(session.Values); err != nil {
return err
}
_, err = s.db.exec("update sessions set data = @data, modified = @modified where id = @id",
- sql.Named("data", encoded), sql.Named("modified", utcNowString()), sql.Named("id", session.ID))
+ sql.Named("data", encoded.Bytes()), sql.Named("modified", utcNowString()), sql.Named("id", session.ID))
if err != nil {
return err
}
return nil
}
+
+func deleteSessionValuesNotNeededForDb(session *sessions.Session) {
+ delete(session.Values, sessionCreatedOn)
+ delete(session.Values, sessionExpiresOn)
+ delete(session.Values, sessionModifiedOn)
+}
diff --git a/telegram_test.go b/telegram_test.go
index 6bbb67a..d0a8514 100644
--- a/telegram_test.go
+++ b/telegram_test.go
@@ -2,7 +2,6 @@ package main
import (
"net/http"
- "path/filepath"
"testing"
"time"
@@ -11,42 +10,15 @@ import (
)
func Test_configTelegram_enabled(t *testing.T) {
- if (&configTelegram{}).enabled() == true {
- t.Error("Telegram shouldn't be enabled")
- }
-
+ assert.False(t, (&configTelegram{}).enabled())
var tg *configTelegram
- if tg.enabled() == true {
- t.Error("Telegram shouldn't be enabled")
- }
+ assert.False(t, tg.enabled())
- if (&configTelegram{
- Enabled: true,
- }).enabled() == true {
- t.Error("Telegram shouldn't be enabled")
- }
+ assert.False(t, (&configTelegram{Enabled: true}).enabled())
+ assert.False(t, (&configTelegram{Enabled: true, ChatID: "abc"}).enabled())
+ assert.False(t, (&configTelegram{Enabled: true, BotToken: "abc"}).enabled())
- if (&configTelegram{
- Enabled: true,
- ChatID: "abc",
- }).enabled() == true {
- t.Error("Telegram shouldn't be enabled")
- }
-
- if (&configTelegram{
- Enabled: true,
- BotToken: "abc",
- }).enabled() == true {
- t.Error("Telegram shouldn't be enabled")
- }
-
- if (&configTelegram{
- Enabled: true,
- BotToken: "abc",
- ChatID: "abc",
- }).enabled() != true {
- t.Error("Telegram should be enabled")
- }
+ assert.True(t, (&configTelegram{Enabled: true, ChatID: "abc", BotToken: "abc"}).enabled())
}
func Test_configTelegram_generateHTML(t *testing.T) {
@@ -78,11 +50,11 @@ func Test_configTelegram_send(t *testing.T) {
fakeClient.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if r.URL.String() == "https://api.telegram.org/botbottoken/getMe" {
rw.WriteHeader(http.StatusOK)
- rw.Write([]byte(`{"ok":true,"result":{"id":123456789,"is_bot":true,"first_name":"Test","username":"testbot"}}`))
+ _, _ = rw.Write([]byte(`{"ok":true,"result":{"id":123456789,"is_bot":true,"first_name":"Test","username":"testbot"}}`))
return
}
rw.WriteHeader(http.StatusOK)
- rw.Write([]byte(`{"ok":true,"result":{"message_id":123,"from":{"id":123456789,"is_bot":true,"first_name":"Test","username":"testbot"},"chat":{"id":789,"first_name":"Test","username":"testbot"},"date":1564181818,"text":"Message"}}`))
+ _, _ = rw.Write([]byte(`{"ok":true,"result":{"message_id":123,"from":{"id":123456789,"is_bot":true,"first_name":"Test","username":"testbot"},"chat":{"id":789,"first_name":"Test","username":"testbot"},"date":1564181818,"text":"Message"}}`))
}))
tg := &configTelegram{
@@ -126,38 +98,31 @@ func Test_goBlog_initTelegram(t *testing.T) {
func Test_telegram(t *testing.T) {
t.Run("Send post to Telegram", func(t *testing.T) {
fakeClient := newFakeHttpClient()
-
fakeClient.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if r.URL.String() == "https://api.telegram.org/botbottoken/getMe" {
rw.WriteHeader(http.StatusOK)
- rw.Write([]byte(`{"ok":true,"result":{"id":123456789,"is_bot":true,"first_name":"Test","username":"testbot"}}`))
+ _, _ = rw.Write([]byte(`{"ok":true,"result":{"id":123456789,"is_bot":true,"first_name":"Test","username":"testbot"}}`))
return
}
rw.WriteHeader(http.StatusOK)
- rw.Write([]byte(`{"ok":true,"result":{"message_id":123,"from":{"id":123456789,"is_bot":true,"first_name":"Test","username":"testbot"},"chat":{"id":123456789,"first_name":"Test","username":"testbot"},"date":1564181818,"text":"Message"}}`))
+ _, _ = rw.Write([]byte(`{"ok":true,"result":{"message_id":123,"from":{"id":123456789,"is_bot":true,"first_name":"Test","username":"testbot"},"chat":{"id":123456789,"first_name":"Test","username":"testbot"},"date":1564181818,"text":"Message"}}`))
}))
+ cfg := createDefaultTestConfig(t)
+ cfg.Blogs = map[string]*configBlog{
+ "en": createDefaultBlog(),
+ }
+ cfg.Blogs["en"].Telegram = &configTelegram{
+ Enabled: true,
+ ChatID: "chatid",
+ BotToken: "bottoken",
+ }
+
app := &goBlog{
- pPostHooks: []postHookFunc{},
- cfg: &config{
- Db: &configDb{
- File: filepath.Join(t.TempDir(), "test.db"),
- },
- Server: &configServer{
- PublicAddress: "https://example.com",
- },
- Blogs: map[string]*configBlog{
- "en": {
- Telegram: &configTelegram{
- Enabled: true,
- ChatID: "chatid",
- BotToken: "bottoken",
- },
- },
- },
- },
+ cfg: cfg,
httpClient: fakeClient.Client,
}
+ _ = app.initConfig()
_ = app.initDatabase(false)
app.initMarkdown()
@@ -179,43 +144,32 @@ func Test_telegram(t *testing.T) {
req := fakeClient.req
assert.Equal(t, "chatid", req.FormValue("chat_id"))
assert.Equal(t, "HTML", req.FormValue("parse_mode"))
- assert.Equal(t, "Title\n\nhttps://example.com/s/1", req.FormValue("text"))
+ assert.Equal(t, "Title\n\nhttp://localhost:8080/s/1", req.FormValue("text"))
})
t.Run("Telegram disabled", func(t *testing.T) {
fakeClient := newFakeHttpClient()
app := &goBlog{
- pPostHooks: []postHookFunc{},
- cfg: &config{
- Db: &configDb{
- File: filepath.Join(t.TempDir(), "test.db"),
- },
- Server: &configServer{
- PublicAddress: "https://example.com",
- },
- Blogs: map[string]*configBlog{
- "en": {},
- },
- },
+ cfg: createDefaultTestConfig(t),
httpClient: fakeClient.Client,
}
+
+ _ = app.initConfig()
_ = app.initDatabase(false)
app.initTelegram()
- p := &post{
+ app.postPostHooks(&post{
Path: "/test",
Parameters: map[string][]string{
"title": {"Title"},
},
Published: time.Now().String(),
Section: "test",
- Blog: "en",
+ Blog: "default",
Status: statusPublished,
- }
-
- app.pPostHooks[0](p)
+ })
assert.Nil(t, fakeClient.req)
})