mirror of https://github.com/jlelse/GoBlog
Add a list of features to the docs and some small code improvements
This commit is contained in:
parent
9815abf4bb
commit
e9632720a3
|
@ -17,7 +17,7 @@ import (
|
||||||
const defaultBlogrollPath = "/blogroll"
|
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(blogKey).(string)
|
blog, bc := a.getBlog(r)
|
||||||
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)
|
||||||
})
|
})
|
||||||
|
@ -26,7 +26,7 @@ func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) {
|
||||||
a.serveError(w, r, "", http.StatusInternalServerError)
|
a.serveError(w, r, "", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c := a.cfg.Blogs[blog].Blogroll
|
c := bc.Blogroll
|
||||||
can := a.getRelativePath(blog, defaultIfEmpty(c.Path, defaultBlogrollPath))
|
can := a.getRelativePath(blog, defaultIfEmpty(c.Path, defaultBlogrollPath))
|
||||||
a.render(w, r, templateBlogroll, &renderData{
|
a.render(w, r, templateBlogroll, &renderData{
|
||||||
BlogString: blog,
|
BlogString: blog,
|
||||||
|
@ -41,7 +41,7 @@ func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) {
|
||||||
blog := r.Context().Value(blogKey).(string)
|
blog, _ := a.getBlog(r)
|
||||||
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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,8 +22,7 @@ func (a *goBlog) initBlogStats() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) serveBlogStats(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveBlogStats(w http.ResponseWriter, r *http.Request) {
|
||||||
blog := r.Context().Value(blogKey).(string)
|
blog, bc := a.getBlog(r)
|
||||||
bc := a.cfg.Blogs[blog]
|
|
||||||
canonical := bc.getRelativePath(defaultIfEmpty(bc.BlogStats.Path, defaultBlogStatsPath))
|
canonical := bc.getRelativePath(defaultIfEmpty(bc.BlogStats.Path, defaultBlogStatsPath))
|
||||||
a.render(w, r, templateBlogStats, &renderData{
|
a.render(w, r, templateBlogStats, &renderData{
|
||||||
BlogString: blog,
|
BlogString: blog,
|
||||||
|
|
12
config.go
12
config.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -375,3 +376,14 @@ func (a *goBlog) httpsConfigured(checkAddress bool) bool {
|
||||||
a.cfg.Server.SecurityHeaders ||
|
a.cfg.Server.SecurityHeaders ||
|
||||||
(checkAddress && strings.HasPrefix(a.cfg.Server.PublicAddress, "https"))
|
(checkAddress && strings.HasPrefix(a.cfg.Server.PublicAddress, "https"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *goBlog) getBlog(r *http.Request) (string, *configBlog) {
|
||||||
|
if r == nil {
|
||||||
|
return a.cfg.DefaultBlog, a.cfg.Blogs[a.cfg.DefaultBlog]
|
||||||
|
}
|
||||||
|
blog := r.Context().Value(blogKey).(string)
|
||||||
|
if blog == "" {
|
||||||
|
return a.cfg.DefaultBlog, a.cfg.Blogs[a.cfg.DefaultBlog]
|
||||||
|
}
|
||||||
|
return blog, a.cfg.Blogs[blog]
|
||||||
|
}
|
||||||
|
|
10
contact.go
10
contact.go
|
@ -13,8 +13,8 @@ import (
|
||||||
const defaultContactPath = "/contact"
|
const defaultContactPath = "/contact"
|
||||||
|
|
||||||
func (a *goBlog) serveContactForm(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveContactForm(w http.ResponseWriter, r *http.Request) {
|
||||||
blog := r.Context().Value(blogKey).(string)
|
blog, bc := a.getBlog(r)
|
||||||
cc := a.cfg.Blogs[blog].Contact
|
cc := bc.Contact
|
||||||
a.render(w, r, templateContact, &renderData{
|
a.render(w, r, templateContact, &renderData{
|
||||||
BlogString: blog,
|
BlogString: blog,
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
|
@ -58,12 +58,12 @@ func (a *goBlog) sendContactSubmission(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
_, _ = message.WriteString(formMessage)
|
_, _ = message.WriteString(formMessage)
|
||||||
// Send submission
|
// Send submission
|
||||||
blog := r.Context().Value(blogKey).(string)
|
blog, bc := a.getBlog(r)
|
||||||
if cc := a.cfg.Blogs[blog].Contact; cc != nil && cc.SMTPHost != "" && cc.EmailFrom != "" && cc.EmailTo != "" {
|
if cc := bc.Contact; cc != nil && cc.SMTPHost != "" && cc.EmailFrom != "" && cc.EmailTo != "" {
|
||||||
// Build email
|
// Build email
|
||||||
var email bytes.Buffer
|
var email bytes.Buffer
|
||||||
if ef := cc.EmailFrom; ef != "" {
|
if ef := cc.EmailFrom; ef != "" {
|
||||||
_, _ = fmt.Fprintf(&email, "From: %s <%s>", defaultIfEmpty(a.cfg.Blogs[blog].Title, "GoBlog"), cc.EmailFrom)
|
_, _ = fmt.Fprintf(&email, "From: %s <%s>", defaultIfEmpty(bc.Title, "GoBlog"), cc.EmailFrom)
|
||||||
_, _ = fmt.Fprintln(&email)
|
_, _ = fmt.Fprintln(&email)
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintf(&email, "To: %s", cc.EmailTo)
|
_, _ = fmt.Fprintf(&email, "To: %s", cc.EmailTo)
|
||||||
|
|
|
@ -10,7 +10,47 @@ License: MIT License
|
||||||
[GitHub mirror](https://github.com/jlelse/GoBlog)
|
[GitHub mirror](https://github.com/jlelse/GoBlog)
|
||||||
[Codeberg mirror](https://codeberg.org/jlelse/GoBlog)
|
[Codeberg mirror](https://codeberg.org/jlelse/GoBlog)
|
||||||
|
|
||||||
More information about GoBlog:
|
## Features
|
||||||
|
|
||||||
|
Here's an (incomplete) list of features:
|
||||||
|
|
||||||
|
- Single user with multiple blogs
|
||||||
|
- Publish, edit and delete Markdown posts using Micropub or the web-based editor
|
||||||
|
- Editor with live preview
|
||||||
|
- Drafts, private and unlisted posts
|
||||||
|
- SQLite database for storing posts and data
|
||||||
|
- Built-in full-text search
|
||||||
|
- Micropub with media endpoint for uploads
|
||||||
|
- Local storage for uploads or remote storage via FTP or BunnyCDN
|
||||||
|
- Automatic image resizing and compression
|
||||||
|
- Uploads possible via the web-based editor
|
||||||
|
- Send and receive Webmentions
|
||||||
|
- Webmention-based commenting
|
||||||
|
- IndieAuth
|
||||||
|
- Login with your own blog as an identity on the internet
|
||||||
|
- Two-factor authentication
|
||||||
|
- ActivityPub
|
||||||
|
- Publish posts to the Fediverse (Mastodon etc.)
|
||||||
|
- ActivityPub-based commenting
|
||||||
|
- Web feeds
|
||||||
|
- Multiple feed formats: RSS, Atom, JSON
|
||||||
|
- Feeds on any archive page
|
||||||
|
- Sitemap
|
||||||
|
- Automatic HTTPS using Let's Encrypt
|
||||||
|
- Tor Hidden Service
|
||||||
|
- Tailscale integration for private blogs with HTTPS
|
||||||
|
- Fast in-memory caching for even faster performance
|
||||||
|
- Automatic asset minification of HTML, CSS and JavaScript
|
||||||
|
- Statistics page with information about posts
|
||||||
|
- Map page with a map of all posts with a location
|
||||||
|
- Option to create post aliases for automatic redirects
|
||||||
|
- Redirects using regular expressions
|
||||||
|
- Hooks to execute custom commands on certain events
|
||||||
|
- Short URLs with option for a separate short domain
|
||||||
|
- Command to check for broken links
|
||||||
|
- Command to export all posts to Markdown files
|
||||||
|
|
||||||
|
## More information about GoBlog:
|
||||||
|
|
||||||
- [How to install and run GoBlog](./install.md)
|
- [How to install and run GoBlog](./install.md)
|
||||||
- [How to build GoBlog](./build.md)
|
- [How to build GoBlog](./build.md)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -99,7 +99,7 @@ require (
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
||||||
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 // indirect
|
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c // indirect
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -682,8 +682,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c h1:QOfDMdrf/UwlVR0UBq2Mpr58UzNtvgJRXA4BgPfFACs=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
||||||
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
||||||
|
|
|
@ -8,6 +8,9 @@ import (
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Make sure the httpsCache type implements the Cache interface
|
||||||
|
var _ autocert.Cache = &httpsCache{}
|
||||||
|
|
||||||
type httpsCache struct {
|
type httpsCache struct {
|
||||||
db *database
|
db *database
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *goBlog) serveOpenSearch(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveOpenSearch(w http.ResponseWriter, r *http.Request) {
|
||||||
blog := r.Context().Value(blogKey).(string)
|
_, b := a.getBlog(r)
|
||||||
b := a.cfg.Blogs[blog]
|
|
||||||
title := a.renderMdTitle(b.Title)
|
title := a.renderMdTitle(b.Title)
|
||||||
sURL := a.getFullAddress(b.getRelativePath(defaultIfEmpty(b.Search.Path, defaultSearchPath)))
|
sURL := a.getFullAddress(b.getRelativePath(defaultIfEmpty(b.Search.Path, defaultSearchPath)))
|
||||||
xml := fmt.Sprintf("<?xml version=\"1.0\"?><OpenSearchDescription xmlns=\"http://a9.com/-/spec/opensearch/1.1/\" xmlns:moz=\"http://www.mozilla.org/2006/browser/search/\">"+
|
xml := fmt.Sprintf("<?xml version=\"1.0\"?><OpenSearchDescription xmlns=\"http://a9.com/-/spec/opensearch/1.1/\" xmlns:moz=\"http://www.mozilla.org/2006/browser/search/\">"+
|
||||||
|
|
|
@ -12,14 +12,14 @@ import (
|
||||||
func TestMapRouter(t *testing.T) {
|
func TestMapRouter(t *testing.T) {
|
||||||
router := &MapRouter{
|
router := &MapRouter{
|
||||||
DefaultHandler: http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
DefaultHandler: http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
rw.Write([]byte("Default"))
|
_, _ = rw.Write([]byte("Default"))
|
||||||
}),
|
}),
|
||||||
Handlers: map[string]http.Handler{
|
Handlers: map[string]http.Handler{
|
||||||
"a.example.org": http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
"a.example.org": http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
rw.Write([]byte("a"))
|
_, _ = rw.Write([]byte("a"))
|
||||||
}),
|
}),
|
||||||
"b.example.org": http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
"b.example.org": http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
rw.Write([]byte("b"))
|
_, _ = rw.Write([]byte("b"))
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue