feat: use cobra to provide subcommands, move sources to lib (#506)

- Use cobra in order to provide subcommands `serve` and `db`.
  - Subdir `cmd` is removed.
  - Subdir `cli` is created, which is a standard cobra structure.
- Sources related to the core are moved to subdir `lib`.
- #497 and #504 are merged.
- Deprecated flags are added. See https://github.com/filebrowser/filebrowser/pull/497#discussion_r209428120.
- [`viper.BindPFlags`](https://godoc.org/github.com/spf13/viper#BindPFlags) is used in order to reduce the verbosity in `serve.go`.
pull/494/head
1138-4EB 2018-08-21 22:42:03 +01:00
parent b1eb90767d
commit 69a3f853bd
35 changed files with 535 additions and 840 deletions

View File

@ -17,8 +17,7 @@ stages:
- release
cache:
directories:
- vendor
- rice-box.go
- lib/rice-box.go
jobs:
include:
- stage: lint

View File

@ -2,9 +2,11 @@
"port": 80,
"address": "",
"database": "/database.db",
"scope": "/srv",
"allowCommands": true,
"allowEdit": true,
"allowNew": true,
"commands": []
"defaults": {
"scope": "/srv",
"allowCommands": true,
"allowEdit": true,
"allowNew": true,
"commands": []
}
}

395
Gopkg.lock generated
View File

@ -1,395 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
digest = "1:af3215cd158218c92f0706bb32f67b0ce3d37da4c4d135e7ed5e2dc36a90fca5"
name = "github.com/BurntSushi/toml"
packages = ["."]
pruneopts = "UT"
revision = "a368813c5e648fee92e5f6c30e3944ff9d5e8895"
[[projects]]
branch = "master"
digest = "1:65796e5fdb94d94bc0ee6bc422aa3541dbe69ed4da275cb5a27f8fa141f4e35c"
name = "github.com/GeertJohan/go.rice"
packages = [
".",
"embedded",
]
pruneopts = "UT"
revision = "c02ca9a983da5807ddf7d796784928f5be4afd09"
[[projects]]
digest = "1:9ede7940cd19ac5d92896381abac71954feb57608b617152e48112c3dc667e87"
name = "github.com/asdine/storm"
packages = [
".",
"codec",
"codec/json",
"index",
"internal",
"q",
]
pruneopts = "UT"
revision = "bda68dab90fc908ee5dbccb36400edf4f54972d6"
version = "v2.1.1"
[[projects]]
digest = "1:68eae87f8e5391e83ed2f7568e727709c598672f9c8390460241dcc1c2db2d34"
name = "github.com/chaseadamsio/goorgeous"
packages = ["."]
pruneopts = "UT"
revision = "dcf1ef873b8987bf12596fe6951c48347986eb2f"
version = "v1.1.0"
[[projects]]
digest = "1:c28625428387b63dd7154eb857f51e700465cfbf7c06f619e71f2da33cefe47e"
name = "github.com/coreos/bbolt"
packages = ["."]
pruneopts = "UT"
revision = "583e8937c61f1af6513608ccc75c97b6abdf4ff9"
version = "v1.3.0"
[[projects]]
branch = "master"
digest = "1:5fd5c4d4282935b7a575299494f2c09e9d2cacded7815c83aff7c1602aff3154"
name = "github.com/daaku/go.zipexe"
packages = ["."]
pruneopts = "UT"
revision = "a5fe2436ffcb3236e175e5149162b41cd28bd27d"
[[projects]]
digest = "1:bb2130e54ac8ea3ff78312ca365ce3b8a23423ed362530551bdceaccfa4d8e77"
name = "github.com/dgrijalva/jwt-go"
packages = [
".",
"request",
]
pruneopts = "UT"
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
version = "v3.2.0"
[[projects]]
branch = "master"
digest = "1:988cfde37f089c50ba1ac8a1032c3f81d2ac0359e1872093b946a30139665eb6"
name = "github.com/dsnet/compress"
packages = [
".",
"bzip2",
"bzip2/internal/sais",
"internal",
"internal/errors",
"internal/prefix",
]
pruneopts = "UT"
revision = "cc9eb1d7ad760af14e8f918698f745e80377af4f"
[[projects]]
branch = "master"
digest = "1:50a46ab1d5edbbdd55125b4d37f1bf503d0807c26461f9ad7b358d6006641d09"
name = "github.com/flynn/go-shlex"
packages = ["."]
pruneopts = "UT"
revision = "3f9db97f856818214da2e1057f8ad84803971cff"
[[projects]]
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
name = "github.com/fsnotify/fsnotify"
packages = ["."]
pruneopts = "UT"
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
digest = "1:402c1a5e502d338e285ade7820bcd7bc6d860a262e1809eee5b1c34ef7692627"
name = "github.com/gohugoio/hugo"
packages = ["parser"]
pruneopts = "UT"
revision = "25e88ccabe9b04c42ffb43528c86743f623fac46"
version = "v0.36.1"
[[projects]]
branch = "master"
digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009"
name = "github.com/golang/snappy"
packages = ["."]
pruneopts = "UT"
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]]
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
name = "github.com/gorilla/websocket"
packages = ["."]
pruneopts = "UT"
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:837e92fe1b970f26d25d35c755106d86ba45717d11374c4f344654baa4add7b3"
name = "github.com/hacdias/fileutils"
packages = ["."]
pruneopts = "UT"
revision = "76b1c6ab906773727a1ce2f7fb22830685166f85"
[[projects]]
branch = "master"
digest = "1:a6c3d669d54b94899aa207bb3da0a38f4d3054e396a80cae2ed7634e2843c1ba"
name = "github.com/hacdias/varutils"
packages = ["."]
pruneopts = "UT"
revision = "82d3b57f667a756cfc4b1535951b46878881f3e1"
[[projects]]
branch = "master"
digest = "1:a361611b8c8c75a1091f00027767f7779b29cb37c456a71b8f2604c88057ab40"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token",
]
pruneopts = "UT"
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
[[projects]]
branch = "master"
digest = "1:caf6db28595425c0e0f2301a00257d11712f65c1878e12cffc42f6b9a9cf3f23"
name = "github.com/kardianos/osext"
packages = ["."]
pruneopts = "UT"
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
[[projects]]
digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7"
name = "github.com/magiconair/properties"
packages = ["."]
pruneopts = "UT"
revision = "c2353362d570a7bfa228149c62842019201cfb71"
version = "v1.8.0"
[[projects]]
branch = "master"
digest = "1:4bceeb937bfdd1fc2c545c685c43fdad909d78508b308b318e79db6b8fd6c792"
name = "github.com/maruel/natural"
packages = ["."]
pruneopts = "UT"
revision = "dbcb3e2e8cf10eb839718ba666ef1e21b1c8b847"
[[projects]]
digest = "1:f17bcb6a3694fe34b29fa9f0d93e1984450f6854b66c2df49524ba9541f9952d"
name = "github.com/mholt/archiver"
packages = ["."]
pruneopts = "UT"
revision = "26cf5bb32d07aa4e8d0de15f56ce516f4641d7df"
[[projects]]
digest = "1:fa577f7189e08bb551a16f655bb53ef27e1bebf0280e523f651eeced0cd98514"
name = "github.com/mholt/caddy"
packages = [
".",
"caddyfile",
]
pruneopts = "UT"
revision = "2922d09bef3c504dde66bc12f7441668fcef6a20"
version = "v0.10.14"
[[projects]]
branch = "master"
digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
[[projects]]
branch = "master"
digest = "1:3bdb4203c03569a564d6a4bd54d84315575cebb2d76471f8676f8ee8c402005e"
name = "github.com/nwaples/rardecode"
packages = ["."]
pruneopts = "UT"
revision = "e06696f847aeda6f39a8f0b7cdff193b7690aef6"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
name = "github.com/pelletier/go-toml"
packages = ["."]
pruneopts = "UT"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
[[projects]]
digest = "1:29803f52611cbcc1dfe55b456e9fdac362af7248b3d29d7ea1bec0a12e71dff4"
name = "github.com/pierrec/lz4"
packages = [
".",
"internal/xxh32",
]
pruneopts = "UT"
revision = "1958fd8fff7f115e79725b1288e0b878b3e06b00"
version = "v2.0.3"
[[projects]]
digest = "1:ed615c5430ecabbb0fb7629a182da65ecee6523900ac1ac932520860878ffcad"
name = "github.com/robfig/cron"
packages = ["."]
pruneopts = "UT"
revision = "b41be1df696709bb6395fe435af20370037c0b4c"
version = "v1.1"
[[projects]]
digest = "1:8bc629776d035c003c7814d4369521afe67fdb8efc4b5f66540d29343b98cf23"
name = "github.com/russross/blackfriday"
packages = ["."]
pruneopts = "UT"
revision = "55d61fa8aa702f59229e6cff85793c22e580eaf5"
version = "v1.5.1"
[[projects]]
branch = "master"
digest = "1:def689e73e9252f6f7fe66834a76751a41b767e03daab299e607e7226c58a855"
name = "github.com/shurcooL/sanitized_anchor_name"
packages = ["."]
pruneopts = "UT"
revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
[[projects]]
digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84"
name = "github.com/spf13/afero"
packages = [
".",
"mem",
]
pruneopts = "UT"
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
version = "v1.1.1"
[[projects]]
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f"
name = "github.com/spf13/cast"
packages = ["."]
pruneopts = "UT"
revision = "8965335b8c7107321228e3e3702cab9832751bac"
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:080e5f630945ad754f4b920e60b4d3095ba0237ebf88dc462eb28002932e3805"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
pruneopts = "UT"
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
[[projects]]
digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "UT"
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
[[projects]]
digest = "1:4fc8a61287ccfb4286e1ca5ad2ce3b0b301d746053bf44ac38cf34e40ae10372"
name = "github.com/spf13/viper"
packages = ["."]
pruneopts = "UT"
revision = "907c19d40d9a6c9bb55f040ff4ae45271a4754b9"
version = "v1.1.0"
[[projects]]
digest = "1:4aeb3860275fa1fd60cccfb5a6ef85da438bf17402e1e84412ade4d4b55066a0"
name = "github.com/ulikunitz/xz"
packages = [
".",
"internal/hash",
"internal/xlog",
"lzma",
]
pruneopts = "UT"
revision = "0c6b41e72360850ca4f98dc341fd999726ea007f"
version = "v0.5.4"
[[projects]]
branch = "master"
digest = "1:1ecf2a49df33be51e757d0033d5d51d5f784f35f68e5a38f797b2d3f03357d71"
name = "golang.org/x/crypto"
packages = [
"bcrypt",
"blowfish",
]
pruneopts = "UT"
revision = "de0752318171da717af4ce24d0a2e8626afaeb11"
[[projects]]
branch = "master"
digest = "1:76b5ca88193bf744f729c2fde12151b24364ffaca3fd092069d9ca2ea6e1f999"
name = "golang.org/x/sys"
packages = ["unix"]
pruneopts = "UT"
revision = "904bdc257025c7b3f43c19360ad3ab85783fad78"
[[projects]]
digest = "1:8029e9743749d4be5bc9f7d42ea1659471767860f0cdc34d37c3111bd308a295"
name = "golang.org/x/text"
packages = [
"internal/gen",
"internal/triegen",
"internal/ucd",
"transform",
"unicode/cldr",
"unicode/norm",
]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
digest = "1:c805e517269b0ba4c21ded5836019ed7d16953d4026cb7d00041d039c7906be9"
name = "gopkg.in/natefinch/lumberjack.v2"
packages = ["."]
pruneopts = "UT"
revision = "a96e63847dc3c67d17befa69c303767e2f84e54f"
version = "v2.1"
[[projects]]
branch = "v2"
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/GeertJohan/go.rice",
"github.com/GeertJohan/go.rice/embedded",
"github.com/asdine/storm",
"github.com/asdine/storm/q",
"github.com/dgrijalva/jwt-go",
"github.com/dgrijalva/jwt-go/request",
"github.com/gohugoio/hugo/parser",
"github.com/gorilla/websocket",
"github.com/hacdias/fileutils",
"github.com/hacdias/varutils",
"github.com/maruel/natural",
"github.com/mholt/archiver",
"github.com/mholt/caddy",
"github.com/mitchellh/mapstructure",
"github.com/robfig/cron",
"github.com/spf13/pflag",
"github.com/spf13/viper",
"golang.org/x/crypto/bcrypt",
"gopkg.in/natefinch/lumberjack.v2",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,74 +0,0 @@
[[constraint]]
branch = "master"
name = "github.com/GeertJohan/go.rice"
[[constraint]]
name = "github.com/asdine/storm"
version = "2.0.2"
[[constraint]]
name = "github.com/dgrijalva/jwt-go"
version = "3.1.0"
[[constraint]]
name = "github.com/gohugoio/hugo"
version = "0.36.1"
[[constraint]]
name = "github.com/gorilla/websocket"
version = "1.2.0"
[[constraint]]
branch = "master"
name = "github.com/hacdias/fileutils"
[[constraint]]
branch = "master"
name = "github.com/hacdias/varutils"
[[constraint]]
name = "github.com/mholt/archiver"
# TODO: switch to version when it's available
# this is for Archiver.Write() which was introduced in 548c791
revision = "26cf5bb32d07aa4e8d0de15f56ce516f4641d7df"
# version = "2.0.0"
[[constraint]]
name = "github.com/mholt/caddy"
version = "0.10.11"
[[constraint]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
[[constraint]]
name = "github.com/robfig/cron"
version = "1.0.0"
[[constraint]]
name = "github.com/spf13/pflag"
version = "1.0.0"
[[constraint]]
name = "github.com/spf13/viper"
version = "1.0.0"
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"
[[constraint]]
name = "gopkg.in/natefinch/lumberjack.v2"
version = "2.1.0"
[[constraint]]
branch = "master"
name = "github.com/maruel/natural"
[[override]]
name = "github.com/russross/blackfriday"
version = "^1.0.0"
[prune]
go-tests = true
unused-packages = true

View File

@ -2,22 +2,19 @@
set -e
cd $(dirname $0)/..
cd $(dirname $0)/../cli
dep ensure -vendor-only
go get -v ./...
if [ "$COMMIT_SHA" != "" ]; then
echo "Set version to ($COMMIT_SHA)"
sed -i.bak "s|(untracked)|($COMMIT_SHA)|g" filebrowser.go
sed -i.bak "s|(untracked)|($COMMIT_SHA)|g" ../lib/filebrowser.go
fi
echo "Build cmd/filebrowser"
cd cmd/filebrowser
CGO_ENABLED=0 go build -a
cd ../..
cp cmd/filebrowser/filebrowser ./
CGO_ENABLED=0 go build -a -o filebrowser
if [ "$COMMIT_SHA" != "" ]; then
echo "Reset version to (untracked)"
sed -i "s|($COMMIT_SHA)|(untracked)|g" filebrowser.go
sed -i "s|($COMMIT_SHA)|(untracked)|g" ../lib/filebrowser.go
fi

View File

@ -2,8 +2,8 @@
cd $(dirname $0)/..
if [ -d "rice-box.go" ]; then
rm -rf rice-box.go
if [ -d lib/"rice-box.go" ]; then
rm -rf lib/rice-box.go
fi
if [ "$USE_DOCKER" != "" ]; then
@ -41,9 +41,8 @@ if [ "$USE_DOCKER" != "" ]; then
for d in "dist/" "node_modules/"; do
docker cp filebrowser-tmp:/$WDIR/frontend/$d frontend
done
for d in "vendor/" "rice-box.go" "filebrowser"; do
docker cp filebrowser-tmp:/$WDIR/$d ./
done
docker cp filebrowser-tmp:/$WDIR/cli/filebrowser ./filebrowser
docker cp filebrowser-tmp:/$WDIR/lib/rice-box.go ./lib/rice-box.go
fi
docker rm -f filebrowser-tmp
else

View File

@ -19,4 +19,5 @@ if ! [ -x "$(command -v rice)" ]; then
fi
# Embed the assets using rice
cd lib
rice embed-go

View File

@ -11,8 +11,8 @@ openssl aes-256-cbc -K $encrypted_9ca81b5594f5_key -iv $encrypted_9ca81b5594f5_i
git clone git@github.com:filebrowser/caddy caddy
cd caddy
cp ../../rice-box.go assets/
sed -i 's/package filebrowser/package assets/g' assets/rice-box.go
cp ../../lib/rice-box.go assets/
sed -i 's/package lib/package assets/g' assets/rice-box.go
git checkout -b update-rice-box origin/master
git config --local user.name "Filebrowser Bot"
git config --local user.email "FilebrowserBot@users.noreply.github.com"

View File

@ -8,7 +8,7 @@ dolint='gometalinter --exclude="rice-box.go" --exclude="vendor" --deadline=300s
if [ "$USE_DOCKER" != "" ]; then
docker run --rm -itv $(pwd):/src filebrowser/dev sh -c "\
cp -r /src/. ./ && dep ensure -v -vendor-only && \
cp -r /src/. ./ && cd cli && go get -v ./... && \
CGO_ENABLED=0 $dolint"
else
$dolint

24
cli/cmd/db.go Normal file
View File

@ -0,0 +1,24 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
// dbCmd represents the db command
var dbCmd = &cobra.Command{
Use: "db",
Version: rootCmd.Version,
Aliases: []string{"database"},
Short: "Manage a filebrowser database",
Long: `This is a CLI tool to ease the management of
filebrowser database files.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("db called. Command not implemented, yet.")
},
}
func init() {
rootCmd.AddCommand(dbCmd)
}

75
cli/cmd/root.go Normal file
View File

@ -0,0 +1,75 @@
package cmd
import (
"log"
"strings"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
v "github.com/spf13/viper"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "filebrowser",
Version: "(untracked)",
Aliases: []string{"serve"},
Short: "A stylish web-based file manager",
Long: `Command 'serve' is the default. Filebrowser is started
with the provided envvars, flags and/or config file. For example:
filebrowser -c config.json -p 80 -s ./srv
File Browser is a static binary composed of a golang backend and
a Vue.js frontend to create, edit, copy, move, download your files
easily, everywhere, every time.`,
// Run: func(cmd *cobra.Command, args []string) {},
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
checkRootAlias()
if err := rootCmd.Execute(); err != nil {
panic(err)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.SetVersionTemplate("File Browser {{printf \"version %s\" .Version}}\n")
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (defaults are './.filebrowser[ext]', '$HOME/.filebrowser[ext]' or '/etc/filebrowser/.filebrowser[ext]')")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile == "" {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
panic(err)
}
v.AddConfigPath(".")
v.AddConfigPath(home)
v.AddConfigPath("/etc/filebrowser/")
v.SetConfigName(".filebrowser")
} else {
// Use config file from the flag.
v.SetConfigFile(cfgFile)
}
v.SetEnvPrefix("FB")
v.AutomaticEnv()
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
if err := v.ReadInConfig(); err != nil {
if _, ok := err.(v.ConfigParseError); ok {
panic(err)
}
} else {
log.Println("Using config file:", v.ConfigFileUsed())
}
}

46
cli/cmd/rootalias.go Normal file
View File

@ -0,0 +1,46 @@
package cmd
import (
"log"
"os"
)
// checkRootAlias compares the first argument provided in the CLI with a list of
// subcmds and aliases. If no match is found, the first alias of rootCmd is added.
func checkRootAlias() {
l := len(rootCmd.Aliases)
if l == 0 {
return
}
if l > 1 {
log.Printf("rootCmd.Aliases should contain a single string. '%s' is used.\n", rootCmd.Aliases[0])
}
if len(os.Args) > 1 {
for _, v := range append(nonRootSubCmds(), []string{"--help", "--version"}...) {
if os.Args[1] == v {
return
}
}
}
os.Args = append([]string{os.Args[0], rootCmd.Aliases[0]}, os.Args[1:]...)
}
// nonRootSubCmds traverses the list of subcommands of rootCmd and returns a string
// slice containing the names and aliases of all the subcmds, except the one defined
// in the Aliases field of rootCmd.
func nonRootSubCmds() (l []string) {
for _, c := range rootCmd.Commands() {
isAlias := false
for _, a := range append(c.Aliases, c.Name()) {
if a == rootCmd.Aliases[0] {
isAlias = true
break
}
}
if !isAlias {
l = append(l, c.Name())
l = append(l, c.Aliases...)
}
}
return
}

111
cli/cmd/serve.go Normal file
View File

@ -0,0 +1,111 @@
package cmd
import (
filebrowser "github.com/filebrowser/filebrowser/lib"
"github.com/spf13/cobra"
v "github.com/spf13/viper"
)
// serveCmd represents the serve command
var serveCmd = &cobra.Command{
Use: "serve",
Version: rootCmd.Version,
Aliases: []string{"server"},
Short: "Start filebrowser service",
Long: rootCmd.Long,
Run: func(cmd *cobra.Command, args []string) {
Serve()
},
Args: cobra.NoArgs,
}
func init() {
rootCmd.AddCommand(serveCmd)
f := serveCmd.PersistentFlags()
flag := func(k string, i interface{}, u string) {
switch y := i.(type) {
case bool:
f.Bool(k, y, u)
case int:
f.Int(k, y, u)
case string:
f.String(k, y, u)
}
v.SetDefault(k, i)
}
flagP := func(k, p string, i interface{}, u string) {
switch y := i.(type) {
case bool:
f.BoolP(k, p, y, u)
case int:
f.IntP(k, p, y, u)
case string:
f.StringP(k, p, y, u)
}
v.SetDefault(k, i)
}
deprecated := func(k string, i interface{}, u, m string) {
switch y := i.(type) {
case bool:
f.Bool(k, y, u)
case int:
f.Int(k, y, u)
case string:
f.String(k, y, u)
}
f.MarkDeprecated(k, m)
}
// Global settings
flagP("port", "p", 0, "HTTP Port (default is random)")
flagP("address", "a", "", "Address to listen to (default is all of them)")
flagP("database", "d", "./filebrowser.db", "Database file")
flagP("log", "l", "stdout", "Errors logger; can use 'stdout', 'stderr' or file")
flagP("baseurl", "b", "", "Base URL")
flag("prefixurl", "", "Prefix URL")
flag("staticgen", "", "Static Generator you want to enable")
// User default settings
f.String("defaults.commands", "git svn hg", "Default commands option for new users")
v.SetDefault("defaults.commands", []string{"git", "svn", "hg"})
flagP("defaults.scope", "s", ".", "Default scope option for new users")
flag("defaults.viewMode", filebrowser.MosaicViewMode, "Default view mode for new users")
flag("defaults.allowCommands", true, "Default allow commands option for new users")
flag("defaults.allowEdit", true, "Default allow edit option for new users")
flag("defaults.allowNew", true, "Default allow new option for new users")
flag("defaults.allowPublish", true, "Default allow publish option for new users")
flag("defaults.locale", "", "Default locale for new users, set it empty to enable auto detect from browser")
// Recaptcha settings
flag("recaptcha.host", "https://www.google.com", "Use another host for ReCAPTCHA. recaptcha.net might be useful in China")
flag("recaptcha.key", "", "ReCaptcha site key")
flag("recaptcha.secret", "", "ReCaptcha secret")
// Auth settings
flag("auth.method", "default", "Switch between 'none', 'default' and 'proxy' authentication")
flag("auth.header", "X-Forwarded-User", "The header name used for proxy authentication")
// Bind the full flag set to the configuration
if err := v.BindPFlags(f); err != nil {
panic(err)
}
// Deprecated flags
deprecated("no-auth", false, "Disables authentication", "use --auth.method='none' instead")
deprecated("alternative-recaptcha", false, "Use recaptcha.net for serving and handling, useful in China", "use --recaptcha.host instead")
deprecated("recaptcha-key", "", "ReCaptcha site key", "use --recaptcha.key instead")
deprecated("recaptcha-secret", "", "ReCaptcha secret", "use --recaptcha.secret instead")
deprecated("scope", ".", "Default scope option for new users", "use --defaults.scope instead")
deprecated("commands", "git svn hg", "Default commands option for new users", "use --defaults.commands instead")
deprecated("view-mode", "mosaic", "Default view mode for new users", "use --defaults.viewMode instead")
deprecated("locale", "", "Default locale for new users, set it empty to enable auto detect from browser", "use --defaults.locale instead")
deprecated("allow-commands", true, "Default allow commands option for new users", "use --defaults.allowCommands instead")
deprecated("allow-edit", true, "Default allow edit option for new users", "use --defaults.allowEdit instead")
deprecated("allow-publish", true, "Default allow publish option for new users", "use --defaults.allowPublish instead")
deprecated("allow-new", true, "Default allow new option for new users", "use --defaults.allowNew instead")
}

150
cli/cmd/server.go Normal file
View File

@ -0,0 +1,150 @@
package cmd
import (
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path/filepath"
"github.com/asdine/storm"
filebrowser "github.com/filebrowser/filebrowser/lib"
"github.com/filebrowser/filebrowser/lib/bolt"
h "github.com/filebrowser/filebrowser/lib/http"
"github.com/filebrowser/filebrowser/lib/staticgen"
"github.com/hacdias/fileutils"
"github.com/spf13/viper"
"gopkg.in/natefinch/lumberjack.v2"
)
func Serve() {
// Set up process log before anything bad happens.
switch l := viper.GetString("log"); l {
case "stdout":
log.SetOutput(os.Stdout)
case "stderr":
log.SetOutput(os.Stderr)
case "":
log.SetOutput(ioutil.Discard)
default:
log.SetOutput(&lumberjack.Logger{
Filename: l,
MaxSize: 100,
MaxAge: 14,
MaxBackups: 10,
})
}
// Validate the provided config before moving forward
{
// Map of valid authentication methods, containing a boolean value to indicate the need of Auth.Header
validMethods := make(map[string]bool)
validMethods["none"] = false
validMethods["default"] = false
validMethods["proxy"] = true
m := viper.GetString("auth.method")
b, ok := validMethods[m]
if !ok {
log.Fatal("The property 'auth.method' needs to be set to 'none', 'default' or 'proxy'.")
}
if b {
if viper.GetString("auth.header") == "" {
log.Fatal("The 'auth.header' needs to be specified when '", m, "' authentication is used.")
}
log.Println("[WARN] Filebrowser authentication is configured to '", m, "' authentication. This can cause a huge security issue if the infrastructure is not configured correctly.")
}
}
// Builds the address and a listener.
laddr := viper.GetString("address") + ":" + viper.GetString("port")
listener, err := net.Listen("tcp", laddr)
if err != nil {
log.Fatal(err)
}
// Tell the user the port in which is listening.
log.Println("Listening on", listener.Addr().String())
// Starts the server.
if err := http.Serve(listener, handler()); err != nil {
log.Fatal(err)
}
}
func handler() http.Handler {
db, err := storm.Open(viper.GetString("database"))
if err != nil {
log.Fatal(err)
}
fb := &filebrowser.FileBrowser{
Auth: &filebrowser.Auth{
Method: viper.GetString("auth.method"),
Header: viper.GetString("auth.header"),
},
ReCaptcha: &filebrowser.ReCaptcha{
Host: viper.GetString("recaptcha.host"),
Key: viper.GetString("recaptcha.key"),
Secret: viper.GetString("recaptcha.secret"),
},
DefaultUser: &filebrowser.User{
AllowCommands: viper.GetBool("defaults.allowCommands"),
AllowEdit: viper.GetBool("defaults.allowEdit"),
AllowNew: viper.GetBool("defaults.allowNew"),
AllowPublish: viper.GetBool("defaults.allowPublish"),
Commands: viper.GetStringSlice("defaults.commands"),
Rules: []*filebrowser.Rule{},
Locale: viper.GetString("defaults.locale"),
CSS: "",
Scope: viper.GetString("defaults.scope"),
FileSystem: fileutils.Dir(viper.GetString("defaults.scope")),
ViewMode: viper.GetString("defaults.viewMode"),
},
Store: &filebrowser.Store{
Config: bolt.ConfigStore{DB: db},
Users: bolt.UsersStore{DB: db},
Share: bolt.ShareStore{DB: db},
},
NewFS: func(scope string) filebrowser.FileSystem {
return fileutils.Dir(scope)
},
}
fb.SetBaseURL(viper.GetString("baseurl"))
fb.SetPrefixURL(viper.GetString("prefixurl"))
err = fb.Setup()
if err != nil {
log.Fatal(err)
}
switch viper.GetString("staticgen") {
case "hugo":
hugo := &staticgen.Hugo{
Root: viper.GetString("Scope"),
Public: filepath.Join(viper.GetString("Scope"), "public"),
Args: []string{},
CleanPublic: true,
}
if err = fb.Attach(hugo); err != nil {
log.Fatal(err)
}
case "jekyll":
jekyll := &staticgen.Jekyll{
Root: viper.GetString("Scope"),
Public: filepath.Join(viper.GetString("Scope"), "_site"),
Args: []string{"build"},
CleanPublic: true,
}
if err = fb.Attach(jekyll); err != nil {
log.Fatal(err)
}
}
return h.Handler(fb)
}

26
cli/cmd/version.go Normal file
View File

@ -0,0 +1,26 @@
package cmd
import (
"text/template"
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of File Browser",
Long: `All software has versions. This is File Browser's`,
Run: func(cmd *cobra.Command, args []string) {
// https://github.com/spf13/cobra/issues/724
t := template.New("version")
template.Must(t.Parse(rootCmd.VersionTemplate()))
err := t.Execute(rootCmd.OutOrStdout(), rootCmd)
if err != nil {
rootCmd.Println(err)
}
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}

7
cli/main.go Normal file
View File

@ -0,0 +1,7 @@
package main
import "github.com/filebrowser/filebrowser/cli/cmd"
func main() {
cmd.Execute()
}

View File

@ -1,279 +0,0 @@
package main
import (
"fmt"
"github.com/asdine/storm"
"github.com/filebrowser/filebrowser"
"github.com/filebrowser/filebrowser/bolt"
h "github.com/filebrowser/filebrowser/http"
"github.com/filebrowser/filebrowser/staticgen"
"github.com/hacdias/fileutils"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
"gopkg.in/natefinch/lumberjack.v2"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path/filepath"
"strings")
var (
addr string
config string
database string
scope string
commands string
logfile string
staticg string
locale string
baseurl string
prefixurl string
viewMode string
recaptchakey string
recaptchasecret string
port int
auth struct {
method string
loginHeader string
}
noAuth bool
allowCommands bool
allowEdit bool
allowNew bool
allowPublish bool
showVer bool
alterRecaptcha bool
)
func init() {
flag.StringVarP(&config, "config", "c", "", "Configuration file")
flag.IntVarP(&port, "port", "p", 0, "HTTP Port (default is random)")
flag.StringVarP(&addr, "address", "a", "", "Address to listen to (default is all of them)")
flag.StringVarP(&database, "database", "d", "./filebrowser.db", "Database file")
flag.StringVarP(&logfile, "log", "l", "stdout", "Errors logger; can use 'stdout', 'stderr' or file")
flag.StringVarP(&scope, "scope", "s", ".", "Default scope option for new users")
flag.StringVarP(&baseurl, "baseurl", "b", "", "Base URL")
flag.StringVar(&commands, "commands", "git svn hg", "Default commands option for new users")
flag.StringVar(&prefixurl, "prefixurl", "", "Prefix URL")
flag.StringVar(&viewMode, "view-mode", "mosaic", "Default view mode for new users")
flag.StringVar(&recaptchakey, "recaptcha-key", "", "ReCaptcha site key")
flag.StringVar(&recaptchasecret, "recaptcha-secret", "", "ReCaptcha secret")
flag.BoolVar(&allowCommands, "allow-commands", true, "Default allow commands option for new users")
flag.BoolVar(&allowEdit, "allow-edit", true, "Default allow edit option for new users")
flag.BoolVar(&allowPublish, "allow-publish", true, "Default allow publish option for new users")
flag.StringVar(&auth.method, "auth.method", "default", "Switch between 'none', 'default' and 'proxy' authentication.")
flag.StringVar(&auth.loginHeader, "auth.loginHeader", "X-Forwarded-User", "The header name used for proxy authentication.")
flag.BoolVar(&allowNew, "allow-new", true, "Default allow new option for new users")
flag.BoolVar(&noAuth, "no-auth", false, "Disables authentication")
flag.BoolVar(&alterRecaptcha, "alternative-recaptcha", false, "Use recaptcha.net for serving and handling, useful in China")
flag.StringVar(&locale, "locale", "", "Default locale for new users, set it empty to enable auto detect from browser")
flag.StringVar(&staticg, "staticgen", "", "Static Generator you want to enable")
flag.BoolVarP(&showVer, "version", "v", false, "Show version")
}
func setupViper() {
viper.SetDefault("Address", "")
viper.SetDefault("Port", "0")
viper.SetDefault("Database", "./filebrowser.db")
viper.SetDefault("Scope", ".")
viper.SetDefault("Logger", "stdout")
viper.SetDefault("Commands", []string{"git", "svn", "hg"})
viper.SetDefault("AllowCommmands", true)
viper.SetDefault("AllowEdit", true)
viper.SetDefault("AllowNew", true)
viper.SetDefault("AllowPublish", true)
viper.SetDefault("StaticGen", "")
viper.SetDefault("Locale", "")
viper.SetDefault("AuthMethod", "default")
viper.SetDefault("LoginHeader", "X-Fowarded-User")
viper.SetDefault("NoAuth", false)
viper.SetDefault("BaseURL", "")
viper.SetDefault("PrefixURL", "")
viper.SetDefault("ViewMode", filebrowser.MosaicViewMode)
viper.SetDefault("AlternativeRecaptcha", false)
viper.SetDefault("ReCaptchaKey", "")
viper.SetDefault("ReCaptchaSecret", "")
viper.BindPFlag("Port", flag.Lookup("port"))
viper.BindPFlag("Address", flag.Lookup("address"))
viper.BindPFlag("Database", flag.Lookup("database"))
viper.BindPFlag("Scope", flag.Lookup("scope"))
viper.BindPFlag("Logger", flag.Lookup("log"))
viper.BindPFlag("Commands", flag.Lookup("commands"))
viper.BindPFlag("AllowCommands", flag.Lookup("allow-commands"))
viper.BindPFlag("AllowEdit", flag.Lookup("allow-edit"))
viper.BindPFlag("AllowNew", flag.Lookup("allow-new"))
viper.BindPFlag("AllowPublish", flag.Lookup("allow-publish"))
viper.BindPFlag("Locale", flag.Lookup("locale"))
viper.BindPFlag("StaticGen", flag.Lookup("staticgen"))
viper.BindPFlag("AuthMethod", flag.Lookup("auth.method"))
viper.BindPFlag("LoginHeader", flag.Lookup("auth.loginHeader"))
viper.BindPFlag("NoAuth", flag.Lookup("no-auth"))
viper.BindPFlag("BaseURL", flag.Lookup("baseurl"))
viper.BindPFlag("PrefixURL", flag.Lookup("prefixurl"))
viper.BindPFlag("ViewMode", flag.Lookup("view-mode"))
viper.BindPFlag("AlternativeRecaptcha", flag.Lookup("alternative-recaptcha"))
viper.BindPFlag("ReCaptchaKey", flag.Lookup("recaptcha-key"))
viper.BindPFlag("ReCaptchaSecret", flag.Lookup("recaptcha-secret"))
}
func printVersion() {
fmt.Println("filebrowser version", filebrowser.Version)
os.Exit(0)
}
func initConfig() {
// Add a configuration file if set.
if config != "" {
cfg := strings.TrimSuffix(config, filepath.Ext(config))
if dir := filepath.Dir(cfg); dir != "" {
viper.AddConfigPath(dir)
cfg = strings.TrimPrefix(cfg, dir)
}
viper.SetConfigName(cfg)
} else {
viper.SetConfigName("filebrowser")
viper.AddConfigPath(".")
}
// Read configuration from a file if exists.
err := viper.ReadInConfig()
if err != nil {
if _, ok := err.(viper.ConfigParseError); ok {
panic(err)
}
}
}
func main() {
setupViper()
flag.Parse()
if showVer {
printVersion()
}
initConfig();
// Set up process log before anything bad happens.
switch viper.GetString("Logger") {
case "stdout":
log.SetOutput(os.Stdout)
case "stderr":
log.SetOutput(os.Stderr)
case "":
log.SetOutput(ioutil.Discard)
default:
log.SetOutput(&lumberjack.Logger{
Filename: logfile,
MaxSize: 100,
MaxAge: 14,
MaxBackups: 10,
})
}
// Validate the provided config before moving forward
if viper.GetString("AuthMethod") != "none" && viper.GetString("AuthMethod") != "default" && viper.GetString("AuthMethod") != "proxy" {
log.Fatal("The property 'auth.method' needs to be set to 'default' or 'proxy'.")
}
if viper.GetString("AuthMethod") == "proxy" {
if viper.GetString("LoginHeader") == "" {
log.Fatal("The 'loginHeader' needs to be specified when 'proxy' authentication is used.")
}
log.Println("[WARN] Filebrowser authentication is configured to 'proxy' authentication. This can cause a huge security issue if the infrastructure is not configured correctly.")
}
// Builds the address and a listener.
laddr := viper.GetString("Address") + ":" + viper.GetString("Port")
listener, err := net.Listen("tcp", laddr)
if err != nil {
log.Fatal(err)
}
// Tell the user the port in which is listening.
fmt.Println("Listening on", listener.Addr().String())
// Starts the server.
if err := http.Serve(listener, handler()); err != nil {
log.Fatal(err)
}
}
func handler() http.Handler {
db, err := storm.Open(viper.GetString("Database"))
if err != nil {
log.Fatal(err)
}
recaptchaHost := "https://www.google.com"
if viper.GetBool("AlternativeRecaptcha") {
recaptchaHost = "https://recaptcha.net"
}
fm := &filebrowser.FileBrowser{
AuthMethod: viper.GetString("AuthMethod"),
LoginHeader: viper.GetString("LoginHeader"),
NoAuth: viper.GetBool("NoAuth"),
BaseURL: viper.GetString("BaseURL"),
PrefixURL: viper.GetString("PrefixURL"),
ReCaptchaHost: recaptchaHost,
ReCaptchaKey: viper.GetString("ReCaptchaKey"),
ReCaptchaSecret: viper.GetString("ReCaptchaSecret"),
DefaultUser: &filebrowser.User{
AllowCommands: viper.GetBool("AllowCommands"),
AllowEdit: viper.GetBool("AllowEdit"),
AllowNew: viper.GetBool("AllowNew"),
AllowPublish: viper.GetBool("AllowPublish"),
Commands: viper.GetStringSlice("Commands"),
Rules: []*filebrowser.Rule{},
Locale: viper.GetString("Locale"),
CSS: "",
Scope: viper.GetString("Scope"),
FileSystem: fileutils.Dir(viper.GetString("Scope")),
ViewMode: viper.GetString("ViewMode"),
},
Store: &filebrowser.Store{
Config: bolt.ConfigStore{DB: db},
Users: bolt.UsersStore{DB: db},
Share: bolt.ShareStore{DB: db},
},
NewFS: func(scope string) filebrowser.FileSystem {
return fileutils.Dir(scope)
},
}
err = fm.Setup()
if err != nil {
log.Fatal(err)
}
switch viper.GetString("StaticGen") {
case "hugo":
hugo := &staticgen.Hugo{
Root: viper.GetString("Scope"),
Public: filepath.Join(viper.GetString("Scope"), "public"),
Args: []string{},
CleanPublic: true,
}
if err = fm.Attach(hugo); err != nil {
log.Fatal(err)
}
case "jekyll":
jekyll := &staticgen.Jekyll{
Root: viper.GetString("Scope"),
Public: filepath.Join(viper.GetString("Scope"), "_site"),
Args: []string{"build"},
CleanPublic: true,
}
if err = fm.Attach(jekyll); err != nil {
log.Fatal(err)
}
}
return h.Handler(fm)
}

View File

@ -2,7 +2,7 @@ package bolt
import (
"github.com/asdine/storm"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
// ConfigStore is a configuration store.

View File

@ -3,7 +3,7 @@ package bolt
import (
"github.com/asdine/storm"
"github.com/asdine/storm/q"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
// ShareStore is a shareable links store.

View File

@ -4,7 +4,7 @@ import (
"reflect"
"github.com/asdine/storm"
fm "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
// UsersStore is a users store.
@ -13,11 +13,11 @@ type UsersStore struct {
}
// Get gets a user with a certain id from the database.
func (u UsersStore) Get(id int, builder fm.FSBuilder) (*fm.User, error) {
var us fm.User
func (u UsersStore) Get(id int, builder fb.FSBuilder) (*fb.User, error) {
var us fb.User
err := u.DB.One("ID", id, &us)
if err == storm.ErrNotFound {
return nil, fm.ErrNotExist
return nil, fb.ErrNotExist
}
if err != nil {
@ -29,11 +29,11 @@ func (u UsersStore) Get(id int, builder fm.FSBuilder) (*fm.User, error) {
}
// GetByUsername gets a user with a certain username from the database.
func (u UsersStore) GetByUsername(username string, builder fm.FSBuilder) (*fm.User, error) {
var us fm.User
func (u UsersStore) GetByUsername(username string, builder fb.FSBuilder) (*fb.User, error) {
var us fb.User
err := u.DB.One("Username", username, &us)
if err == storm.ErrNotFound {
return nil, fm.ErrNotExist
return nil, fb.ErrNotExist
}
if err != nil {
@ -45,11 +45,11 @@ func (u UsersStore) GetByUsername(username string, builder fm.FSBuilder) (*fm.Us
}
// Gets gets all the users from the database.
func (u UsersStore) Gets(builder fm.FSBuilder) ([]*fm.User, error) {
var us []*fm.User
func (u UsersStore) Gets(builder fb.FSBuilder) ([]*fb.User, error) {
var us []*fb.User
err := u.DB.All(&us)
if err == storm.ErrNotFound {
return nil, fm.ErrNotExist
return nil, fb.ErrNotExist
}
if err != nil {
@ -64,7 +64,7 @@ func (u UsersStore) Gets(builder fm.FSBuilder) ([]*fm.User, error) {
}
// Update updates the whole user object or only certain fields.
func (u UsersStore) Update(us *fm.User, fields ...string) error {
func (u UsersStore) Update(us *fb.User, fields ...string) error {
if len(fields) == 0 {
return u.Save(us)
}
@ -80,11 +80,11 @@ func (u UsersStore) Update(us *fm.User, fields ...string) error {
}
// Save saves a user to the database.
func (u UsersStore) Save(us *fm.User) error {
func (u UsersStore) Save(us *fb.User) error {
return u.DB.Save(us)
}
// Delete deletes a user from the database.
func (u UsersStore) Delete(id int) error {
return u.DB.DeleteStruct(&fm.User{ID: id})
return u.DB.DeleteStruct(&fb.User{ID: id})
}

View File

@ -74,4 +74,4 @@ One simple implementation for this, at port 80, in the root of the domain, would
http.ListenAndServe(":80", h.Handler(m))
*/
package filebrowser
package lib

View File

@ -1,4 +1,4 @@
package filebrowser
package lib
import (
"bytes"

View File

@ -1,4 +1,4 @@
package filebrowser
package lib
import (
"crypto/rand"
@ -15,7 +15,7 @@ import (
"golang.org/x/crypto/bcrypt"
"github.com/GeertJohan/go.rice"
rice "github.com/GeertJohan/go.rice"
"github.com/hacdias/fileutils"
"github.com/mholt/caddy"
"github.com/robfig/cron"
@ -41,6 +41,25 @@ var (
ErrInvalidOption = errors.New("invalid option")
)
// ReCaptcha settings.
type ReCaptcha struct {
Host string
Key string
Secret string
}
// Auth settings.
type Auth struct {
// Define if which of the following authentication mechansims should be used:
// - 'default', which requires a user and a password.
// - 'proxy', which requires a valid user and the user name has to be provided through an
// http header.
// - 'none', which allows anyone to access the filebrowser instance.
Method string
// If 'Method' is set to 'proxy' the header configured below is used to identify the user.
Header string
}
// FileBrowser is a file manager instance. It should be creating using the
// 'New' function and not directly.
type FileBrowser struct {
@ -67,24 +86,11 @@ type FileBrowser struct {
// edited directly. Use SetBaseURL.
BaseURL string
// NoAuth disables the authentication. When the authentication is disabled,
// there will only exist one user, called "admin".
NoAuth bool
// Define if which of the following authentication mechansims should be used:
// - 'default', which requires a user and a password.
// - 'proxy', which requires a valid user and the user name has to be provided through an
// http header.
// - 'none', which allows anyone to access the filebrowser instance.
AuthMethod string
// When 'AuthMethod' is set to 'proxy' the header configured below is used to identify the user.
LoginHeader string
// Authentication configuration.
Auth *Auth
// ReCaptcha host, key and secret.
ReCaptchaHost string
ReCaptchaKey string
ReCaptchaSecret string
ReCaptcha *ReCaptcha
// StaticGen is the static websit generator handler.
StaticGen StaticGen
@ -129,7 +135,7 @@ type FSBuilder func(scope string) FileSystem
func (m *FileBrowser) Setup() error {
// Creates a new File Browser instance with the Users
// map and Assets box.
m.Assets = rice.MustFindBox("./frontend/dist")
m.Assets = rice.MustFindBox("../frontend/dist")
m.Cron = cron.New()
// Tries to get the encryption key from the database.

View File

@ -9,7 +9,7 @@ import (
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
const reCaptchaAPI = "/recaptcha/api/siteverify"
@ -51,14 +51,14 @@ func reCaptcha(host, secret, response string) (bool, error) {
// authHandler processes the authentication for the user.
func authHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
if c.NoAuth {
if c.Auth.Method == "none" {
// NoAuth instances shouldn't call this method.
return 0, nil
}
if c.AuthMethod == "proxy" {
if c.Auth.Method == "proxy" {
// Receive the Username from the Header and check if it exists.
u, err := c.Store.Users.GetByUsername(r.Header.Get(c.LoginHeader), c.NewFS)
u, err := c.Store.Users.GetByUsername(r.Header.Get(c.Auth.Header), c.NewFS)
if err != nil {
return http.StatusForbidden, nil
}
@ -80,8 +80,8 @@ func authHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, er
}
// If ReCaptcha is enabled, check the code.
if len(c.ReCaptchaSecret) > 0 {
ok, err := reCaptcha(c.ReCaptchaHost, c.ReCaptchaSecret, cred.ReCaptcha)
if len(c.ReCaptcha.Secret) > 0 {
ok, err := reCaptcha(c.ReCaptcha.Host, c.ReCaptcha.Secret, cred.ReCaptcha)
if err != nil {
return http.StatusForbidden, err
}
@ -178,14 +178,14 @@ func (e extractor) ExtractToken(r *http.Request) (string, error) {
// validateAuth is used to validate the authentication and returns the
// User if it is valid.
func validateAuth(c *fb.Context, r *http.Request) (bool, *fb.User) {
if c.NoAuth {
if c.Auth.Method == "none" {
c.User = c.DefaultUser
return true, c.User
}
// If proxy auth is used do not verify the JWT token if the header is provided.
if c.AuthMethod == "proxy" {
u, err := c.Store.Users.GetByUsername(r.Header.Get(c.LoginHeader), c.NewFS)
if c.Auth.Method == "proxy" {
u, err := c.Store.Users.GetByUsername(r.Header.Get(c.Auth.Header), c.NewFS)
if err != nil {
return false, nil
}

View File

@ -7,7 +7,7 @@ import (
"path/filepath"
"strings"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
"github.com/hacdias/fileutils"
"github.com/mholt/archiver"
)

View File

@ -10,7 +10,7 @@ import (
"strings"
"time"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
// Handler returns a function compatible with http.HandleFunc.
@ -227,17 +227,17 @@ func renderFile(c *fb.Context, w http.ResponseWriter, file string) (int, error)
w.Header().Set("Content-Type", contentType+"; charset=utf-8")
data := map[string]interface{}{
"BaseURL": c.RootURL(),
"NoAuth": c.NoAuth,
"baseurl": c.RootURL(),
"NoAuth": c.Auth.Method == "none",
"Version": fb.Version,
"CSS": template.CSS(c.CSS),
"ReCaptcha": c.ReCaptchaKey != "" && c.ReCaptchaSecret != "",
"ReCaptchaHost": c.ReCaptchaHost,
"ReCaptchaKey": c.ReCaptchaKey,
"ReCaptcha": c.ReCaptcha.Key != "" && c.ReCaptcha.Secret != "",
"ReCaptchaHost": c.ReCaptcha.Host,
"ReCaptchaKey": c.ReCaptcha.Key,
}
if c.StaticGen != nil {
data["StaticGen"] = c.StaticGen.Name()
data["staticgen"] = c.StaticGen.Name()
}
err := tpl.Execute(w, data)
@ -291,7 +291,7 @@ func sharePage(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, erro
w.Header().Set("Content-Type", "text/html; charset=utf-8")
err := tpl.Execute(w, map[string]interface{}{
"BaseURL": c.RootURL(),
"baseurl": c.RootURL(),
"File": c.File,
})

View File

@ -13,7 +13,7 @@ import (
"strings"
"time"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
"github.com/hacdias/fileutils"
)

View File

@ -6,7 +6,7 @@ import (
"net/http"
"reflect"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
"github.com/mitchellh/mapstructure"
)

View File

@ -8,7 +8,7 @@ import (
"strings"
"time"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
func shareHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {

View File

@ -8,19 +8,19 @@ import (
"path/filepath"
"regexp"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
func subtitlesHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
files, err := ReadDir(filepath.Dir(c.File.Path))
files, err := readDir(filepath.Dir(c.File.Path))
if err != nil {
return http.StatusInternalServerError, err
}
var subtitles = make([]map[string]string, 0)
subtitles := make([]map[string]string, 0)
for _, file := range files {
ext := filepath.Ext(file.Name())
if ext == ".vtt" || ext == ".srt" {
var sub map[string]string = make(map[string]string)
sub := make(map[string]string)
sub["src"] = filepath.Dir(c.File.Path) + "/" + file.Name()
sub["kind"] = "subtitles"
sub["label"] = file.Name()
@ -31,7 +31,7 @@ func subtitlesHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (in
}
func subtitleHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
str, err := CleanSubtitle(c.File.Path)
str, err := cleanSubtitle(c.File.Path)
if err != nil {
return http.StatusInternalServerError, err
}
@ -55,7 +55,7 @@ func subtitleHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int
}
func CleanSubtitle(filename string) (string, error) {
func cleanSubtitle(filename string) (string, error) {
b, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
@ -69,7 +69,7 @@ func CleanSubtitle(filename string) (string, error) {
return str, err
}
func ReadDir(dirname string) ([]os.FileInfo, error) {
func readDir(dirname string) ([]os.FileInfo, error) {
f, err := os.Open(dirname)
if err != nil {
return nil, err

View File

@ -9,7 +9,7 @@ import (
"strconv"
"strings"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
type modifyRequest struct {
@ -276,7 +276,7 @@ func usersPutHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int
// If we're updating the default user. Only for NoAuth
// implementations. Used to change the viewMode.
if id == 0 && c.NoAuth {
if id == 0 && c.Auth.Method == "none" {
c.DefaultUser.ViewMode = u.ViewMode
return http.StatusOK, nil
}

View File

@ -12,7 +12,7 @@ import (
"strings"
"time"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
"github.com/gorilla/websocket"
)

View File

@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
"github.com/hacdias/varutils"
)

View File

@ -9,7 +9,7 @@ import (
"path/filepath"
"strings"
fb "github.com/filebrowser/filebrowser"
fb "github.com/filebrowser/filebrowser/lib"
)
// Jekyll is the Jekyll static website generator.