feat: make server options a struct (#615)

Former-commit-id: a54bc7c8a0fb700d4f90f78c54d6cb028e8acea7 [formerly a0946a1d5b82ce5681684bc0f871bd892c4d6c3f] [formerly 6b23b0abbe5cde6a6348f982975d15a1161359de [formerly 0e7abaa7fb]]
Former-commit-id: cf9ea1110bcb8b5eb30ecd25db1928659ecea922 [formerly 38cebeff1f9e6b03760f7b277a3941f7bd733fb5]
Former-commit-id: db44c1d5ff8fed361849b5bf1fe48e04c75d7ce7
This commit is contained in:
Henrique Dias 2019-01-08 10:29:09 +00:00 committed by GitHub
parent 802318f903
commit 01929c72ea
13 changed files with 159 additions and 90 deletions

View File

@ -9,12 +9,12 @@ import (
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/filebrowser/filebrowser/v2/auth"
fbhttp "github.com/filebrowser/filebrowser/v2/http"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
@ -36,7 +36,7 @@ func init() {
vaddP(pf, "database", "d", "./filebrowser.db", "path to the database")
vaddP(f, "address", "a", "127.0.0.1", "address to listen on")
vaddP(f, "log", "l", "stdout", "log output")
vaddP(f, "port", "p", 8080, "port to listen on")
vaddP(f, "port", "p", "8080", "port to listen on")
vaddP(f, "cert", "t", "", "tls certificate")
vaddP(f, "key", "k", "", "tls key")
vaddP(f, "root", "r", ".", "root to prepend to relative paths")
@ -91,59 +91,26 @@ Also, if the database path doesn't exist, File Browser will enter into
the quick setup mode and a new database will be bootstraped and a new
user created with the credentials from options "username" and "password".`,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
switch logMethod := v.GetString("log"); logMethod {
case "stdout":
log.SetOutput(os.Stdout)
case "stderr":
log.SetOutput(os.Stderr)
case "":
log.SetOutput(ioutil.Discard)
default:
log.SetOutput(&lumberjack.Logger{
Filename: logMethod,
MaxSize: 100,
MaxAge: 14,
MaxBackups: 10,
})
}
if !d.hadDB {
quickSetup(d)
}
port := v.GetInt("port")
address := v.GetString("address")
cert := v.GetString("cert")
key := v.GetString("key")
root := v.GetString("root")
server := getServer(d.store)
setupLog(server.Log)
root, err := filepath.Abs(root)
checkErr(err)
settings, err := d.store.Settings.Get()
checkErr(err)
// Despite Base URL and Scope being "server" type of
// variables, we persist them to the database because
// they are needed during the execution and not only
// to start up the server.
settings.BaseURL = v.GetString("baseurl")
settings.Root = root
err = d.store.Settings.Save(settings)
checkErr(err)
handler, err := fbhttp.NewHandler(d.store)
handler, err := fbhttp.NewHandler(d.store, server)
checkErr(err)
var listener net.Listener
if key != "" && cert != "" {
cer, err := tls.LoadX509KeyPair(cert, key)
if server.TLSKey != "" && server.TLSCert != "" {
cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey)
checkErr(err)
config := &tls.Config{Certificates: []tls.Certificate{cer}}
listener, err = tls.Listen("tcp", address+":"+strconv.Itoa(port), config)
listener, err = tls.Listen("tcp", server.Address+":"+server.Port, config)
checkErr(err)
} else {
listener, err = net.Listen("tcp", address+":"+strconv.Itoa(port))
listener, err = net.Listen("tcp", server.Address+":"+server.Port)
checkErr(err)
}
@ -154,10 +121,52 @@ user created with the credentials from options "username" and "password".`,
}, pythonConfig{allowNoDB: true}),
}
// TODO: get server settings and only replace
// them if set on Viper. Although viper.IsSet
// is bugged and if binded to a pflag, it will
// always return true.
// Also, when doing that, add this options to
// config init, config import, printConfig
// and config set since the DB values will actually
// be used. For now, despite being stored in the DB,
// they won't be used.
func getServer(st *storage.Storage) *settings.Server {
root := v.GetString("root")
root, err := filepath.Abs(root)
checkErr(err)
server := &settings.Server{}
server.BaseURL = v.GetString("baseurl")
server.Root = root
server.Address = v.GetString("address")
server.Port = v.GetString("port")
server.TLSKey = v.GetString("key")
server.TLSCert = v.GetString("cert")
server.Log = v.GetString("log")
return server
}
func setupLog(logMethod string) {
switch logMethod {
case "stdout":
log.SetOutput(os.Stdout)
case "stderr":
log.SetOutput(os.Stderr)
case "":
log.SetOutput(ioutil.Discard)
default:
log.SetOutput(&lumberjack.Logger{
Filename: logMethod,
MaxSize: 100,
MaxAge: 14,
MaxBackups: 10,
})
}
}
func quickSetup(d pythonData) {
set := &settings.Settings{
Key: generateRandomBytes(64), // 256 bit
BaseURL: v.GetString("baseurl"),
Signup: false,
AuthMethod: auth.MethodJSONAuth,
Defaults: settings.UserDefaults{
@ -176,9 +185,21 @@ func quickSetup(d pythonData) {
},
}
ser := &settings.Server{
BaseURL: v.GetString("baseurl"),
Log: v.GetString("log"),
TLSKey: v.GetString("key"),
TLSCert: v.GetString("cert"),
Address: v.GetString("address"),
Root: v.GetString("root"),
}
err := d.store.Settings.Save(set)
checkErr(err)
err = d.store.Settings.SaveServer(ser)
checkErr(err)
err = d.store.Auth.Save(&auth.JSONAuth{})
checkErr(err)

View File

@ -98,8 +98,8 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
if os.IsNotExist(err) {
data.hadDB = false
if !cfg.noDB || !cfg.allowNoDB {
log.Fatal(path + " does not exid.store. Please run 'filebrowser config init' fird.store.")
if !cfg.noDB && !cfg.allowNoDB {
log.Fatal(path + " does not exist. Please run 'filebrowser config init' first.")
}
} else if err != nil {
panic(err)

View File

@ -67,7 +67,7 @@ func withUser(fn handleFunc) handleFunc {
w.Header().Add("X-Renew-Token", "true")
}
d.user, err = d.store.Users.Get(d.settings.Root, tk.User.ID)
d.user, err = d.store.Users.Get(d.server.Root, tk.User.ID)
if err != nil {
return http.StatusInternalServerError, err
}
@ -91,7 +91,7 @@ var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, e
return http.StatusInternalServerError, err
}
user, err := auther.Auth(r, d.store.Users, d.Settings.Root)
user, err := auther.Auth(r, d.store.Users, d.server.Root)
if err == os.ErrPermission {
return http.StatusForbidden, nil
} else if err != nil {

View File

@ -16,6 +16,7 @@ type handleFunc func(w http.ResponseWriter, r *http.Request, d *data) (int, erro
type data struct {
*runner.Runner
settings *settings.Settings
server *settings.Server
store *storage.Storage
user *users.User
raw interface{}
@ -38,7 +39,7 @@ func (d *data) Check(path string) bool {
return true
}
func handle(fn handleFunc, prefix string, storage *storage.Storage) http.Handler {
func handle(fn handleFunc, prefix string, storage *storage.Storage, server *settings.Server) http.Handler {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
settings, err := storage.Settings.Get()
if err != nil {
@ -50,6 +51,7 @@ func handle(fn handleFunc, prefix string, storage *storage.Storage) http.Handler
Runner: &runner.Runner{Settings: settings},
store: storage,
settings: settings,
server: server,
})
if status != 0 {

View File

@ -3,6 +3,7 @@ package http
import (
"net/http"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/gorilla/mux"
)
@ -12,47 +13,51 @@ type modifyRequest struct {
Which []string `json:"which"` // Answer to: which fields?
}
func NewHandler(storage *storage.Storage) (http.Handler, error) {
func NewHandler(storage *storage.Storage, server *settings.Server) (http.Handler, error) {
r := mux.NewRouter()
index, static := getStaticHandlers(storage)
index, static := getStaticHandlers(storage, server)
monkey := func(fn handleFunc, prefix string) http.Handler {
return handle(fn, prefix, storage, server)
}
r.PathPrefix("/static").Handler(static)
r.NotFoundHandler = index
api := r.PathPrefix("/api").Subrouter()
api.Handle("/login", handle(loginHandler, "", storage))
api.Handle("/signup", handle(signupHandler, "", storage))
api.Handle("/renew", handle(renewHandler, "", storage))
api.Handle("/login", monkey(loginHandler, ""))
api.Handle("/signup", monkey(signupHandler, ""))
api.Handle("/renew", monkey(renewHandler, ""))
users := api.PathPrefix("/users").Subrouter()
users.Handle("", handle(usersGetHandler, "", storage)).Methods("GET")
users.Handle("", handle(userPostHandler, "", storage)).Methods("POST")
users.Handle("/{id:[0-9]+}", handle(userPutHandler, "", storage)).Methods("PUT")
users.Handle("/{id:[0-9]+}", handle(userGetHandler, "", storage)).Methods("GET")
users.Handle("/{id:[0-9]+}", handle(userDeleteHandler, "", storage)).Methods("DELETE")
users.Handle("", monkey(usersGetHandler, "")).Methods("GET")
users.Handle("", monkey(userPostHandler, "")).Methods("POST")
users.Handle("/{id:[0-9]+}", monkey(userPutHandler, "")).Methods("PUT")
users.Handle("/{id:[0-9]+}", monkey(userGetHandler, "")).Methods("GET")
users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE")
api.PathPrefix("/resources").Handler(handle(resourceGetHandler, "/api/resources", storage)).Methods("GET")
api.PathPrefix("/resources").Handler(handle(resourceDeleteHandler, "/api/resources", storage)).Methods("DELETE")
api.PathPrefix("/resources").Handler(handle(resourcePostPutHandler, "/api/resources", storage)).Methods("POST")
api.PathPrefix("/resources").Handler(handle(resourcePostPutHandler, "/api/resources", storage)).Methods("PUT")
api.PathPrefix("/resources").Handler(handle(resourcePatchHandler, "/api/resources", storage)).Methods("PATCH")
api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET")
api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler, "/api/resources")).Methods("DELETE")
api.PathPrefix("/resources").Handler(monkey(resourcePostPutHandler, "/api/resources")).Methods("POST")
api.PathPrefix("/resources").Handler(monkey(resourcePostPutHandler, "/api/resources")).Methods("PUT")
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler, "/api/resources")).Methods("PATCH")
api.PathPrefix("/share").Handler(handle(shareGetsHandler, "/api/share", storage)).Methods("GET")
api.PathPrefix("/share").Handler(handle(sharePostHandler, "/api/share", storage)).Methods("POST")
api.PathPrefix("/share").Handler(handle(shareDeleteHandler, "/api/share", storage)).Methods("DELETE")
api.PathPrefix("/share").Handler(monkey(shareGetsHandler, "/api/share")).Methods("GET")
api.PathPrefix("/share").Handler(monkey(sharePostHandler, "/api/share")).Methods("POST")
api.PathPrefix("/share").Handler(monkey(shareDeleteHandler, "/api/share")).Methods("DELETE")
api.Handle("/settings", handle(settingsGetHandler, "", storage)).Methods("GET")
api.Handle("/settings", handle(settingsPutHandler, "", storage)).Methods("PUT")
api.Handle("/settings", monkey(settingsGetHandler, "")).Methods("GET")
api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT")
api.PathPrefix("/raw").Handler(handle(rawHandler, "/api/raw", storage)).Methods("GET")
api.PathPrefix("/command").Handler(handle(commandsHandler, "/api/command", storage)).Methods("GET")
api.PathPrefix("/search").Handler(handle(searchHandler, "/api/search", storage)).Methods("GET")
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET")
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
public := api.PathPrefix("/public").Subrouter()
public.PathPrefix("/dl").Handler(handle(publicDlHandler, "/api/public/dl/", storage)).Methods("GET")
public.PathPrefix("/share").Handler(handle(publicShareHandler, "/api/public/share/", storage)).Methods("GET")
public.PathPrefix("/dl").Handler(monkey(publicDlHandler, "/api/public/dl/")).Methods("GET")
public.PathPrefix("/share").Handler(monkey(publicShareHandler, "/api/public/share/")).Methods("GET")
return r, nil
}

View File

@ -13,7 +13,7 @@ var withHashFile = func(fn handleFunc) handleFunc {
return errToStatus(err), err
}
user, err := d.store.Users.Get(d.settings.Root, link.UserID)
user, err := d.store.Users.Get(d.server.Root, link.UserID)
if err != nil {
return errToStatus(err), err
}

View File

@ -56,7 +56,7 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
var err error
s, err = d.store.Share.GetPermanent(r.URL.Path, d.user.ID)
if err == nil {
w.Write([]byte(d.settings.BaseURL + "/share/" + s.Hash))
w.Write([]byte(d.server.BaseURL + "/share/" + s.Hash))
return 0, nil
}
}

View File

@ -11,6 +11,7 @@ import (
"github.com/GeertJohan/go.rice"
"github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/version"
)
@ -18,12 +19,12 @@ import (
func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *data, box *rice.Box, file, contentType string) (int, error) {
w.Header().Set("Content-Type", contentType)
staticURL := strings.TrimPrefix(d.settings.BaseURL+"/static", "/")
staticURL := strings.TrimPrefix(d.server.BaseURL+"/static", "/")
data := map[string]interface{}{
"Name": d.settings.Branding.Name,
"DisableExternal": d.settings.Branding.DisableExternal,
"BaseURL": d.settings.BaseURL,
"BaseURL": d.server.BaseURL,
"Version": version.Version,
"StaticURL": staticURL,
"Signup": d.settings.Signup,
@ -76,7 +77,7 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *data, box *
return 0, nil
}
func getStaticHandlers(storage *storage.Storage) (http.Handler, http.Handler) {
func getStaticHandlers(storage *storage.Storage, server *settings.Server) (http.Handler, http.Handler) {
box := rice.MustFindBox("../frontend/dist")
handler := http.FileServer(box.HTTPBox())
@ -87,7 +88,7 @@ func getStaticHandlers(storage *storage.Storage) (http.Handler, http.Handler) {
w.Header().Set("x-xss-protection", "1; mode=block")
return handleWithStaticData(w, r, d, box, "index.html", "text/html; charset=utf-8")
}, "", storage)
}, "", storage, server)
static := handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
if r.Method != http.MethodGet {
@ -113,7 +114,7 @@ func getStaticHandlers(storage *storage.Storage) (http.Handler, http.Handler) {
}
return handleWithStaticData(w, r, d, box, r.URL.Path, "application/javascript; charset=utf-8")
}, "/static/", storage)
}, "/static/", storage, server)
return index, static
}

View File

@ -61,7 +61,7 @@ func withSelfOrAdmin(fn handleFunc) handleFunc {
}
var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
users, err := d.store.Users.Gets(d.settings.Root)
users, err := d.store.Users.Gets(d.server.Root)
if err != nil {
return http.StatusInternalServerError, err
}
@ -78,7 +78,7 @@ var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
})
var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
u, err := d.store.Users.Get(d.settings.Root, d.raw.(uint))
u, err := d.store.Users.Get(d.server.Root, d.raw.(uint))
if err == errors.ErrNotExist {
return http.StatusNotFound, err
}
@ -147,7 +147,7 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
req.Data.Password, err = users.HashPwd(req.Data.Password)
} else {
var suser *users.User
suser, err = d.store.Users.Get(d.settings.Root, d.raw.(uint))
suser, err = d.store.Users.Get(d.server.Root, d.raw.(uint))
req.Data.Password = suser.Password
}

View File

@ -8,8 +8,6 @@ type AuthMethod string
// Settings contain the main settings of the application.
type Settings struct {
Key []byte `json:"key"`
BaseURL string `json:"baseURL"`
Root string `json:"root"`
Signup bool `json:"signup"`
Defaults UserDefaults `json:"defaults"`
AuthMethod AuthMethod `json:"authMethod"`
@ -19,6 +17,17 @@ type Settings struct {
Rules []rules.Rule `json:"rules"`
}
// Server specific settings.
type Server struct {
Root string `json:"root"`
BaseURL string `json:"baseURL"`
TLSKey string `json:"tlsKey"`
TLSCert string `json:"tlsCert"`
Port string `json:"port"`
Address string `json:"address"`
Log string `json:"log"`
}
// GetRules implements rules.Provider.
func (s *Settings) GetRules() []rules.Rule {
return s.Rules

View File

@ -12,6 +12,8 @@ import (
type StorageBackend interface {
Get() (*Settings, error)
Save(*Settings) error
GetServer() (*Server, error)
SaveServer(*Server) error
}
// Storage is a settings storage.
@ -39,8 +41,6 @@ var defaultEvents = []string{
// Save saves the settings for the current instance.
func (s *Storage) Save(set *Settings) error {
set.BaseURL = strings.TrimSuffix(set.BaseURL, "/")
if len(set.Key) == 0 {
return errors.ErrEmptyKey
}
@ -86,3 +86,14 @@ func (s *Storage) Save(set *Settings) error {
return nil
}
// GetServer wraps StorageBackend.GetServer.
func (s *Storage) GetServer() (*Server, error) {
return s.back.GetServer()
}
// SaveServer wraps StorageBackend.SaveServer and adds some verification.
func (s *Storage) SaveServer(ser *Server) error {
ser.BaseURL = strings.TrimSuffix(ser.BaseURL, "/")
return s.back.SaveServer(ser)
}

View File

@ -17,3 +17,12 @@ func (s settingsBackend) Get() (*settings.Settings, error) {
func (s settingsBackend) Save(settings *settings.Settings) error {
return save(s.db, "settings", settings)
}
func (s settingsBackend) GetServer() (*settings.Server, error) {
server := &settings.Server{}
return server, get(s.db, "server", server)
}
func (s settingsBackend) SaveServer(server *settings.Server) error {
return save(s.db, "server", server)
}

View File

@ -33,7 +33,7 @@ type oldAuth struct {
}
type oldConf struct {
Port int `json:"port" yaml:"port" toml:"port"`
Port string `json:"port" yaml:"port" toml:"port"`
BaseURL string `json:"baseURL" yaml:"baseURL" toml:"baseURL"`
Log string `json:"log" yaml:"log" toml:"log"`
Address string `json:"address" yaml:"address" toml:"address"`
@ -47,7 +47,7 @@ type oldConf struct {
}
var defaults = &oldConf{
Port: 0,
Port: "0",
Log: "stdout",
Defaults: oldDefs{
Commands: []string{"git", "svn", "hg"},
@ -110,7 +110,6 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
s := &settings.Settings{
Key: key,
BaseURL: cfg.BaseURL,
Signup: false,
Defaults: settings.UserDefaults{
Scope: cfg.Defaults.Scope,
@ -130,6 +129,13 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
},
}
server := &settings.Server{
BaseURL : cfg.BaseURL,
Port : cfg.Port,
Address : cfg.Address,
Log : cfg.Log,
}
var auther auth.Auther
switch cfg.Auth.Method {
case "proxy":
@ -159,6 +165,11 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
return err
}
err = sto.Settings.SaveServer(server)
if err != nil {
return err
}
fmt.Println("Configuration successfully imported.")
return nil
}