Browse Source

Break functionality out of Serve() func

- Adds a new interface, Apper, that enables loading and persisting
  instance-level data in new ways
- Converts some initialization funcs to methods
- Exports funcs and methods needed for intialization
- In general, moves a ton of stuff around

Overall, this should maintain all existing functionality, but with the
ability to now better manage a WF instance.

Ref T613
pull/102/head
Matt Baer 4 years ago
parent
commit
034db22f8c
7 changed files with 245 additions and 137 deletions
  1. +185
    -89
      app.go
  2. +17
    -2
      cmd/writefreely/main.go
  3. +13
    -0
      handle.go
  4. +9
    -31
      keys.go
  5. +16
    -11
      routes.go
  6. +3
    -3
      session.go
  7. +2
    -1
      templates.go

+ 185
- 89
app.go View File

@@ -14,6 +14,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"html/template" "html/template"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@@ -76,10 +77,109 @@ type App struct {
timeline *localTimeline timeline *localTimeline
} }


// DB returns the App's datastore
func (app *App) DB() *datastore {
return app.db
}

// Router returns the App's router
func (app *App) Router() *mux.Router {
return app.router
}

// Config returns the App's current configuration.
func (app *App) Config() *config.Config {
return app.cfg
}

// SetConfig updates the App's Config to the given value.
func (app *App) SetConfig(cfg *config.Config) {
app.cfg = cfg
}

// SetKeys updates the App's Keychain to the given value.
func (app *App) SetKeys(k *key.Keychain) { func (app *App) SetKeys(k *key.Keychain) {
app.keys = k app.keys = k
} }


// Apper is the interface for getting data into and out of a WriteFreely
// instance (or "App").
//
// App returns the App for the current instance.
//
// LoadConfig reads an app configuration into the App, returning any error
// encountered.
//
// SaveConfig persists the current App configuration.
//
// LoadKeys reads the App's encryption keys and loads them into its
// key.Keychain.
type Apper interface {
App() *App

LoadConfig() error
SaveConfig(*config.Config) error

LoadKeys() error
}

// App returns the App
func (app *App) App() *App {
return app
}

// LoadConfig loads and parses a config file.
func (app *App) LoadConfig() error {
log.Info("Loading %s configuration...", app.cfgFile)
cfg, err := config.Load(app.cfgFile)
if err != nil {
log.Error("Unable to load configuration: %v", err)
os.Exit(1)
return err
}
app.cfg = cfg
return nil
}

// SaveConfig saves the given Config to disk -- namely, to the App's cfgFile.
func (app *App) SaveConfig(c *config.Config) error {
return config.Save(c, app.cfgFile)
}

// LoadKeys reads all needed keys from disk into the App. In order to use the
// configured `Server.KeysParentDir`, you must call initKeyPaths(App) before
// this.
func (app *App) LoadKeys() error {
var err error
app.keys = &key.Keychain{}

if debugging {
log.Info(" %s", emailKeyPath)
}
app.keys.EmailKey, err = ioutil.ReadFile(emailKeyPath)
if err != nil {
return err
}

if debugging {
log.Info(" %s", cookieAuthKeyPath)
}
app.keys.CookieAuthKey, err = ioutil.ReadFile(cookieAuthKeyPath)
if err != nil {
return err
}

if debugging {
log.Info(" %s", cookieKeyPath)
}
app.keys.CookieKey, err = ioutil.ReadFile(cookieKeyPath)
if err != nil {
return err
}

return nil
}

// handleViewHome shows page at root path. Will be the Pad if logged in and the // handleViewHome shows page at root path. Will be the Pad if logged in and the
// catch-all landing page otherwise. // catch-all landing page otherwise.
func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error { func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {
@@ -198,80 +298,49 @@ func pageForReq(app *App, r *http.Request) page.StaticPage {


var fileRegex = regexp.MustCompile("/([^/]*\\.[^/]*)$") var fileRegex = regexp.MustCompile("/([^/]*\\.[^/]*)$")


func Serve(app *App, debug bool) {
// Initialize loads the app configuration and initializes templates, keys,
// session, route handlers, and the database connection.
func Initialize(apper Apper, debug bool) (*App, error) {
debugging = debug debugging = debug


log.Info("Initializing...")

loadConfig(app)

hostName = app.cfg.App.Host
isSingleUser = app.cfg.App.SingleUser
app.cfg.Server.Dev = debugging
apper.LoadConfig()


err := initTemplates(app.cfg)
// Load templates
err := InitTemplates(apper.App().Config())
if err != nil { if err != nil {
log.Error("load templates: %s", err)
os.Exit(1)
return nil, fmt.Errorf("load templates: %s", err)
} }


// Load keys
log.Info("Loading encryption keys...")
initKeyPaths(app)
err = initKeys(app)
// Load keys and set up session
initKeyPaths(apper.App()) // TODO: find a better way to do this, since it's unneeded in all Apper implementations
err = InitKeys(apper)
if err != nil { if err != nil {
log.Error("\n%s\n", err)
}

// Initialize modules
app.sessionStore = initSession(app)
app.formDecoder = schema.NewDecoder()
app.formDecoder.RegisterConverter(converter.NullJSONString{}, converter.ConvertJSONNullString)
app.formDecoder.RegisterConverter(converter.NullJSONBool{}, converter.ConvertJSONNullBool)
app.formDecoder.RegisterConverter(sql.NullString{}, converter.ConvertSQLNullString)
app.formDecoder.RegisterConverter(sql.NullBool{}, converter.ConvertSQLNullBool)
app.formDecoder.RegisterConverter(sql.NullInt64{}, converter.ConvertSQLNullInt64)
app.formDecoder.RegisterConverter(sql.NullFloat64{}, converter.ConvertSQLNullFloat64)

// Check database configuration
if app.cfg.Database.Type == driverMySQL && (app.cfg.Database.User == "" || app.cfg.Database.Password == "") {
log.Error("Database user or password not set.")
os.Exit(1)
}
if app.cfg.Database.Host == "" {
app.cfg.Database.Host = "localhost"
}
if app.cfg.Database.Database == "" {
app.cfg.Database.Database = "writefreely"
return nil, fmt.Errorf("init keys: %s", err)
} }
apper.App().InitSession()


connectToDatabase(app)
defer shutdown(app)
apper.App().InitDecoder()


// Test database connection
err = app.db.Ping()
err = ConnectToDatabase(apper.App())
if err != nil { if err != nil {
log.Error("Database ping failed: %s", err)
return nil, fmt.Errorf("connect to DB: %s", err)
} }


r := mux.NewRouter()
handler := NewHandler(app)
handler.SetErrorPages(&ErrorPages{
NotFound: pages["404-general.tmpl"],
Gone: pages["410.tmpl"],
InternalServerError: pages["500.tmpl"],
Blank: pages["blank.tmpl"],
})

// Handle app routes
initRoutes(handler, r, app.cfg, app.db)

// Handle local timeline, if enabled // Handle local timeline, if enabled
if app.cfg.App.LocalTimeline {
if apper.App().cfg.App.LocalTimeline {
log.Info("Initializing local timeline...") log.Info("Initializing local timeline...")
initLocalTimeline(app)
initLocalTimeline(apper.App())
} }


return apper.App(), nil
}

func Serve(app *App, r *mux.Router) {
log.Info("Going to serve...")

hostName = app.cfg.App.Host
isSingleUser = app.cfg.App.SingleUser
app.cfg.Server.Dev = debugging


// Handle shutdown // Handle shutdown
c := make(chan os.Signal, 2) c := make(chan os.Signal, 2)
@@ -284,13 +353,12 @@ func Serve(app *App, debug bool) {
os.Exit(0) os.Exit(0)
}() }()


http.Handle("/", r)

// Start web application server // Start web application server
var bindAddress = app.cfg.Server.Bind var bindAddress = app.cfg.Server.Bind
if bindAddress == "" { if bindAddress == "" {
bindAddress = "localhost" bindAddress = "localhost"
} }
var err error
if app.cfg.IsSecureStandalone() { if app.cfg.IsSecureStandalone() {
log.Info("Serving redirects on http://%s:80", bindAddress) log.Info("Serving redirects on http://%s:80", bindAddress)
go func() { go func() {
@@ -304,11 +372,11 @@ func Serve(app *App, debug bool) {
log.Info("Serving on https://%s:443", bindAddress) log.Info("Serving on https://%s:443", bindAddress)
log.Info("---") log.Info("---")
err = http.ListenAndServeTLS( err = http.ListenAndServeTLS(
fmt.Sprintf("%s:443", bindAddress), app.cfg.Server.TLSCertPath, app.cfg.Server.TLSKeyPath, nil)
fmt.Sprintf("%s:443", bindAddress), app.cfg.Server.TLSCertPath, app.cfg.Server.TLSKeyPath, r)
} else { } else {
log.Info("Serving on http://%s:%d\n", bindAddress, app.cfg.Server.Port) log.Info("Serving on http://%s:%d\n", bindAddress, app.cfg.Server.Port)
log.Info("---") log.Info("---")
err = http.ListenAndServe(fmt.Sprintf("%s:%d", bindAddress, app.cfg.Server.Port), nil)
err = http.ListenAndServe(fmt.Sprintf("%s:%d", bindAddress, app.cfg.Server.Port), r)
} }
if err != nil { if err != nil {
log.Error("Unable to start: %v", err) log.Error("Unable to start: %v", err)
@@ -316,6 +384,44 @@ func Serve(app *App, debug bool) {
} }
} }


func (app *App) InitDecoder() {
// TODO: do this at the package level, instead of the App level
// Initialize modules
app.formDecoder = schema.NewDecoder()
app.formDecoder.RegisterConverter(converter.NullJSONString{}, converter.ConvertJSONNullString)
app.formDecoder.RegisterConverter(converter.NullJSONBool{}, converter.ConvertJSONNullBool)
app.formDecoder.RegisterConverter(sql.NullString{}, converter.ConvertSQLNullString)
app.formDecoder.RegisterConverter(sql.NullBool{}, converter.ConvertSQLNullBool)
app.formDecoder.RegisterConverter(sql.NullInt64{}, converter.ConvertSQLNullInt64)
app.formDecoder.RegisterConverter(sql.NullFloat64{}, converter.ConvertSQLNullFloat64)
}

// ConnectToDatabase validates and connects to the configured database, then
// tests the connection.
func ConnectToDatabase(app *App) error {
// Check database configuration
if app.cfg.Database.Type == driverMySQL && (app.cfg.Database.User == "" || app.cfg.Database.Password == "") {
return fmt.Errorf("Database user or password not set.")
}
if app.cfg.Database.Host == "" {
app.cfg.Database.Host = "localhost"
}
if app.cfg.Database.Database == "" {
app.cfg.Database.Database = "writefreely"
}

// TODO: check err
connectToDatabase(app)

// Test database connection
err := app.db.Ping()
if err != nil {
return fmt.Errorf("Database ping failed: %s", err)
}

return nil
}

// OutputVersion prints out the version of the application. // OutputVersion prints out the version of the application.
func OutputVersion() { func OutputVersion() {
fmt.Println(serverSoftware + " " + softwareVer) fmt.Println(serverSoftware + " " + softwareVer)
@@ -378,10 +484,10 @@ func DoConfig(app *App) {
os.Exit(0) os.Exit(0)
} }


// GenerateKeys creates app encryption keys and saves them into the configured KeysParentDir.
func GenerateKeys(app *App) error {
// GenerateKeyFiles creates app encryption keys and saves them into the configured KeysParentDir.
func GenerateKeyFiles(app *App) error {
// Read keys path from config // Read keys path from config
loadConfig(app)
app.LoadConfig()


// Create keys dir if it doesn't exist yet // Create keys dir if it doesn't exist yet
fullKeysDir := filepath.Join(app.cfg.Server.KeysParentDir, keysDir) fullKeysDir := filepath.Join(app.cfg.Server.KeysParentDir, keysDir)
@@ -412,11 +518,11 @@ func GenerateKeys(app *App) error {
} }


// CreateSchema creates all database tables needed for the application. // CreateSchema creates all database tables needed for the application.
func CreateSchema(app *App) error {
loadConfig(app)
connectToDatabase(app)
defer shutdown(app)
err := adminInitDatabase(app)
func CreateSchema(apper Apper) error {
apper.LoadConfig()
connectToDatabase(apper.App())
defer shutdown(apper.App())
err := adminInitDatabase(apper.App())
if err != nil { if err != nil {
return err return err
} }
@@ -425,7 +531,7 @@ func CreateSchema(app *App) error {


// Migrate runs all necessary database migrations. // Migrate runs all necessary database migrations.
func Migrate(app *App) error { func Migrate(app *App) error {
loadConfig(app)
app.LoadConfig()
connectToDatabase(app) connectToDatabase(app)
defer shutdown(app) defer shutdown(app)


@@ -439,7 +545,7 @@ func Migrate(app *App) error {
// ResetPassword runs the interactive password reset process. // ResetPassword runs the interactive password reset process.
func ResetPassword(app *App, username string) error { func ResetPassword(app *App, username string) error {
// Connect to the database // Connect to the database
loadConfig(app)
app.LoadConfig()
connectToDatabase(app) connectToDatabase(app)
defer shutdown(app) defer shutdown(app)


@@ -475,16 +581,6 @@ func ResetPassword(app *App, username string) error {
return nil return nil
} }


func loadConfig(app *App) {
log.Info("Loading %s configuration...", app.cfgFile)
cfg, err := config.Load(app.cfgFile)
if err != nil {
log.Error("Unable to load configuration: %v", err)
os.Exit(1)
}
app.cfg = cfg
}

func connectToDatabase(app *App) { func connectToDatabase(app *App) {
log.Info("Connecting to %s database...", app.cfg.Database.Type) log.Info("Connecting to %s database...", app.cfg.Database.Type)


@@ -521,14 +617,14 @@ func shutdown(app *App) {
} }


// CreateUser creates a new admin or normal user from the given credentials. // CreateUser creates a new admin or normal user from the given credentials.
func CreateUser(app *App, username, password string, isAdmin bool) error {
func CreateUser(apper Apper, username, password string, isAdmin bool) error {
// Create an admin user with --create-admin // Create an admin user with --create-admin
loadConfig(app)
connectToDatabase(app)
defer shutdown(app)
apper.LoadConfig()
connectToDatabase(apper.App())
defer shutdown(apper.App())


// Ensure an admin / first user doesn't already exist // Ensure an admin / first user doesn't already exist
firstUser, _ := app.db.GetUserByID(1)
firstUser, _ := apper.App().db.GetUserByID(1)
if isAdmin { if isAdmin {
// Abort if trying to create admin user, but one already exists // Abort if trying to create admin user, but one already exists
if firstUser != nil { if firstUser != nil {
@@ -551,8 +647,8 @@ func CreateUser(app *App, username, password string, isAdmin bool) error {
usernameDesc += " (originally: " + desiredUsername + ")" usernameDesc += " (originally: " + desiredUsername + ")"
} }


if !author.IsValidUsername(app.cfg, username) {
return fmt.Errorf("Username %s is invalid, reserved, or shorter than configured minimum length (%d characters).", usernameDesc, app.cfg.App.MinUsernameLen)
if !author.IsValidUsername(apper.App().cfg, username) {
return fmt.Errorf("Username %s is invalid, reserved, or shorter than configured minimum length (%d characters).", usernameDesc, apper.App().cfg.App.MinUsernameLen)
} }


// Hash the password // Hash the password
@@ -572,7 +668,7 @@ func CreateUser(app *App, username, password string, isAdmin bool) error {
userType = "admin" userType = "admin"
} }
log.Info("Creating %s %s...", userType, usernameDesc) log.Info("Creating %s %s...", userType, usernameDesc)
err = app.db.CreateUser(u, desiredUsername)
err = apper.App().db.CreateUser(u, desiredUsername)
if err != nil { if err != nil {
return fmt.Errorf("Unable to create user: %s", err) return fmt.Errorf("Unable to create user: %s", err)
} }


+ 17
- 2
cmd/writefreely/main.go View File

@@ -13,6 +13,7 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/gorilla/mux"
"github.com/writeas/web-core/log" "github.com/writeas/web-core/log"
"github.com/writeas/writefreely" "github.com/writeas/writefreely"
"os" "os"
@@ -54,7 +55,7 @@ func main() {
writefreely.DoConfig(app) writefreely.DoConfig(app)
os.Exit(0) os.Exit(0)
} else if *genKeys { } else if *genKeys {
err := writefreely.GenerateKeys(app)
err := writefreely.GenerateKeyFiles(app)
if err != nil { if err != nil {
log.Error(err.Error()) log.Error(err.Error())
os.Exit(1) os.Exit(1)
@@ -107,7 +108,21 @@ func main() {
os.Exit(0) os.Exit(0)
} }


writefreely.Serve(app, *debugPtr)
// Initialize the application
var err error
app, err = writefreely.Initialize(app, *debugPtr)
if err != nil {
log.Error("%s", err)
os.Exit(1)
}

// Set app routes
r := mux.NewRouter()
app.InitRoutes(r)
app.InitStaticRoutes(r)

// Serve the application
writefreely.Serve(app, r)
} }


func userPass(credStr string, isAdmin bool) (user string, pass string, err error) { func userPass(credStr string, isAdmin bool) (user string, pass string, err error) {


+ 13
- 0
handle.go View File

@@ -74,6 +74,19 @@ func NewHandler(app *App) *Handler {
return h return h
} }


// NewWFHandler returns a new Handler instance, using WriteFreely template files.
// You MUST call writefreely.InitTemplates() before this.
func NewWFHandler(app *App) *Handler {
h := NewHandler(app)
h.SetErrorPages(&ErrorPages{
NotFound: pages["404-general.tmpl"],
Gone: pages["410.tmpl"],
InternalServerError: pages["500.tmpl"],
Blank: pages["blank.tmpl"],
})
return h
}

// SetErrorPages sets the given set of ErrorPages as templates for any errors // SetErrorPages sets the given set of ErrorPages as templates for any errors
// that come up. // that come up.
func (h *Handler) SetErrorPages(e *ErrorPages) { func (h *Handler) SetErrorPages(e *ErrorPages) {


+ 9
- 31
keys.go View File

@@ -28,6 +28,15 @@ var (
cookieKeyPath = filepath.Join(keysDir, "cookies_enc.aes256") cookieKeyPath = filepath.Join(keysDir, "cookies_enc.aes256")
) )


// InitKeys loads encryption keys into memory via the given Apper interface
func InitKeys(apper Apper) error {
log.Info("Loading encryption keys...")
err := apper.LoadKeys()
if err != nil {
return err
}
return nil
}


func initKeyPaths(app *App) { func initKeyPaths(app *App) {
emailKeyPath = filepath.Join(app.cfg.Server.KeysParentDir, emailKeyPath) emailKeyPath = filepath.Join(app.cfg.Server.KeysParentDir, emailKeyPath)
@@ -35,37 +44,6 @@ func initKeyPaths(app *App) {
cookieKeyPath = filepath.Join(app.cfg.Server.KeysParentDir, cookieKeyPath) cookieKeyPath = filepath.Join(app.cfg.Server.KeysParentDir, cookieKeyPath)
} }


func initKeys(app *App) error {
var err error
app.keys = &key.Keychain{}

if debugging {
log.Info(" %s", emailKeyPath)
}
app.keys.EmailKey, err = ioutil.ReadFile(emailKeyPath)
if err != nil {
return err
}

if debugging {
log.Info(" %s", cookieAuthKeyPath)
}
app.keys.CookieAuthKey, err = ioutil.ReadFile(cookieAuthKeyPath)
if err != nil {
return err
}

if debugging {
log.Info(" %s", cookieKeyPath)
}
app.keys.CookieKey, err = ioutil.ReadFile(cookieKeyPath)
if err != nil {
return err
}

return nil
}

// generateKey generates a key at the given path used for the encryption of // generateKey generates a key at the given path used for the encryption of
// certain user data. Because user data becomes unrecoverable without these // certain user data. Because user data becomes unrecoverable without these
// keys, this won't overwrite any existing key, and instead outputs a message. // keys, this won't overwrite any existing key, and instead outputs a message.


+ 16
- 11
routes.go View File

@@ -14,7 +14,6 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/writeas/go-webfinger" "github.com/writeas/go-webfinger"
"github.com/writeas/web-core/log" "github.com/writeas/web-core/log"
"github.com/writeas/writefreely/config"
"github.com/writefreely/go-nodeinfo" "github.com/writefreely/go-nodeinfo"
"net/http" "net/http"
"path/filepath" "path/filepath"
@@ -31,9 +30,14 @@ func (app *App) InitStaticRoutes(r *mux.Router) {
r.PathPrefix("/").Handler(fs) r.PathPrefix("/").Handler(fs)
} }


func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datastore) {
hostSubroute := cfg.App.Host[strings.Index(cfg.App.Host, "://")+3:]
if cfg.App.SingleUser {
// InitRoutes adds dynamic routes for the given mux.Router.
func (app *App) InitRoutes(r *mux.Router) *mux.Router {
// Create handler
handler := NewWFHandler(app)

// Set up routes
hostSubroute := app.cfg.App.Host[strings.Index(app.cfg.App.Host, "://")+3:]
if app.cfg.App.SingleUser {
hostSubroute = "{domain}" hostSubroute = "{domain}"
} else { } else {
if strings.HasPrefix(hostSubroute, "localhost") { if strings.HasPrefix(hostSubroute, "localhost") {
@@ -41,7 +45,7 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto
} }
} }


if cfg.App.SingleUser {
if app.cfg.App.SingleUser {
log.Info("Adding %s routes (single user)...", hostSubroute) log.Info("Adding %s routes (single user)...", hostSubroute)
} else { } else {
log.Info("Adding %s routes (multi-user)...", hostSubroute) log.Info("Adding %s routes (multi-user)...", hostSubroute)
@@ -51,7 +55,7 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto
write := r.PathPrefix("/").Subrouter() write := r.PathPrefix("/").Subrouter()


// Federation endpoint configurations // Federation endpoint configurations
wf := webfinger.Default(wfResolver{db, cfg})
wf := webfinger.Default(wfResolver{app.db, app.cfg})
wf.NoTLSHandler = nil wf.NoTLSHandler = nil


// Federation endpoints // Federation endpoints
@@ -60,15 +64,15 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto
// webfinger // webfinger
write.HandleFunc(webfinger.WebFingerPath, handler.LogHandlerFunc(http.HandlerFunc(wf.Webfinger))) write.HandleFunc(webfinger.WebFingerPath, handler.LogHandlerFunc(http.HandlerFunc(wf.Webfinger)))
// nodeinfo // nodeinfo
niCfg := nodeInfoConfig(db, cfg)
ni := nodeinfo.NewService(*niCfg, nodeInfoResolver{cfg, db})
niCfg := nodeInfoConfig(app.db, app.cfg)
ni := nodeinfo.NewService(*niCfg, nodeInfoResolver{app.cfg, app.db})
write.HandleFunc(nodeinfo.NodeInfoPath, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfoDiscover))) write.HandleFunc(nodeinfo.NodeInfoPath, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfoDiscover)))
write.HandleFunc(niCfg.InfoURL, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfo))) write.HandleFunc(niCfg.InfoURL, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfo)))


// Set up dyamic page handlers // Set up dyamic page handlers
// Handle auth // Handle auth
auth := write.PathPrefix("/api/auth/").Subrouter() auth := write.PathPrefix("/api/auth/").Subrouter()
if cfg.App.OpenRegistration {
if app.cfg.App.OpenRegistration {
auth.HandleFunc("/signup", handler.All(apiSignup)).Methods("POST") auth.HandleFunc("/signup", handler.All(apiSignup)).Methods("POST")
} }
auth.HandleFunc("/login", handler.All(login)).Methods("POST") auth.HandleFunc("/login", handler.All(login)).Methods("POST")
@@ -155,7 +159,7 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto
RouteRead(handler, readPerm, write.PathPrefix("/read").Subrouter()) RouteRead(handler, readPerm, write.PathPrefix("/read").Subrouter())


draftEditPrefix := "" draftEditPrefix := ""
if cfg.App.SingleUser {
if app.cfg.App.SingleUser {
draftEditPrefix = "/d" draftEditPrefix = "/d"
write.HandleFunc("/me/new", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET") write.HandleFunc("/me/new", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET")
} else { } else {
@@ -166,7 +170,7 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto
write.HandleFunc(draftEditPrefix+"/{action}/edit", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET") write.HandleFunc(draftEditPrefix+"/{action}/edit", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET")
write.HandleFunc(draftEditPrefix+"/{action}/meta", handler.Web(handleViewMeta, UserLevelOptional)).Methods("GET") write.HandleFunc(draftEditPrefix+"/{action}/meta", handler.Web(handleViewMeta, UserLevelOptional)).Methods("GET")
// Collections // Collections
if cfg.App.SingleUser {
if app.cfg.App.SingleUser {
RouteCollections(handler, write.PathPrefix("/").Subrouter()) RouteCollections(handler, write.PathPrefix("/").Subrouter())
} else { } else {
write.HandleFunc("/{prefix:[@~$!\\-+]}{collection}", handler.Web(handleViewCollection, UserLevelOptional)) write.HandleFunc("/{prefix:[@~$!\\-+]}{collection}", handler.Web(handleViewCollection, UserLevelOptional))
@@ -176,6 +180,7 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto
} }
write.HandleFunc(draftEditPrefix+"/{post}", handler.Web(handleViewPost, UserLevelOptional)) write.HandleFunc(draftEditPrefix+"/{post}", handler.Web(handleViewPost, UserLevelOptional))
write.HandleFunc("/", handler.Web(handleViewHome, UserLevelOptional)) write.HandleFunc("/", handler.Web(handleViewHome, UserLevelOptional))
return r
} }


func RouteCollections(handler *Handler, r *mux.Router) { func RouteCollections(handler *Handler, r *mux.Router) {


+ 3
- 3
session.go View File

@@ -27,9 +27,9 @@ const (
blogPassCookieName = "ub" blogPassCookieName = "ub"
) )


// initSession creates the cookie store. It depends on the keychain already
// InitSession creates the cookie store. It depends on the keychain already
// being loaded. // being loaded.
func initSession(app *App) *sessions.CookieStore {
func (app *App) InitSession() {
// Register complex data types we'll be storing in cookies // Register complex data types we'll be storing in cookies
gob.Register(&User{}) gob.Register(&User{})


@@ -41,7 +41,7 @@ func initSession(app *App) *sessions.CookieStore {
HttpOnly: true, HttpOnly: true,
Secure: strings.HasPrefix(app.cfg.App.Host, "https://"), Secure: strings.HasPrefix(app.cfg.App.Host, "https://"),
} }
return store
app.sessionStore = store
} }


func getSessionFlashes(app *App, w http.ResponseWriter, r *http.Request, session *sessions.Session) ([]string, error) { func getSessionFlashes(app *App, w http.ResponseWriter, r *http.Request, session *sessions.Session) ([]string, error) {


+ 2
- 1
templates.go View File

@@ -98,7 +98,8 @@ func initUserPage(parentDir, path, key string) {
)) ))
} }


func initTemplates(cfg *config.Config) error {
// InitTemplates loads all template files from the configured parent dir.
func InitTemplates(cfg *config.Config) error {
log.Info("Loading templates...") log.Info("Loading templates...")
tmplFiles, err := ioutil.ReadDir(filepath.Join(cfg.Server.TemplatesParentDir, templatesDir)) tmplFiles, err := ioutil.ReadDir(filepath.Join(cfg.Server.TemplatesParentDir, templatesDir))
if err != nil { if err != nil {


Loading…
Cancel
Save