2019-01-05 22:44:33 +00:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
2023-09-03 22:03:00 +00:00
|
|
|
"flag"
|
2024-07-30 17:45:27 +00:00
|
|
|
"fmt"
|
2021-03-09 17:59:19 +00:00
|
|
|
"io/fs"
|
2019-01-05 22:44:33 +00:00
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
2019-05-17 10:48:06 +00:00
|
|
|
"os/signal"
|
2023-06-18 15:04:31 +00:00
|
|
|
"strconv"
|
2024-10-07 22:44:53 +00:00
|
|
|
"strings"
|
2019-05-17 10:48:06 +00:00
|
|
|
"syscall"
|
2019-01-05 22:44:33 +00:00
|
|
|
|
2024-08-04 17:50:35 +00:00
|
|
|
"embed"
|
|
|
|
|
2023-06-15 01:08:09 +00:00
|
|
|
"github.com/gtsteffaniak/filebrowser/diskcache"
|
2023-12-01 23:47:00 +00:00
|
|
|
"github.com/gtsteffaniak/filebrowser/files"
|
2023-06-15 01:08:09 +00:00
|
|
|
fbhttp "github.com/gtsteffaniak/filebrowser/http"
|
|
|
|
"github.com/gtsteffaniak/filebrowser/img"
|
|
|
|
"github.com/gtsteffaniak/filebrowser/settings"
|
2024-10-07 22:44:53 +00:00
|
|
|
"github.com/gtsteffaniak/filebrowser/storage"
|
2023-06-15 01:08:09 +00:00
|
|
|
"github.com/gtsteffaniak/filebrowser/users"
|
2024-10-07 22:44:53 +00:00
|
|
|
"github.com/gtsteffaniak/filebrowser/utils"
|
2024-09-16 21:01:16 +00:00
|
|
|
"github.com/gtsteffaniak/filebrowser/version"
|
2019-01-05 22:44:33 +00:00
|
|
|
)
|
|
|
|
|
2024-08-04 17:50:35 +00:00
|
|
|
//go:embed dist/*
|
|
|
|
var assets embed.FS
|
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
var (
|
|
|
|
nonEmbededFS = os.Getenv("FILEBROWSER_NO_EMBEDED") == "true"
|
|
|
|
)
|
2024-08-04 17:50:35 +00:00
|
|
|
|
2023-06-13 13:15:11 +00:00
|
|
|
type dirFS struct {
|
|
|
|
http.Dir
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d dirFS) Open(name string) (fs.File, error) {
|
|
|
|
return d.Dir.Open(name)
|
|
|
|
}
|
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
func getStore(config string) (*storage.Storage, bool) {
|
|
|
|
// Use the config file (global flag)
|
|
|
|
log.Printf("Using Config file : %v", config)
|
|
|
|
settings.Initialize(config)
|
|
|
|
store, hasDB, err := storage.InitializeDb(settings.Config.Server.Database)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("could not load db info: ", err)
|
|
|
|
}
|
|
|
|
return store, hasDB
|
2019-01-08 14:07:55 +00:00
|
|
|
}
|
2019-01-06 05:11:15 +00:00
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
func generalUsage() {
|
|
|
|
fmt.Printf(`usage: ./html-web-crawler <command> [options] --urls <urls>
|
|
|
|
commands:
|
|
|
|
collect Collect data from URLs
|
|
|
|
crawl Crawl URLs and collect data
|
|
|
|
install Install chrome browser for javascript enabled scraping.
|
|
|
|
Note: Consider instead to install via native package manager,
|
|
|
|
then set "CHROME_EXECUTABLE" in the environment
|
|
|
|
` + "\n")
|
|
|
|
}
|
2024-08-24 22:02:33 +00:00
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
func StartFilebrowser() {
|
|
|
|
// Global flags
|
|
|
|
var configPath string
|
|
|
|
var help bool
|
|
|
|
// Override the default usage output to use generalUsage()
|
|
|
|
flag.Usage = generalUsage
|
|
|
|
flag.StringVar(&configPath, "c", "filebrowser.yaml", "Path to the config file.")
|
|
|
|
flag.BoolVar(&help, "h", false, "Get help about commands")
|
|
|
|
|
|
|
|
// Parse global flags (before subcommands)
|
|
|
|
flag.Parse() // print generalUsage on error
|
|
|
|
|
|
|
|
// Show help if requested
|
|
|
|
if help {
|
|
|
|
generalUsage()
|
|
|
|
return
|
|
|
|
}
|
2024-08-24 22:02:33 +00:00
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
// Create a new FlagSet for the 'set' subcommand
|
|
|
|
setCmd := flag.NewFlagSet("set", flag.ExitOnError)
|
|
|
|
var user, scope, dbConfig string
|
|
|
|
var asAdmin bool
|
|
|
|
|
|
|
|
setCmd.StringVar(&user, "u", "", "Comma-separated username and password: \"set -u <username>,<password>\"")
|
|
|
|
setCmd.BoolVar(&asAdmin, "a", false, "Create user as admin user, used in combination with -u")
|
|
|
|
setCmd.StringVar(&scope, "s", "", "Specify a user scope, otherwise default user config scope is used")
|
|
|
|
setCmd.StringVar(&dbConfig, "c", "filebrowser.yaml", "Path to the config file.")
|
|
|
|
|
|
|
|
// Parse subcommand flags only if a subcommand is specified
|
|
|
|
if len(os.Args) > 1 {
|
|
|
|
switch os.Args[1] {
|
|
|
|
case "set":
|
|
|
|
err := setCmd.Parse(os.Args)
|
2024-08-24 22:02:33 +00:00
|
|
|
if err != nil {
|
2024-10-07 22:44:53 +00:00
|
|
|
setCmd.PrintDefaults()
|
|
|
|
os.Exit(1)
|
2020-07-27 17:01:02 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
userInfo := strings.Split(user, ",")
|
|
|
|
if len(userInfo) < 2 {
|
|
|
|
fmt.Println("not enough info to create user: \"set -u username,password\"")
|
|
|
|
setCmd.PrintDefaults()
|
|
|
|
os.Exit(1)
|
2024-08-04 17:50:35 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
username := userInfo[0]
|
|
|
|
password := userInfo[1]
|
|
|
|
getStore(dbConfig)
|
|
|
|
// Create the user logic
|
|
|
|
if asAdmin {
|
|
|
|
log.Printf("Creating user as admin: %s\n", username)
|
|
|
|
} else {
|
|
|
|
log.Printf("Creating user: %s\n", username)
|
2024-08-04 17:50:35 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
newUser := users.User{
|
|
|
|
Username: username,
|
|
|
|
Password: password,
|
2024-08-04 17:50:35 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
if scope != "" {
|
|
|
|
newUser.Scope = scope
|
|
|
|
}
|
|
|
|
err = storage.CreateUser(newUser, asAdmin)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Could not create user: ", err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
case "version":
|
|
|
|
fmt.Println("FileBrowser Quantum - A modern web-based file manager")
|
|
|
|
fmt.Printf("Version : %v\n", version.Version)
|
|
|
|
fmt.Printf("Commit : %v\n", version.CommitSHA)
|
|
|
|
fmt.Printf("Release Info : https://github.com/gtsteffaniak/filebrowser/releases/tag/%v\n", version.Version)
|
|
|
|
return
|
2019-01-07 20:24:23 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
}
|
|
|
|
store, dbExists := getStore(configPath)
|
|
|
|
indexingInterval := fmt.Sprint(settings.Config.Server.IndexingInterval, " minutes")
|
|
|
|
if !settings.Config.Server.Indexing {
|
|
|
|
indexingInterval = "disabled"
|
|
|
|
}
|
|
|
|
database := fmt.Sprintf("Using existing database : %v", settings.Config.Server.Database)
|
|
|
|
if !dbExists {
|
|
|
|
database = fmt.Sprintf("Creating new database : %v", settings.Config.Server.Database)
|
|
|
|
}
|
|
|
|
log.Printf("Initializing FileBrowser Quantum (%v)\n", version.Version)
|
|
|
|
log.Println("Embeded frontend :", !nonEmbededFS)
|
|
|
|
log.Println(database)
|
|
|
|
log.Println("Sources :", settings.Config.Server.Root)
|
|
|
|
log.Print("Indexing interval : ", indexingInterval)
|
|
|
|
|
|
|
|
serverConfig := settings.Config.Server
|
|
|
|
// initialize indexing and schedule indexing ever n minutes (default 5)
|
|
|
|
go files.InitializeIndex(serverConfig.IndexingInterval, serverConfig.Indexing)
|
|
|
|
if err := rootCMD(store, &serverConfig); err != nil {
|
2024-07-30 17:45:27 +00:00
|
|
|
log.Fatal("Error starting filebrowser:", err)
|
2023-09-02 16:05:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-31 23:12:36 +00:00
|
|
|
func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfacer
|
2019-05-17 10:48:06 +00:00
|
|
|
sig := <-c
|
|
|
|
log.Printf("Caught signal %s: shutting down.", sig)
|
|
|
|
listener.Close()
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
func rootCMD(store *storage.Storage, serverConfig *settings.Server) error {
|
|
|
|
if serverConfig.NumImageProcessors < 1 {
|
|
|
|
log.Fatal("Image resize workers count could not be < 1")
|
|
|
|
}
|
|
|
|
imgSvc := img.New(serverConfig.NumImageProcessors)
|
|
|
|
|
|
|
|
cacheDir := "/tmp"
|
|
|
|
var fileCache diskcache.Interface
|
|
|
|
|
|
|
|
// Use file cache if cacheDir is specified
|
|
|
|
if cacheDir != "" {
|
|
|
|
var err error
|
|
|
|
fileCache, err = diskcache.NewFileCache(cacheDir)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to create file cache: %v", err)
|
|
|
|
}
|
2023-09-02 00:22:38 +00:00
|
|
|
} else {
|
2024-10-07 22:44:53 +00:00
|
|
|
// No-op cache if no cacheDir is specified
|
|
|
|
fileCache = diskcache.NewNoOp()
|
|
|
|
}
|
|
|
|
|
|
|
|
fbhttp.SetupEnv(store, serverConfig, fileCache)
|
|
|
|
|
|
|
|
_, err := os.Stat(serverConfig.Root)
|
|
|
|
utils.CheckErr(fmt.Sprint("cmd os.Stat ", serverConfig.Root), err)
|
|
|
|
var listener net.Listener
|
|
|
|
address := serverConfig.Address + ":" + strconv.Itoa(serverConfig.Port)
|
|
|
|
switch {
|
|
|
|
case serverConfig.Socket != "":
|
|
|
|
listener, err = net.Listen("unix", serverConfig.Socket)
|
|
|
|
utils.CheckErr("net.Listen", err)
|
|
|
|
err = os.Chmod(serverConfig.Socket, os.FileMode(0666)) // socket-perm
|
|
|
|
utils.CheckErr("os.Chmod", err)
|
|
|
|
case serverConfig.TLSKey != "" && serverConfig.TLSCert != "":
|
|
|
|
cer, err := tls.LoadX509KeyPair(serverConfig.TLSCert, serverConfig.TLSKey) //nolint:govet
|
|
|
|
utils.CheckErr("tls.LoadX509KeyPair", err)
|
|
|
|
listener, err = tls.Listen("tcp", address, &tls.Config{
|
|
|
|
MinVersion: tls.VersionTLS12,
|
|
|
|
Certificates: []tls.Certificate{cer}},
|
|
|
|
)
|
|
|
|
utils.CheckErr("tls.Listen", err)
|
|
|
|
default:
|
|
|
|
listener, err = net.Listen("tcp", address)
|
|
|
|
utils.CheckErr("net.Listen", err)
|
2023-09-02 00:22:38 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
sigc := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
|
|
|
|
go cleanupHandler(listener, sigc)
|
|
|
|
if !nonEmbededFS {
|
|
|
|
assetsFs, err := fs.Sub(assets, "dist")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Could not embed frontend. Does backend/cmd/dist exist? Must be built and exist first")
|
|
|
|
}
|
|
|
|
handler, err := fbhttp.NewHandler(imgSvc, assetsFs)
|
|
|
|
utils.CheckErr("fbhttp.NewHandler", err)
|
|
|
|
defer listener.Close()
|
|
|
|
log.Println("Listening on", listener.Addr().String())
|
|
|
|
//nolint: gosec
|
|
|
|
if err := http.Serve(listener, handler); err != nil {
|
|
|
|
log.Fatalf("Could not start server on port %d: %v", serverConfig.Port, err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assetsFs := dirFS{Dir: http.Dir("frontend/dist")}
|
|
|
|
handler, err := fbhttp.NewHandler(imgSvc, assetsFs)
|
|
|
|
utils.CheckErr("fbhttp.NewHandler", err)
|
|
|
|
defer listener.Close()
|
|
|
|
log.Println("Listening on", listener.Addr().String())
|
|
|
|
//nolint: gosec
|
|
|
|
if err := http.Serve(listener, handler); err != nil {
|
|
|
|
log.Fatalf("Could not start server on port %d: %v", serverConfig.Port, err)
|
|
|
|
}
|
2023-10-09 22:24:48 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
return nil
|
2019-01-05 22:44:33 +00:00
|
|
|
}
|