Add 'tomlo', configure cowyo via TOML

This commit is contained in:
Daniel Heath 2018-05-27 21:51:33 +10:00
parent f567f86bab
commit 3b91b699e3
7 changed files with 320 additions and 0 deletions

14
cmd/tomlo/tomlo.go Normal file
View File

@ -0,0 +1,14 @@
package main
import (
"github.com/schollz/cowyo/config"
)
func main() {
c, err := config.ParseFile("multisite_sample.toml")
if err != nil {
panic(err)
}
panic(c.ListenAndServe())
}

55
config/config.go Normal file
View File

@ -0,0 +1,55 @@
package config
import (
"github.com/BurntSushi/toml"
)
func ParseFile(path string) (Config, error) {
c := Config{}
if _, err := toml.DecodeFile("multisite_sample.toml", &c); err != nil {
// handle error
return c, err
}
c.SetDefaults()
c.Validate()
return c, nil
}
type Config struct {
Default SiteConfig
Sites []SiteConfig
}
type SiteConfig struct {
Host *string
Port *int
DataDir *string
DefaultPage *string
AllowInsecureMarkup *bool
Lock *string
DebounceSave *int
Diary *bool
AccessCode *string
FileUploadsAllowed *bool
MaxFileUploadMb *uint
MaxDocumentLength *uint
TLS *TLSConfig
CookieKeys []CookieKey
}
type TLSConfig struct {
CertPath string
KeyPath string
Port int
}
type CookieKey struct {
AuthenticateBase64 string
EncryptBase64 string
}
func (c Config) Validate() {
for _, v := range c.Sites {
v.sessionStore()
}
}

102
config/defaults.go Normal file
View File

@ -0,0 +1,102 @@
package config
import (
"encoding/base64"
"crypto/rand"
)
var DefaultSiteConfig SiteConfig
func makeAuthKey() string {
secret := make([]byte, 32)
_, err := rand.Read(secret)
if err != nil {
panic(err)
}
return base64.StdEncoding.EncodeToString(secret)
}
func init() {
host := "*"
port := 8050
debounce := 500
dataDir := "data"
empty := ""
zer := uint(0)
lots := uint(100000000)
fal := false
ck := CookieKey{
AuthenticateBase64: "",
EncryptBase64: "",
}
DefaultSiteConfig = SiteConfig{
Host:&host,
Port:&port,
DataDir:&dataDir,
DebounceSave:&debounce,
CookieKeys: []CookieKey{ck},
DefaultPage:&empty,
AllowInsecureMarkup:&fal,
Lock:&empty,
Diary:&fal,
AccessCode:&empty,
FileUploadsAllowed:&fal,
MaxFileUploadMb:&zer,
MaxDocumentLength:&lots,
}
}
func copyDefaults(base, defaults *SiteConfig) {
if base.Host == nil {
base.Host = defaults.Host
}
if base.Port == nil {
base.Port = defaults.Port
}
if base.DataDir == nil {
base.DataDir = defaults.DataDir
}
if base.DefaultPage == nil {
base.DefaultPage = defaults.DefaultPage
}
if base.AllowInsecureMarkup == nil {
base.AllowInsecureMarkup = defaults.AllowInsecureMarkup
}
if base.Lock == nil {
base.Lock = defaults.Lock
}
if base.DebounceSave == nil {
base.DebounceSave = defaults.DebounceSave
}
if base.Diary == nil {
base.Diary = defaults.Diary
}
if base.AccessCode == nil {
base.AccessCode = defaults.AccessCode
}
if base.FileUploadsAllowed == nil {
base.FileUploadsAllowed = defaults.FileUploadsAllowed
}
if base.MaxFileUploadMb == nil {
base.MaxFileUploadMb = defaults.MaxFileUploadMb
}
if base.MaxDocumentLength == nil {
base.MaxDocumentLength = defaults.MaxDocumentLength
}
if base.TLS == nil {
base.TLS = defaults.TLS
}
if base.CookieKeys == nil {
base.CookieKeys = defaults.CookieKeys
}
}
func (c *Config) SetDefaults() {
copyDefaults(&c.Default, &DefaultSiteConfig)
for i := range c.Sites {
copyDefaults(&c.Sites[i], &c.Default)
}
}

105
config/http.go Normal file
View File

@ -0,0 +1,105 @@
package config
import (
"fmt"
"log"
"encoding/base64"
"net/http"
"github.com/gin-contrib/sessions"
"github.com/jcelliott/lumber"
"github.com/schollz/cowyo/server"
"strings"
)
func (c Config) ListenAndServe() error {
insecurePorts := map[int]bool{}
securePorts := map[int]bool{}
err := make(chan error)
for _, s := range c.Sites {
if !insecurePorts[*s.Port] {
insecurePorts[*s.Port] = true
go func(s SiteConfig) {
err <- http.ListenAndServe(fmt.Sprintf("localhost:%d", *s.Port), c)
}(s)
}
if s.TLS != nil && !securePorts[s.TLS.Port] {
securePorts[s.TLS.Port] = true
go func(s SiteConfig) {
err <- http.ListenAndServeTLS(
fmt.Sprintf("localhost:%d", s.TLS.Port),
s.TLS.CertPath,
s.TLS.KeyPath,
c,
)
}(s)
}
}
for {
return <- err
}
return nil
}
func (c Config) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
for i := range c.Sites {
if c.Sites[i].MatchesRequest(r) {
c.Sites[i].Handle(rw, r)
return
}
}
http.NotFound(rw, r)
}
func (s SiteConfig) MatchesRequest(r *http.Request) bool {
sh := *s.Host
if strings.HasPrefix(sh, "*") {
return strings.HasSuffix(r.Host, sh[1:])
}
return sh == r.Host
}
func (s SiteConfig) sessionStore() sessions.Store {
keys := [][]byte{}
for _, k := range s.CookieKeys {
key, err := base64.StdEncoding.DecodeString(k.AuthenticateBase64)
if err != nil {
panic(err)
}
if len(key) != 32 {
log.Panicf("AuthenticateBase64 key %s must be 32 bytes; suggest %s", k.AuthenticateBase64, makeAuthKey())
}
keys = append(keys, key)
key, err = base64.StdEncoding.DecodeString(k.EncryptBase64)
if err != nil {
panic(err)
}
if len(key) != 32 {
log.Panicf("EncryptBase64 key %s must be 32 bytes, suggest %s", k.EncryptBase64, makeAuthKey())
}
keys = append(keys, key)
}
return sessions.NewCookieStore(keys...)
}
func (s SiteConfig) Handle(rw http.ResponseWriter, r *http.Request) {
dataDir := strings.Replace(*s.DataDir, "${HOST}", r.Host, -1)
router := server.Site{
PathToData: dataDir,
Css: []byte{},
DefaultPage: *s.DefaultPage,
DefaultPassword: *s.Lock,
Debounce: *s.DebounceSave,
Diary: *s.Diary,
SessionStore: s.sessionStore(),
SecretCode: *s.AccessCode,
AllowInsecure: *s.AllowInsecureMarkup,
Fileuploads: *s.MaxFileUploadMb > 0,
MaxUploadSize: *s.MaxFileUploadMb,
Logger: lumber.NewConsoleLogger(server.LogLevel),
MaxDocumentSize: *s.MaxDocumentLength,
}.Router()
router.ServeHTTP(rw, r)
}

40
multisite_sample.toml Normal file
View File

@ -0,0 +1,40 @@
[default]
dataDir = "root_data/${HOST}"
maxDocumentLength = 100000000
# Specify multiple times to change keys without expiring sessions
[[default.CookieKeys]]
authenticateBase64 = "RpW4LjGCPNOx75G8DrywmzlEHLB/ISXCAAayZ47Ifkc="
encryptBase64 = "ofCKkrfosQb5T4cvz7R5IMP4BQUDHOPsLSMZZy2CUOA="
[[sites]]
host = "nerdy.party"
dataDir = "somewhere else"
# theme = "custom.css" # TODO: Theme support. Would prefer to move to a complete directory replacement.
defaultPage = "welcome"
allowInsecureMarkup = true
lock = "1234"
debounceSave = 600
diary = true
accessCode = "correct horse battery staple"
fileUploadsAllowed = true
maxFileUploadMb = 6
port = 8090
#[sites.TLS]
# TODO: ACME support eg letsencrypt
#certPath = "path.crt"
#keyPath = "path.key"
#port = 8443
[[sites]]
host = "cowyo.com"
allowInsecureMarkup = false
fileUploadsAllowed = false
port = 8090
# Catchall config
[[sites]]
host = "*"
port = 8100
cookieSecret = "ASADFGKLJSH+4t4cC2X3f7GzsLZ+wtST67qoLuErpugJz06ZIpdDHEjcMxR+XOLM"

View File

@ -2,6 +2,9 @@
package server
import "github.com/jcelliott/lumber"
func init() {
hotTemplateReloading = true
LogLevel = lumber.TRACE
}

View File

@ -51,6 +51,7 @@ func (s *Site) defaultLock() string {
}
var hotTemplateReloading bool
var LogLevel int = lumber.WARN
func Serve(
filepathToData,