204 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			204 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
|  | package cmd | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"crypto/rand" | ||
|  | 	"crypto/tls" | ||
|  | 	"errors" | ||
|  | 	"io/ioutil" | ||
|  | 	"log" | ||
|  | 	"net" | ||
|  | 	"net/http" | ||
|  | 	"os" | ||
|  | 	"strconv" | ||
|  | 
 | ||
|  | 	"github.com/asdine/storm" | ||
|  | 	"github.com/filebrowser/filebrowser/v2/auth" | ||
|  | 	"github.com/filebrowser/filebrowser/v2/settings" | ||
|  | 	"github.com/filebrowser/filebrowser/v2/storage" | ||
|  | 	"github.com/filebrowser/filebrowser/v2/users" | ||
|  | 
 | ||
|  | 	fbhttp "github.com/filebrowser/filebrowser/v2/http" | ||
|  | 	"github.com/spf13/cobra" | ||
|  | 	"github.com/spf13/pflag" | ||
|  | 	lumberjack "gopkg.in/natefinch/lumberjack.v2" | ||
|  | ) | ||
|  | 
 | ||
|  | var ( | ||
|  | 	databasePath string | ||
|  | ) | ||
|  | 
 | ||
|  | func init() { | ||
|  | 	rootCmd.PersistentFlags().StringVarP(&databasePath, "database", "d", "./filebrowser.db", "path to the database") | ||
|  | 
 | ||
|  | 	rootCmd.Flags().StringP("address", "a", "", "address to listen on (default comes from database)") | ||
|  | 	rootCmd.Flags().StringP("log", "l", "", "log output (default comes from database)") | ||
|  | 	rootCmd.Flags().IntP("port", "p", 0, "port to listen on (default comes from database)") | ||
|  | 	rootCmd.Flags().StringP("cert", "c", "", "tls certificate (default comes from database)") | ||
|  | 	rootCmd.Flags().StringP("key", "k", "", "tls key (default comes from database)") | ||
|  | 	rootCmd.Flags().StringP("scope", "s", "", "scope for users") | ||
|  | } | ||
|  | 
 | ||
|  | var rootCmd = &cobra.Command{ | ||
|  | 	Use:   "filebrowser", | ||
|  | 	Short: "A stylish web-based file browser", | ||
|  | 	Long: `File Browser CLI lets you create the database to use with File Browser, | ||
|  | manage your user and all the configurations without accessing the | ||
|  | web interface. | ||
|  | 
 | ||
|  | If you've never run File Browser, you will need to create the database. | ||
|  | See 'filebrowser help config init' for more information. | ||
|  | 
 | ||
|  | This command is used to start up the server. By default it starts listening | ||
|  | on localhost on a random port unless specified otherwise in the database or | ||
|  | via flags. | ||
|  | 
 | ||
|  | Use the available flags to override the database/default options. These flags | ||
|  | values won't be persisted to the database. To persist configuration to the database | ||
|  | use the command 'filebrowser config set'.`, | ||
|  | 	Run: func(cmd *cobra.Command, args []string) { | ||
|  | 		if _, err := os.Stat(databasePath); os.IsNotExist(err) { | ||
|  | 			quickSetup(cmd) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		db := getDB() | ||
|  | 		defer db.Close() | ||
|  | 		st := getStorage(db) | ||
|  | 		startServer(cmd, st) | ||
|  | 	}, | ||
|  | } | ||
|  | 
 | ||
|  | func setupLogger(s *settings.Settings) { | ||
|  | 	switch s.Log { | ||
|  | 	case "stdout": | ||
|  | 		log.SetOutput(os.Stdout) | ||
|  | 	case "stderr": | ||
|  | 		log.SetOutput(os.Stderr) | ||
|  | 	case "": | ||
|  | 		log.SetOutput(ioutil.Discard) | ||
|  | 	default: | ||
|  | 		log.SetOutput(&lumberjack.Logger{ | ||
|  | 			Filename:   s.Log, | ||
|  | 			MaxSize:    100, | ||
|  | 			MaxAge:     14, | ||
|  | 			MaxBackups: 10, | ||
|  | 		}) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func serverVisitAndReplace(cmd *cobra.Command, s *settings.Settings) { | ||
|  | 	cmd.Flags().Visit(func(flag *pflag.Flag) { | ||
|  | 		switch flag.Name { | ||
|  | 		case "log": | ||
|  | 			s.Log = mustGetString(cmd, flag.Name) | ||
|  | 		case "address": | ||
|  | 			s.Server.Address = mustGetString(cmd, flag.Name) | ||
|  | 		case "port": | ||
|  | 			s.Server.Port = mustGetInt(cmd, flag.Name) | ||
|  | 		case "cert": | ||
|  | 			s.Server.TLSCert = mustGetString(cmd, flag.Name) | ||
|  | 		case "key": | ||
|  | 			s.Server.TLSKey = mustGetString(cmd, flag.Name) | ||
|  | 		} | ||
|  | 	}) | ||
|  | } | ||
|  | 
 | ||
|  | func quickSetup(cmd *cobra.Command) { | ||
|  | 	scope := mustGetString(cmd, "scope") | ||
|  | 	if scope == "" { | ||
|  | 		panic(errors.New("scope flag must be set for quick setup")) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	db, err := storm.Open(databasePath) | ||
|  | 	checkErr(err) | ||
|  | 	defer db.Close() | ||
|  | 
 | ||
|  | 	set := &settings.Settings{ | ||
|  | 		Key:        generateRandomBytes(64), // 256 bit
 | ||
|  | 		BaseURL:    "", | ||
|  | 		Log:        "stderr", | ||
|  | 		Signup:     false, | ||
|  | 		AuthMethod: auth.MethodJSONAuth, | ||
|  | 		Server: settings.Server{ | ||
|  | 			Port:    0, | ||
|  | 			Address: "127.0.0.1", | ||
|  | 			TLSCert: mustGetString(cmd, "cert"), | ||
|  | 			TLSKey:  mustGetString(cmd, "key"), | ||
|  | 		}, | ||
|  | 		Defaults: settings.UserDefaults{ | ||
|  | 			Scope:  scope, | ||
|  | 			Locale: "en", | ||
|  | 			Perm: users.Permissions{ | ||
|  | 				Admin:    false, | ||
|  | 				Execute:  true, | ||
|  | 				Create:   true, | ||
|  | 				Rename:   true, | ||
|  | 				Modify:   true, | ||
|  | 				Delete:   true, | ||
|  | 				Share:    true, | ||
|  | 				Download: true, | ||
|  | 			}, | ||
|  | 		}, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	serverVisitAndReplace(cmd, set) | ||
|  | 	st := getStorage(db) | ||
|  | 
 | ||
|  | 	err = st.Settings.Save(set) | ||
|  | 	checkErr(err) | ||
|  | 
 | ||
|  | 	err = st.Auth.Save(&auth.JSONAuth{}) | ||
|  | 	checkErr(err) | ||
|  | 
 | ||
|  | 	password, err := users.HashPwd("admin") | ||
|  | 	checkErr(err) | ||
|  | 
 | ||
|  | 	user := &users.User{ | ||
|  | 		Username:     "admin", | ||
|  | 		Password:     password, | ||
|  | 		LockPassword: false, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	set.Defaults.Apply(user) | ||
|  | 	user.Perm.Admin = true | ||
|  | 
 | ||
|  | 	err = st.Users.Save(user) | ||
|  | 	checkErr(err) | ||
|  | } | ||
|  | 
 | ||
|  | func startServer(cmd *cobra.Command, st *storage.Storage) { | ||
|  | 	settings, err := st.Settings.Get() | ||
|  | 	checkErr(err) | ||
|  | 
 | ||
|  | 	serverVisitAndReplace(cmd, settings) | ||
|  | 	setupLogger(settings) | ||
|  | 
 | ||
|  | 	handler, err := fbhttp.NewHandler(st) | ||
|  | 	checkErr(err) | ||
|  | 
 | ||
|  | 	var listener net.Listener | ||
|  | 
 | ||
|  | 	if settings.Server.TLSKey != "" && settings.Server.TLSCert != "" { | ||
|  | 		cer, err := tls.LoadX509KeyPair(settings.Server.TLSCert, settings.Server.TLSKey) | ||
|  | 		checkErr(err) | ||
|  | 		config := &tls.Config{Certificates: []tls.Certificate{cer}} | ||
|  | 		listener, err = tls.Listen("tcp", settings.Server.Address+":"+strconv.Itoa(settings.Server.Port), config) | ||
|  | 		checkErr(err) | ||
|  | 	} else { | ||
|  | 		listener, err = net.Listen("tcp", settings.Server.Address+":"+strconv.Itoa(settings.Server.Port)) | ||
|  | 		checkErr(err) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	log.Println("Listening on", listener.Addr().String()) | ||
|  | 	if err := http.Serve(listener, handler); err != nil { | ||
|  | 		log.Fatal(err) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func generateRandomBytes(n int) []byte { | ||
|  | 	b := make([]byte, n) | ||
|  | 	_, err := rand.Read(b) | ||
|  | 	checkErr(err) | ||
|  | 	// Note that err == nil only if we read len(b) bytes.
 | ||
|  | 	return b | ||
|  | } |