mirror of https://github.com/jlelse/GoBlog
Add database-based settings, settings screen and migrate sections to db and allow to configure them
parent
22fce13246
commit
cd9d500a55
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -15,15 +14,10 @@ import (
|
|||
func Test_loadActivityPubPrivateKey(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
err := app.initDatabase(false)
|
||||
err := app.initConfig(false)
|
||||
require.NoError(t, err)
|
||||
defer app.db.close()
|
||||
require.NotNil(t, app.db)
|
||||
|
||||
// Generate
|
||||
|
@ -55,9 +49,7 @@ func Test_webfinger(t *testing.T) {
|
|||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://example.com"
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.prepareWebfinger()
|
||||
|
|
|
@ -29,9 +29,7 @@ func Test_authMiddleware(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -17,34 +16,26 @@ func Test_blogroll(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: fc.Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
Blogroll: &configBlogroll{
|
||||
Enabled: true,
|
||||
Path: "/br",
|
||||
AuthHeader: "Authheader",
|
||||
AuthValue: "Authtoken",
|
||||
Opml: "https://example.com/opml",
|
||||
Categories: []string{"A", "B"},
|
||||
},
|
||||
},
|
||||
},
|
||||
User: &configUser{},
|
||||
Cache: &configCache{
|
||||
Enable: false,
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
app.cfg.Cache.Enable = false
|
||||
app.cfg.DefaultBlog = "en"
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
Blogroll: &configBlogroll{
|
||||
Enabled: true,
|
||||
Path: "/br",
|
||||
AuthHeader: "Authheader",
|
||||
AuthValue: "Authtoken",
|
||||
Opml: "https://example.com/opml",
|
||||
Categories: []string{"A", "B"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
fc.setFakeResponse(http.StatusOK, `
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -14,36 +13,26 @@ import (
|
|||
)
|
||||
|
||||
func Test_blogStats(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
BlogStats: &configBlogStats{
|
||||
Enabled: true,
|
||||
Path: "/stats",
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
BlogStats: &configBlogStats{
|
||||
Enabled: true,
|
||||
Path: "/stats",
|
||||
},
|
||||
Sections: map[string]*configSection{
|
||||
"test": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
User: &configUser{},
|
||||
Webmention: &configWebmention{
|
||||
DisableSending: true,
|
||||
Sections: map[string]*configSection{
|
||||
"test": {},
|
||||
},
|
||||
},
|
||||
}
|
||||
app.cfg.DefaultBlog = "en"
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Insert post
|
||||
|
|
|
@ -23,9 +23,7 @@ func Test_captchaMiddleware(t *testing.T) {
|
|||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = alice.New(app.checkIsCaptcha, app.captchaMiddleware).ThenFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -18,25 +17,20 @@ import (
|
|||
|
||||
func Test_comments(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
Comments: &configComments{
|
||||
Enabled: true,
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
User: &configUser{},
|
||||
},
|
||||
}
|
||||
app.cfg.DefaultBlog = "en"
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
err := app.initConfig(false)
|
||||
require.NoError(t, err)
|
||||
app.initComponents(false)
|
||||
|
||||
t.Run("Successful comment", func(t *testing.T) {
|
||||
|
@ -44,7 +38,7 @@ func Test_comments(t *testing.T) {
|
|||
// Create comment
|
||||
|
||||
data := url.Values{}
|
||||
data.Add("target", "https://example.com/test")
|
||||
data.Add("target", "http://localhost:8080/test")
|
||||
data.Add("comment", "This is just a test")
|
||||
data.Add("name", "Test name")
|
||||
data.Add("website", "https://goblog.app")
|
||||
|
@ -115,7 +109,7 @@ func Test_comments(t *testing.T) {
|
|||
// Create comment
|
||||
|
||||
data := url.Values{}
|
||||
data.Add("target", "https://example.com/test")
|
||||
data.Add("target", "http://localhost:8080/test")
|
||||
data.Add("comment", "This is just a test")
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, commentPath, strings.NewReader(data.Encode()))
|
||||
|
@ -151,7 +145,7 @@ func Test_comments(t *testing.T) {
|
|||
t.Run("Empty comment", func(t *testing.T) {
|
||||
|
||||
data := url.Values{}
|
||||
data.Add("target", "https://example.com/test")
|
||||
data.Add("target", "http://localhost:8080/test")
|
||||
data.Add("comment", "")
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, commentPath, strings.NewReader(data.Encode()))
|
||||
|
|
26
config.go
26
config.go
|
@ -341,13 +341,17 @@ func (a *goBlog) loadConfigFile(file string) error {
|
|||
return v.Unmarshal(a.cfg)
|
||||
}
|
||||
|
||||
func (a *goBlog) initConfig() error {
|
||||
func (a *goBlog) initConfig(logging bool) error {
|
||||
if a.cfg == nil {
|
||||
a.cfg = createDefaultConfig()
|
||||
}
|
||||
if a.cfg.initialized {
|
||||
return nil
|
||||
}
|
||||
// Init database
|
||||
if err := a.initDatabase(logging); err != nil {
|
||||
return err
|
||||
}
|
||||
// Check config
|
||||
// Parse addresses and hostnames
|
||||
if a.cfg.Server.PublicAddress == "" {
|
||||
|
@ -402,12 +406,24 @@ func (a *goBlog) initConfig() error {
|
|||
b.Comments = &configComments{Enabled: false}
|
||||
}
|
||||
}
|
||||
// Check if sections already migrated to db
|
||||
const sectionMigrationKey = "sections_migrated"
|
||||
if val, err := a.getSettingValue(sectionMigrationKey); err != nil {
|
||||
return err
|
||||
} else if val == "" {
|
||||
if err = a.saveAllSections(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = a.saveSettingValue(sectionMigrationKey, "1"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Load db sections
|
||||
if err = a.loadSections(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Check config for each blog
|
||||
for _, blog := range a.cfg.Blogs {
|
||||
// Copy sections key to section name
|
||||
for k, s := range blog.Sections {
|
||||
s.Name = k
|
||||
}
|
||||
// Check if language is set
|
||||
if blog.Lang == "" {
|
||||
blog.Lang = "en"
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -24,35 +23,27 @@ func Test_contact(t *testing.T) {
|
|||
|
||||
// Init everything
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
// Config for contact
|
||||
Contact: &configContact{
|
||||
Enabled: true,
|
||||
SMTPPort: port,
|
||||
SMTPHost: "127.0.0.1",
|
||||
SMTPUser: "user",
|
||||
SMTPPassword: "pass",
|
||||
EmailTo: "to@example.org",
|
||||
EmailFrom: "from@example.org",
|
||||
EmailSubject: "Neue Kontaktnachricht",
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
// Config for contact
|
||||
Contact: &configContact{
|
||||
Enabled: true,
|
||||
SMTPPort: port,
|
||||
SMTPHost: "127.0.0.1",
|
||||
SMTPUser: "user",
|
||||
SMTPPassword: "pass",
|
||||
EmailTo: "to@example.org",
|
||||
EmailFrom: "from@example.org",
|
||||
EmailSubject: "Neue Kontaktnachricht",
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
User: &configUser{},
|
||||
},
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
app.cfg.DefaultBlog = "en"
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Make contact form request
|
||||
|
|
|
@ -31,6 +31,9 @@ type database struct {
|
|||
}
|
||||
|
||||
func (a *goBlog) initDatabase(logging bool) (err error) {
|
||||
if a.db != nil {
|
||||
return
|
||||
}
|
||||
if logging {
|
||||
log.Println("Initialize database...")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
create table sections (
|
||||
blog text not null,
|
||||
name text not null,
|
||||
title text not null default '',
|
||||
description text not null default '',
|
||||
pathtemplate text not null default '',
|
||||
showfull boolean not null default false,
|
||||
primary key (blog, name)
|
||||
);
|
|
@ -0,0 +1,5 @@
|
|||
create table settings (
|
||||
name text not null,
|
||||
value text not null default '',
|
||||
primary key (name)
|
||||
);
|
|
@ -15,9 +15,7 @@ func Test_editorPreview(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
h := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -13,25 +12,10 @@ import (
|
|||
|
||||
func Test_errors(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.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
t.Run("Test 404, no HTML", func(t *testing.T) {
|
||||
|
|
|
@ -11,14 +11,9 @@ import (
|
|||
|
||||
func Test_export(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initMarkdown()
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
|
|
|
@ -15,10 +15,9 @@ func Test_feeds(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
handlerClient := newHandlerClient(app.d)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -11,24 +10,18 @@ import (
|
|||
|
||||
func Test_geoTrack(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{},
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
"de": {
|
||||
Lang: "de",
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
"de": {
|
||||
Lang: "de",
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// First test (just with track)
|
||||
|
|
|
@ -16,9 +16,8 @@ func Test_geo(t *testing.T) {
|
|||
httpClient: fc.Client,
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
p := &post{
|
||||
|
|
10
http.go
10
http.go
|
@ -32,7 +32,7 @@ const (
|
|||
func (a *goBlog) startServer() (err error) {
|
||||
log.Println("Start server(s)...")
|
||||
// Load router
|
||||
a.d = a.buildRouter()
|
||||
a.reloadRouter()
|
||||
// Set basic middlewares
|
||||
h := alice.New()
|
||||
h = h.Append(middleware.Heartbeat("/ping"))
|
||||
|
@ -43,7 +43,9 @@ func (a *goBlog) startServer() (err error) {
|
|||
if a.httpsConfigured(false) {
|
||||
h = h.Append(a.securityHeaders)
|
||||
}
|
||||
finalHandler := h.Then(a.d)
|
||||
finalHandler := h.ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
a.d.ServeHTTP(w, r)
|
||||
})
|
||||
// Start Onion service
|
||||
if a.cfg.Server.Tor {
|
||||
go func() {
|
||||
|
@ -116,6 +118,10 @@ const (
|
|||
feedPath = ".{feed:(rss|json|atom)}"
|
||||
)
|
||||
|
||||
func (a *goBlog) reloadRouter() {
|
||||
a.d = a.buildRouter()
|
||||
}
|
||||
|
||||
func (a *goBlog) buildRouter() http.Handler {
|
||||
mapRouter := &maprouter.MapRouter{
|
||||
Handlers: map[string]http.Handler{},
|
||||
|
|
|
@ -15,7 +15,7 @@ func Test_httpLogsConfig(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
assert.Equal(t, false, app.cfg.Server.Logging)
|
||||
assert.Equal(t, "data/access.log", app.cfg.Server.LogFile)
|
||||
|
|
|
@ -170,6 +170,9 @@ func (a *goBlog) blogRouter(blog string, conf *configBlog) func(r chi.Router) {
|
|||
// Sitemap
|
||||
r.Group(a.blogSitemapRouter(conf))
|
||||
|
||||
// Settings
|
||||
r.Route(conf.getRelativePath(settingsPath), a.blogSettingsRouter(conf))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,3 +445,14 @@ func (a *goBlog) blogSitemapRouter(conf *configBlog) func(r chi.Router) {
|
|||
r.Get(conf.getRelativePath(sitemapBlogPostsPath), a.serveSitemapBlogPosts)
|
||||
}
|
||||
}
|
||||
|
||||
// Blog - Settings
|
||||
func (a *goBlog) blogSettingsRouter(_ *configBlog) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
r.Use(a.authMiddleware)
|
||||
r.Get("/", a.serveSettings)
|
||||
r.Post(settingsDeleteSectionPath, a.settingsDeleteSection)
|
||||
r.Post(settingsCreateSectionPath, a.settingsCreateSection)
|
||||
r.Post(settingsUpdateSectionPath, a.settingsUpdateSection)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,8 @@ func Test_indexNow(t *testing.T) {
|
|||
httpClient: fc.Client,
|
||||
}
|
||||
app.cfg.IndexNow = &configIndexNow{Enabled: true}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Create http router
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -22,35 +21,25 @@ func Test_indieAuthServer(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: newFakeHttpClient().Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.org",
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
User: &configUser{
|
||||
Name: "John Doe",
|
||||
Nick: "jdoe",
|
||||
},
|
||||
Cache: &configCache{
|
||||
Enable: false,
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://example.org"
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
}
|
||||
app.cfg.User = &configUser{
|
||||
Name: "John Doe",
|
||||
Nick: "jdoe",
|
||||
}
|
||||
app.cfg.Cache.Enable = false
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
app.initComponents(false)
|
||||
|
||||
app.ias.Client = newHandlerClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -15,22 +14,10 @@ func Test_checkIndieAuth(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: newFakeHttpClient().Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
|
|
8
main.go
8
main.go
|
@ -64,7 +64,7 @@ func main() {
|
|||
app.logErrAndQuit("Failed to load config file:", err.Error())
|
||||
return
|
||||
}
|
||||
if err = app.initConfig(); err != nil {
|
||||
if err = app.initConfig(false); err != nil {
|
||||
app.logErrAndQuit("Failed to init config:", err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -129,12 +129,6 @@ func main() {
|
|||
// Execute pre-start hooks
|
||||
app.preStartHooks()
|
||||
|
||||
// Initialize database
|
||||
if err = app.initDatabase(true); err != nil {
|
||||
app.logErrAndQuit("Failed to init database:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Link check tool after init of markdown
|
||||
if len(os.Args) >= 2 && os.Args[1] == "check" {
|
||||
app.initMarkdown()
|
||||
|
|
|
@ -14,9 +14,7 @@ func Test_micropubQuery(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Create a test post with tags
|
||||
|
|
|
@ -18,9 +18,7 @@ func Test_ntfySending(t *testing.T) {
|
|||
httpClient: fakeClient.Client,
|
||||
}
|
||||
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -25,9 +24,7 @@ func Test_postsDb(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
now := toLocalSafe(time.Now().String())
|
||||
|
@ -229,14 +226,9 @@ func Test_ftsWithoutTitle(t *testing.T) {
|
|||
// Added because there was a bug where there were no search results without title
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initMarkdown()
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
|
@ -261,14 +253,9 @@ func Test_postsPriority(t *testing.T) {
|
|||
// Added because there was a bug where there were no search results without title
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initMarkdown()
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
|
@ -312,14 +299,9 @@ func Test_postsPriority(t *testing.T) {
|
|||
|
||||
func Test_usesOfMediaFile(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
Path: "/test/abc",
|
||||
|
@ -368,8 +350,7 @@ func Test_replaceParams(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
Path: "/test/abc",
|
||||
|
@ -399,9 +380,7 @@ func Test_postDeletesParams(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
err := app.createPost(&post{
|
||||
|
|
|
@ -11,9 +11,8 @@ func Test_checkDeletedPosts(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Create a post
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -12,28 +11,18 @@ import (
|
|||
func Test_postsScheduler(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Sections: map[string]*configSection{
|
||||
"test": {},
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Sections: map[string]*configSection{
|
||||
"test": {},
|
||||
},
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
Micropub: &configMicropub{},
|
||||
Lang: "en",
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
|
|
|
@ -16,9 +16,7 @@ func Test_serveDate(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
|
@ -106,9 +104,7 @@ func Test_servePost(t *testing.T) {
|
|||
Username: "test",
|
||||
Password: "test",
|
||||
})
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
@ -16,36 +15,28 @@ func Test_privateMode(t *testing.T) {
|
|||
// Init
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "db.db"),
|
||||
},
|
||||
Server: &configServer{},
|
||||
PrivateMode: &configPrivateMode{
|
||||
Enabled: true,
|
||||
},
|
||||
User: &configUser{
|
||||
Name: "Test",
|
||||
Nick: "test",
|
||||
Password: "testpw",
|
||||
AppPasswords: []*configAppPassword{
|
||||
{
|
||||
Username: "testapp",
|
||||
Password: "pw",
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.PrivateMode = &configPrivateMode{Enabled: true}
|
||||
app.cfg.User =
|
||||
&configUser{
|
||||
Name: "Test",
|
||||
Nick: "test",
|
||||
Password: "testpw",
|
||||
AppPasswords: []*configAppPassword{
|
||||
{
|
||||
Username: "testapp",
|
||||
Password: "pw",
|
||||
},
|
||||
},
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
handler := alice.New(middleware.WithValue(blogKey, "en"), app.privateModeHandler).ThenFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -12,14 +11,9 @@ import (
|
|||
func Test_queue(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
time1 := time.Now()
|
||||
|
||||
|
@ -65,30 +59,3 @@ func Test_queue(t *testing.T) {
|
|||
require.Equal(t, []byte("1"), qi.content)
|
||||
|
||||
}
|
||||
|
||||
func Benchmark_queue(b *testing.B) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(b.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
err := app.enqueue("test", []byte("1"), time.Now())
|
||||
require.NoError(b, err)
|
||||
|
||||
b.Run("Peek with item", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = app.peekQueue(context.Background(), "test")
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Peek without item", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = app.peekQueue(context.Background(), "abc")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@ func Test_reactionsLowLevel(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
err := app.saveReaction("🖕", "/testpost")
|
||||
|
@ -97,9 +95,7 @@ func Test_reactionsHighLevel(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Send unsuccessful reaction
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
const settingsPath = "/settings"
|
||||
|
||||
func (a *goBlog) serveSettings(w http.ResponseWriter, r *http.Request) {
|
||||
blog, bc := a.getBlog(r)
|
||||
|
||||
sections := lo.Values(bc.Sections)
|
||||
sort.Slice(sections, func(i, j int) bool { return sections[i].Name < sections[j].Name })
|
||||
|
||||
a.render(w, r, a.renderSettings, &renderData{
|
||||
Data: &settingsRenderData{
|
||||
blog: blog,
|
||||
sections: sections,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const settingsDeleteSectionPath = "/deletesection"
|
||||
|
||||
func (a *goBlog) settingsDeleteSection(w http.ResponseWriter, r *http.Request) {
|
||||
blog, bc := a.getBlog(r)
|
||||
section := r.FormValue("sectionname")
|
||||
// Check if any post uses this section
|
||||
count, err := a.db.countPosts(&postsRequestConfig{
|
||||
blog: blog,
|
||||
sections: []string{section},
|
||||
})
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to check if section is still used", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if count > 0 {
|
||||
a.serveError(w, r, "Section is still used", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Delete section
|
||||
err = a.deleteSection(blog, section)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to delete section from the database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Reload sections
|
||||
err = a.loadSections()
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to reload section configuration from the database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
a.reloadRouter()
|
||||
a.cache.purge()
|
||||
http.Redirect(w, r, bc.getRelativePath(settingsPath), http.StatusFound)
|
||||
}
|
||||
|
||||
const settingsCreateSectionPath = "/createsection"
|
||||
|
||||
func (a *goBlog) settingsCreateSection(w http.ResponseWriter, r *http.Request) {
|
||||
blog, bc := a.getBlog(r)
|
||||
// Read values
|
||||
sectionName := r.FormValue("sectionname")
|
||||
sectionTitle := r.FormValue("sectiontitle")
|
||||
if sectionName == "" || sectionTitle == "" {
|
||||
a.serveError(w, r, "Missing values for name or title", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Create section
|
||||
section := &configSection{
|
||||
Name: sectionName,
|
||||
Title: sectionTitle,
|
||||
}
|
||||
err := a.addSection(blog, section)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to insert section into database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Reload sections
|
||||
err = a.loadSections()
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to reload section configuration from the database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
a.reloadRouter()
|
||||
a.cache.purge()
|
||||
http.Redirect(w, r, bc.getRelativePath(settingsPath), http.StatusFound)
|
||||
}
|
||||
|
||||
const settingsUpdateSectionPath = "/updatesection"
|
||||
|
||||
func (a *goBlog) settingsUpdateSection(w http.ResponseWriter, r *http.Request) {
|
||||
blog, bc := a.getBlog(r)
|
||||
// Read values
|
||||
sectionName := r.FormValue("sectionname")
|
||||
sectionTitle := r.FormValue("sectiontitle")
|
||||
if sectionName == "" || sectionTitle == "" {
|
||||
a.serveError(w, r, "Missing values for name or title", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
sectionDescription := r.FormValue("sectiondescription")
|
||||
sectionPathTemplate := r.FormValue("sectionpathtemplate")
|
||||
sectionShowFull := r.FormValue("sectionshowfull") == "on"
|
||||
// Create section
|
||||
section := &configSection{
|
||||
Name: sectionName,
|
||||
Title: sectionTitle,
|
||||
Description: sectionDescription,
|
||||
PathTemplate: sectionPathTemplate,
|
||||
ShowFull: sectionShowFull,
|
||||
}
|
||||
err := a.updateSection(blog, sectionName, section)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to update section in database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Reload sections
|
||||
err = a.loadSections()
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to reload section configuration from the database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
a.reloadRouter()
|
||||
a.cache.purge()
|
||||
http.Redirect(w, r, bc.getRelativePath(settingsPath), http.StatusFound)
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (a *goBlog) getSettingValue(name string) (string, error) {
|
||||
row, err := a.db.queryRow("select value from settings where name = @name", sql.Named("name", name))
|
||||
if err != nil {
|
||||
return "",
|
||||
err
|
||||
}
|
||||
var value string
|
||||
err = row.Scan(&value)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return "", nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (a *goBlog) saveSettingValue(name, value string) error {
|
||||
_, err := a.db.exec(
|
||||
"insert into settings (name, value) values (@name, @value) on conflict (name) do update set value = @value2",
|
||||
sql.Named("name", name),
|
||||
sql.Named("value", value),
|
||||
sql.Named("value2", value),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *goBlog) loadSections() error {
|
||||
for blog, bc := range a.cfg.Blogs {
|
||||
sections, err := a.getSections(blog)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bc.Sections = sections
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *goBlog) getSections(blog string) (map[string]*configSection, error) {
|
||||
rows, err := a.db.query("select name, title, description, pathtemplate, showfull from sections where blog = @blog", sql.Named("blog", blog))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sections := map[string]*configSection{}
|
||||
for rows.Next() {
|
||||
section := &configSection{}
|
||||
err = rows.Scan(§ion.Name, §ion.Title, §ion.Description, §ion.PathTemplate, §ion.ShowFull)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sections[section.Name] = section
|
||||
}
|
||||
re |