commit
db80387cf6
|
@ -8,7 +8,9 @@ All notable changes to this project will be documented in this file. See [standa
|
||||||
- Works with new more advanced filebrowser.json
|
- Works with new more advanced filebrowser.json
|
||||||
- improved GUI
|
- improved GUI
|
||||||
- more unified coehisive look
|
- more unified coehisive look
|
||||||
-
|
- The shell is dead.
|
||||||
|
- If you need to use the shell, exec into the docker container.
|
||||||
|
- All configuration is done via filebrowser.yml
|
||||||
|
|
||||||
# v0.1.4
|
# v0.1.4
|
||||||
- various UI fixes
|
- various UI fixes
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
|
|
||||||
"port": 8080,
|
|
||||||
"baseURL": "",
|
|
||||||
"address": "",
|
|
||||||
"log": "stdout",
|
|
||||||
"database": "./database.db",
|
|
||||||
"root": "/srv"
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,9 +15,6 @@ import (
|
||||||
"github.com/gtsteffaniak/filebrowser/users"
|
"github.com/gtsteffaniak/filebrowser/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MethodHookAuth is used to identify hook auth.
|
|
||||||
const MethodHookAuth = "hook"
|
|
||||||
|
|
||||||
type hookCred struct {
|
type hookCred struct {
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
|
|
@ -11,9 +11,6 @@ import (
|
||||||
"github.com/gtsteffaniak/filebrowser/users"
|
"github.com/gtsteffaniak/filebrowser/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MethodJSONAuth is used to identify json auth.
|
|
||||||
const MethodJSONAuth = "json"
|
|
||||||
|
|
||||||
type jsonCred struct {
|
type jsonCred struct {
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
|
|
@ -28,51 +28,28 @@ var configCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func addConfigFlags(flags *pflag.FlagSet) {
|
func addConfigFlags(flags *pflag.FlagSet) {
|
||||||
addServerFlags(flags)
|
|
||||||
addUserFlags(flags)
|
addUserFlags(flags)
|
||||||
flags.BoolP("signup", "s", false, "allow users to signup")
|
flags.BoolP("signup", "s", false, "allow users to signup")
|
||||||
flags.String("shell", "", "shell command to which other commands should be appended")
|
flags.String("shell", "", "shell command to which other commands should be appended")
|
||||||
|
|
||||||
flags.String("auth.method", string(auth.MethodJSONAuth), "authentication type")
|
|
||||||
flags.String("auth.header", "", "HTTP header for auth.method=proxy")
|
|
||||||
flags.String("auth.command", "", "command for auth.method=hook")
|
|
||||||
|
|
||||||
flags.String("recaptcha.host", "https://www.google.com", "use another host for ReCAPTCHA. recaptcha.net might be useful in China")
|
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.key", "", "ReCaptcha site key")
|
||||||
flags.String("recaptcha.secret", "", "ReCaptcha secret")
|
flags.String("recaptcha.secret", "", "ReCaptcha secret")
|
||||||
|
|
||||||
flags.String("branding.name", "", "replace 'File Browser' by this name")
|
flags.String("frontend.name", "", "replace 'File Browser' by this name")
|
||||||
flags.String("branding.color", "", "set the theme color")
|
flags.String("frontend.color", "", "set the theme color")
|
||||||
flags.String("branding.files", "", "path to directory with images and custom styles")
|
flags.String("frontend.files", "", "path to directory with images and custom styles")
|
||||||
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
|
flags.Bool("frontend.disableExternal", false, "disable external links such as GitHub links")
|
||||||
flags.Bool("branding.disableUsedPercentage", false, "disable used disk percentage graph")
|
flags.Bool("frontend.disableUsedPercentage", false, "disable used disk percentage graph")
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (string, auth.Auther) {
|
func getAuthentication() (string, auth.Auther) {
|
||||||
method := mustGetString(flags, "auth.method")
|
method := settings.GlobalConfiguration.Auth.Method
|
||||||
|
|
||||||
var defaultAuther map[string]interface{}
|
var defaultAuther map[string]interface{}
|
||||||
if len(defaults) > 0 {
|
|
||||||
if hasAuth := defaults[0]; hasAuth != true {
|
|
||||||
for _, arg := range defaults {
|
|
||||||
switch def := arg.(type) {
|
|
||||||
case *settings.Settings:
|
|
||||||
method = def.AuthMethod
|
|
||||||
case auth.Auther:
|
|
||||||
ms, err := json.Marshal(def)
|
|
||||||
checkErr(err)
|
|
||||||
err = json.Unmarshal(ms, &defaultAuther)
|
|
||||||
checkErr(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var auther auth.Auther
|
var auther auth.Auther
|
||||||
if method == auth.MethodProxyAuth {
|
if method == "proxy" {
|
||||||
header := mustGetString(flags, "auth.header")
|
header := settings.GlobalConfiguration.Auth.Header
|
||||||
|
|
||||||
if header == "" {
|
if header == "" {
|
||||||
header = defaultAuther["header"].(string)
|
header = defaultAuther["header"].(string)
|
||||||
}
|
}
|
||||||
|
@ -84,15 +61,15 @@ func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (string, a
|
||||||
auther = &auth.ProxyAuth{Header: header}
|
auther = &auth.ProxyAuth{Header: header}
|
||||||
}
|
}
|
||||||
|
|
||||||
if method == auth.MethodNoAuth {
|
if method == "noauth" {
|
||||||
auther = &auth.NoAuth{}
|
auther = &auth.NoAuth{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if method == auth.MethodJSONAuth {
|
if method == "password" {
|
||||||
jsonAuth := &auth.JSONAuth{}
|
jsonAuth := &auth.JSONAuth{}
|
||||||
host := mustGetString(flags, "recaptcha.host")
|
host := settings.GlobalConfiguration.Auth.Recaptcha.Host
|
||||||
key := mustGetString(flags, "recaptcha.key")
|
key := settings.GlobalConfiguration.Auth.Recaptcha.Key
|
||||||
secret := mustGetString(flags, "recaptcha.secret")
|
secret := settings.GlobalConfiguration.Auth.Recaptcha.Secret
|
||||||
|
|
||||||
if key == "" {
|
if key == "" {
|
||||||
if kmap, ok := defaultAuther["recaptcha"].(map[string]interface{}); ok {
|
if kmap, ok := defaultAuther["recaptcha"].(map[string]interface{}); ok {
|
||||||
|
@ -116,8 +93,8 @@ func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (string, a
|
||||||
auther = jsonAuth
|
auther = jsonAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
if method == auth.MethodHookAuth {
|
if method == "hook" {
|
||||||
command := mustGetString(flags, "auth.command")
|
command := settings.GlobalConfiguration.Auth.Command
|
||||||
|
|
||||||
if command == "" {
|
if command == "" {
|
||||||
command = defaultAuther["command"].(string)
|
command = defaultAuther["command"].(string)
|
||||||
|
@ -142,14 +119,14 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
|
||||||
|
|
||||||
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
|
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
|
||||||
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
|
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
|
||||||
fmt.Fprintf(w, "Auth method:\t%s\n", set.AuthMethod)
|
fmt.Fprintf(w, "Auth method:\t%s\n", set.Auth.Method)
|
||||||
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
|
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
|
||||||
fmt.Fprintln(w, "\nBranding:")
|
fmt.Fprintln(w, "\nFrontend:")
|
||||||
fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name)
|
fmt.Fprintf(w, "\tName:\t%s\n", set.Frontend.Name)
|
||||||
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files)
|
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Frontend.Files)
|
||||||
fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Branding.DisableExternal)
|
fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Frontend.DisableExternal)
|
||||||
fmt.Fprintf(w, "\tDisable used disk percentage graph:\t%t\n", set.Branding.DisableUsedPercentage)
|
fmt.Fprintf(w, "\tDisable used disk percentage graph:\t%t\n", set.Frontend.DisableUsedPercentage)
|
||||||
fmt.Fprintf(w, "\tColor:\t%s\n", set.Branding.Color)
|
fmt.Fprintf(w, "\tColor:\t%s\n", set.Frontend.Color)
|
||||||
fmt.Fprintln(w, "\nServer:")
|
fmt.Fprintln(w, "\nServer:")
|
||||||
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
|
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
|
||||||
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
|
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
|
||||||
|
|
|
@ -18,7 +18,7 @@ var configCatCmd = &cobra.Command{
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
ser, err := d.store.Settings.GetServer()
|
ser, err := d.store.Settings.GetServer()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
auther, err := d.store.Auth.Get(set.AuthMethod)
|
auther, err := d.store.Auth.Get(set.Auth.Method)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
printSettings(ser, set, auther)
|
printSettings(ser, set, auther)
|
||||||
}, pythonConfig{}),
|
}, pythonConfig{}),
|
||||||
|
|
|
@ -22,7 +22,7 @@ and imported again with 'config import' command.`,
|
||||||
server, err := d.store.Settings.GetServer()
|
server, err := d.store.Settings.GetServer()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
auther, err := d.store.Auth.Get(settings.AuthMethod)
|
auther, err := d.store.Auth.Get(settings.Auth.Method)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
data := &settingsFile{
|
data := &settingsFile{
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
@ -43,11 +44,10 @@ The path must be for a json or yaml file.`,
|
||||||
} else {
|
} else {
|
||||||
key = generateKey()
|
key = generateKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
file := settingsFile{}
|
file := settingsFile{}
|
||||||
err := unmarshal(args[0], &file)
|
err := unmarshal(args[0], &file)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
log.Println(file.Settings)
|
||||||
file.Settings.Key = key
|
file.Settings.Key = key
|
||||||
err = d.store.Settings.Save(file.Settings)
|
err = d.store.Settings.Save(file.Settings)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
@ -61,16 +61,16 @@ The path must be for a json or yaml file.`,
|
||||||
} else {
|
} else {
|
||||||
rawAuther = file.Auther
|
rawAuther = file.Auther
|
||||||
}
|
}
|
||||||
|
log.Println("config_import",file.Settings.Auth)
|
||||||
var auther auth.Auther
|
var auther auth.Auther
|
||||||
switch file.Settings.AuthMethod {
|
switch file.Settings.Auth.Method {
|
||||||
case auth.MethodJSONAuth:
|
case "password":
|
||||||
auther = getAuther(auth.JSONAuth{}, rawAuther).(*auth.JSONAuth)
|
auther = getAuther(auth.JSONAuth{}, rawAuther).(*auth.JSONAuth)
|
||||||
case auth.MethodNoAuth:
|
case "noauth":
|
||||||
auther = getAuther(auth.NoAuth{}, rawAuther).(*auth.NoAuth)
|
auther = getAuther(auth.NoAuth{}, rawAuther).(*auth.NoAuth)
|
||||||
case auth.MethodProxyAuth:
|
case "proxy":
|
||||||
auther = getAuther(auth.ProxyAuth{}, rawAuther).(*auth.ProxyAuth)
|
auther = getAuther(auth.ProxyAuth{}, rawAuther).(*auth.ProxyAuth)
|
||||||
case auth.MethodHookAuth:
|
case "hook":
|
||||||
auther = getAuther(&auth.HookAuth{}, rawAuther).(*auth.HookAuth)
|
auther = getAuther(&auth.HookAuth{}, rawAuther).(*auth.HookAuth)
|
||||||
default:
|
default:
|
||||||
checkErr(errors.New("invalid auth method"))
|
checkErr(errors.New("invalid auth method"))
|
||||||
|
|
|
@ -26,33 +26,9 @@ override the options.`,
|
||||||
defaults := settings.UserDefaults{}
|
defaults := settings.UserDefaults{}
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
getUserDefaults(flags, &defaults, true)
|
getUserDefaults(flags, &defaults, true)
|
||||||
authMethod, auther := getAuthentication(flags)
|
_, auther := getAuthentication()
|
||||||
|
ser := &settings.GlobalConfiguration.Server
|
||||||
s := &settings.Settings{
|
err := d.store.Settings.Save(&settings.GlobalConfiguration)
|
||||||
Key: generateKey(),
|
|
||||||
Signup: mustGetBool(flags, "signup"),
|
|
||||||
Shell: convertCmdStrToCmdArray(mustGetString(flags, "shell")),
|
|
||||||
AuthMethod: authMethod,
|
|
||||||
Defaults: defaults,
|
|
||||||
Branding: settings.Branding{
|
|
||||||
Name: mustGetString(flags, "branding.name"),
|
|
||||||
DisableExternal: mustGetBool(flags, "branding.disableExternal"),
|
|
||||||
DisableUsedPercentage: mustGetBool(flags, "branding.DisableUsedPercentage"),
|
|
||||||
Files: mustGetString(flags, "branding.files"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ser := &settings.Server{
|
|
||||||
Address: mustGetString(flags, "address"),
|
|
||||||
Socket: mustGetString(flags, "socket"),
|
|
||||||
Root: mustGetString(flags, "root"),
|
|
||||||
BaseURL: mustGetString(flags, "baseurl"),
|
|
||||||
TLSKey: mustGetString(flags, "key"),
|
|
||||||
TLSCert: mustGetString(flags, "cert"),
|
|
||||||
Port: mustGetString(flags, "port"),
|
|
||||||
Log: mustGetString(flags, "log"),
|
|
||||||
}
|
|
||||||
err := d.store.Settings.Save(s)
|
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
err = d.store.Settings.SaveServer(ser)
|
err = d.store.Settings.SaveServer(ser)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
@ -64,6 +40,6 @@ 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, s, auther)
|
printSettings(ser, &settings.GlobalConfiguration, auther)
|
||||||
}, pythonConfig{noDB: true}),
|
}, pythonConfig{noDB: true}),
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ you want to change. Other options will remain unchanged.`,
|
||||||
ser, err := d.store.Settings.GetServer()
|
ser, err := d.store.Settings.GetServer()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
hasAuth := false
|
|
||||||
flags.Visit(func(flag *pflag.Flag) {
|
flags.Visit(func(flag *pflag.Flag) {
|
||||||
switch flag.Name {
|
switch flag.Name {
|
||||||
case "baseurl":
|
case "baseurl":
|
||||||
|
@ -40,37 +39,30 @@ you want to change. Other options will remain unchanged.`,
|
||||||
case "address":
|
case "address":
|
||||||
ser.Address = mustGetString(flags, flag.Name)
|
ser.Address = mustGetString(flags, flag.Name)
|
||||||
case "port":
|
case "port":
|
||||||
ser.Port = mustGetString(flags, flag.Name)
|
ser.Port = 8080
|
||||||
case "log":
|
case "log":
|
||||||
ser.Log = mustGetString(flags, flag.Name)
|
ser.Log = mustGetString(flags, flag.Name)
|
||||||
case "signup":
|
case "signup":
|
||||||
set.Signup = mustGetBool(flags, flag.Name)
|
set.Signup = mustGetBool(flags, flag.Name)
|
||||||
case "auth.method":
|
|
||||||
hasAuth = true
|
|
||||||
case "shell":
|
case "shell":
|
||||||
set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name))
|
set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name))
|
||||||
case "branding.name":
|
case "frontend.name":
|
||||||
set.Branding.Name = mustGetString(flags, flag.Name)
|
set.Frontend.Name = mustGetString(flags, flag.Name)
|
||||||
case "branding.color":
|
case "frontend.color":
|
||||||
set.Branding.Color = mustGetString(flags, flag.Name)
|
set.Frontend.Color = mustGetString(flags, flag.Name)
|
||||||
case "branding.disableExternal":
|
case "frontend.disableExternal":
|
||||||
set.Branding.DisableExternal = mustGetBool(flags, flag.Name)
|
set.Frontend.DisableExternal = mustGetBool(flags, flag.Name)
|
||||||
case "branding.disableUsedPercentage":
|
case "frontend.disableUsedPercentage":
|
||||||
set.Branding.DisableUsedPercentage = mustGetBool(flags, flag.Name)
|
set.Frontend.DisableUsedPercentage = mustGetBool(flags, flag.Name)
|
||||||
case "branding.files":
|
case "frontend.files":
|
||||||
set.Branding.Files = mustGetString(flags, flag.Name)
|
set.Frontend.Files = mustGetString(flags, flag.Name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
getUserDefaults(flags, &set.Defaults, false)
|
getUserDefaults(flags, &set.Defaults, false)
|
||||||
|
|
||||||
// read the defaults
|
// read the defaults
|
||||||
auther, err := d.store.Auth.Get(set.AuthMethod)
|
_, auther := getAuthentication()
|
||||||
checkErr(err)
|
|
||||||
|
|
||||||
// check if there are new flags for existing auth method
|
|
||||||
set.AuthMethod, auther = getAuthentication(flags, hasAuth, set, auther)
|
|
||||||
|
|
||||||
err = d.store.Auth.Save(auther)
|
err = d.store.Auth.Save(auther)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
err = d.store.Settings.Save(set)
|
err = d.store.Settings.Save(set)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
|
@ -10,7 +9,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -57,55 +55,19 @@ func init() {
|
||||||
flags.Bool("noauth", false, "use the noauth auther when using quick setup")
|
flags.Bool("noauth", false, "use the noauth auther when using quick setup")
|
||||||
flags.String("username", "admin", "username for the first user when using quick config")
|
flags.String("username", "admin", "username for the first user when using quick config")
|
||||||
flags.String("password", "", "hashed password for the first user when using quick config (default \"admin\")")
|
flags.String("password", "", "hashed password for the first user when using quick config (default \"admin\")")
|
||||||
|
|
||||||
addServerFlags(flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEnvVariableAsUint32(key string) uint32 {
|
|
||||||
valueStr := os.Getenv(key)
|
|
||||||
value, err := strconv.ParseUint(valueStr, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return 5 // default value every 5 minutes
|
|
||||||
}
|
|
||||||
return uint32(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addServerFlags(flags *pflag.FlagSet) {
|
|
||||||
flags.StringP("address", "a", "127.0.0.1", "address to listen on")
|
|
||||||
flags.StringP("log", "l", "stdout", "log output")
|
|
||||||
flags.StringP("port", "p", "8080", "port to listen on")
|
|
||||||
flags.StringP("cert", "t", "", "tls certificate")
|
|
||||||
flags.StringP("key", "k", "", "tls key")
|
|
||||||
flags.StringP("root", "r", ".", "root to prepend to relative paths")
|
|
||||||
flags.String("socket", "", "socket to listen to (cannot be used with address, port, cert nor key flags)")
|
|
||||||
flags.Uint32("socket-perm", 0666, "unix socket file permissions") //nolint:gomnd
|
|
||||||
flags.StringP("baseurl", "b", "", "base url")
|
|
||||||
flags.String("cache-dir", "", "file cache directory (disabled if empty)")
|
|
||||||
flags.Int("img-processors", 4, "image processors count") //nolint:gomnd
|
|
||||||
flags.Bool("disable-thumbnails", false, "disable image thumbnails")
|
|
||||||
flags.Bool("disable-preview-resize", true, "disable resize of image previews")
|
|
||||||
flags.Bool("disable-exec", false, "disables Command Runner feature")
|
|
||||||
flags.Bool("disable-type-detection-by-header", false, "disables type detection by reading file headers")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "filebrowser",
|
Use: "filebrowser",
|
||||||
Short: "A stylish web-based file browser",
|
Short: "A stylish web-based file browser",
|
||||||
Long: `File Browser CLI lets you create the database to use with File Browser,
|
Long: `
|
||||||
manage your users and all the configurations without acessing the
|
|
||||||
web interface.
|
|
||||||
|
|
||||||
If you've never run File Browser, you'll need to have a database for
|
If you've never run File Browser, you'll need to have a database for
|
||||||
it. Don't worry: you don't need to setup a separate database server.
|
it. Don't worry: you don't need to setup a separate database server.
|
||||||
We're using Bolt DB which is a single file database and all managed
|
We're using Bolt DB which is a single file database and all managed
|
||||||
by ourselves.
|
by ourselves.
|
||||||
|
|
||||||
For this specific command, all the flags you have available (except
|
|
||||||
"config" for the configuration file), can be given either through
|
|
||||||
environment variables or configuration files.
|
|
||||||
|
|
||||||
If you don't set "config", it will look for a configuration file called
|
If you don't set "config", it will look for a configuration file called
|
||||||
.filebrowser.{json, toml, yaml, yml} in the following directories:
|
filebrowser.{json, toml, yaml, yml} in the following directories:
|
||||||
|
|
||||||
- ./
|
- ./
|
||||||
- $HOME/
|
- $HOME/
|
||||||
|
@ -119,31 +81,20 @@ The precedence of the configuration values are as follows:
|
||||||
- database values
|
- database values
|
||||||
- defaults
|
- defaults
|
||||||
|
|
||||||
The environment variables are prefixed by "FB_" followed by the option
|
|
||||||
name in caps. So to set "database" via an env variable, you should
|
|
||||||
set FB_DATABASE.
|
|
||||||
|
|
||||||
Also, if the database path doesn't exist, File Browser will enter into
|
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
|
the quick setup mode and a new database will be bootstraped and a new
|
||||||
user created with the credentials from options "username" and "password".`,
|
user created with the credentials from options "username" and "password".`,
|
||||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||||
log.Println(cfgFile)
|
serverConfig := settings.GlobalConfiguration.Server
|
||||||
|
|
||||||
if !d.hadDB {
|
if !d.hadDB {
|
||||||
quickSetup(cmd.Flags(), d)
|
quickSetup(cmd.Flags(), d)
|
||||||
}
|
}
|
||||||
|
if serverConfig.NumImageProcessors < 1 {
|
||||||
// build img service
|
|
||||||
workersCount, err := cmd.Flags().GetInt("img-processors")
|
|
||||||
checkErr(err)
|
|
||||||
if workersCount < 1 {
|
|
||||||
log.Fatal("Image resize workers count could not be < 1")
|
log.Fatal("Image resize workers count could not be < 1")
|
||||||
}
|
}
|
||||||
imgSvc := img.New(workersCount)
|
imgSvc := img.New(serverConfig.NumImageProcessors)
|
||||||
|
|
||||||
var fileCache diskcache.Interface = diskcache.NewNoOp()
|
var fileCache diskcache.Interface = diskcache.NewNoOp()
|
||||||
cacheDir, err := cmd.Flags().GetString("cache-dir")
|
cacheDir := "/tmp"
|
||||||
checkErr(err)
|
|
||||||
if cacheDir != "" {
|
if cacheDir != "" {
|
||||||
if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet,gomnd
|
if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet,gomnd
|
||||||
log.Fatalf("can't make directory %s: %s", cacheDir, err)
|
log.Fatalf("can't make directory %s: %s", cacheDir, err)
|
||||||
|
@ -151,51 +102,38 @@ user created with the credentials from options "username" and "password".`,
|
||||||
fileCache = diskcache.New(afero.NewOsFs(), cacheDir)
|
fileCache = diskcache.New(afero.NewOsFs(), cacheDir)
|
||||||
}
|
}
|
||||||
// initialize indexing and schedule indexing ever n minutes (default 5)
|
// initialize indexing and schedule indexing ever n minutes (default 5)
|
||||||
indexingInterval := getEnvVariableAsUint32("INDEXING_INTERVAL")
|
go search.InitializeIndex(serverConfig.IndexingInterval)
|
||||||
go search.InitializeIndex(indexingInterval)
|
_, err := os.Stat(serverConfig.Root)
|
||||||
|
|
||||||
server := getRunParams(cmd.Flags(), d.store)
|
|
||||||
setupLog(server.Log)
|
|
||||||
|
|
||||||
root, err := filepath.Abs(server.Root)
|
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
server.Root = root
|
|
||||||
|
|
||||||
adr := server.Address + ":" + server.Port
|
|
||||||
|
|
||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
|
address := serverConfig.Address + ":" + strconv.Itoa(serverConfig.Port)
|
||||||
switch {
|
switch {
|
||||||
case server.Socket != "":
|
case serverConfig.Socket != "":
|
||||||
listener, err = net.Listen("unix", server.Socket)
|
listener, err = net.Listen("unix", serverConfig.Socket)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
socketPerm, err := cmd.Flags().GetUint32("socket-perm") //nolint:govet
|
socketPerm, err := cmd.Flags().GetUint32("socket-perm") //nolint:govet
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
err = os.Chmod(server.Socket, os.FileMode(socketPerm))
|
err = os.Chmod(serverConfig.Socket, os.FileMode(socketPerm))
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
case server.TLSKey != "" && server.TLSCert != "":
|
case serverConfig.TLSKey != "" && serverConfig.TLSCert != "":
|
||||||
cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) //nolint:govet
|
cer, err := tls.LoadX509KeyPair(serverConfig.TLSCert, serverConfig.TLSKey) //nolint:govet
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
listener, err = tls.Listen("tcp", adr, &tls.Config{
|
listener, err = tls.Listen("tcp", address, &tls.Config{
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
Certificates: []tls.Certificate{cer}},
|
Certificates: []tls.Certificate{cer}},
|
||||||
)
|
)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
default:
|
default:
|
||||||
listener, err = net.Listen("tcp", adr)
|
listener, err = net.Listen("tcp", address)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sigc := make(chan os.Signal, 1)
|
sigc := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
|
||||||
go cleanupHandler(listener, sigc)
|
go cleanupHandler(listener, sigc)
|
||||||
|
|
||||||
assetsFs := dirFS{Dir: http.Dir("frontend/dist")}
|
assetsFs := dirFS{Dir: http.Dir("frontend/dist")}
|
||||||
handler, err := fbhttp.NewHandler(imgSvc, fileCache, d.store, server, assetsFs)
|
handler, err := fbhttp.NewHandler(imgSvc, fileCache, d.store, &serverConfig, assetsFs)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
|
|
||||||
log.Println("Listening on", listener.Addr().String())
|
log.Println("Listening on", listener.Addr().String())
|
||||||
//nolint: gosec
|
//nolint: gosec
|
||||||
if err := http.Serve(listener, handler); err != nil {
|
if err := http.Serve(listener, handler); err != nil {
|
||||||
|
@ -215,68 +153,6 @@ func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfac
|
||||||
func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
|
func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
|
||||||
server, err := st.Settings.GetServer()
|
server, err := st.Settings.GetServer()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
if val, set := getParamB(flags, "root"); set {
|
|
||||||
server.Root = val
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "baseurl"); set {
|
|
||||||
server.BaseURL = val
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "log"); set {
|
|
||||||
server.Log = val
|
|
||||||
}
|
|
||||||
|
|
||||||
isSocketSet := false
|
|
||||||
isAddrSet := false
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "address"); set {
|
|
||||||
server.Address = val
|
|
||||||
isAddrSet = isAddrSet || set
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "port"); set {
|
|
||||||
server.Port = val
|
|
||||||
isAddrSet = isAddrSet || set
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "key"); set {
|
|
||||||
server.TLSKey = val
|
|
||||||
isAddrSet = isAddrSet || set
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "cert"); set {
|
|
||||||
server.TLSCert = val
|
|
||||||
isAddrSet = isAddrSet || set
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "socket"); set {
|
|
||||||
server.Socket = val
|
|
||||||
isSocketSet = isSocketSet || set
|
|
||||||
}
|
|
||||||
|
|
||||||
if isAddrSet && isSocketSet {
|
|
||||||
checkErr(errors.New("--socket flag cannot be used with --address, --port, --key nor --cert"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not use saved Socket if address was manually set.
|
|
||||||
if isAddrSet && server.Socket != "" {
|
|
||||||
server.Socket = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
_, disableThumbnails := getParamB(flags, "disable-thumbnails")
|
|
||||||
server.EnableThumbnails = !disableThumbnails
|
|
||||||
|
|
||||||
_, disablePreviewResize := getParamB(flags, "disable-preview-resize")
|
|
||||||
server.ResizePreview = !disablePreviewResize
|
|
||||||
|
|
||||||
_, disableTypeDetectionByHeader := getParamB(flags, "disable-type-detection-by-header")
|
|
||||||
server.TypeDetectionByHeader = !disableTypeDetectionByHeader
|
|
||||||
|
|
||||||
_, disableExec := getParamB(flags, "disable-exec")
|
|
||||||
server.EnableExec = !disableExec
|
|
||||||
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,32 +224,27 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
|
||||||
Download: true,
|
Download: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AuthMethod: "",
|
Frontend: settings.Frontend{},
|
||||||
Branding: settings.Branding{},
|
Commands: nil,
|
||||||
Commands: nil,
|
Shell: nil,
|
||||||
Shell: nil,
|
Rules: nil,
|
||||||
Rules: nil,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if _, noauth := getParamB(flags, "noauth"); noauth {
|
if settings.GlobalConfiguration.Auth.Method == "noauth" {
|
||||||
set.AuthMethod = auth.MethodNoAuth
|
set.Auth.Method = "noauth"
|
||||||
err = d.store.Auth.Save(&auth.NoAuth{})
|
err = d.store.Auth.Save(&auth.NoAuth{})
|
||||||
} else {
|
} else {
|
||||||
set.AuthMethod = auth.MethodJSONAuth
|
set.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(set)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
ser := &settings.Server{
|
ser := &settings.Server{
|
||||||
BaseURL: getParam(flags, "baseurl"),
|
BaseURL: getParam(flags, "baseurl"),
|
||||||
Port: getParam(flags, "port"),
|
|
||||||
Log: getParam(flags, "log"),
|
Log: getParam(flags, "log"),
|
||||||
TLSKey: getParam(flags, "key"),
|
TLSKey: getParam(flags, "key"),
|
||||||
TLSCert: getParam(flags, "cert"),
|
TLSCert: getParam(flags, "cert"),
|
||||||
Address: getParam(flags, "address"),
|
|
||||||
Root: getParam(flags, "root"),
|
Root: getParam(flags, "root"),
|
||||||
}
|
}
|
||||||
err = d.store.Settings.SaveServer(ser)
|
err = d.store.Settings.SaveServer(ser)
|
||||||
|
@ -408,7 +279,7 @@ func initConfig() {
|
||||||
if cfgFile == "" {
|
if cfgFile == "" {
|
||||||
v.AddConfigPath(".")
|
v.AddConfigPath(".")
|
||||||
v.AddConfigPath("/etc/filebrowser/")
|
v.AddConfigPath("/etc/filebrowser/")
|
||||||
v.SetConfigName(".filebrowser")
|
v.SetConfigName("filebrowser")
|
||||||
} else {
|
} else {
|
||||||
v.SetConfigFile(cfgFile)
|
v.SetConfigFile(cfgFile)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
server:
|
||||||
|
indexingInterval: 5
|
||||||
|
numImageProcessors: 2
|
||||||
|
socket: ""
|
||||||
|
tlsKey: ""
|
||||||
|
tlsCert: ""
|
||||||
|
enableThumbnails: true
|
||||||
|
resizePreview: false
|
||||||
|
typeDetectionByHeader: true
|
||||||
|
port: 8080
|
||||||
|
baseURL: "/"
|
||||||
|
address: ""
|
||||||
|
log: "stdout"
|
||||||
|
database: "database.db"
|
||||||
|
root: "/srv"
|
||||||
|
auth:
|
||||||
|
recaptcha:
|
||||||
|
host: ""
|
||||||
|
key: ""
|
||||||
|
secret: ""
|
||||||
|
header: ""
|
||||||
|
method: noauth
|
||||||
|
command: ""
|
||||||
|
signup: false
|
||||||
|
shell: ""
|
||||||
|
frontend:
|
||||||
|
name: ""
|
||||||
|
disableExternal: false
|
||||||
|
disableUsedPercentage: false
|
||||||
|
files: ""
|
||||||
|
theme: ""
|
||||||
|
color: ""
|
||||||
|
userDefaults:
|
||||||
|
scope: ""
|
||||||
|
locale: ""
|
||||||
|
viewMode: ""
|
||||||
|
singleClick: false
|
||||||
|
sorting:
|
||||||
|
by: ""
|
||||||
|
asc: false
|
||||||
|
perm:
|
||||||
|
admin: false
|
||||||
|
execute: false
|
||||||
|
create: false
|
||||||
|
rename: false
|
||||||
|
modify: false
|
||||||
|
delete: false
|
||||||
|
share: false
|
||||||
|
download: false
|
||||||
|
commands: []
|
||||||
|
hideDotfiles: false
|
||||||
|
dateFormat: false
|
|
@ -7,6 +7,7 @@ require (
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.1
|
github.com/dsoprea/go-exif/v3 v3.0.1
|
||||||
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/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
@ -35,6 +36,7 @@ require (
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
|
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
|
||||||
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/go-errors/errors v1.4.2 // indirect
|
github.com/go-errors/errors v1.4.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
@ -45,6 +47,8 @@ require (
|
||||||
github.com/klauspost/compress v1.11.4 // indirect
|
github.com/klauspost/compress v1.11.4 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.12 // 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/pierrec/lz4/v4 v4.1.2 // indirect
|
github.com/pierrec/lz4/v4 v4.1.2 // indirect
|
||||||
|
@ -58,6 +62,7 @@ require (
|
||||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
github.com/yusufpapurcu/wmi v1.2.3 // 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
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
@ -86,6 +86,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||||
|
@ -101,6 +103,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
|
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||||
|
github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
|
||||||
|
github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||||
|
@ -198,6 +205,7 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
@ -205,6 +213,12 @@ github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
|
||||||
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
|
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
|
||||||
github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
|
github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
|
||||||
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
|
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||||
|
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
||||||
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
@ -403,6 +417,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -427,6 +442,8 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
@ -507,6 +524,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
|
|
@ -102,7 +102,7 @@ func withAdmin(fn handleFunc) handleFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
auther, err := d.store.Auth.Get(d.settings.AuthMethod)
|
auther, err := d.store.Auth.Get(d.settings.Auth.Method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ type settingsData struct {
|
||||||
UserHomeBasePath string `json:"userHomeBasePath"`
|
UserHomeBasePath string `json:"userHomeBasePath"`
|
||||||
Defaults settings.UserDefaults `json:"defaults"`
|
Defaults settings.UserDefaults `json:"defaults"`
|
||||||
Rules []rules.Rule `json:"rules"`
|
Rules []rules.Rule `json:"rules"`
|
||||||
Branding settings.Branding `json:"branding"`
|
Frontend settings.Frontend `json:"frontend"`
|
||||||
Shell []string `json:"shell"`
|
Shell []string `json:"shell"`
|
||||||
Commands map[string][]string `json:"commands"`
|
Commands map[string][]string `json:"commands"`
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
|
||||||
UserHomeBasePath: d.settings.UserHomeBasePath,
|
UserHomeBasePath: d.settings.UserHomeBasePath,
|
||||||
Defaults: d.settings.Defaults,
|
Defaults: d.settings.Defaults,
|
||||||
Rules: d.settings.Rules,
|
Rules: d.settings.Rules,
|
||||||
Branding: d.settings.Branding,
|
Frontend: d.settings.Frontend,
|
||||||
Shell: d.settings.Shell,
|
Shell: d.settings.Shell,
|
||||||
Commands: d.settings.Commands,
|
Commands: d.settings.Commands,
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,9 @@ var settingsPutHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
|
||||||
d.settings.UserHomeBasePath = req.UserHomeBasePath
|
d.settings.UserHomeBasePath = req.UserHomeBasePath
|
||||||
d.settings.Defaults = req.Defaults
|
d.settings.Defaults = req.Defaults
|
||||||
d.settings.Rules = req.Rules
|
d.settings.Rules = req.Rules
|
||||||
d.settings.Branding = req.Branding
|
d.settings.Frontend = req.Frontend
|
||||||
d.settings.Shell = req.Shell
|
d.settings.Shell = req.Shell
|
||||||
d.settings.Commands = req.Commands
|
d.settings.Commands = req.Commands
|
||||||
|
|
||||||
err = d.store.Settings.Save(d.settings)
|
err = d.store.Settings.Save(d.settings)
|
||||||
return errToStatus(err), err
|
return errToStatus(err), err
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,33 +21,33 @@ import (
|
||||||
func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys fs.FS, file, contentType string) (int, error) {
|
func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys fs.FS, file, contentType string) (int, error) {
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
auther, err := d.store.Auth.Get(d.settings.AuthMethod)
|
auther, err := d.store.Auth.Get(d.settings.Auth.Method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"Name": d.settings.Branding.Name,
|
"Name": d.settings.Frontend.Name,
|
||||||
"DisableExternal": d.settings.Branding.DisableExternal,
|
"DisableExternal": d.settings.Frontend.DisableExternal,
|
||||||
"DisableUsedPercentage": d.settings.Branding.DisableUsedPercentage,
|
"DisableUsedPercentage": d.settings.Frontend.DisableUsedPercentage,
|
||||||
"Color": d.settings.Branding.Color,
|
"Color": d.settings.Frontend.Color,
|
||||||
"BaseURL": d.server.BaseURL,
|
"BaseURL": d.server.BaseURL,
|
||||||
"Version": version.Version,
|
"Version": version.Version,
|
||||||
"StaticURL": path.Join(d.server.BaseURL, "/static"),
|
"StaticURL": path.Join(d.server.BaseURL, "/static"),
|
||||||
"Signup": d.settings.Signup,
|
"Signup": d.settings.Signup,
|
||||||
"NoAuth": d.settings.AuthMethod == auth.MethodNoAuth,
|
"NoAuth": d.settings.Auth.Method == "noauth",
|
||||||
"AuthMethod": d.settings.AuthMethod,
|
"AuthMethod": d.settings.Auth.Method,
|
||||||
"LoginPage": auther.LoginPage(),
|
"LoginPage": auther.LoginPage(),
|
||||||
"CSS": false,
|
"CSS": false,
|
||||||
"ReCaptcha": false,
|
"ReCaptcha": false,
|
||||||
"Theme": d.settings.Branding.Theme,
|
"Theme": d.settings.Frontend.Theme,
|
||||||
"EnableThumbs": d.server.EnableThumbnails,
|
"EnableThumbs": d.server.EnableThumbnails,
|
||||||
"ResizePreview": d.server.ResizePreview,
|
"ResizePreview": d.server.ResizePreview,
|
||||||
"EnableExec": d.server.EnableExec,
|
"EnableExec": d.server.EnableExec,
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.settings.Branding.Files != "" {
|
if d.settings.Frontend.Files != "" {
|
||||||
fPath := filepath.Join(d.settings.Branding.Files, "custom.css")
|
fPath := filepath.Join(d.settings.Frontend.Files, "custom.css")
|
||||||
_, err := os.Stat(fPath) //nolint:govet
|
_, err := os.Stat(fPath) //nolint:govet
|
||||||
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
@ -59,8 +59,8 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.settings.AuthMethod == auth.MethodJSONAuth {
|
if d.settings.Auth.Method == "password" {
|
||||||
raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:govet
|
raw, err := d.store.Auth.Get(d.settings.Auth.Method) //nolint:govet
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
@ -115,15 +115,15 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs
|
||||||
const maxAge = 86400 // 1 day
|
const maxAge = 86400 // 1 day
|
||||||
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge))
|
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge))
|
||||||
|
|
||||||
if d.settings.Branding.Files != "" {
|
if d.settings.Frontend.Files != "" {
|
||||||
if strings.HasPrefix(r.URL.Path, "img/") {
|
if strings.HasPrefix(r.URL.Path, "img/") {
|
||||||
fPath := filepath.Join(d.settings.Branding.Files, r.URL.Path)
|
fPath := filepath.Join(d.settings.Frontend.Files, r.URL.Path)
|
||||||
if _, err := os.Stat(fPath); err == nil {
|
if _, err := os.Stat(fPath); err == nil {
|
||||||
http.ServeFile(w, r, fPath)
|
http.ServeFile(w, r, fPath)
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
} else if r.URL.Path == "custom.css" && d.settings.Branding.Files != "" {
|
} else if r.URL.Path == "custom.css" && d.settings.Frontend.Files != "" {
|
||||||
http.ServeFile(w, r, filepath.Join(d.settings.Branding.Files, "custom.css"))
|
http.ServeFile(w, r, filepath.Join(d.settings.Frontend.Files, "custom.css"))
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
var GlobalConfiguration Settings
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Open and read the YAML file
|
||||||
|
yamlFile, err := os.Open("filebrowser.yml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error opening YAML file: %v", err)
|
||||||
|
}
|
||||||
|
defer yamlFile.Close()
|
||||||
|
|
||||||
|
stat, err := yamlFile.Stat()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error getting file information: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
yamlData := make([]byte, stat.Size())
|
||||||
|
_, err = yamlFile.Read(yamlData)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error reading YAML data: %v", err)
|
||||||
|
}
|
||||||
|
setDefaults()
|
||||||
|
// 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() {
|
||||||
|
GlobalConfiguration = Settings{
|
||||||
|
Signup: true,
|
||||||
|
Server: Server{
|
||||||
|
IndexingInterval: 5,
|
||||||
|
Port: 8080,
|
||||||
|
NumImageProcessors: 1,
|
||||||
|
BaseURL: "",
|
||||||
|
},
|
||||||
|
Auth: Auth{
|
||||||
|
Method: "password",
|
||||||
|
Recaptcha: Recaptcha{
|
||||||
|
Host: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,3 +35,7 @@ func GenerateKey() ([]byte, error) {
|
||||||
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSettingsConfig(nameType string,Value string) string {
|
||||||
|
return nameType + Value
|
||||||
|
}
|
|
@ -29,21 +29,31 @@ type Settings struct {
|
||||||
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"`
|
||||||
AuthMethod string `json:"authMethod"`
|
Auth Auth `json:"auth"`
|
||||||
Auth struct {
|
|
||||||
Header string `json:"header"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Command string `json:"command"`
|
|
||||||
Signup bool `json:"signup"`
|
|
||||||
Shell string `json:"shell"`
|
|
||||||
} `json:"auth"`
|
|
||||||
|
|
||||||
Branding Branding `json:"branding"`
|
Frontend Frontend `json:"frontend"`
|
||||||
|
|
||||||
UserDefaults UserDefaults `json:"userDefaults"`
|
UserDefaults UserDefaults `json:"userDefaults"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
Recaptcha Recaptcha
|
||||||
|
Header string `json:"header"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Command string `json:"command"`
|
||||||
|
Signup bool `json:"signup"`
|
||||||
|
Shell string `json:"shell"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Recaptcha struct {
|
||||||
|
Host string
|
||||||
|
Key string
|
||||||
|
Secret string
|
||||||
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
IndexingInterval uint32 `json:"indexingInterval"`
|
||||||
|
NumImageProcessors int `json:"numImageProcessors"`
|
||||||
Socket string `json:"socket"`
|
Socket string `json:"socket"`
|
||||||
TLSKey string `json:"tlsKey"`
|
TLSKey string `json:"tlsKey"`
|
||||||
TLSCert string `json:"tlsCert"`
|
TLSCert string `json:"tlsCert"`
|
||||||
|
@ -52,7 +62,7 @@ type Server struct {
|
||||||
EnableExec bool `json:"enableExec"`
|
EnableExec bool `json:"enableExec"`
|
||||||
TypeDetectionByHeader bool `json:"typeDetectionByHeader"`
|
TypeDetectionByHeader bool `json:"typeDetectionByHeader"`
|
||||||
AuthHook string `json:"authHook"`
|
AuthHook string `json:"authHook"`
|
||||||
Port string `json:"port"`
|
Port int `json:"port"`
|
||||||
BaseURL string `json:"baseURL"`
|
BaseURL string `json:"baseURL"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Log string `json:"log"`
|
Log string `json:"log"`
|
||||||
|
@ -61,7 +71,7 @@ type Server struct {
|
||||||
EnablePreviewResize bool `json:"disable-preview-resize"`
|
EnablePreviewResize bool `json:"disable-preview-resize"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Branding struct {
|
type Frontend struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
DisableExternal bool `json:"disableExternal"`
|
DisableExternal bool `json:"disableExternal"`
|
||||||
DisableUsedPercentage bool `json:"disableUsedPercentage"`
|
DisableUsedPercentage bool `json:"disableUsedPercentage"`
|
||||||
|
|
|
@ -2,7 +2,6 @@ package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/asdine/storm/v3"
|
"github.com/asdine/storm/v3"
|
||||||
|
|
||||||
"github.com/gtsteffaniak/filebrowser/auth"
|
"github.com/gtsteffaniak/filebrowser/auth"
|
||||||
"github.com/gtsteffaniak/filebrowser/errors"
|
"github.com/gtsteffaniak/filebrowser/errors"
|
||||||
)
|
)
|
||||||
|
@ -13,20 +12,18 @@ type authBackend struct {
|
||||||
|
|
||||||
func (s authBackend) Get(t string) (auth.Auther, error) {
|
func (s authBackend) Get(t string) (auth.Auther, error) {
|
||||||
var auther auth.Auther
|
var auther auth.Auther
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case auth.MethodJSONAuth:
|
case "password":
|
||||||
auther = &auth.JSONAuth{}
|
auther = &auth.JSONAuth{}
|
||||||
case auth.MethodProxyAuth:
|
case "proxy":
|
||||||
auther = &auth.ProxyAuth{}
|
auther = &auth.ProxyAuth{}
|
||||||
case auth.MethodHookAuth:
|
case "hook":
|
||||||
auther = &auth.HookAuth{}
|
auther = &auth.HookAuth{}
|
||||||
case auth.MethodNoAuth:
|
case "noauth":
|
||||||
auther = &auth.NoAuth{}
|
auther = &auth.NoAuth{}
|
||||||
default:
|
default:
|
||||||
return nil, errors.ErrInvalidAuthMethod
|
return nil, errors.ErrInvalidAuthMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
return auther, get(s.db, "auther", auther)
|
return auther, get(s.db, "auther", auther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/asdine/storm/v3"
|
"github.com/asdine/storm/v3"
|
||||||
|
|
||||||
"github.com/gtsteffaniak/filebrowser/settings"
|
"github.com/gtsteffaniak/filebrowser/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,7 +19,10 @@ func (s settingsBackend) Save(set *settings.Settings) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s settingsBackend) GetServer() (*settings.Server, error) {
|
func (s settingsBackend) GetServer() (*settings.Server, error) {
|
||||||
server := &settings.Server{}
|
server := &settings.Server{
|
||||||
|
Port: 8080,
|
||||||
|
NumImageProcessors: 1,
|
||||||
|
}
|
||||||
return server, get(s.db, "server", server)
|
return server, get(s.db, "server", server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,22 +140,22 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
|
||||||
|
|
||||||
server := &settings.Server{
|
server := &settings.Server{
|
||||||
BaseURL: cfg.BaseURL,
|
BaseURL: cfg.BaseURL,
|
||||||
Port: cfg.Port,
|
Port: 8080,
|
||||||
Address: cfg.Address,
|
Address: cfg.Address,
|
||||||
Log: cfg.Log,
|
Log: cfg.Log,
|
||||||
}
|
}
|
||||||
|
fmt.Println("config.go", server)
|
||||||
var auther auth.Auther
|
var auther auth.Auther
|
||||||
switch cfg.Auth.Method {
|
switch cfg.Auth.Method {
|
||||||
case "proxy":
|
case "proxy":
|
||||||
auther = &auth.ProxyAuth{Header: cfg.Auth.Header}
|
auther = &auth.ProxyAuth{Header: cfg.Auth.Header}
|
||||||
s.AuthMethod = string(auth.MethodProxyAuth)
|
s.Auth.Method = "proxy"
|
||||||
case "hook":
|
case "hook":
|
||||||
auther = &auth.HookAuth{Command: cfg.Auth.Command}
|
auther = &auth.HookAuth{Command: cfg.Auth.Command}
|
||||||
s.AuthMethod = string(auth.MethodHookAuth)
|
s.Auth.Method = "hoook"
|
||||||
case "none":
|
case "none":
|
||||||
auther = &auth.NoAuth{}
|
auther = &auth.NoAuth{}
|
||||||
s.AuthMethod = string(auth.MethodNoAuth)
|
s.Auth.Method = "noauth"
|
||||||
default:
|
default:
|
||||||
auther = &auth.JSONAuth{
|
auther = &auth.JSONAuth{
|
||||||
ReCaptcha: &auth.ReCaptcha{
|
ReCaptcha: &auth.ReCaptcha{
|
||||||
|
@ -164,7 +164,7 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
|
||||||
Secret: cfg.ReCaptcha.Secret,
|
Secret: cfg.ReCaptcha.Secret,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
s.AuthMethod = string(auth.MethodJSONAuth)
|
s.Auth.Method = "password"
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sto.Auth.Save(auther)
|
err = sto.Auth.Save(auther)
|
||||||
|
|
|
@ -2,12 +2,14 @@ package importer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/asdine/storm/v3"
|
"github.com/asdine/storm/v3"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/gtsteffaniak/filebrowser/storage/bolt"
|
"github.com/gtsteffaniak/filebrowser/storage/bolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Import imports an old configuration to a newer database.
|
// Import imports an old configuration to a newer database.
|
||||||
func Import(oldDBPath, oldConf, newDBPath string) error {
|
func Import(oldDBPath, oldConf, newDBPath string) error {
|
||||||
|
log.Println(oldDBPath, oldConf, newDBPath)
|
||||||
oldDB, err := storm.Open(oldDBPath)
|
oldDB, err := storm.Open(oldDBPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -2,7 +2,7 @@ package version
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Version is the current File Browser version.
|
// Version is the current File Browser version.
|
||||||
Version = "(0.1.4)"
|
Version = "(0.2.0)"
|
||||||
// CommitSHA is the commmit sha.
|
// CommitSHA is the commmit sha.
|
||||||
CommitSHA = "(unknown)"
|
CommitSHA = "(unknown)"
|
||||||
)
|
)
|
||||||
|
|
|
@ -213,3 +213,7 @@ nav {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
color: white
|
color: white
|
||||||
}
|
}
|
||||||
|
#result-desktop #result-list {
|
||||||
|
background: #2a3137;
|
||||||
|
|
||||||
|
}
|
|
@ -211,7 +211,7 @@
|
||||||
border-top: none;
|
border-top: none;
|
||||||
border-top-left-radius: 0px;
|
border-top-left-radius: 0px;
|
||||||
border-top-right-radius: 0px;
|
border-top-right-radius: 0px;
|
||||||
background-color: white;
|
background: white;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
-webkit-transform: translateX(-50%);
|
-webkit-transform: translateX(-50%);
|
||||||
|
|
|
@ -1,125 +0,0 @@
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
@click="focus"
|
|
||||||
class="shell"
|
|
||||||
ref="scrollable"
|
|
||||||
:class="{ ['shell--hidden']: !showShell }"
|
|
||||||
>
|
|
||||||
<div v-for="(c, index) in content" :key="index" class="shell__result">
|
|
||||||
<div class="shell__prompt">
|
|
||||||
<i class="material-icons">chevron_right</i>
|
|
||||||
</div>
|
|
||||||
<pre class="shell__text">{{ c.text }}</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="shell__result" :class="{ 'shell__result--hidden': !canInput }">
|
|
||||||
<div class="shell__prompt">
|
|
||||||
<i class="material-icons">chevron_right</i>
|
|
||||||
</div>
|
|
||||||
<pre
|
|
||||||
tabindex="0"
|
|
||||||
ref="input"
|
|
||||||
class="shell__text"
|
|
||||||
contenteditable="true"
|
|
||||||
@keydown.prevent.38="historyUp"
|
|
||||||
@keydown.prevent.40="historyDown"
|
|
||||||
@keypress.prevent.enter="submit"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapMutations, mapState, mapGetters } from "vuex";
|
|
||||||
import { commands } from "@/api";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "shell",
|
|
||||||
computed: {
|
|
||||||
...mapState(["user", "showShell"]),
|
|
||||||
...mapGetters(["isFiles", "isLogged"]),
|
|
||||||
path: function () {
|
|
||||||
if (this.isFiles) {
|
|
||||||
return this.$route.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data: () => ({
|
|
||||||
content: [],
|
|
||||||
history: [],
|
|
||||||
historyPos: 0,
|
|
||||||
canInput: true,
|
|
||||||
}),
|
|
||||||
methods: {
|
|
||||||
...mapMutations(["toggleShell"]),
|
|
||||||
scroll: function () {
|
|
||||||
this.$refs.scrollable.scrollTop = this.$refs.scrollable.scrollHeight;
|
|
||||||
},
|
|
||||||
focus: function () {
|
|
||||||
this.$refs.input.focus();
|
|
||||||
},
|
|
||||||
historyUp() {
|
|
||||||
if (this.historyPos > 0) {
|
|
||||||
this.$refs.input.innerText = this.history[--this.historyPos];
|
|
||||||
this.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
historyDown() {
|
|
||||||
if (this.historyPos >= 0 && this.historyPos < this.history.length - 1) {
|
|
||||||
this.$refs.input.innerText = this.history[++this.historyPos];
|
|
||||||
this.focus();
|
|
||||||
} else {
|
|
||||||
this.historyPos = this.history.length;
|
|
||||||
this.$refs.input.innerText = "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
submit: function (event) {
|
|
||||||
const cmd = event.target.innerText.trim();
|
|
||||||
|
|
||||||
if (cmd === "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd === "clear") {
|
|
||||||
this.content = [];
|
|
||||||
event.target.innerHTML = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd === "exit") {
|
|
||||||
event.target.innerHTML = "";
|
|
||||||
this.toggleShell();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.canInput = false;
|
|
||||||
event.target.innerHTML = "";
|
|
||||||
|
|
||||||
let results = {
|
|
||||||
text: `${cmd}\n\n`,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.history.push(cmd);
|
|
||||||
this.historyPos = this.history.length;
|
|
||||||
this.content.push(results);
|
|
||||||
|
|
||||||
commands(
|
|
||||||
this.path,
|
|
||||||
cmd,
|
|
||||||
(event) => {
|
|
||||||
results.text += `${event.data}\n`;
|
|
||||||
this.scroll();
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
results.text = results.text.trimEnd();
|
|
||||||
this.canInput = true;
|
|
||||||
this.$refs.input.focus();
|
|
||||||
this.scroll();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,24 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<header>
|
<header>
|
||||||
<action
|
|
||||||
class="menu-button"
|
|
||||||
icon="menu"
|
|
||||||
:label="$t('buttons.toggleSidebar')"
|
|
||||||
@action="toggleSidebar()"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
<div id="dropdown" :class="{ active: this.$store.state.show === 'more' }">
|
|
||||||
<slot name="actions" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { logoURL } from "@/utils/constants";
|
import { logoURL } from "@/utils/constants";
|
||||||
import Action from "@/components/header/Action";
|
import Action from "@/components/header/Action.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "header-bar",
|
name: "header-bar",
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
@import "./_variables.css";
|
@import "./_variables.css";
|
||||||
@import "./_buttons.css";
|
@import "./_buttons.css";
|
||||||
@import "./_inputs.css";
|
@import "./_inputs.css";
|
||||||
@import "./_shell.css";
|
|
||||||
@import "./_share.css";
|
@import "./_share.css";
|
||||||
@import "./fonts.css";
|
@import "./fonts.css";
|
||||||
@import "./base.css";
|
@import "./base.css";
|
||||||
|
|
|
@ -3,16 +3,13 @@
|
||||||
<div v-if="progress" class="progress">
|
<div v-if="progress" class="progress">
|
||||||
<div v-bind:style="{ width: this.progress + '%' }"></div>
|
<div v-bind:style="{ width: this.progress + '%' }"></div>
|
||||||
</div>
|
</div>
|
||||||
<header-bar showMenu showLogo>
|
<editorBar v-if="getCurrentView === 'editor'"></editorBar>
|
||||||
<search />
|
<listingBar v-else-if="getCurrentView === 'listing'"></listingBar>
|
||||||
<template #actions>
|
<previewBar v-else-if="getCurrentView === 'preview'"></previewBar>
|
||||||
<action icon="grid_view" :label="$t('buttons.switchView')" @action="switchView" />
|
<defaultBar v-else></defaultBar>
|
||||||
</template>
|
|
||||||
</header-bar>
|
|
||||||
<sidebar></sidebar>
|
<sidebar></sidebar>
|
||||||
<main>
|
<main>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
<shell v-if="isExecEnabled && isLogged && user.perm.execute" />
|
|
||||||
</main>
|
</main>
|
||||||
<prompts></prompts>
|
<prompts></prompts>
|
||||||
<upload-files></upload-files>
|
<upload-files></upload-files>
|
||||||
|
@ -20,60 +17,64 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters } from "vuex";
|
import editorBar from "./files/Editor.vue"
|
||||||
import Sidebar from "@/components/Sidebar";
|
import defaultBar from "./files/Default.vue"
|
||||||
|
import listingBar from"./files/Listing.vue"
|
||||||
|
import previewBar from "./files/Preview.vue"
|
||||||
import Prompts from "@/components/prompts/Prompts";
|
import Prompts from "@/components/prompts/Prompts";
|
||||||
import Shell from "@/components/Shell";
|
import Action from "@/components/header/Action";
|
||||||
|
import { mapState, mapGetters } from "vuex";
|
||||||
|
import Sidebar from "@/components/Sidebar.vue";
|
||||||
import UploadFiles from "../components/prompts/UploadFiles";
|
import UploadFiles from "../components/prompts/UploadFiles";
|
||||||
import { enableExec } from "@/utils/constants";
|
import { enableExec } from "@/utils/constants";
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
|
||||||
import Search from "@/components/Search";
|
|
||||||
import Action from "@/components/header/Action";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "layout",
|
name: "layout",
|
||||||
components: {
|
components: {
|
||||||
|
defaultBar,
|
||||||
|
editorBar,
|
||||||
|
listingBar,
|
||||||
|
previewBar,
|
||||||
Action,
|
Action,
|
||||||
HeaderBar,
|
|
||||||
Search,
|
|
||||||
Sidebar,
|
Sidebar,
|
||||||
Prompts,
|
Prompts,
|
||||||
Shell,
|
|
||||||
UploadFiles,
|
UploadFiles,
|
||||||
},
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
showContexts: true,
|
||||||
|
dragCounter: 0,
|
||||||
|
width: window.innerWidth,
|
||||||
|
itemWeight: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["isLogged", "progress"]),
|
...mapGetters(["isLogged", "progress"]),
|
||||||
...mapState(["user"]),
|
...mapState(["req", "user", "currentView"]),
|
||||||
|
|
||||||
isExecEnabled: () => enableExec,
|
isExecEnabled: () => enableExec,
|
||||||
|
getCurrentView() {
|
||||||
|
return this.currentView;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
getCurrentView: function () {
|
||||||
|
console.log(this.currentView)
|
||||||
|
},
|
||||||
$route: function () {
|
$route: function () {
|
||||||
this.$store.commit("resetSelected");
|
this.$store.commit("resetSelected");
|
||||||
this.$store.commit("multiple", false);
|
this.$store.commit("multiple", false);
|
||||||
if (this.$store.state.show !== "success")
|
if (this.$store.state.show !== "success") this.$store.commit("closeHovers");
|
||||||
this.$store.commit("closeHovers");
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
switchView: async function () {
|
getTitle() {
|
||||||
this.$store.commit("closeHovers");
|
let title = "Title"
|
||||||
const modes = {
|
if (this.$route.path.startsWith('/settings/')){
|
||||||
list: "mosaic",
|
title = "Settings"
|
||||||
mosaic: "mosaic gallery",
|
}
|
||||||
"mosaic gallery": "list",
|
return title
|
||||||
};
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
id: this.user.id,
|
|
||||||
viewMode: modes[this.user.viewMode] || "list",
|
|
||||||
};
|
|
||||||
//users.update(data, ["viewMode"]).catch(this.$showError);
|
|
||||||
this.$store.commit("updateUser", data);
|
|
||||||
|
|
||||||
//this.setItemWeight();
|
|
||||||
//this.fillWindow();
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
|
@ -0,0 +1,630 @@
|
||||||
|
<template>
|
||||||
|
<header-bar>
|
||||||
|
<action
|
||||||
|
class="menu-button"
|
||||||
|
icon="menu"
|
||||||
|
:label="$t('buttons.toggleSidebar')"
|
||||||
|
@action="toggleSidebar()"
|
||||||
|
/>
|
||||||
|
<search />
|
||||||
|
<action
|
||||||
|
class="menu-button"
|
||||||
|
icon="grid_view"
|
||||||
|
:label="$t('buttons.switchView')"
|
||||||
|
@action="switchView"
|
||||||
|
/>
|
||||||
|
</header-bar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.flexbar {
|
||||||
|
display:flex;
|
||||||
|
flex-direction:block;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from "vue";
|
||||||
|
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||||
|
import { users, files as api } from "@/api";
|
||||||
|
import { enableExec } from "@/utils/constants";
|
||||||
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
|
import Action from "@/components/header/Action.vue";
|
||||||
|
import * as upload from "@/utils/upload";
|
||||||
|
import css from "@/utils/css";
|
||||||
|
import throttle from "lodash.throttle";
|
||||||
|
import Search from "@/components/Search.vue";
|
||||||
|
|
||||||
|
import Item from "@/components/files/ListingItem.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "listing",
|
||||||
|
components: {
|
||||||
|
HeaderBar,
|
||||||
|
Action,
|
||||||
|
Search,
|
||||||
|
Item,
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
showLimit: 50,
|
||||||
|
columnWidth: 280,
|
||||||
|
dragCounter: 0,
|
||||||
|
width: window.innerWidth,
|
||||||
|
itemWeight: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(["req", "selected", "user", "show", "multiple", "selected", "loading"]),
|
||||||
|
...mapGetters(["selectedCount"]),
|
||||||
|
nameSorted() {
|
||||||
|
return this.req.sorting.by === "name";
|
||||||
|
},
|
||||||
|
sizeSorted() {
|
||||||
|
return this.req.sorting.by === "size";
|
||||||
|
},
|
||||||
|
modifiedSorted() {
|
||||||
|
return this.req.sorting.by === "modified";
|
||||||
|
},
|
||||||
|
ascOrdered() {
|
||||||
|
return this.req.sorting.asc;
|
||||||
|
},
|
||||||
|
items() {
|
||||||
|
const dirs = [];
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
this.req.items.forEach((item) => {
|
||||||
|
if (item.isDir) {
|
||||||
|
dirs.push(item);
|
||||||
|
} else {
|
||||||
|
files.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { dirs, files };
|
||||||
|
},
|
||||||
|
dirs() {
|
||||||
|
return this.items.dirs.slice(0, this.showLimit);
|
||||||
|
},
|
||||||
|
files() {
|
||||||
|
let showLimit = this.showLimit - this.items.dirs.length;
|
||||||
|
|
||||||
|
if (showLimit < 0) showLimit = 0;
|
||||||
|
|
||||||
|
return this.items.files.slice(0, showLimit);
|
||||||
|
},
|
||||||
|
nameIcon() {
|
||||||
|
if (this.nameSorted && !this.ascOrdered) {
|
||||||
|
return "arrow_upward";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "arrow_downward";
|
||||||
|
},
|
||||||
|
sizeIcon() {
|
||||||
|
if (this.sizeSorted && this.ascOrdered) {
|
||||||
|
return "arrow_downward";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "arrow_upward";
|
||||||
|
},
|
||||||
|
modifiedIcon() {
|
||||||
|
if (this.modifiedSorted && this.ascOrdered) {
|
||||||
|
return "arrow_downward";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "arrow_upward";
|
||||||
|
},
|
||||||
|
viewIcon() {
|
||||||
|
const icons = {
|
||||||
|
list: "view_module",
|
||||||
|
mosaic: "grid_view",
|
||||||
|
"mosaic gallery": "view_list",
|
||||||
|
};
|
||||||
|
return icons[this.user.viewMode];
|
||||||
|
},
|
||||||
|
headerButtons() {
|
||||||
|
return {
|
||||||
|
select: this.selectedCount > 0,
|
||||||
|
upload: this.user.perm.create && this.selectedCount > 0,
|
||||||
|
download: this.user.perm.download && this.selectedCount > 0,
|
||||||
|
delete: this.selectedCount > 0 && this.user.perm.delete,
|
||||||
|
rename: this.selectedCount === 1 && this.user.perm.rename,
|
||||||
|
share: this.selectedCount === 1 && this.user.perm.share,
|
||||||
|
move: this.selectedCount > 0 && this.user.perm.rename,
|
||||||
|
copy: this.selectedCount > 0 && this.user.perm.create,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
req: function () {
|
||||||
|
// Reset the show value
|
||||||
|
this.showLimit = 50;
|
||||||
|
|
||||||
|
// Ensures that the listing is displayed
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
// How much every listing item affects the window height
|
||||||
|
this.setItemWeight();
|
||||||
|
|
||||||
|
// Fill and fit the window with listing items
|
||||||
|
this.fillWindow(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
// Check the columns size for the first time.
|
||||||
|
this.colunmsResize();
|
||||||
|
|
||||||
|
// How much every listing item affects the window height
|
||||||
|
this.setItemWeight();
|
||||||
|
|
||||||
|
// Fill and fit the window with listing items
|
||||||
|
this.fillWindow(true);
|
||||||
|
|
||||||
|
// Add the needed event listeners to the window and document.
|
||||||
|
window.addEventListener("keydown", this.keyEvent);
|
||||||
|
window.addEventListener("scroll", this.scrollEvent);
|
||||||
|
window.addEventListener("resize", this.windowsResize);
|
||||||
|
|
||||||
|
if (!this.user.perm.create) return;
|
||||||
|
document.addEventListener("dragover", this.preventDefault);
|
||||||
|
document.addEventListener("dragenter", this.dragEnter);
|
||||||
|
document.addEventListener("dragleave", this.dragLeave);
|
||||||
|
document.addEventListener("drop", this.drop);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// Remove event listeners before destroying this page.
|
||||||
|
window.removeEventListener("keydown", this.keyEvent);
|
||||||
|
window.removeEventListener("scroll", this.scrollEvent);
|
||||||
|
window.removeEventListener("resize", this.windowsResize);
|
||||||
|
|
||||||
|
if (this.user && !this.user.perm.create) return;
|
||||||
|
document.removeEventListener("dragover", this.preventDefault);
|
||||||
|
document.removeEventListener("dragenter", this.dragEnter);
|
||||||
|
document.removeEventListener("dragleave", this.dragLeave);
|
||||||
|
document.removeEventListener("drop", this.drop);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
action: function () {
|
||||||
|
if (this.show) {
|
||||||
|
this.$store.commit("showHover", this.show);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit("action");
|
||||||
|
},
|
||||||
|
toggleSidebar() {
|
||||||
|
if (this.$store.state.show == "sidebar") {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
} else {
|
||||||
|
this.$store.commit("showHover", "sidebar");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...mapMutations(["updateUser", "addSelected"]),
|
||||||
|
base64: function (name) {
|
||||||
|
return window.btoa(unescape(encodeURIComponent(name)));
|
||||||
|
},
|
||||||
|
keyEvent(event) {
|
||||||
|
// No prompts are shown
|
||||||
|
if (this.show !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esc!
|
||||||
|
if (event.keyCode === 27) {
|
||||||
|
// Reset files selection.
|
||||||
|
this.$store.commit("resetSelected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del!
|
||||||
|
if (event.keyCode === 46) {
|
||||||
|
if (!this.user.perm.delete || this.selectedCount == 0) return;
|
||||||
|
|
||||||
|
// Show delete prompt.
|
||||||
|
this.$store.commit("showHover", "delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
// F2!
|
||||||
|
if (event.keyCode === 113) {
|
||||||
|
if (!this.user.perm.rename || this.selectedCount !== 1) return;
|
||||||
|
|
||||||
|
// Show rename prompt.
|
||||||
|
this.$store.commit("showHover", "rename");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl is pressed
|
||||||
|
if (!event.ctrlKey && !event.metaKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = String.fromCharCode(event.which).toLowerCase();
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "f":
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("showHover", "search");
|
||||||
|
break;
|
||||||
|
case "c":
|
||||||
|
case "x":
|
||||||
|
this.copyCut(event, key);
|
||||||
|
break;
|
||||||
|
case "v":
|
||||||
|
this.paste(event);
|
||||||
|
break;
|
||||||
|
case "a":
|
||||||
|
event.preventDefault();
|
||||||
|
for (let file of this.items.files) {
|
||||||
|
if (this.$store.state.selected.indexOf(file.index) === -1) {
|
||||||
|
this.addSelected(file.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let dir of this.items.dirs) {
|
||||||
|
if (this.$store.state.selected.indexOf(dir.index) === -1) {
|
||||||
|
this.addSelected(dir.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "s":
|
||||||
|
event.preventDefault();
|
||||||
|
document.getElementById("download-button").click();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
switchView: async function () {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
const modes = {
|
||||||
|
list: "mosaic",
|
||||||
|
mosaic: "mosaic gallery",
|
||||||
|
"mosaic gallery": "list",
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
id: this.user.id,
|
||||||
|
viewMode: modes[this.user.viewMode] || "list",
|
||||||
|
};
|
||||||
|
//users.update(data, ["viewMode"]).catch(this.$showError);
|
||||||
|
this.$store.commit("updateUser", data);
|
||||||
|
|
||||||
|
//this.setItemWeight();
|
||||||
|
//this.fillWindow();
|
||||||
|
},
|
||||||
|
preventDefault(event) {
|
||||||
|
// Wrapper around prevent default.
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
copyCut(event, key) {
|
||||||
|
if (event.target.tagName.toLowerCase() === "input") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
|
||||||
|
for (let i of this.selected) {
|
||||||
|
items.push({
|
||||||
|
from: this.req.items[i].url,
|
||||||
|
name: this.req.items[i].name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit("updateClipboard", {
|
||||||
|
key: key,
|
||||||
|
items: items,
|
||||||
|
path: this.$route.path,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
paste(event) {
|
||||||
|
if (event.target.tagName.toLowerCase() === "input") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
|
||||||
|
for (let item of this.$store.state.clipboard.items) {
|
||||||
|
const from = item.from.endsWith("/") ? item.from.slice(0, -1) : item.from;
|
||||||
|
const to = this.$route.path + encodeURIComponent(item.name);
|
||||||
|
items.push({ from, to, name: item.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let action = (overwrite, rename) => {
|
||||||
|
api
|
||||||
|
.copy(items, overwrite, rename)
|
||||||
|
.then(() => {
|
||||||
|
this.$store.commit("setReload", true);
|
||||||
|
})
|
||||||
|
.catch(this.$showError);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.$store.state.clipboard.key === "x") {
|
||||||
|
action = (overwrite, rename) => {
|
||||||
|
api
|
||||||
|
.move(items, overwrite, rename)
|
||||||
|
.then(() => {
|
||||||
|
this.$store.commit("resetClipboard");
|
||||||
|
this.$store.commit("setReload", true);
|
||||||
|
})
|
||||||
|
.catch(this.$showError);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$store.state.clipboard.path == this.$route.path) {
|
||||||
|
action(false, true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let conflict = upload.checkConflict(items, this.req.items);
|
||||||
|
|
||||||
|
let overwrite = false;
|
||||||
|
let rename = false;
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "replace-rename",
|
||||||
|
confirm: (event, option) => {
|
||||||
|
overwrite = option == "overwrite";
|
||||||
|
rename = option == "rename";
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
action(overwrite, rename);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
action(overwrite, rename);
|
||||||
|
},
|
||||||
|
colunmsResize() {
|
||||||
|
// Update the columns size based on the window width.
|
||||||
|
let columns = Math.floor(
|
||||||
|
document.querySelector("main").offsetWidth / this.columnWidth
|
||||||
|
);
|
||||||
|
let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]);
|
||||||
|
if (columns === 0) columns = 1;
|
||||||
|
items.style.width = `calc(${100 / columns}% - 1em)`;
|
||||||
|
},
|
||||||
|
scrollEvent: throttle(function () {
|
||||||
|
const totalItems = this.req.numDirs + this.req.numFiles;
|
||||||
|
|
||||||
|
// All items are displayed
|
||||||
|
if (this.showLimit >= totalItems) return;
|
||||||
|
|
||||||
|
const currentPos = window.innerHeight + window.scrollY;
|
||||||
|
|
||||||
|
// Trigger at the 75% of the window height
|
||||||
|
const triggerPos = document.body.offsetHeight - window.innerHeight * 0.25;
|
||||||
|
|
||||||
|
if (currentPos > triggerPos) {
|
||||||
|
// Quantity of items needed to fill 2x of the window height
|
||||||
|
const showQuantity = Math.ceil((window.innerHeight * 2) / this.itemWeight);
|
||||||
|
|
||||||
|
// Increase the number of displayed items
|
||||||
|
this.showLimit += showQuantity;
|
||||||
|
}
|
||||||
|
}, 100),
|
||||||
|
dragEnter() {
|
||||||
|
this.dragCounter++;
|
||||||
|
|
||||||
|
// When the user starts dragging an item, put every
|
||||||
|
// file on the listing with 50% opacity.
|
||||||
|
let items = document.getElementsByClassName("item");
|
||||||
|
|
||||||
|
Array.from(items).forEach((file) => {
|
||||||
|
file.style.opacity = 0.5;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dragLeave() {
|
||||||
|
this.dragCounter--;
|
||||||
|
|
||||||
|
if (this.dragCounter == 0) {
|
||||||
|
this.resetOpacity();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
drop: async function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.dragCounter = 0;
|
||||||
|
this.resetOpacity();
|
||||||
|
|
||||||
|
let dt = event.dataTransfer;
|
||||||
|
let el = event.target;
|
||||||
|
|
||||||
|
if (dt.files.length <= 0) return;
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
if (el !== null && !el.classList.contains("item")) {
|
||||||
|
el = el.parentElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let files = await upload.scanFiles(dt);
|
||||||
|
let items = this.req.items;
|
||||||
|
let path = this.$route.path.endsWith("/")
|
||||||
|
? this.$route.path
|
||||||
|
: this.$route.path + "/";
|
||||||
|
|
||||||
|
if (el !== null && el.classList.contains("item") && el.dataset.dir === "true") {
|
||||||
|
// Get url from ListingItem instance
|
||||||
|
path = el.__vue__.url;
|
||||||
|
|
||||||
|
try {
|
||||||
|
items = (await api.fetch(path)).items;
|
||||||
|
} catch (error) {
|
||||||
|
this.$showError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let conflict = upload.checkConflict(files, items);
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "replace",
|
||||||
|
confirm: (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
upload.handleFiles(files, path, true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload.handleFiles(files, path);
|
||||||
|
},
|
||||||
|
uploadInput(event) {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
|
||||||
|
let files = event.currentTarget.files;
|
||||||
|
let folder_upload =
|
||||||
|
files[0].webkitRelativePath !== undefined && files[0].webkitRelativePath !== "";
|
||||||
|
|
||||||
|
if (folder_upload) {
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files[i];
|
||||||
|
files[i].fullPath = file.webkitRelativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = this.$route.path.endsWith("/")
|
||||||
|
? this.$route.path
|
||||||
|
: this.$route.path + "/";
|
||||||
|
let conflict = upload.checkConflict(files, this.req.items);
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "replace",
|
||||||
|
confirm: (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
upload.handleFiles(files, path, true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload.handleFiles(files, path);
|
||||||
|
},
|
||||||
|
resetOpacity() {
|
||||||
|
let items = document.getElementsByClassName("item");
|
||||||
|
|
||||||
|
Array.from(items).forEach((file) => {
|
||||||
|
file.style.opacity = 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async sort(by) {
|
||||||
|
let asc = false;
|
||||||
|
|
||||||
|
if (by === "name") {
|
||||||
|
if (this.nameIcon === "arrow_upward") {
|
||||||
|
asc = true;
|
||||||
|
}
|
||||||
|
} else if (by === "size") {
|
||||||
|
if (this.sizeIcon === "arrow_upward") {
|
||||||
|
asc = true;
|
||||||
|
}
|
||||||
|
} else if (by === "modified") {
|
||||||
|
if (this.modifiedIcon === "arrow_upward") {
|
||||||
|
asc = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await users.update({ id: this.user.id, sorting: { by, asc } }, ["sorting"]);
|
||||||
|
} catch (e) {
|
||||||
|
this.$showError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit("setReload", true);
|
||||||
|
},
|
||||||
|
openSearch() {
|
||||||
|
this.$store.commit("showHover", "search");
|
||||||
|
},
|
||||||
|
toggleMultipleSelection() {
|
||||||
|
this.$store.commit("multiple", !this.multiple);
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
},
|
||||||
|
windowsResize: throttle(function () {
|
||||||
|
this.colunmsResize();
|
||||||
|
this.width = window.innerWidth;
|
||||||
|
|
||||||
|
// Listing element is not displayed
|
||||||
|
if (this.$refs.listing == null) return;
|
||||||
|
|
||||||
|
// How much every listing item affects the window height
|
||||||
|
this.setItemWeight();
|
||||||
|
|
||||||
|
// Fill but not fit the window
|
||||||
|
this.fillWindow();
|
||||||
|
}, 100),
|
||||||
|
download() {
|
||||||
|
if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) {
|
||||||
|
api.download(null, this.req.items[this.selected[0]].url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "download",
|
||||||
|
confirm: (format) => {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
let files = [];
|
||||||
|
if (this.selectedCount > 0) {
|
||||||
|
for (let i of this.selected) {
|
||||||
|
files.push(this.req.items[i].url);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
files.push(this.$route.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
api.download(format, ...files);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
upload: function () {
|
||||||
|
if (
|
||||||
|
typeof window.DataTransferItem !== "undefined" &&
|
||||||
|
typeof DataTransferItem.prototype.webkitGetAsEntry !== "undefined"
|
||||||
|
) {
|
||||||
|
this.$store.commit("showHover", "upload");
|
||||||
|
} else {
|
||||||
|
document.getElementById("upload-input").click();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setItemWeight() {
|
||||||
|
// Listing element is not displayed
|
||||||
|
if (this.$refs.listing == null) return;
|
||||||
|
|
||||||
|
let itemQuantity = this.req.numDirs + this.req.numFiles;
|
||||||
|
if (itemQuantity > this.showLimit) itemQuantity = this.showLimit;
|
||||||
|
|
||||||
|
// How much every listing item affects the window height
|
||||||
|
this.itemWeight = this.$refs.listing.offsetHeight / itemQuantity;
|
||||||
|
},
|
||||||
|
fillWindow(fit = false) {
|
||||||
|
const totalItems = this.req.numDirs + this.req.numFiles;
|
||||||
|
|
||||||
|
// More items are displayed than the total
|
||||||
|
if (this.showLimit >= totalItems && !fit) return;
|
||||||
|
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
|
||||||
|
// Quantity of items needed to fill 2x of the window height
|
||||||
|
const showQuantity = Math.ceil((windowHeight + windowHeight * 2) / this.itemWeight);
|
||||||
|
|
||||||
|
// Less items to display than current
|
||||||
|
if (this.showLimit > showQuantity && !fit) return;
|
||||||
|
|
||||||
|
// Set the number of displayed items
|
||||||
|
this.showLimit = showQuantity > totalItems ? totalItems : showQuantity;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -301,7 +301,6 @@ export default {
|
||||||
select: this.selectedCount > 0,
|
select: this.selectedCount > 0,
|
||||||
upload: this.user.perm.create && this.selectedCount > 0,
|
upload: this.user.perm.create && this.selectedCount > 0,
|
||||||
download: this.user.perm.download && this.selectedCount > 0,
|
download: this.user.perm.download && this.selectedCount > 0,
|
||||||
shell: this.user.perm.execute && enableExec,
|
|
||||||
delete: this.selectedCount > 0 && this.user.perm.delete,
|
delete: this.selectedCount > 0 && this.user.perm.delete,
|
||||||
rename: this.selectedCount === 1 && this.user.perm.rename,
|
rename: this.selectedCount === 1 && this.user.perm.rename,
|
||||||
share: this.selectedCount === 1 && this.user.perm.share,
|
share: this.selectedCount === 1 && this.user.perm.share,
|
||||||
|
|
Loading…
Reference in New Issue