Use yaml for config, build filename and slug using configurable templates

master
Jan-Lukas Else 2020-03-26 15:45:46 +01:00
parent 442395a83c
commit cbf53ccb23
4 changed files with 162 additions and 63 deletions

155
config.go
View File

@ -2,8 +2,9 @@ package main
import ( import (
"errors" "errors"
"github.com/caarlos0/env/v6" "gopkg.in/yaml.v2"
"log" "log"
"os"
"strings" "strings"
) )
@ -17,6 +18,8 @@ var (
SelectedCdn Cdn SelectedCdn Cdn
SelectedNotificationServices NotificationServices SelectedNotificationServices NotificationServices
SelectedImageCompression ImageCompression SelectedImageCompression ImageCompression
DefaultLanguage string
Languages map[string]Language
) )
type SyndicationTarget struct { type SyndicationTarget struct {
@ -24,41 +27,109 @@ type SyndicationTarget struct {
Name string `json:"name"` Name string `json:"name"`
} }
type config struct { type YamlConfig struct {
BlogUrl string `env:"BLOG_URL,required"` BlogUrl string `yaml:"blogUrl"`
BaseUrl string `env:"BASE_URL,required"` BaseUrl string `yaml:"baseUrl"`
MediaUrl string `env:"MEDIA_URL"` MediaUrl string `yaml:"mediaUrl"`
GitFilepath string `env:"GIT_FILEPATH" envDefault:"/tmp/micropubrepo"` DefaultLanguage string `yaml:"defaultLang"`
GitUrl string `env:"GIT_URL"` Languages map[string]Language `yaml:"languages"`
GitUsername string `env:"GIT_USERNAME"` Git GitConfig `yaml:"git"`
GitPassword string `env:"GIT_PASSWORD"` BunnyCdn BunnyCdnConfig `yaml:"bunnyCdn"`
GitAuthorName string `env:"GIT_AUTHOR_NAME" envDefault:"hugo-micropub"` Telegram TelegramConfig `yaml:"telegram"`
GitAuthorEmail string `env:"GIT_AUTHOR_EMAIL" envDefault:"hugo-micropub@example.com"` Tinify TinifyConfig `yaml:"tinify"`
BunnyCdnKey string `env:"BUNNY_CDN_KEY"` IgnoredWebmentionUrls []string `yaml:"ignoreWebmention"`
BunnyCdnStorageKey string `env:"BUNNY_CDN_STORAGE_KEY"` SyndicationTargets []string `yaml:"syndication"`
BunnyCdnStorageName string `env:"BUNNY_CDN_STORAGE_NAME"` }
TelegramUserId int `env:"TELEGRAM_USER_ID"`
TelegramBotToken string `env:"TELEGRAM_BOT_TOKEN"` type BunnyCdnConfig struct {
IgnoredWebmentionUrls []string `env:"WEBMENTION_IGNORED" envSeparator:","` Key string `yaml:"key"`
SyndicationTargets []string `env:"SYNDICATION" envSeparator:","` StorageKey string `yaml:"storageKey"`
TinifyKey string `env:"TINIFY_KEY"` StorageName string `yaml:"storageName"`
}
type TelegramConfig struct {
UserId int `yaml:"userId"`
BotToken string `yaml:"botToken"`
}
type TinifyConfig struct {
Key string `yaml:"key"`
}
type GitConfig struct {
Filepath string `yaml:"filepath"`
Url string `yaml:"url"`
Username string `yaml:"username"`
Password string `yaml:"password"`
AuthorName string `yaml:"authorName"`
AuthorEmail string `yaml:"authorEmail"`
}
type Language struct {
BlogUrl string `yaml:"blogUrl"`
ContentDir string `yaml:"contentDir"`
DefaultSection string `yaml:"defaultSection"`
Sections map[string]Section `yaml:"sections"`
}
type Section struct {
FilenameTemplate string `yaml:"file"`
LocationTemplate string `yaml:"location"`
} }
func initConfig() (err error) { func initConfig() (err error) {
cfg := config{} configFileName, configSet := os.LookupEnv("CONFIG")
if err := env.Parse(&cfg); err != nil { if !configSet {
return errors.New("failed to parse config, probably not all required env vars set") configFileName = "config.yml"
}
configFile, err := os.Open(configFileName)
if err != nil {
return errors.New("failed to open config file")
}
cfg := YamlConfig{}
err = yaml.NewDecoder(configFile).Decode(&cfg)
if err != nil {
return errors.New("failed to parse yaml")
} }
// Blog URL (required) // Blog URL (required)
if len(cfg.BlogUrl) < 1 {
return errors.New("blogUrl not configured")
}
if !strings.HasSuffix(cfg.BlogUrl, "/") { if !strings.HasSuffix(cfg.BlogUrl, "/") {
return errors.New("missing trailing slash in BLOG_URL") return errors.New("missing trailing slash in configured blogUrl")
} }
BlogUrl = cfg.BlogUrl BlogUrl = cfg.BlogUrl
// Media endpoint // Media endpoint (required)
if len(cfg.BaseUrl) < 1 {
return errors.New("baseUrl not configured")
}
if len(cfg.MediaUrl) < 1 {
return errors.New("mediaUrl not configured")
}
if !strings.HasSuffix(cfg.BaseUrl, "/") { if !strings.HasSuffix(cfg.BaseUrl, "/") {
return errors.New("missing trailing slash in BASE_URL") return errors.New("missing trailing slash in configured baseUrl")
} }
MediaEndpointUrl = cfg.BaseUrl + "media" MediaEndpointUrl = cfg.BaseUrl + "media"
// Languages (required)
if len(cfg.DefaultLanguage) < 1 {
return errors.New("no default language configured")
}
DefaultLanguage = cfg.DefaultLanguage
if len(cfg.Languages) > 0 {
for _, lang := range cfg.Languages {
if len(lang.ContentDir) < 1 || len(lang.DefaultSection) < 1 || len(lang.Sections) < 1 {
return errors.New("language not completely configured")
}
for _, section := range lang.Sections {
if len(section.FilenameTemplate) < 1 || len(section.LocationTemplate) < 1 {
return errors.New("section not completely configured")
}
}
}
Languages = cfg.Languages
} else {
return errors.New("no languages configured")
}
// Ignored Webmention URLs (optional) // Ignored Webmention URLs (optional)
IgnoredWebmentionUrls = cfg.IgnoredWebmentionUrls IgnoredWebmentionUrls = cfg.IgnoredWebmentionUrls
// Syndication Targets (optional) // Syndication Targets (optional)
@ -73,14 +144,14 @@ func initConfig() (err error) {
// Find selected storage // Find selected storage
SelectedStorage = func() Storage { SelectedStorage = func() Storage {
// Git // Git
if len(cfg.GitFilepath) > 0 && len(cfg.GitUrl) > 0 && len(cfg.GitUsername) > 0 && len(cfg.GitPassword) > 0 && len(cfg.GitAuthorName) > 0 && len(cfg.GitAuthorEmail) > 0 { if len(cfg.Git.Filepath) > 0 && len(cfg.Git.Url) > 0 && len(cfg.Git.Username) > 0 && len(cfg.Git.Password) > 0 && len(cfg.Git.AuthorName) > 0 && len(cfg.Git.AuthorEmail) > 0 {
return &Git{ return &Git{
filepath: cfg.GitFilepath, filepath: cfg.Git.Filepath,
url: cfg.GitUrl, url: cfg.Git.Url,
username: cfg.GitUsername, username: cfg.Git.Username,
password: cfg.GitPassword, password: cfg.Git.Password,
name: cfg.GitAuthorName, name: cfg.Git.AuthorName,
email: cfg.GitAuthorEmail, email: cfg.Git.AuthorEmail,
} }
} }
return nil return nil
@ -92,10 +163,10 @@ func initConfig() (err error) {
SelectedMediaStorage = func() MediaStorage { SelectedMediaStorage = func() MediaStorage {
// BunnyCDN // BunnyCDN
// MEDIA_URL needs trailing slash too // MEDIA_URL needs trailing slash too
if len(cfg.BunnyCdnStorageKey) > 0 && len(cfg.BunnyCdnStorageName) > 0 && len(cfg.MediaUrl) > 0 && strings.HasSuffix(cfg.MediaUrl, "/") { if len(cfg.BunnyCdn.StorageKey) > 0 && len(cfg.BunnyCdn.StorageName) > 0 && len(cfg.MediaUrl) > 0 && strings.HasSuffix(cfg.MediaUrl, "/") {
return &BunnyCdnStorage{ return &BunnyCdnStorage{
key: cfg.BunnyCdnStorageKey, key: cfg.BunnyCdn.StorageKey,
storageZoneName: cfg.BunnyCdnStorageName, storageZoneName: cfg.BunnyCdn.StorageName,
baseLocation: cfg.MediaUrl, baseLocation: cfg.MediaUrl,
} }
} }
@ -107,8 +178,8 @@ func initConfig() (err error) {
// Find selected CDN (optional) // Find selected CDN (optional)
SelectedCdn = func() Cdn { SelectedCdn = func() Cdn {
// BunnyCDN // BunnyCDN
if len(cfg.BunnyCdnKey) > 0 { if len(cfg.BunnyCdn.Key) > 0 {
return &BunnyCdn{key: cfg.BunnyCdnKey} return &BunnyCdn{key: cfg.BunnyCdn.Key}
} }
return nil return nil
}() }()
@ -119,10 +190,10 @@ func initConfig() (err error) {
SelectedNotificationServices = func() NotificationServices { SelectedNotificationServices = func() NotificationServices {
var notificationServices []NotificationService = nil var notificationServices []NotificationService = nil
// Telegram // Telegram
if cfg.TelegramUserId > 0 && len(cfg.TelegramBotToken) > 0 { if cfg.Telegram.UserId > 0 && len(cfg.Telegram.BotToken) > 0 {
notificationServices = append(notificationServices, &Telegram{ notificationServices = append(notificationServices, &Telegram{
userId: cfg.TelegramUserId, userId: cfg.Telegram.UserId,
botToken: cfg.TelegramBotToken, botToken: cfg.Telegram.BotToken,
}) })
} }
return notificationServices return notificationServices
@ -133,9 +204,9 @@ func initConfig() (err error) {
// Find configured image compression service (optional) // Find configured image compression service (optional)
SelectedImageCompression = func() ImageCompression { SelectedImageCompression = func() ImageCompression {
// Tinify // Tinify
if len(cfg.TinifyKey) > 0 { if len(cfg.Tinify.Key) > 0 {
return &Tinify{ return &Tinify{
key: cfg.TinifyKey, key: cfg.Tinify.Key,
} }
} }
return nil return nil

View File

@ -9,6 +9,7 @@ import (
"math/rand" "math/rand"
"net/http" "net/http"
"strings" "strings"
"text/template"
"time" "time"
) )
@ -258,26 +259,57 @@ func computeExtraSettings(entry *Entry) error {
random := generateRandomString(now, 5) random := generateRandomString(now, 5)
entry.slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), random) entry.slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), random)
} }
// Compute filename and location // Set language
contentFolder := "content" if len(entry.language) == 0 {
localizedBlogUrl := BlogUrl entry.language = DefaultLanguage
if len(entry.language) > 0 && entry.language != "en" {
// Append language to content folder: "content-de" for language "de"
contentFolder += "-" + entry.language
// Append language to BlogUrl
localizedBlogUrl += entry.language + "/"
} }
if len(entry.section) < 1 { // Compute filename and location
entry.section = "micro" lang := Languages[entry.language]
contentFolder := lang.ContentDir
localizedBlogUrl := BlogUrl
if len(lang.BlogUrl) != 0 {
localizedBlogUrl = lang.BlogUrl
}
if len(entry.section) == 0 {
entry.section = lang.DefaultSection
} }
entry.section = strings.ToLower(entry.section) entry.section = strings.ToLower(entry.section)
if entry.section == "thoughts" || entry.section == "links" || entry.section == "micro" { section := lang.Sections[entry.section]
entry.filename = fmt.Sprintf("%v/%v/%02d/%02d/%v.md", contentFolder, entry.section, now.Year(), int(now.Month()), entry.slug) pathVars := struct {
entry.location = fmt.Sprintf("%v%v/%02d/%02d/%v/", localizedBlogUrl, entry.section, now.Year(), int(now.Month()), entry.slug) LocalContentFolder string
} else { LocalBlogUrl string
entry.filename = fmt.Sprintf("%v/%v/%v.md", contentFolder, entry.section, entry.slug) Year int
entry.location = fmt.Sprintf("%v%v/%v/", localizedBlogUrl, entry.section, entry.slug) Month int
Slug string
Section string
}{
LocalContentFolder: contentFolder,
LocalBlogUrl: localizedBlogUrl,
Year: now.Year(),
Month: int(now.Month()),
Slug: entry.slug,
Section: entry.section,
} }
filenameTmpl, err := template.New("filename").Parse(section.FilenameTemplate)
if err != nil {
return errors.New("failed to parse filename template")
}
filename := new(bytes.Buffer)
err = filenameTmpl.Execute(filename, pathVars)
if err != nil {
return errors.New("failed to execute filename template")
}
entry.filename = filename.String()
locationTmpl, err := template.New("location").Parse(section.LocationTemplate)
if err != nil {
return errors.New("failed to parse location template")
}
location := new(bytes.Buffer)
err = locationTmpl.Execute(location, pathVars)
if err != nil {
return errors.New("failed to execute location template")
}
entry.location = location.String()
return nil return nil
} }

1
go.mod
View File

@ -4,7 +4,6 @@ go 1.14
require ( require (
codeberg.org/jlelse/tinify v0.0.0-20200123222407-7fc9c21822b0 codeberg.org/jlelse/tinify v0.0.0-20200123222407-7fc9c21822b0
github.com/caarlos0/env/v6 v6.2.1
github.com/go-git/go-git/v5 v5.0.0 github.com/go-git/go-git/v5 v5.0.0
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.2.8
willnorris.com/go/webmention v0.0.0-20200126231626-5a55fff6bf71 willnorris.com/go/webmention v0.0.0-20200126231626-5a55fff6bf71

5
go.sum
View File

@ -8,8 +8,6 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/caarlos0/env/v6 v6.2.1 h1:/bFpX1dg4TNioJjg7mrQaSrBoQvRfLUHNfXivdFbbEo=
github.com/caarlos0/env/v6 v6.2.1/go.mod h1:3LpmfcAYCG6gCiSgDLaFR5Km1FRpPwFvBbRcjHar6Sw=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -51,9 +49,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
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=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=