Merge pull request #18 from gtsteffaniak/remove-settings

Remove settings
This commit is contained in:
Graham Steffaniak 2023-09-03 12:29:25 -05:00 committed by GitHub
commit f2811ae54d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 431 additions and 1037 deletions

View File

@ -5,12 +5,16 @@ All notable changes to this project will be documented in this file. See [standa
# v0.2.0 # v0.2.0
- Works with new more advanced filebrowser.json - improved UI
- improved GUI
- more unified coehisive look - more unified coehisive look
- Adjusted header bar look and icon behavior
- The shell is dead. - The shell is dead.
- If you need to use the shell, exec into the docker container. - If you need to use custom commands, exec into the docker container.
- All configuration is done via filebrowser.yml - The json config file is dead.
- All configuration is done via advanced `filebrowser.yaml`
- The only flag that is allowed is flag to specify config file.
- Removed old code to migrate database versions
- Removed all unused cmd code
# v0.1.4 # v0.1.4
- various UI fixes - various UI fixes

View File

@ -17,7 +17,7 @@ RUN apk --no-cache add \
VOLUME /srv VOLUME /srv
EXPOSE 8080 EXPOSE 8080
WORKDIR / WORKDIR /
COPY --from=base /app/.filebrowser.json /.filebrowser.json COPY --from=base /app/settings/filebrowser.yaml /filebrowser.yaml
COPY --from=base /app/filebrowser /filebrowser COPY --from=base /app/filebrowser /filebrowser
COPY --from=nbuild /app/dist/ /frontend/dist/ COPY --from=nbuild /app/dist/ /frontend/dist/
ENTRYPOINT [ "./filebrowser" ] ENTRYPOINT [ "./filebrowser" ]

View File

@ -3,14 +3,13 @@ package auth
import ( import (
"net/http" "net/http"
"github.com/gtsteffaniak/filebrowser/settings"
"github.com/gtsteffaniak/filebrowser/users" "github.com/gtsteffaniak/filebrowser/users"
) )
// Auther is the authentication interface. // Auther is the authentication interface.
type Auther interface { type Auther interface {
// Auth is called to authenticate a request. // Auth is called to authenticate a request.
Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) Auth(r *http.Request, usr users.Store) (*users.User, error)
// LoginPage indicates if this auther needs a login page. // LoginPage indicates if this auther needs a login page.
LoginPage() bool LoginPage() bool
} }

View File

@ -31,7 +31,7 @@ type HookAuth struct {
} }
// Auth authenticates the user via a json in content body. // Auth authenticates the user via a json in content body.
func (a *HookAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) { func (a *HookAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
var cred hookCred var cred hookCred
if r.Body == nil { if r.Body == nil {
@ -44,8 +44,8 @@ func (a *HookAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings
} }
a.Users = usr a.Users = usr
a.Settings = stg a.Settings = &settings.GlobalConfiguration
a.Server = srv a.Server = &settings.GlobalConfiguration.Server
a.Cred = cred a.Cred = cred
action, err := a.RunCommand() action, err := a.RunCommand()
@ -150,19 +150,18 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// create user with the provided credentials // create user with the provided credentials
d := &users.User{ d := &users.User{
Username: a.Cred.Username, Username: a.Cred.Username,
Password: pass, Password: pass,
Scope: a.Settings.Defaults.Scope, Scope: a.Settings.UserDefaults.Scope,
Locale: a.Settings.Defaults.Locale, Locale: a.Settings.UserDefaults.Locale,
ViewMode: a.Settings.Defaults.ViewMode, ViewMode: a.Settings.UserDefaults.ViewMode,
SingleClick: a.Settings.Defaults.SingleClick, SingleClick: a.Settings.UserDefaults.SingleClick,
Sorting: a.Settings.Defaults.Sorting, Sorting: a.Settings.UserDefaults.Sorting,
Perm: a.Settings.Defaults.Perm, Perm: a.Settings.UserDefaults.Perm,
Commands: a.Settings.Defaults.Commands, Commands: a.Settings.UserDefaults.Commands,
HideDotfiles: a.Settings.Defaults.HideDotfiles, HideDotfiles: a.Settings.UserDefaults.HideDotfiles,
} }
u = a.GetUser(d) u = a.GetUser(d)
@ -219,7 +218,7 @@ func (a *HookAuth) GetUser(d *users.User) *users.User {
Password: d.Password, Password: d.Password,
Scope: a.Fields.GetString("user.scope", d.Scope), Scope: a.Fields.GetString("user.scope", d.Scope),
Locale: a.Fields.GetString("user.locale", d.Locale), Locale: a.Fields.GetString("user.locale", d.Locale),
ViewMode: users.ViewMode(a.Fields.GetString("user.viewMode", string(d.ViewMode))), ViewMode: d.ViewMode,
SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick), SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick),
Sorting: files.Sorting{ Sorting: files.Sorting{
Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc), Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc),

View File

@ -23,7 +23,8 @@ type JSONAuth struct {
} }
// Auth authenticates the user via a json in content body. // Auth authenticates the user via a json in content body.
func (a JSONAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) { func (a JSONAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
config := &settings.GlobalConfiguration
var cred jsonCred var cred jsonCred
if r.Body == nil { if r.Body == nil {
@ -48,7 +49,7 @@ func (a JSONAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings,
} }
} }
u, err := usr.Get(srv.Root, cred.Username) u, err := usr.Get(config.Server.Root, cred.Username)
if err != nil || !users.CheckPwd(cred.Password, u.Password) { if err != nil || !users.CheckPwd(cred.Password, u.Password) {
return nil, os.ErrPermission return nil, os.ErrPermission
} }

View File

@ -14,8 +14,8 @@ const MethodNoAuth = "noauth"
type NoAuth struct{} type NoAuth struct{}
// Auth uses authenticates user 1. // Auth uses authenticates user 1.
func (a NoAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) { func (a NoAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
return usr.Get(srv.Root, uint(1)) return usr.Get(settings.GlobalConfiguration.Server.Root, uint(1))
} }
// LoginPage tells that no auth doesn't require a login page. // LoginPage tells that no auth doesn't require a login page.

View File

@ -4,8 +4,9 @@ import (
"net/http" "net/http"
"os" "os"
"github.com/gtsteffaniak/filebrowser/errors"
"github.com/gtsteffaniak/filebrowser/settings" "github.com/gtsteffaniak/filebrowser/settings"
"github.com/gtsteffaniak/filebrowser/errors"
"github.com/gtsteffaniak/filebrowser/users" "github.com/gtsteffaniak/filebrowser/users"
) )
@ -18,9 +19,9 @@ type ProxyAuth struct {
} }
// Auth authenticates the user via an HTTP header. // Auth authenticates the user via an HTTP header.
func (a ProxyAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) { func (a ProxyAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
username := r.Header.Get(a.Header) username := r.Header.Get(a.Header)
user, err := usr.Get(srv.Root, username) user, err := usr.Get(settings.GlobalConfiguration.Server.Root, username)
if err == errors.ErrNotExist { if err == errors.ErrNotExist {
return nil, os.ErrPermission return nil, os.ErrPermission
} }

View File

@ -1,40 +1,42 @@
== Running benchmark == == Running benchmark ==
/usr/local/go/bin/go
? github.com/gtsteffaniak/filebrowser [no test files] ? github.com/gtsteffaniak/filebrowser [no test files]
? github.com/gtsteffaniak/filebrowser/auth [no test files] ? github.com/gtsteffaniak/filebrowser/auth [no test files]
? github.com/gtsteffaniak/filebrowser/cmd [no test files] ? github.com/gtsteffaniak/filebrowser/cmd [no test files]
PASS PASS
ok github.com/gtsteffaniak/filebrowser/diskcache 0.588s ok github.com/gtsteffaniak/filebrowser/diskcache 0.004s
? github.com/gtsteffaniak/filebrowser/errors [no test files] ? github.com/gtsteffaniak/filebrowser/errors [no test files]
? github.com/gtsteffaniak/filebrowser/files [no test files] ? github.com/gtsteffaniak/filebrowser/files [no test files]
PASS PASS
ok github.com/gtsteffaniak/filebrowser/fileutils 0.212s ok github.com/gtsteffaniak/filebrowser/fileutils 0.004s
2023/08/18 17:10:41 h: 401 <nil> 2023/09/02 19:15:20 h: 401 <nil>
2023/08/18 17:10:41 h: 401 <nil> 2023/09/02 19:15:20 h: 401 <nil>
2023/08/18 17:10:41 h: 401 <nil> 2023/09/02 19:15:20 h: 401 <nil>
2023/08/18 17:10:41 h: 401 <nil> 2023/09/02 19:15:20 h: 401 <nil>
2023/08/18 17:10:41 h: 401 <nil> 2023/09/02 19:15:20 h: 401 <nil>
2023/08/18 17:10:41 h: 401 <nil> 2023/09/02 19:15:20 h: 401 <nil>
PASS PASS
ok github.com/gtsteffaniak/filebrowser/http 0.753s ok github.com/gtsteffaniak/filebrowser/http 0.094s
PASS PASS
ok github.com/gtsteffaniak/filebrowser/img 0.362s ok github.com/gtsteffaniak/filebrowser/img 0.122s
PASS PASS
ok github.com/gtsteffaniak/filebrowser/rules 0.182s ok github.com/gtsteffaniak/filebrowser/rules 0.002s
PASS PASS
ok github.com/gtsteffaniak/filebrowser/runner 0.198s ok github.com/gtsteffaniak/filebrowser/runner 0.004s
goos: darwin goos: linux
goarch: arm64 goarch: amd64
pkg: github.com/gtsteffaniak/filebrowser/search pkg: github.com/gtsteffaniak/filebrowser/search
BenchmarkSearchAllIndexes-10 10 5802738 ns/op 2756774 B/op 42606 allocs/op cpu: 11th Gen Intel(R) Core(TM) i5-11320H @ 3.20GHz
BenchmarkFillIndex-10 10 4769329 ns/op 18512 B/op 453 allocs/op BenchmarkSearchAllIndexes-8 10 5176084 ns/op 2743632 B/op 42785 allocs/op
BenchmarkFillIndex-8 10 3263308 ns/op 18485 B/op 453 allocs/op
PASS PASS
ok github.com/gtsteffaniak/filebrowser/search 0.356s ok github.com/gtsteffaniak/filebrowser/search 0.109s
? github.com/gtsteffaniak/filebrowser/settings [no test files] PASS
ok github.com/gtsteffaniak/filebrowser/settings 0.004s
? github.com/gtsteffaniak/filebrowser/share [no test files] ? github.com/gtsteffaniak/filebrowser/share [no test files]
? github.com/gtsteffaniak/filebrowser/storage [no test files] ? github.com/gtsteffaniak/filebrowser/storage [no test files]
? github.com/gtsteffaniak/filebrowser/storage/bolt [no test files] ? github.com/gtsteffaniak/filebrowser/storage/bolt [no test files]
? github.com/gtsteffaniak/filebrowser/storage/bolt/importer [no test files]
PASS PASS
ok github.com/gtsteffaniak/filebrowser/users 0.201s ok github.com/gtsteffaniak/filebrowser/users 0.004s
? github.com/gtsteffaniak/filebrowser/version [no test files] ? github.com/gtsteffaniak/filebrowser/version [no test files]

View File

@ -1,163 +0,0 @@
package cmd
import (
"encoding/json"
nerrors "errors"
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/gtsteffaniak/filebrowser/auth"
"github.com/gtsteffaniak/filebrowser/errors"
"github.com/gtsteffaniak/filebrowser/settings"
)
func init() {
rootCmd.AddCommand(configCmd)
}
var configCmd = &cobra.Command{
Use: "config",
Short: "Configuration management utility",
Long: `Configuration management utility.`,
Args: cobra.NoArgs,
}
func addConfigFlags(flags *pflag.FlagSet) {
addUserFlags(flags)
flags.BoolP("signup", "s", false, "allow users to signup")
flags.String("shell", "", "shell command to which other commands should be appended")
flags.String("recaptcha.host", "https://www.google.com", "use another host for ReCAPTCHA. recaptcha.net might be useful in China")
flags.String("recaptcha.key", "", "ReCaptcha site key")
flags.String("recaptcha.secret", "", "ReCaptcha secret")
flags.String("frontend.name", "", "replace 'File Browser' by this name")
flags.String("frontend.color", "", "set the theme color")
flags.String("frontend.files", "", "path to directory with images and custom styles")
flags.Bool("frontend.disableExternal", false, "disable external links such as GitHub links")
flags.Bool("frontend.disableUsedPercentage", false, "disable used disk percentage graph")
}
//nolint:gocyclo
func getAuthentication() (string, auth.Auther) {
method := settings.GlobalConfiguration.Auth.Method
var defaultAuther map[string]interface{}
var auther auth.Auther
if method == "proxy" {
header := settings.GlobalConfiguration.Auth.Header
if header == "" {
header = defaultAuther["header"].(string)
}
if header == "" {
checkErr(nerrors.New("you must set the flag 'auth.header' for method 'proxy'"))
}
auther = &auth.ProxyAuth{Header: header}
}
if method == "noauth" {
auther = &auth.NoAuth{}
}
if method == "password" {
jsonAuth := &auth.JSONAuth{}
host := settings.GlobalConfiguration.Auth.Recaptcha.Host
key := settings.GlobalConfiguration.Auth.Recaptcha.Key
secret := settings.GlobalConfiguration.Auth.Recaptcha.Secret
if key == "" {
if kmap, ok := defaultAuther["recaptcha"].(map[string]interface{}); ok {
key = kmap["key"].(string)
}
}
if secret == "" {
if smap, ok := defaultAuther["recaptcha"].(map[string]interface{}); ok {
secret = smap["secret"].(string)
}
}
if key != "" && secret != "" {
jsonAuth.ReCaptcha = &auth.ReCaptcha{
Host: host,
Key: key,
Secret: secret,
}
}
auther = jsonAuth
}
if method == "hook" {
command := settings.GlobalConfiguration.Auth.Command
if command == "" {
command = defaultAuther["command"].(string)
}
if command == "" {
checkErr(nerrors.New("you must set the flag 'auth.command' for method 'hook'"))
}
auther = &auth.HookAuth{Command: command}
}
if auther == nil {
panic(errors.ErrInvalidAuthMethod)
}
return method, auther
}
func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Auther) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
fmt.Fprintf(w, "Auth method:\t%s\n", set.Auth.Method)
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
fmt.Fprintln(w, "\nFrontend:")
fmt.Fprintf(w, "\tName:\t%s\n", set.Frontend.Name)
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Frontend.Files)
fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Frontend.DisableExternal)
fmt.Fprintf(w, "\tDisable used disk percentage graph:\t%t\n", set.Frontend.DisableUsedPercentage)
fmt.Fprintf(w, "\tColor:\t%s\n", set.Frontend.Color)
fmt.Fprintln(w, "\nServer:")
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
fmt.Fprintf(w, "\tBase URL:\t%s\n", ser.BaseURL)
fmt.Fprintf(w, "\tRoot:\t%s\n", ser.Root)
fmt.Fprintf(w, "\tSocket:\t%s\n", ser.Socket)
fmt.Fprintf(w, "\tAddress:\t%s\n", ser.Address)
fmt.Fprintf(w, "\tTLS Cert:\t%s\n", ser.TLSCert)
fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey)
fmt.Fprintf(w, "\tExec Enabled:\t%t\n", ser.EnableExec)
fmt.Fprintln(w, "\nDefaults:")
fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope)
fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale)
fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode)
fmt.Fprintf(w, "\tSingle Click:\t%t\n", set.Defaults.SingleClick)
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " "))
fmt.Fprintf(w, "\tSorting:\n")
fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By)
fmt.Fprintf(w, "\t\tAsc:\t%t\n", set.Defaults.Sorting.Asc)
fmt.Fprintf(w, "\tPermissions:\n")
fmt.Fprintf(w, "\t\tAdmin:\t%t\n", set.Defaults.Perm.Admin)
fmt.Fprintf(w, "\t\tExecute:\t%t\n", set.Defaults.Perm.Execute)
fmt.Fprintf(w, "\t\tCreate:\t%t\n", set.Defaults.Perm.Create)
fmt.Fprintf(w, "\t\tRename:\t%t\n", set.Defaults.Perm.Rename)
fmt.Fprintf(w, "\t\tModify:\t%t\n", set.Defaults.Perm.Modify)
fmt.Fprintf(w, "\t\tDelete:\t%t\n", set.Defaults.Perm.Delete)
fmt.Fprintf(w, "\t\tShare:\t%t\n", set.Defaults.Perm.Share)
fmt.Fprintf(w, "\t\tDownload:\t%t\n", set.Defaults.Perm.Download)
w.Flush()
b, err := json.MarshalIndent(auther, "", " ")
checkErr(err)
fmt.Printf("\nAuther configuration (raw):\n\n%s\n\n", string(b))
}

View File

@ -1,25 +0,0 @@
package cmd
import (
"github.com/spf13/cobra"
)
func init() {
configCmd.AddCommand(configCatCmd)
}
var configCatCmd = &cobra.Command{
Use: "cat",
Short: "Prints the configuration",
Long: `Prints the configuration.`,
Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
set, err := d.store.Settings.Get()
checkErr(err)
ser, err := d.store.Settings.GetServer()
checkErr(err)
auther, err := d.store.Auth.Get(set.Auth.Method)
checkErr(err)
printSettings(ser, set, auther)
}, pythonConfig{}),
}

View File

@ -1,37 +0,0 @@
package cmd
import (
"github.com/spf13/cobra"
)
func init() {
configCmd.AddCommand(configExportCmd)
}
var configExportCmd = &cobra.Command{
Use: "export <path>",
Short: "Export the configuration to a file",
Long: `Export the configuration to a file. The path must be for a
json or yaml file. This exported configuration can be changed,
and imported again with 'config import' command.`,
Args: jsonYamlArg,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
settings, err := d.store.Settings.Get()
checkErr(err)
server, err := d.store.Settings.GetServer()
checkErr(err)
auther, err := d.store.Auth.Get(settings.Auth.Method)
checkErr(err)
data := &settingsFile{
Settings: settings,
Auther: auther,
Server: server,
}
err = marshal(args[0], data)
checkErr(err)
}, pythonConfig{}),
}

View File

@ -3,9 +3,9 @@ package cmd
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"log"
"path/filepath" "path/filepath"
"reflect" "reflect"
"log"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -13,10 +13,6 @@ import (
"github.com/gtsteffaniak/filebrowser/settings" "github.com/gtsteffaniak/filebrowser/settings"
) )
func init() {
configCmd.AddCommand(configImportCmd)
}
type settingsFile struct { type settingsFile struct {
Settings *settings.Settings `json:"settings"` Settings *settings.Settings `json:"settings"`
Server *settings.Server `json:"server"` Server *settings.Server `json:"server"`
@ -79,7 +75,6 @@ The path must be for a json or yaml file.`,
err = d.store.Auth.Save(auther) err = d.store.Auth.Save(auther)
checkErr(err) checkErr(err)
printSettings(file.Server, file.Settings, auther)
}, pythonConfig{allowNoDB: true}), }, pythonConfig{allowNoDB: true}),
} }

View File

@ -5,14 +5,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/gtsteffaniak/filebrowser/auth"
"github.com/gtsteffaniak/filebrowser/errors"
"github.com/gtsteffaniak/filebrowser/settings" "github.com/gtsteffaniak/filebrowser/settings"
) )
func init() {
configCmd.AddCommand(configInitCmd)
addConfigFlags(configInitCmd.Flags())
}
var configInitCmd = &cobra.Command{ var configInitCmd = &cobra.Command{
Use: "init", Use: "init",
Short: "Initialize a new database", Short: "Initialize a new database",
@ -23,14 +20,12 @@ to the defaults when creating new users and you don't
override the options.`, override the options.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
defaults := settings.UserDefaults{} auther := getAuthentication()
flags := cmd.Flags() s := settings.GlobalConfiguration
getUserDefaults(flags, &defaults, true) s.Key = generateKey()
_, auther := getAuthentication() err := d.store.Settings.Save(&s)
ser := &settings.GlobalConfiguration.Server
err := d.store.Settings.Save(&settings.GlobalConfiguration)
checkErr(err) checkErr(err)
err = d.store.Settings.SaveServer(ser) err = d.store.Settings.SaveServer(&s.Server)
checkErr(err) checkErr(err)
err = d.store.Auth.Save(auther) err = d.store.Auth.Save(auther)
checkErr(err) checkErr(err)
@ -40,6 +35,45 @@ Congratulations! You've set up your database to use with File Browser.
Now add your first user via 'filebrowser users add' and then you just Now add your first user via 'filebrowser users add' and then you just
need to call the main command to boot up the server. need to call the main command to boot up the server.
`) `)
printSettings(ser, &settings.GlobalConfiguration, auther)
}, pythonConfig{noDB: true}), }, pythonConfig{noDB: true}),
} }
//nolint:gocyclo
func getAuthentication() auth.Auther {
method := settings.GlobalConfiguration.Auth.Method
var auther auth.Auther
if method == "proxy" {
header := settings.GlobalConfiguration.Auth.Header
auther = &auth.ProxyAuth{Header: header}
}
if method == "noauth" {
auther = &auth.NoAuth{}
}
if method == "password" {
jsonAuth := &auth.JSONAuth{}
host := settings.GlobalConfiguration.Auth.Recaptcha.Host
key := settings.GlobalConfiguration.Auth.Recaptcha.Key
secret := settings.GlobalConfiguration.Auth.Recaptcha.Secret
if key != "" && secret != "" {
jsonAuth.ReCaptcha = &auth.ReCaptcha{
Host: host,
Key: key,
Secret: secret,
}
}
auther = jsonAuth
}
if method == "hook" {
command := settings.GlobalConfiguration.Auth.Command
auther = &auth.HookAuth{Command: command}
}
if auther == nil {
panic(errors.ErrInvalidAuthMethod)
}
return auther
}

View File

@ -1,74 +0,0 @@
package cmd
import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func init() {
configCmd.AddCommand(configSetCmd)
addConfigFlags(configSetCmd.Flags())
}
var configSetCmd = &cobra.Command{
Use: "set",
Short: "Updates the configuration",
Long: `Updates the configuration. Set the flags for the options
you want to change. Other options will remain unchanged.`,
Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
flags := cmd.Flags()
set, err := d.store.Settings.Get()
checkErr(err)
ser, err := d.store.Settings.GetServer()
checkErr(err)
flags.Visit(func(flag *pflag.Flag) {
switch flag.Name {
case "baseurl":
ser.BaseURL = mustGetString(flags, flag.Name)
case "root":
ser.Root = mustGetString(flags, flag.Name)
case "socket":
ser.Socket = mustGetString(flags, flag.Name)
case "cert":
ser.TLSCert = mustGetString(flags, flag.Name)
case "key":
ser.TLSKey = mustGetString(flags, flag.Name)
case "address":
ser.Address = mustGetString(flags, flag.Name)
case "port":
ser.Port = 8080
case "log":
ser.Log = mustGetString(flags, flag.Name)
case "signup":
set.Signup = mustGetBool(flags, flag.Name)
case "shell":
set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name))
case "frontend.name":
set.Frontend.Name = mustGetString(flags, flag.Name)
case "frontend.color":
set.Frontend.Color = mustGetString(flags, flag.Name)
case "frontend.disableExternal":
set.Frontend.DisableExternal = mustGetBool(flags, flag.Name)
case "frontend.disableUsedPercentage":
set.Frontend.DisableUsedPercentage = mustGetBool(flags, flag.Name)
case "frontend.files":
set.Frontend.Files = mustGetString(flags, flag.Name)
}
})
getUserDefaults(flags, &set.Defaults, false)
// read the defaults
_, auther := getAuthentication()
err = d.store.Auth.Save(auther)
checkErr(err)
err = d.store.Settings.Save(set)
checkErr(err)
err = d.store.Settings.SaveServer(ser)
checkErr(err)
printSettings(ser, set, auther)
}, pythonConfig{}),
}

View File

@ -142,6 +142,12 @@ user created with the credentials from options "username" and "password".`,
}, pythonConfig{allowNoDB: true}), }, pythonConfig{allowNoDB: true}),
} }
func StartFilebrowser() {
if err := rootCmd.Execute(); err != nil {
log.Fatal(err)
}
}
func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfacer func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfacer
sig := <-c sig := <-c
log.Printf("Caught signal %s: shutting down.", sig) log.Printf("Caught signal %s: shutting down.", sig)
@ -204,50 +210,18 @@ func setupLog(logMethod string) {
} }
func quickSetup(flags *pflag.FlagSet, d pythonData) { func quickSetup(flags *pflag.FlagSet, d pythonData) {
set := &settings.Settings{ settings.GlobalConfiguration.Key = generateKey()
Key: generateKey(),
Signup: false,
CreateUserDir: false,
UserHomeBasePath: settings.DefaultUsersHomeBasePath,
Defaults: settings.UserDefaults{
Scope: ".",
Locale: "en",
SingleClick: false,
Perm: users.Permissions{
Admin: false,
Execute: true,
Create: true,
Rename: true,
Modify: true,
Delete: true,
Share: true,
Download: true,
},
},
Frontend: settings.Frontend{},
Commands: nil,
Shell: nil,
Rules: nil,
}
var err error var err error
if settings.GlobalConfiguration.Auth.Method == "noauth" { if settings.GlobalConfiguration.Auth.Method == "noauth" {
set.Auth.Method = "noauth" settings.GlobalConfiguration.Auth.Method = "noauth"
err = d.store.Auth.Save(&auth.NoAuth{}) err = d.store.Auth.Save(&auth.NoAuth{})
} else { } else {
set.Auth.Method = "password" settings.GlobalConfiguration.Auth.Method = "password"
err = d.store.Auth.Save(&auth.JSONAuth{}) err = d.store.Auth.Save(&auth.JSONAuth{})
} }
err = d.store.Settings.Save(set) err = d.store.Settings.Save(&settings.GlobalConfiguration)
checkErr(err) checkErr(err)
err = d.store.Settings.SaveServer(&settings.GlobalConfiguration.Server)
ser := &settings.Server{
BaseURL: getParam(flags, "baseurl"),
Log: getParam(flags, "log"),
TLSKey: getParam(flags, "key"),
TLSCert: getParam(flags, "cert"),
Root: getParam(flags, "root"),
}
err = d.store.Settings.SaveServer(ser)
checkErr(err) checkErr(err)
username := getParam(flags, "username") username := getParam(flags, "username")
@ -268,7 +242,7 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
LockPassword: false, LockPassword: false,
} }
set.Defaults.Apply(user) settings.GlobalConfiguration.UserDefaults.Apply(user)
user.Perm.Admin = true user.Perm.Admin = true
err = d.store.Users.Save(user) err = d.store.Users.Save(user)

View File

@ -1,31 +0,0 @@
package cmd
import (
"github.com/spf13/cobra"
"github.com/gtsteffaniak/filebrowser/storage/bolt/importer"
)
func init() {
rootCmd.AddCommand(upgradeCmd)
upgradeCmd.Flags().String("old.database", "", "")
upgradeCmd.Flags().String("old.config", "", "")
_ = upgradeCmd.MarkFlagRequired("old.database")
}
var upgradeCmd = &cobra.Command{
Use: "upgrade",
Short: "Upgrades an old configuration",
Long: `Upgrades an old configuration. This command DOES NOT
import share links because they are incompatible with
this version.`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
flags := cmd.Flags()
oldDB := mustGetString(flags, "old.database")
oldConf := mustGetString(flags, "old.config")
err := importer.Import(oldDB, oldConf, getParam(flags, "database"))
checkErr(err)
},
}

View File

@ -79,8 +79,8 @@ func addUserFlags(flags *pflag.FlagSet) {
flags.Bool("singleClick", false, "use single clicks only") flags.Bool("singleClick", false, "use single clicks only")
} }
func getViewMode(flags *pflag.FlagSet) users.ViewMode { func getViewMode(flags *pflag.FlagSet) string {
viewMode := users.ViewMode(mustGetString(flags, "viewMode")) viewMode := settings.GlobalConfiguration.UserDefaults.ViewMode
if viewMode != users.ListViewMode && viewMode != users.MosaicViewMode { if viewMode != users.ListViewMode && viewMode != users.MosaicViewMode {
checkErr(errors.New("view mode must be \"" + string(users.ListViewMode) + "\" or \"" + string(users.MosaicViewMode) + "\"")) checkErr(errors.New("view mode must be \"" + string(users.ListViewMode) + "\" or \"" + string(users.MosaicViewMode) + "\""))
} }

View File

@ -19,7 +19,6 @@ var usersAddCmd = &cobra.Command{
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := d.store.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
getUserDefaults(cmd.Flags(), &s.Defaults, false)
password, err := users.HashPwd(args[1]) password, err := users.HashPwd(args[1])
checkErr(err) checkErr(err)
@ -30,7 +29,7 @@ var usersAddCmd = &cobra.Command{
LockPassword: mustGetBool(cmd.Flags(), "lockPassword"), LockPassword: mustGetBool(cmd.Flags(), "lockPassword"),
} }
s.Defaults.Apply(user) s.UserDefaults.Apply(user)
servSettings, err := d.store.Settings.GetServer() servSettings, err := d.store.Settings.GetServer()
checkErr(err) checkErr(err)

View File

@ -49,7 +49,6 @@ options you want to change.`,
Sorting: user.Sorting, Sorting: user.Sorting,
Commands: user.Commands, Commands: user.Commands,
} }
getUserDefaults(flags, &defaults, false)
user.Scope = defaults.Scope user.Scope = defaults.Scope
user.Locale = defaults.Locale user.Locale = defaults.Locale
user.ViewMode = defaults.ViewMode user.ViewMode = defaults.ViewMode

View File

@ -85,8 +85,7 @@ func dbExists(path string) (bool, error) {
func python(fn pythonFunc, cfg pythonConfig) cobraFunc { func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
return func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) {
data := pythonData{hadDB: true} data := pythonData{hadDB: true}
path := settings.GlobalConfiguration.Server.Database
path := getParam(cmd.Flags(), "database")
exists, err := dbExists(path) exists, err := dbExists(path)
if err != nil { if err != nil {
@ -100,6 +99,7 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
data.hadDB = exists data.hadDB = exists
db, err := storm.Open(path) db, err := storm.Open(path)
checkErr(err) checkErr(err)
defer db.Close() defer db.Close()
data.store, err = bolt.NewStorage(db) data.store, err = bolt.NewStorage(db)
checkErr(err) checkErr(err)

View File

@ -1,6 +1,6 @@
server: server:
indexingInterval: 5 indexingInterval: 60
numImageProcessors: 2 numImageProcessors: 8
socket: "" socket: ""
tlsKey: "" tlsKey: ""
tlsCert: "" tlsCert: ""
@ -21,12 +21,12 @@ auth:
header: "" header: ""
method: noauth method: noauth
command: "" command: ""
signup: false signup: true
shell: "" shell: ""
frontend: frontend:
name: "" name: ""
disableExternal: false disableExternal: false
disableUsedPercentage: false disableUsedPercentage: true
files: "" files: ""
theme: "" theme: ""
color: "" color: ""
@ -37,16 +37,16 @@ userDefaults:
singleClick: false singleClick: false
sorting: sorting:
by: "" by: ""
asc: false asc: true
perm: perm:
admin: false admin: true
execute: false execute: true
create: false create: true
rename: false rename: true
modify: false modify: true
delete: false delete: true
share: false share: true
download: false download: true
commands: [] commands: []
hideDotfiles: false hideDotfiles: true
dateFormat: false dateFormat: false

View File

@ -9,12 +9,11 @@ require (
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
github.com/goccy/go-yaml v1.11.0 github.com/goccy/go-yaml v1.11.0
github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/go-cmp v0.5.9
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/maruel/natural v1.1.0 github.com/maruel/natural v1.1.0
github.com/marusama/semaphore/v2 v2.5.0 github.com/marusama/semaphore/v2 v2.5.0
github.com/mholt/archiver/v3 v3.5.1 github.com/mholt/archiver/v3 v3.5.1
github.com/pelletier/go-toml/v2 v2.0.9
github.com/shirou/gopsutil/v3 v3.23.7 github.com/shirou/gopsutil/v3 v3.23.7
github.com/spf13/afero v1.9.5 github.com/spf13/afero v1.9.5
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
@ -22,7 +21,6 @@ require (
github.com/spf13/viper v1.16.0 github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
go.etcd.io/bbolt v1.3.7
golang.org/x/crypto v0.12.0 golang.org/x/crypto v0.12.0
golang.org/x/image v0.11.0 golang.org/x/image v0.11.0
golang.org/x/text v0.12.0 golang.org/x/text v0.12.0
@ -51,6 +49,7 @@ require (
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect github.com/nwaples/rardecode v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
github.com/pierrec/lz4/v4 v4.1.2 // indirect github.com/pierrec/lz4/v4 v4.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
@ -60,6 +59,7 @@ require (
github.com/ulikunitz/xz v0.5.9 // indirect github.com/ulikunitz/xz v0.5.9 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.11.0 // indirect golang.org/x/sys v0.11.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect

View File

@ -178,8 +178,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=

View File

@ -22,7 +22,7 @@ const (
type userInfo struct { type userInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
Locale string `json:"locale"` Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"` ViewMode string `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
Perm users.Permissions `json:"perm"` Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"` Commands []string `json:"commands"`
@ -107,7 +107,7 @@ var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, e
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
user, err := auther.Auth(r, d.store.Users, d.settings, d.server) user, err := auther.Auth(r, d.store.Users)
if err == os.ErrPermission { if err == os.ErrPermission {
return http.StatusForbidden, nil return http.StatusForbidden, nil
} else if err != nil { } else if err != nil {
@ -145,7 +145,7 @@ var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int,
Username: info.Username, Username: info.Username,
} }
d.settings.Defaults.Apply(user) d.settings.UserDefaults.Apply(user)
pwd, err := users.HashPwd(info.Password) pwd, err := users.HashPwd(info.Password)
if err != nil { if err != nil {
@ -177,6 +177,7 @@ var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data
}) })
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) { func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) {
log.Printf("%#v", user)
claims := &authToken{ claims := &authToken{
User: userInfo{ User: userInfo{
ID: user.ID, ID: user.ID,

View File

@ -1,111 +0,0 @@
package http
import (
"bufio"
"io"
"log"
"net/http"
"os/exec"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/gtsteffaniak/filebrowser/runner"
)
const (
WSWriteDeadline = 10 * time.Second
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
var (
cmdNotAllowed = []byte("Command not allowed.")
)
//nolint:unparam
func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) {
txt := http.StatusText(status)
if err != nil || status >= 400 {
log.Printf("%s: %v %s %v", r.URL.Path, status, r.RemoteAddr, err)
}
if err := ws.WriteControl(websocket.CloseInternalServerErr, []byte(txt), time.Now().Add(WSWriteDeadline)); err != nil {
log.Print(err)
}
}
var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return http.StatusInternalServerError, err
}
defer conn.Close()
var raw string
for {
_, msg, err := conn.ReadMessage() //nolint:govet
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
return 0, nil
}
raw = strings.TrimSpace(string(msg))
if raw != "" {
break
}
}
command, err := runner.ParseCommand(d.settings, raw)
if err != nil {
if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:govet
wsErr(conn, r, http.StatusInternalServerError, err)
}
return 0, nil
}
if !d.server.EnableExec || !d.user.CanExecute(command[0]) {
if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:govet
wsErr(conn, r, http.StatusInternalServerError, err)
}
return 0, nil
}
cmd := exec.Command(command[0], command[1:]...) //nolint:gosec
cmd.Dir = d.user.FullPath(r.URL.Path)
stdout, err := cmd.StdoutPipe()
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
return 0, nil
}
stderr, err := cmd.StderrPipe()
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
return 0, nil
}
if err := cmd.Start(); err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
return 0, nil
}
s := bufio.NewScanner(io.MultiReader(stdout, stderr))
for s.Scan() {
if err := conn.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil {
log.Print(err)
}
}
if err := cmd.Wait(); err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
}
return 0, nil
})

View File

@ -32,58 +32,44 @@ func NewHandler(
}) })
}) })
index, static := getStaticHandlers(store, server, assetsFs) index, static := getStaticHandlers(store, server, assetsFs)
// NOTE: This fixes the issue where it would redirect if people did not put a // NOTE: This fixes the issue where it would redirect if people did not put a
// trailing slash in the end. I hate this decision since this allows some awful // trailing slash in the end. I hate this decision since this allows some awful
// URLs https://www.gorillatoolkit.org/pkg/mux#Router.SkipClean // URLs https://www.gorillatoolkit.org/pkg/mux#Router.SkipClean
r = r.SkipClean(true) r = r.SkipClean(true)
monkey := func(fn handleFunc, prefix string) http.Handler { monkey := func(fn handleFunc, prefix string) http.Handler {
return handle(fn, prefix, store, server) return handle(fn, prefix, store, server)
} }
r.HandleFunc("/health", healthHandler) r.HandleFunc("/health", healthHandler)
r.PathPrefix("/static").Handler(static) r.PathPrefix("/static").Handler(static)
r.NotFoundHandler = index r.NotFoundHandler = index
api := r.PathPrefix("/api").Subrouter() api := r.PathPrefix("/api").Subrouter()
api.Handle("/login", monkey(loginHandler, "")) api.Handle("/login", monkey(loginHandler, ""))
api.Handle("/signup", monkey(signupHandler, "")) api.Handle("/signup", monkey(signupHandler, ""))
api.Handle("/renew", monkey(renewHandler, "")) api.Handle("/renew", monkey(renewHandler, ""))
users := api.PathPrefix("/users").Subrouter() users := api.PathPrefix("/users").Subrouter()
users.Handle("", monkey(usersGetHandler, "")).Methods("GET") users.Handle("", monkey(usersGetHandler, "")).Methods("GET")
users.Handle("", monkey(userPostHandler, "")).Methods("POST") users.Handle("", monkey(userPostHandler, "")).Methods("POST")
users.Handle("/{id:[0-9]+}", monkey(userPutHandler, "")).Methods("PUT") users.Handle("/{id:[0-9]+}", monkey(userPutHandler, "")).Methods("PUT")
users.Handle("/{id:[0-9]+}", monkey(userGetHandler, "")).Methods("GET") users.Handle("/{id:[0-9]+}", monkey(userGetHandler, "")).Methods("GET")
users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE") users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE")
api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET") api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET")
api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler(fileCache), "/api/resources")).Methods("DELETE") api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler(fileCache), "/api/resources")).Methods("DELETE")
api.PathPrefix("/resources").Handler(monkey(resourcePostHandler(fileCache), "/api/resources")).Methods("POST") api.PathPrefix("/resources").Handler(monkey(resourcePostHandler(fileCache), "/api/resources")).Methods("POST")
api.PathPrefix("/resources").Handler(monkey(resourcePutHandler, "/api/resources")).Methods("PUT") api.PathPrefix("/resources").Handler(monkey(resourcePutHandler, "/api/resources")).Methods("PUT")
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler(fileCache), "/api/resources")).Methods("PATCH") api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler(fileCache), "/api/resources")).Methods("PATCH")
api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET") api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET")
api.Path("/shares").Handler(monkey(shareListHandler, "/api/shares")).Methods("GET") api.Path("/shares").Handler(monkey(shareListHandler, "/api/shares")).Methods("GET")
api.PathPrefix("/share").Handler(monkey(shareGetsHandler, "/api/share")).Methods("GET") 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(sharePostHandler, "/api/share")).Methods("POST")
api.PathPrefix("/share").Handler(monkey(shareDeleteHandler, "/api/share")).Methods("DELETE") api.PathPrefix("/share").Handler(monkey(shareDeleteHandler, "/api/share")).Methods("DELETE")
api.Handle("/settings", monkey(settingsGetHandler, "")).Methods("GET") api.Handle("/settings", monkey(settingsGetHandler, "")).Methods("GET")
api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT") api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT")
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET") api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
api.PathPrefix("/preview/{size}/{path:.*}"). api.PathPrefix("/preview/{size}/{path:.*}").
Handler(monkey(previewHandler(imgSvc, fileCache, server.EnableThumbnails, server.ResizePreview), "/api/preview")).Methods("GET") Handler(monkey(previewHandler(imgSvc, fileCache, server.EnableThumbnails, server.ResizePreview), "/api/preview")).Methods("GET")
api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET")
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET") api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
public := api.PathPrefix("/public").Subrouter() public := api.PathPrefix("/public").Subrouter()
public.PathPrefix("/dl").Handler(monkey(publicDlHandler, "/api/public/dl/")).Methods("GET") public.PathPrefix("/dl").Handler(monkey(publicDlHandler, "/api/public/dl/")).Methods("GET")
public.PathPrefix("/share").Handler(monkey(publicShareHandler, "/api/public/share/")).Methods("GET") public.PathPrefix("/share").Handler(monkey(publicShareHandler, "/api/public/share/")).Methods("GET")
return stripPrefix(server.BaseURL, r), nil return stripPrefix(server.BaseURL, r), nil
} }

View File

@ -24,7 +24,7 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
Signup: d.settings.Signup, Signup: d.settings.Signup,
CreateUserDir: d.settings.CreateUserDir, CreateUserDir: d.settings.CreateUserDir,
UserHomeBasePath: d.settings.UserHomeBasePath, UserHomeBasePath: d.settings.UserHomeBasePath,
Defaults: d.settings.Defaults, Defaults: d.settings.UserDefaults,
Rules: d.settings.Rules, Rules: d.settings.Rules,
Frontend: d.settings.Frontend, Frontend: d.settings.Frontend,
Shell: d.settings.Shell, Shell: d.settings.Shell,
@ -44,7 +44,7 @@ var settingsPutHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
d.settings.Signup = req.Signup d.settings.Signup = req.Signup
d.settings.CreateUserDir = req.CreateUserDir d.settings.CreateUserDir = req.CreateUserDir
d.settings.UserHomeBasePath = req.UserHomeBasePath d.settings.UserHomeBasePath = req.UserHomeBasePath
d.settings.Defaults = req.Defaults d.settings.UserDefaults = req.Defaults
d.settings.Rules = req.Rules d.settings.Rules = req.Rules
d.settings.Frontend = req.Frontend d.settings.Frontend = req.Frontend
d.settings.Shell = req.Shell d.settings.Shell = req.Shell

View File

@ -2,8 +2,10 @@ package main
import ( import (
"github.com/gtsteffaniak/filebrowser/cmd" "github.com/gtsteffaniak/filebrowser/cmd"
"github.com/gtsteffaniak/filebrowser/settings"
) )
func main() { func main() {
cmd.Execute() settings.Initialize()
cmd.StartFilebrowser()
} }

View File

@ -1,6 +1,5 @@
#!/bin/sh #!/bin/sh
## TEST file used by docker testing containers ## TEST file used by docker testing containers
touch render.yml
checkExit() { checkExit() {
if [ "$?" -ne 0 ];then if [ "$?" -ne 0 ];then
exit 1 exit 1

View File

@ -1,6 +1,5 @@
#!/bin/sh #!/bin/sh
## TEST file used by docker testing containers ## TEST file used by docker testing containers
touch render.yml
checkExit() { checkExit() {
if [ "$?" -ne 0 ];then if [ "$?" -ne 0 ];then
exit 1 exit 1

View File

@ -8,18 +8,30 @@ import (
) )
var GlobalConfiguration Settings var GlobalConfiguration Settings
var configYml = "filebrowser.yaml"
func init() { func Initialize() {
// Open and read the YAML file yamlData := loadConfigFile()
yamlFile, err := os.Open("filebrowser.yml") GlobalConfiguration = setDefaults()
err := yaml.Unmarshal(yamlData, &GlobalConfiguration)
if err != nil { if err != nil {
log.Fatalf("Error opening YAML file: %v", err) log.Fatalf("Error unmarshaling YAML data: %v", err)
}
}
func loadConfigFile() []byte {
// Open and read the YAML file
yamlFile, err := os.Open(configYml)
if err != nil {
log.Printf("Error opening config file: %v\nUsing default config only", err)
setDefaults()
return []byte{}
} }
defer yamlFile.Close() defer yamlFile.Close()
stat, err := yamlFile.Stat() stat, err := yamlFile.Stat()
if err != nil { if err != nil {
log.Fatalf("Error getting file information: %v", err) log.Fatalf("Error getting file information: %s", err.Error())
} }
yamlData := make([]byte, stat.Size()) yamlData := make([]byte, stat.Size())
@ -27,23 +39,16 @@ func init() {
if err != nil { if err != nil {
log.Fatalf("Error reading YAML data: %v", err) log.Fatalf("Error reading YAML data: %v", err)
} }
setDefaults() return yamlData
// Unmarshal the YAML data into the Settings struct
err = yaml.Unmarshal(yamlData, &GlobalConfiguration)
if err != nil {
log.Fatalf("Error unmarshaling YAML data: %v", err)
}
// Now you have the Settings struct with values from the YAML file
// You can access the values like: defaultSettings.Key, defaultSettings.Server.Port, etc.
} }
func setDefaults() { func setDefaults() Settings {
GlobalConfiguration = Settings{ return Settings{
Signup: true, Signup: true,
Server: Server{ Server: Server{
IndexingInterval: 5, IndexingInterval: 5,
Port: 8080, Port: 8080,
NumImageProcessors: 1, NumImageProcessors: 4,
BaseURL: "", BaseURL: "",
}, },
Auth: Auth{ Auth: Auth{
@ -52,5 +57,8 @@ func setDefaults() {
Host: "", Host: "",
}, },
}, },
UserDefaults: UserDefaults{
HideDotfiles: true,
},
} }
} }

View File

@ -0,0 +1,46 @@
package settings
import (
"log"
"testing"
"github.com/goccy/go-yaml"
"github.com/google/go-cmp/cmp"
)
func TestConfigLoadChanged(t *testing.T) {
configYml = "./testingConfig.yaml"
yamlData := loadConfigFile()
// Marshal the YAML data to a more human-readable format
newConfig := setDefaults()
GlobalConfiguration := setDefaults()
err := yaml.Unmarshal(yamlData, &newConfig)
if err != nil {
log.Fatalf("Error unmarshaling YAML data: %v", err)
}
// Use go-cmp to compare the two structs
if diff := cmp.Diff(newConfig, GlobalConfiguration); diff == "" {
t.Errorf("No change when there should have been (-want +got):\n%s", diff)
}
}
func TestConfigLoadSpecificValues(t *testing.T) {
configYml = "./testingConfig.yaml"
yamlData := loadConfigFile()
// Marshal the YAML data to a more human-readable format
newConfig := setDefaults()
GlobalConfiguration := setDefaults()
err := yaml.Unmarshal(yamlData, &newConfig)
if err != nil {
log.Fatalf("Error unmarshaling YAML data: %v", err)
}
if GlobalConfiguration.Auth.Method == newConfig.Auth.Method {
log.Fatalf("Differences should have been found, but were not on Auth method")
}
if GlobalConfiguration.UserDefaults.HideDotfiles == newConfig.UserDefaults.HideDotfiles {
log.Fatalf("Differences should have been found, but were not on Auth method")
}
}

View File

@ -50,16 +50,16 @@ func (s *Storage) Save(set *Settings) error {
return errors.ErrEmptyKey return errors.ErrEmptyKey
} }
if set.Defaults.Locale == "" { if set.UserDefaults.Locale == "" {
set.Defaults.Locale = "en" set.UserDefaults.Locale = "en"
} }
if set.Defaults.Commands == nil { if set.UserDefaults.Commands == nil {
set.Defaults.Commands = []string{} set.UserDefaults.Commands = []string{}
} }
if set.Defaults.ViewMode == "" { if set.UserDefaults.ViewMode == "" {
set.Defaults.ViewMode = users.MosaicViewMode set.UserDefaults.ViewMode = users.MosaicViewMode
} }
if set.Rules == nil { if set.Rules == nil {

View File

@ -1,7 +1,6 @@
package settings package settings
import ( import (
"github.com/gtsteffaniak/filebrowser/files"
"github.com/gtsteffaniak/filebrowser/rules" "github.com/gtsteffaniak/filebrowser/rules"
"github.com/gtsteffaniak/filebrowser/users" "github.com/gtsteffaniak/filebrowser/users"
) )
@ -24,20 +23,17 @@ type Settings struct {
Signup bool `json:"signup"` Signup bool `json:"signup"`
CreateUserDir bool `json:"createUserDir"` CreateUserDir bool `json:"createUserDir"`
UserHomeBasePath string `json:"userHomeBasePath"` UserHomeBasePath string `json:"userHomeBasePath"`
Defaults UserDefaults `json:"defaults"`
Commands map[string][]string `json:"commands"` Commands map[string][]string `json:"commands"`
Shell []string `json:"shell"` Shell []string `json:"shell"`
Rules []rules.Rule `json:"rules"` Rules []rules.Rule `json:"rules"`
Server Server `json:"server"` Server Server `json:"server"`
Auth Auth `json:"auth"` Auth Auth `json:"auth"`
Frontend Frontend `json:"frontend"` Frontend Frontend `json:"frontend"`
UserDefaults UserDefaults `json:"userDefaults"` UserDefaults UserDefaults `json:"userDefaults"`
} }
type Auth struct { type Auth struct {
Recaptcha Recaptcha Recaptcha Recaptcha `json:"recaptcha"`
Header string `json:"header"` Header string `json:"header"`
Method string `json:"method"` Method string `json:"method"`
Command string `json:"command"` Command string `json:"command"`
@ -46,9 +42,9 @@ type Auth struct {
} }
type Recaptcha struct { type Recaptcha struct {
Host string Host string `json:"host"`
Key string Key string `json:"key"`
Secret string Secret string `json:"secret"`
} }
type Server struct { type Server struct {
@ -68,7 +64,6 @@ type Server struct {
Log string `json:"log"` Log string `json:"log"`
Database string `json:"database"` Database string `json:"database"`
Root string `json:"root"` Root string `json:"root"`
EnablePreviewResize bool `json:"disable-preview-resize"`
} }
type Frontend struct { type Frontend struct {
@ -85,53 +80,23 @@ type Frontend struct {
type UserDefaults struct { type UserDefaults struct {
Scope string `json:"scope"` Scope string `json:"scope"`
Locale string `json:"locale"` Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"` ViewMode string `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
Sorting files.Sorting `json:"sorting"` Sorting struct {
Perm users.Permissions `json:"perm"` By string `json:"by"`
Asc bool `json:"asc"`
} `json:"sorting"`
Perm struct {
Admin bool `json:"admin"`
Execute bool `json:"execute"`
Create bool `json:"create"`
Rename bool `json:"rename"`
Modify bool `json:"modify"`
Delete bool `json:"delete"`
Share bool `json:"share"`
Download bool `json:"download"`
} `json:"perm"`
Commands []string `json:"commands"` Commands []string `json:"commands"`
HideDotfiles bool `json:"hideDotfiles"` HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"` DateFormat bool `json:"dateFormat"`
} }
//{
// "server":{
// "port":8080,
// "baseURL":"",
// "address":"",
// "log":"stdout",
// "database":"./database.db",
// "root":"/srv",
// "disable-thumbnails":false,
// "disable-preview-resize":false,
// "disable-exec":false,
// "disable-type-detection-by-header":false
// },
// "auth":{
// "header":"",
// "method":"",
// "command":"",
// "signup":false,
// "shell":""
// },
// "branding":{
// "name":"",
// "color":"",
// "files":"",
// "disableExternal":"",
// "disableUsedPercentage":""
// },
// "permissions":{
// "Admin":false,
// "Execute":true,
// "Create":true,
// "Rename":true,
// "Modify":true,
// "Delete":true,
// "Share":true,
// "Download":true
// },
// "commands":{},
// "shell":{},
// "rules":{}
// }

View File

@ -0,0 +1,52 @@
server:
indexingInterval: 5
numImageProcessors: 4
socket: ""
tlsKey: ""
tlsCert: ""
enableThumbnails: false
resizePreview: true
typeDetectionByHeader: true
port: 8080
baseURL: "/"
address: ""
log: "stdout"
database: "database.db"
root: "/srv"
auth:
recaptcha:
host: ""
key: ""
secret: ""
header: ""
method: json
command: ""
signup: false
shell: ""
frontend:
name: ""
disableExternal: false
disableUsedPercentage: true
files: ""
theme: ""
color: ""
userDefaults:
scope: ""
locale: ""
viewMode: ""
singleClick: true
sorting:
by: ""
asc: true
perm:
admin: true
execute: true
create: true
rename: true
modify: true
delete: true
share: true
download: true
commands: []
hideDotfiles: false
dateFormat: false

View File

@ -1,187 +0,0 @@
package importer
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"github.com/asdine/storm/v3"
"github.com/pelletier/go-toml/v2"
"gopkg.in/yaml.v2"
"github.com/gtsteffaniak/filebrowser/auth"
"github.com/gtsteffaniak/filebrowser/settings"
"github.com/gtsteffaniak/filebrowser/storage"
"github.com/gtsteffaniak/filebrowser/users"
)
type oldDefs struct {
Commands []string `json:"commands" yaml:"commands" toml:"commands"`
Scope string `json:"scope" yaml:"scope" toml:"scope"`
ViewMode string `json:"viewMode" yaml:"viewMode" toml:"viewMode"`
Locale string `json:"locale" yaml:"locale" toml:"locale"`
AllowCommands bool `json:"allowCommands" yaml:"allowCommands" toml:"allowCommands"`
AllowEdit bool `json:"allowEdit" yaml:"allowEdit" toml:"allowEdit"`
AllowNew bool `json:"allowNew" yaml:"allowNew" toml:"allowNew"`
}
type oldAuth struct {
Method string `json:"method" yaml:"method" toml:"method"` // default none proxy
Header string `json:"header" yaml:"header" toml:"header"`
Command string `json:"command" yaml:"command" toml:"command"`
}
type oldConf struct {
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"`
Defaults oldDefs `json:"defaults" yaml:"defaults" toml:"defaults"`
ReCaptcha struct {
Key string `json:"key" yaml:"key" toml:"key"`
Secret string `json:"secret" yaml:"secret" toml:"secret"`
Host string `json:"host" yaml:"host" toml:"host"`
} `json:"recaptcha" yaml:"recaptcha" toml:"recaptcha"`
Auth oldAuth `json:"auth" yaml:"auth" toml:"auth"`
}
var defaults = &oldConf{
Port: "0",
Log: "stdout",
Defaults: oldDefs{
Commands: []string{"git", "svn", "hg"},
ViewMode: string(users.MosaicViewMode),
AllowCommands: true,
AllowEdit: true,
AllowNew: true,
Locale: "en",
},
Auth: oldAuth{
Method: "default",
},
}
func readConf(path string) (*oldConf, error) {
cfg := &oldConf{}
if path != "" {
ext := filepath.Ext(path)
fd, err := os.Open(path)
if err != nil {
return nil, err
}
defer fd.Close()
switch ext {
case ".json":
err = json.NewDecoder(fd).Decode(cfg)
case ".toml":
err = toml.NewDecoder(fd).Decode(cfg)
case ".yaml", ".yml":
err = yaml.NewDecoder(fd).Decode(cfg)
default:
return nil, errors.New("unsupported config extension " + ext)
}
if err != nil {
return nil, err
}
} else {
cfg = defaults
path, err := filepath.Abs(".")
if err != nil {
return nil, err
}
cfg.Defaults.Scope = path
}
return cfg, nil
}
func importConf(db *storm.DB, path string, sto *storage.Storage) error {
cfg, err := readConf(path)
if err != nil {
return err
}
commands := map[string][]string{}
err = db.Get("config", "commands", &commands)
if err != nil {
return err
}
key := []byte{}
err = db.Get("config", "key", &key)
if err != nil {
return err
}
s := &settings.Settings{
Key: key,
Signup: false,
Defaults: settings.UserDefaults{
Scope: cfg.Defaults.Scope,
Commands: cfg.Defaults.Commands,
ViewMode: users.ViewMode(cfg.Defaults.ViewMode),
Locale: cfg.Defaults.Locale,
Perm: users.Permissions{
Admin: false,
Execute: cfg.Defaults.AllowCommands,
Create: cfg.Defaults.AllowNew,
Rename: cfg.Defaults.AllowEdit,
Modify: cfg.Defaults.AllowEdit,
Delete: cfg.Defaults.AllowEdit,
Share: true,
Download: true,
},
},
}
server := &settings.Server{
BaseURL: cfg.BaseURL,
Port: 8080,
Address: cfg.Address,
Log: cfg.Log,
}
fmt.Println("config.go", server)
var auther auth.Auther
switch cfg.Auth.Method {
case "proxy":
auther = &auth.ProxyAuth{Header: cfg.Auth.Header}
s.Auth.Method = "proxy"
case "hook":
auther = &auth.HookAuth{Command: cfg.Auth.Command}
s.Auth.Method = "hoook"
case "none":
auther = &auth.NoAuth{}
s.Auth.Method = "noauth"
default:
auther = &auth.JSONAuth{
ReCaptcha: &auth.ReCaptcha{
Host: cfg.ReCaptcha.Host,
Key: cfg.ReCaptcha.Key,
Secret: cfg.ReCaptcha.Secret,
},
}
s.Auth.Method = "password"
}
err = sto.Auth.Save(auther)
if err != nil {
return err
}
err = sto.Settings.Save(s)
if err != nil {
return err
}
err = sto.Settings.SaveServer(server)
if err != nil {
return err
}
fmt.Println("Configuration successfully imported.")
return nil
}

View File

@ -1,41 +0,0 @@
package importer
import (
"github.com/asdine/storm/v3"
"log"
"github.com/gtsteffaniak/filebrowser/storage/bolt"
)
// Import imports an old configuration to a newer database.
func Import(oldDBPath, oldConf, newDBPath string) error {
log.Println(oldDBPath, oldConf, newDBPath)
oldDB, err := storm.Open(oldDBPath)
if err != nil {
return err
}
defer oldDB.Close()
newDB, err := storm.Open(newDBPath)
if err != nil {
return err
}
defer newDB.Close()
sto, err := bolt.NewStorage(newDB)
if err != nil {
return err
}
err = importUsers(oldDB, sto)
if err != nil {
return err
}
err = importConf(oldDB, oldConf, sto)
if err != nil {
return err
}
return err
}

View File

@ -1,114 +0,0 @@
package importer
import (
"encoding/json"
"fmt"
"github.com/asdine/storm/v3"
bolt "go.etcd.io/bbolt"
"github.com/gtsteffaniak/filebrowser/rules"
"github.com/gtsteffaniak/filebrowser/storage"
"github.com/gtsteffaniak/filebrowser/users"
)
type oldUser struct {
ID int `storm:"id,increment"`
Admin bool `json:"admin"`
AllowCommands bool `json:"allowCommands"` // Execute commands
AllowEdit bool `json:"allowEdit"` // Edit/rename files
AllowNew bool `json:"allowNew"` // Create files and folders
AllowPublish bool `json:"allowPublish"` // Publish content (to use with static gen)
LockPassword bool `json:"lockPassword"`
Commands []string `json:"commands"`
Locale string `json:"locale"`
Password string `json:"password"`
Rules []*rules.Rule `json:"rules"`
Scope string `json:"filesystem"`
Username string `json:"username" storm:"index,unique"`
ViewMode string `json:"viewMode"`
}
func readOldUsers(db *storm.DB) ([]*oldUser, error) {
var oldUsers []*oldUser
err := db.Bolt.View(func(tx *bolt.Tx) error {
return tx.Bucket([]byte("User")).ForEach(func(k []byte, v []byte) error {
if len(v) > 0 && string(v)[0] == '{' {
user := &oldUser{}
err := json.Unmarshal(v, user)
if err != nil {
return err
}
oldUsers = append(oldUsers, user)
}
return nil
})
})
return oldUsers, err
}
func convertUsersToNew(old []*oldUser) ([]*users.User, error) {
list := []*users.User{}
for _, oldUser := range old {
user := &users.User{
Username: oldUser.Username,
Password: oldUser.Password,
Scope: oldUser.Scope,
Locale: oldUser.Locale,
LockPassword: oldUser.LockPassword,
ViewMode: users.ViewMode(oldUser.ViewMode),
Commands: oldUser.Commands,
Rules: []rules.Rule{},
Perm: users.Permissions{
Admin: oldUser.Admin,
Execute: oldUser.AllowCommands,
Create: oldUser.AllowNew,
Rename: oldUser.AllowEdit,
Modify: oldUser.AllowEdit,
Delete: oldUser.AllowEdit,
Share: true,
Download: true,
},
}
for _, rule := range oldUser.Rules {
user.Rules = append(user.Rules, *rule)
}
err := user.Clean("")
if err != nil {
return nil, err
}
list = append(list, user)
}
return list, nil
}
func importUsers(old *storm.DB, sto *storage.Storage) error {
oldUsers, err := readOldUsers(old)
if err != nil {
return err
}
newUsers, err := convertUsersToNew(oldUsers)
if err != nil {
return err
}
for _, user := range newUsers {
err = sto.Users.Save(user)
if err != nil {
return err
}
}
fmt.Printf("%d users successfully imported into the new DB.\n", len(newUsers))
return nil
}

View File

@ -1,12 +1,15 @@
package bolt package bolt
import ( import (
"log"
"github.com/asdine/storm/v3" "github.com/asdine/storm/v3"
"github.com/gtsteffaniak/filebrowser/errors" "github.com/gtsteffaniak/filebrowser/errors"
) )
func get(db *storm.DB, name string, to interface{}) error { func get(db *storm.DB, name string, to interface{}) error {
log.Printf("name, %v , to %#v", name, to)
err := db.Get("config", name, to) err := db.Get("config", name, to)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
return errors.ErrNotExist return errors.ErrNotExist
@ -16,5 +19,6 @@ func get(db *storm.DB, name string, to interface{}) error {
} }
func save(db *storm.DB, name string, from interface{}) error { func save(db *storm.DB, name string, from interface{}) error {
log.Printf("name, %v , from %#v", name, from)
return db.Set("config", name, from) return db.Set("config", name, from)
} }

113
backend/test_output.txt Normal file
View File

@ -0,0 +1,113 @@
== Running tests ==
/usr/local/go/bin/go
? github.com/gtsteffaniak/filebrowser [no test files]
? github.com/gtsteffaniak/filebrowser/auth [no test files]
? github.com/gtsteffaniak/filebrowser/cmd [no test files]
? github.com/gtsteffaniak/filebrowser/errors [no test files]
? github.com/gtsteffaniak/filebrowser/files [no test files]
=== RUN TestFileCache
--- PASS: TestFileCache (0.00s)
PASS
ok github.com/gtsteffaniak/filebrowser/diskcache (cached)
=== RUN TestCommonPrefix
=== RUN TestCommonPrefix/sub_folder
=== RUN TestCommonPrefix/relative_path
=== RUN TestCommonPrefix/no_common_path
=== RUN TestCommonPrefix/same_lvl
--- PASS: TestCommonPrefix (0.00s)
--- PASS: TestCommonPrefix/sub_folder (0.00s)
--- PASS: TestCommonPrefix/relative_path (0.00s)
--- PASS: TestCommonPrefix/no_common_path (0.00s)
--- PASS: TestCommonPrefix/same_lvl (0.00s)
PASS
ok github.com/gtsteffaniak/filebrowser/fileutils (cached)
? github.com/gtsteffaniak/filebrowser/settings [no test files]
? github.com/gtsteffaniak/filebrowser/share [no test files]
? github.com/gtsteffaniak/filebrowser/storage [no test files]
? github.com/gtsteffaniak/filebrowser/storage/bolt [no test files]
2023/09/02 13:07:17 Error opening YAML file: open filebrowser.yaml: no such file or directory
FAIL github.com/gtsteffaniak/filebrowser/http 0.008s
=== RUN TestService_Resize
=== RUN TestService_Resize/convert_to_png
=== RUN TestService_Resize/convert_to_tiff
=== RUN TestService_Resize/resize_bmp
=== RUN TestService_Resize/resize_with_medium_quality
=== RUN TestService_Resize/resize_with_low_quality
=== RUN TestService_Resize/get_thumbnail_from_file_with_APP0_JFIF
=== RUN TestService_Resize/fill_upscale
=== RUN TestService_Resize/fit_upscale
=== RUN TestService_Resize/convert_to_gif
=== RUN TestService_Resize/convert_to_bmp
=== RUN TestService_Resize/resize_tiff
=== RUN TestService_Resize/resize_with_high_quality
=== RUN TestService_Resize/fill_downscale
=== RUN TestService_Resize/keep_original_format
=== RUN TestService_Resize/convert_to_unknown
=== RUN TestService_Resize/get_thumbnail_from_file_without_APP0_JFIF
=== RUN TestService_Resize/resize_for_higher_quality_levels
=== RUN TestService_Resize/broken_file
=== RUN TestService_Resize/fit_downscale
=== RUN TestService_Resize/convert_to_jpeg
=== RUN TestService_Resize/resize_png
=== RUN TestService_Resize/resize_gif
=== RUN TestService_Resize/resize_with_unknown_quality
=== RUN TestService_Resize/resize_from_file_without_IFD1_thumbnail
--- PASS: TestService_Resize (1.36s)
--- PASS: TestService_Resize/convert_to_png (0.01s)
--- PASS: TestService_Resize/convert_to_tiff (0.01s)
--- PASS: TestService_Resize/resize_bmp (0.01s)
--- PASS: TestService_Resize/resize_with_medium_quality (0.01s)
--- PASS: TestService_Resize/resize_with_low_quality (0.01s)
--- PASS: TestService_Resize/get_thumbnail_from_file_with_APP0_JFIF (0.02s)
--- PASS: TestService_Resize/fill_upscale (0.01s)
--- PASS: TestService_Resize/fit_upscale (0.00s)
--- PASS: TestService_Resize/convert_to_gif (0.01s)
--- PASS: TestService_Resize/convert_to_bmp (0.01s)
--- PASS: TestService_Resize/resize_tiff (0.01s)
--- PASS: TestService_Resize/resize_with_high_quality (0.01s)
--- PASS: TestService_Resize/fill_downscale (0.01s)
--- PASS: TestService_Resize/keep_original_format (0.01s)
--- PASS: TestService_Resize/convert_to_unknown (0.01s)
--- PASS: TestService_Resize/get_thumbnail_from_file_without_APP0_JFIF (0.03s)
--- PASS: TestService_Resize/resize_for_higher_quality_levels (0.03s)
--- PASS: TestService_Resize/broken_file (0.00s)
--- PASS: TestService_Resize/fit_downscale (0.01s)
--- PASS: TestService_Resize/convert_to_jpeg (0.01s)
--- PASS: TestService_Resize/resize_png (0.02s)
--- PASS: TestService_Resize/resize_gif (0.02s)
--- PASS: TestService_Resize/resize_with_unknown_quality (0.01s)
--- PASS: TestService_Resize/resize_from_file_without_IFD1_thumbnail (1.09s)
=== RUN TestService_FormatFromExtension
=== RUN TestService_FormatFromExtension/gif
=== RUN TestService_FormatFromExtension/tiff
=== RUN TestService_FormatFromExtension/bmp
=== RUN TestService_FormatFromExtension/unknown
=== RUN TestService_FormatFromExtension/jpg
=== RUN TestService_FormatFromExtension/jpeg
=== RUN TestService_FormatFromExtension/png
--- PASS: TestService_FormatFromExtension (0.00s)
--- PASS: TestService_FormatFromExtension/gif (0.00s)
--- PASS: TestService_FormatFromExtension/tiff (0.00s)
--- PASS: TestService_FormatFromExtension/bmp (0.00s)
--- PASS: TestService_FormatFromExtension/unknown (0.00s)
--- PASS: TestService_FormatFromExtension/jpg (0.00s)
--- PASS: TestService_FormatFromExtension/jpeg (0.00s)
--- PASS: TestService_FormatFromExtension/png (0.00s)
PASS
ok github.com/gtsteffaniak/filebrowser/img (cached)
=== RUN TestMatchHidden
--- PASS: TestMatchHidden (0.00s)
PASS
ok github.com/gtsteffaniak/filebrowser/rules (cached)
2023/09/02 13:07:17 Error opening YAML file: open filebrowser.yaml: no such file or directory
FAIL github.com/gtsteffaniak/filebrowser/runner 0.007s
=== RUN TestParseSearch
--- PASS: TestParseSearch (0.00s)
PASS
ok github.com/gtsteffaniak/filebrowser/search (cached)
? github.com/gtsteffaniak/filebrowser/version [no test files]
testing: warning: no tests to run
PASS
ok github.com/gtsteffaniak/filebrowser/users (cached) [no tests to run]
FAIL

View File

@ -11,12 +11,9 @@ import (
"github.com/gtsteffaniak/filebrowser/rules" "github.com/gtsteffaniak/filebrowser/rules"
) )
// ViewMode describes a view mode. var (
type ViewMode string ListViewMode = "list"
MosaicViewMode = "mosaic"
const (
ListViewMode ViewMode = "list"
MosaicViewMode ViewMode = "mosaic"
) )
// User describes a user. // User describes a user.
@ -27,7 +24,7 @@ type User struct {
Scope string `json:"scope"` Scope string `json:"scope"`
Locale string `json:"locale"` Locale string `json:"locale"`
LockPassword bool `json:"lockPassword"` LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"` ViewMode string `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
Perm Permissions `json:"perm"` Perm Permissions `json:"perm"`
Commands []string `json:"commands"` Commands []string `json:"commands"`

View File

@ -56,7 +56,7 @@
<p> <p>
<input <input
type="checkbox" type="checkbox"
v-model="settings.branding.disableExternal" v-model="settings.frontend.disableExternal"
id="branding-links" id="branding-links"
/> />
{{ $t("settings.disableExternalLinks") }} {{ $t("settings.disableExternalLinks") }}
@ -65,7 +65,7 @@
<p> <p>
<input <input
type="checkbox" type="checkbox"
v-model="settings.branding.disableUsedPercentage" v-model="settings.frontend.disableUsedPercentage"
id="branding-links" id="branding-links"
/> />
{{ $t("settings.disableUsedDiskPercentage") }} {{ $t("settings.disableUsedDiskPercentage") }}
@ -75,7 +75,7 @@
<label for="theme">{{ $t("settings.themes.title") }}</label> <label for="theme">{{ $t("settings.themes.title") }}</label>
<themes <themes
class="input input--block" class="input input--block"
:theme.sync="settings.branding.theme" :theme.sync="settings.frontend.theme"
id="theme" id="theme"
></themes> ></themes>
</p> </p>
@ -85,7 +85,7 @@
<input <input
class="input input--block" class="input input--block"
type="text" type="text"
v-model="settings.branding.name" v-model="settings.frontend.name"
id="branding-name" id="branding-name"
/> />
</p> </p>
@ -97,7 +97,7 @@
<input <input
class="input input--block" class="input input--block"
type="text" type="text"
v-model="settings.branding.files" v-model="settings.frontend.files"
id="branding-files" id="branding-files"
/> />
</p> </p>

0
test_output.txt Normal file
View File