Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b66adcb582 | ||
|
|
5ec06fa0cd | ||
|
|
c163318ce5 | ||
|
|
98dabff1e7 | ||
|
|
50dcf35eda | ||
|
|
769e634bdd | ||
|
|
dead024e53 | ||
|
|
f4982cff5e | ||
|
|
bfbb7b5ee1 | ||
|
|
7c09473312 | ||
|
|
b6a8472722 | ||
|
|
db01cfa2f0 | ||
|
|
4267ebf0b4 | ||
|
|
d4e9f5ba53 | ||
|
|
4ace991b8a | ||
|
|
e0c91bb747 | ||
|
|
557e5922d2 | ||
|
|
cea4d2aae9 | ||
|
|
bb5c041d5c | ||
|
|
bf1ef5b0f8 | ||
|
|
08ebe0fbb0 | ||
|
|
bdd7c269ed | ||
|
|
292ef7ea8a | ||
|
|
03ab2199d4 | ||
|
|
41489f9e89 | ||
|
|
3bc5f6e0df | ||
|
|
42d33cf595 | ||
|
|
0fbde826bf | ||
|
|
578f3e9bdd | ||
|
|
f1e5cd490e | ||
|
|
40b0cd4b66 | ||
|
|
f9e3923ae7 | ||
|
|
32f7efbde5 | ||
|
|
56e5005484 | ||
|
|
6bf0e8c063 | ||
|
|
7445d73cf6 | ||
|
|
af095a71e7 | ||
|
|
ab87f76dcb | ||
|
|
2cbe941202 | ||
|
|
5e58c25aa4 | ||
|
|
bdfca1be33 | ||
|
|
281b8dca23 | ||
|
|
2761302ddf | ||
|
|
06f0d36dd8 | ||
|
|
a8863ec90b | ||
|
|
3ed5a64cef | ||
|
|
013c24733e | ||
|
|
d9d6fb656a | ||
|
|
eb72c5d959 | ||
|
|
03bdc57334 | ||
|
|
00e80365da | ||
|
|
aeba656a3b | ||
|
|
dd40590ee6 | ||
|
|
3ffb64abcd | ||
|
|
2dbb080fa2 | ||
|
|
f014828ee6 | ||
|
|
bab6b7760b | ||
|
|
e278dbba65 | ||
|
|
5a8c28fa6d | ||
|
|
e7e2edf76c | ||
|
|
dd2d02e492 | ||
|
|
735e74a858 | ||
|
|
7fc2414476 | ||
|
|
822b8f8e05 | ||
|
|
2d63c288e4 | ||
|
|
218a96eebd | ||
|
|
4fbef557ee | ||
|
|
0a4c803650 | ||
|
|
68c5b8cd52 | ||
|
|
0cff87be24 | ||
|
|
a2fcb8b3b0 | ||
|
|
5eb9e95720 | ||
|
|
967d4e28de | ||
|
|
ee53803fc2 | ||
|
|
fa2ab280aa | ||
|
|
395801016b | ||
|
|
30ed82a70f | ||
|
|
ab643cd0c4 | ||
|
|
85e5beaa4a | ||
|
|
3cab34c68b | ||
|
|
8299f8ed06 | ||
|
|
da05c5048e | ||
|
|
071e1ec19b |
@@ -1,79 +0,0 @@
|
||||
version: 2
|
||||
jobs:
|
||||
linting:
|
||||
docker:
|
||||
- image: circleci/golang:1.9
|
||||
working_directory: /go/src/github.com/filebrowser/filebrowser
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
cd cmd/filebrowser && go get ./... && cd ../..
|
||||
go get github.com/alecthomas/gometalinter
|
||||
gometalinter --install
|
||||
- run:
|
||||
name: Run linting
|
||||
command: |
|
||||
gometalinter --exclude="rice-box.go" \
|
||||
-D goconst \
|
||||
-D gocyclo \
|
||||
-D vetshadow \
|
||||
-D errcheck \
|
||||
-D golint \
|
||||
-D gas
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/golang:1.9
|
||||
working_directory: /go/src/github.com/filebrowser/filebrowser
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
cd cmd/filebrowser
|
||||
go get ./...
|
||||
- run:
|
||||
name: Building
|
||||
command: go build
|
||||
deploy:
|
||||
docker:
|
||||
- image: circleci/golang:1.9
|
||||
working_directory: /go/src/github.com/filebrowser/filebrowser
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
cd cmd/filebrowser
|
||||
go get ./...
|
||||
cd ../..
|
||||
- run:
|
||||
name: Deploy
|
||||
command: curl -sL https://git.io/goreleaser | bash
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
lint-build-deploy:
|
||||
jobs:
|
||||
- linting:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
branches:
|
||||
only: /.*/
|
||||
- build:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
branches:
|
||||
only: /.*/
|
||||
- deploy:
|
||||
requires:
|
||||
- linting
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /v[0-9]+(\.[0-9]+)*(-.*)*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
@@ -1,2 +1,3 @@
|
||||
testdata/
|
||||
.github/
|
||||
**.git
|
||||
|
||||
24
.github/ISSUE_TEMPLATE.md
vendored
24
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,24 +0,0 @@
|
||||
### Instructions (remove before submitting):
|
||||
|
||||
1. Are you asking for help with using Caddy or File Manager? Please use our forum instead: https://forum.caddyserver.com.
|
||||
2. If you are filing a bug report, please answer the following questions.
|
||||
3. If your issue is not a bug report, you do not need to use this template.
|
||||
4. If not using with Caddy, ignore questions 1 and 2.
|
||||
|
||||
### 1. Have you downloaded File Manager from caddyserver.com? If yes, when have you done that? If no, and you are running a custom build, which is the revision of File Manager's repository?
|
||||
|
||||
### 2. What is your entire Caddyfile?
|
||||
```text
|
||||
(Put Caddyfile here)
|
||||
```
|
||||
|
||||
### 3. What are you trying to do?
|
||||
|
||||
|
||||
### 4. What did you expect to see?
|
||||
|
||||
|
||||
### 5. What did you see instead (give full error messages and/or log)?
|
||||
|
||||
|
||||
### 6. How can someone who is starting from scratch reproduce this behaviour as minimally as possible?
|
||||
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
---
|
||||
|
||||
**Description**
|
||||
A clear and concise description of what the issue is about. What are you trying to do?
|
||||
|
||||
**Expected behaviour**
|
||||
What did you expect to happen?
|
||||
|
||||
**What is happening instead?**
|
||||
Please, give full error messages and/or log.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here. If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**How to reproduce?**
|
||||
Tell us how to reproduce this issue. How can someone who is starting from scratch reproduce this behaviour as minimally as possible?
|
||||
|
||||
**Files**
|
||||
A list of relevant files for this issue. Large files can be uploaded one-by-one or in a tarball/zipfile.
|
||||
33
.github/ISSUE_TEMPLATE/caddy_bug_report.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE/caddy_bug_report.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Caddy related bug report
|
||||
about: Create a report to help us improve
|
||||
---
|
||||
|
||||
**Are you asking for help with using Caddy or File Browser?**
|
||||
Please use our forum instead: https://forum.caddyserver.com.
|
||||
|
||||
**When did you download File Browser from caddyserver.com?**
|
||||
|
||||
**What is your entire Caddyfile?**
|
||||
|
||||
```text
|
||||
(Put Caddyfile here)
|
||||
```
|
||||
|
||||
**Description**
|
||||
A clear and concise description of what the issue is about. What are you trying to do?
|
||||
|
||||
**Expected behaviour**
|
||||
What did you expect to happen?
|
||||
|
||||
**What is happening instead?**
|
||||
Please, give full error messages and/or log.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here. If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**How to reproduce?**
|
||||
Tell us how to reproduce this issue. How can someone who is starting from scratch reproduce this behaviour as minimally as possible?
|
||||
|
||||
**Files**
|
||||
A list of relevant files for this issue. Large files can be uploaded one-by-one or in a tarball/zipfile.
|
||||
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
Add a clear and concise description of what the problem is. E.g. *I'm always frustrated when [...]*
|
||||
|
||||
**Describe the solution you'd like**
|
||||
Add a clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
Add a clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
**Description**
|
||||
Please explain the changes you made here.
|
||||
If the feature changes current behaviour, explain why your solution is better.
|
||||
|
||||
:rotating_light: Before submitting your PR, please read [community](https://github.com/filebrowser/community), and indicate which issues (in any of the repos) are either fixed or closed by this PR. See [GitHub Help: Closing issues using keywords](https://help.github.com/articles/closing-issues-via-commit-messages/).
|
||||
|
||||
- [ ] DO make sure you are requesting to **pull a topic/feature/bugfix branch** (right side). Don't request your master!
|
||||
- [ ] DO make sure you are making a pull request against the **master branch** (left side). Also you should start *your branch* off *our master*.
|
||||
- [ ] DO make sure that File Browser can be successfully built. See [builds](https://github.com/filebrowser/community/blob/master/builds.md) and [development](https://github.com/filebrowser/community/blob/master/development.md).
|
||||
- [ ] DO make sure that related issues are opened in other repositories. I.e., the frontend, caddy plugins or the web page need to be updated accordingly.
|
||||
- [ ] AVOID breaking the continuous integration build.
|
||||
|
||||
**Further comments**
|
||||
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did, what alternatives you considered, etc.
|
||||
|
||||
:heart: Thank you!
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,12 +1,15 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
*/dist/*
|
||||
*.db
|
||||
*.db.lock
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.idea
|
||||
.vscode
|
||||
Dockerfile
|
||||
filebrowser
|
||||
rice-box.go
|
||||
vendor
|
||||
npm-debug.log*
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
yarn.lock
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "frontend"]
|
||||
path = frontend
|
||||
url = https://github.com/filebrowser/frontend
|
||||
38
.gometalinter.json
Normal file
38
.gometalinter.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"Enable": [
|
||||
"deadcode",
|
||||
"gotype",
|
||||
"gotypex",
|
||||
"ineffassign",
|
||||
"interfacer",
|
||||
"maligned",
|
||||
"megacheck",
|
||||
"structcheck",
|
||||
"unconvert",
|
||||
"varcheck",
|
||||
"vet"
|
||||
],
|
||||
"Disable": [
|
||||
"dupl",
|
||||
"errcheck",
|
||||
"gochecknoglobals",
|
||||
"gochecknoinits",
|
||||
"goconst",
|
||||
"gocyclo",
|
||||
"gofmt",
|
||||
"goimports",
|
||||
"golint",
|
||||
"gosec",
|
||||
"gosimple",
|
||||
"lll",
|
||||
"misspell",
|
||||
"nakedret",
|
||||
"safesql",
|
||||
"staticcheck",
|
||||
"test",
|
||||
"testify",
|
||||
"unparam",
|
||||
"unused",
|
||||
"vetshadow"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
project_name: filebrowser
|
||||
|
||||
build:
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: cmd/filebrowser/main.go
|
||||
binary: filebrowser
|
||||
goos:
|
||||
@@ -15,23 +19,41 @@ build:
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
- mips
|
||||
- mips64
|
||||
- mipsle
|
||||
- mips64le
|
||||
goarm:
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
goarm: 6
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
goarm: 6
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: netbsd
|
||||
goarch: arm
|
||||
- goos: solaris
|
||||
goarch: arm
|
||||
|
||||
archive:
|
||||
name_template: "{{.Os}}-{{.Arch}}-{{ .ProjectName }}"
|
||||
name_template: "{{.Os}}-{{.Arch}}{{if .Arm}}v{{.Arm}}{{end}}-{{ .ProjectName }}"
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
|
||||
release:
|
||||
disable: true
|
||||
|
||||
dockers:
|
||||
-
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
goarm: ''
|
||||
image: filebrowser/filebrowser
|
||||
skip_push: true
|
||||
tag_templates:
|
||||
- "{{ .Tag }}"
|
||||
- latest
|
||||
extra_files:
|
||||
- Docker.json
|
||||
|
||||
52
.travis.yml
Normal file
52
.travis.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
os: linux
|
||||
services: docker
|
||||
language: minimal
|
||||
install: skip
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- docker-ce
|
||||
- pass
|
||||
env:
|
||||
global:
|
||||
- USE_DOCKER="true"
|
||||
- WDIR=/go/src/github.com/filebrowser/filebrowser
|
||||
stages:
|
||||
- lint
|
||||
- test
|
||||
- release
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
- rice-box.go
|
||||
jobs:
|
||||
include:
|
||||
- stage: lint
|
||||
script: "./build/run_gometalinter.sh"
|
||||
- stage: test
|
||||
script: "./build/build_all.sh"
|
||||
deploy:
|
||||
provider: script
|
||||
skip_cleanup: true
|
||||
script: docker build -t filebrowser/filebrowser . && ./build/docker_login.sh && docker push filebrowser/filebrowser && docker logout
|
||||
on:
|
||||
tags: false
|
||||
repo: filebrowser/filebrowser
|
||||
branch: master
|
||||
- stage: release
|
||||
script:
|
||||
- docker run --rm -itv $(pwd):$WDIR -v /var/run/docker.sock:/var/run/docker.sock filebrowser/dev goreleaser
|
||||
- ./build/push_images.sh
|
||||
- ./build/push_ricebox.sh
|
||||
if: tag IS present
|
||||
deploy:
|
||||
provider: releases
|
||||
skip_cleanup: true
|
||||
api_key:
|
||||
secure: GCURbl9xmjOmeNc7cYSvfSwbEp46cacWmJRczcsU6rQa0aWqzjELYdyIsl6HWW+o0dzuZvbWRD6muxYqIud92oPLYDuXSnra9tM3mCjswrjiPCJ57bksWkSPBfFQcxIyB6c3o+A/FMnX3nnSE/2r5HYZnPNFbEcBbC7WSgwx9ejXUuyWn1PUFK9YQWANdl6J7b7EKsk+9MxS9Pmw6M2ycBwX8ScUQdofkUPvR/nqlXISm+3hs30VubqQi9Ha6DM9Bw3aFK3/Ts/ujCOxP1ZoMCBZ6tfnaQOElIG96WTwnt77eDYlZezBOLym3Z18iif+Qny+XndFKDbexaiUT06VlWFXCKtt3iLs6HJwRcjmiHmB0Z3v+W4cKPl3cEyxxrU2aal54k1PBhU+5L0Xc8ileKbDMYg5tps88zWHNefeZVfaxYSVrmUHkuygMe481oaBLacDXTxs4t6XEpStREuLmvx9NLTwTFAbWjMNM0PqlueDMxO4bdwNvzXg/TcKLWV9FezqAlre8lFNZK5wX6lKFVSZ3hFjxCfwrJL2cPwg5A8Yd5EOC4Nh81WdgYuFGOxZzMAoSJlaVRvQS1trCUP/++ONnDep3ExSxvw4B7vijGZWeXUhrOMiPQHXu+t6BnrlnDjQ4gi44QTW0y/iM2WC2DBKfgYjAKwyHx13hFrmOCg=
|
||||
file: "dist/*.*"
|
||||
file_glob: true
|
||||
on:
|
||||
repo: filebrowser/filebrowser
|
||||
all_branches: true
|
||||
tags: true
|
||||
36
Dockerfile
36
Dockerfile
@@ -1,24 +1,12 @@
|
||||
|
||||
FROM golang:alpine
|
||||
|
||||
COPY . /go/src/github.com/filebrowser/filebrowser
|
||||
|
||||
WORKDIR /go/src/github.com/filebrowser/filebrowser
|
||||
RUN apk add --no-cache git
|
||||
RUN go get ./...
|
||||
|
||||
WORKDIR /go/src/github.com/filebrowser/filebrowser/cmd/filebrowser
|
||||
RUN CGO_ENABLED=0 go build -a
|
||||
RUN mv filebrowser /go/bin/filebrowser
|
||||
|
||||
FROM scratch
|
||||
COPY --from=0 /go/bin/filebrowser /filebrowser
|
||||
|
||||
VOLUME /tmp
|
||||
VOLUME /srv
|
||||
EXPOSE 80
|
||||
|
||||
COPY Docker.json /config.json
|
||||
|
||||
ENTRYPOINT ["/filebrowser"]
|
||||
CMD ["--config", "/config.json"]
|
||||
FROM scratch
|
||||
|
||||
COPY --from=filebrowser/dev /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
VOLUME /tmp
|
||||
VOLUME /srv
|
||||
EXPOSE 80
|
||||
|
||||
COPY filebrowser /filebrowser
|
||||
COPY Docker.json /config.json
|
||||
|
||||
ENTRYPOINT ["/filebrowser", "--config", "/config.json"]
|
||||
|
||||
429
Gopkg.lock
generated
Normal file
429
Gopkg.lock
generated
Normal file
@@ -0,0 +1,429 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/BurntSushi/toml"
|
||||
packages = ["."]
|
||||
revision = "a368813c5e648fee92e5f6c30e3944ff9d5e8895"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/GeertJohan/go.rice"
|
||||
packages = [
|
||||
".",
|
||||
"embedded"
|
||||
]
|
||||
revision = "c02ca9a983da5807ddf7d796784928f5be4afd09"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/aead/chacha20"
|
||||
packages = [
|
||||
".",
|
||||
"chacha"
|
||||
]
|
||||
revision = "c8d29375923a8e1d2a0f0dc0fc1d8a0aba5b97ba"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/asdine/storm"
|
||||
packages = [
|
||||
".",
|
||||
"codec",
|
||||
"codec/json",
|
||||
"index",
|
||||
"internal",
|
||||
"q"
|
||||
]
|
||||
revision = "68fc73b635f890fe7ba2f3b15ce80c85b28a744f"
|
||||
version = "v2.0.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/bifurcation/mint"
|
||||
packages = [
|
||||
".",
|
||||
"syntax"
|
||||
]
|
||||
revision = "340be3ae8c0ff8edce24cf59e7acdb1432bd5ce5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/chaseadamsio/goorgeous"
|
||||
packages = ["."]
|
||||
revision = "dcf1ef873b8987bf12596fe6951c48347986eb2f"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/codahale/aesnicheck"
|
||||
packages = ["."]
|
||||
revision = "349fcc471aaccc29cd074e1275f1a494323826cd"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/coreos/bbolt"
|
||||
packages = ["."]
|
||||
revision = "583e8937c61f1af6513608ccc75c97b6abdf4ff9"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/daaku/go.zipexe"
|
||||
packages = ["."]
|
||||
revision = "a5fe2436ffcb3236e175e5149162b41cd28bd27d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = [
|
||||
".",
|
||||
"request"
|
||||
]
|
||||
revision = "dbeaa9332f19a944acb5736b4456cfcc02140e29"
|
||||
version = "v3.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/dsnet/compress"
|
||||
packages = [
|
||||
".",
|
||||
"bzip2",
|
||||
"bzip2/internal/sais",
|
||||
"internal",
|
||||
"internal/errors",
|
||||
"internal/prefix"
|
||||
]
|
||||
revision = "cc9eb1d7ad760af14e8f918698f745e80377af4f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/flynn/go-shlex"
|
||||
packages = ["."]
|
||||
revision = "3f9db97f856818214da2e1057f8ad84803971cff"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gohugoio/hugo"
|
||||
packages = ["parser"]
|
||||
revision = "25e88ccabe9b04c42ffb43528c86743f623fac46"
|
||||
version = "v0.36.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/snappy"
|
||||
packages = ["."]
|
||||
revision = "553a641470496b2327abcac10b36396bd98e45c9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hacdias/fileutils"
|
||||
packages = ["."]
|
||||
revision = "76b1c6ab906773727a1ce2f7fb22830685166f85"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hacdias/varutils"
|
||||
packages = ["."]
|
||||
revision = "82d3b57f667a756cfc4b1535951b46878881f3e1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-syslog"
|
||||
packages = ["."]
|
||||
revision = "326bf4a7f709d263f964a6a96558676b103f3534"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/golang-lru"
|
||||
packages = [
|
||||
".",
|
||||
"simplelru"
|
||||
]
|
||||
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
"json/parser",
|
||||
"json/scanner",
|
||||
"json/token"
|
||||
]
|
||||
revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kardianos/osext"
|
||||
packages = ["."]
|
||||
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lucas-clemente/aes12"
|
||||
packages = ["."]
|
||||
revision = "cd47fb39b79f867c6e4e5cd39cf7abd799f71670"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lucas-clemente/fnv128a"
|
||||
packages = ["."]
|
||||
revision = "393af48d391698c6ae4219566bfbdfef67269997"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/lucas-clemente/quic-go"
|
||||
packages = [
|
||||
".",
|
||||
"h2quic",
|
||||
"internal/ackhandler",
|
||||
"internal/congestion",
|
||||
"internal/crypto",
|
||||
"internal/flowcontrol",
|
||||
"internal/handshake",
|
||||
"internal/protocol",
|
||||
"internal/utils",
|
||||
"internal/wire",
|
||||
"qerr"
|
||||
]
|
||||
revision = "30851b9a3b4e958490b476fe72adafa67641361a"
|
||||
version = "v0.7.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lucas-clemente/quic-go-certificates"
|
||||
packages = ["."]
|
||||
revision = "d2f86524cced5186554df90d92529757d22c1cb6"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/magiconair/properties"
|
||||
packages = ["."]
|
||||
revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
|
||||
version = "v1.7.6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/maruel/natural"
|
||||
packages = ["."]
|
||||
revision = "dbcb3e2e8cf10eb839718ba666ef1e21b1c8b847"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mholt/archiver"
|
||||
packages = ["."]
|
||||
revision = "26cf5bb32d07aa4e8d0de15f56ce516f4641d7df"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mholt/caddy"
|
||||
packages = [
|
||||
".",
|
||||
"caddyfile",
|
||||
"caddyhttp/httpserver",
|
||||
"caddyhttp/staticfiles",
|
||||
"caddytls"
|
||||
]
|
||||
revision = "d3f338ddab9ee24b376b5c9c51e202581e2d43ba"
|
||||
version = "v0.10.11"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/miekg/dns"
|
||||
packages = ["."]
|
||||
revision = "5364553f1ee9cddc7ac8b62dce148309c386695b"
|
||||
version = "v1.0.4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/nwaples/rardecode"
|
||||
packages = ["."]
|
||||
revision = "e06696f847aeda6f39a8f0b7cdff193b7690aef6"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pierrec/lz4"
|
||||
packages = ["."]
|
||||
revision = "2fcda4cb7018ce05a25959d2fe08c83e3329f169"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pierrec/xxHash"
|
||||
packages = ["xxHash32"]
|
||||
revision = "f051bb7f1d1aaf1b5a665d74fb6b0217712c69f7"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/robfig/cron"
|
||||
packages = ["."]
|
||||
revision = "b024fc5ea0e34bc3f83d9941c8d60b0622bfaca4"
|
||||
version = "v1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
packages = ["."]
|
||||
revision = "4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c"
|
||||
version = "v1.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/shurcooL/sanitized_anchor_name"
|
||||
packages = ["."]
|
||||
revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
"mem"
|
||||
]
|
||||
revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c"
|
||||
version = "v1.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cast"
|
||||
packages = ["."]
|
||||
revision = "8965335b8c7107321228e3e3702cab9832751bac"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/viper"
|
||||
packages = ["."]
|
||||
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ulikunitz/xz"
|
||||
packages = [
|
||||
".",
|
||||
"internal/hash",
|
||||
"internal/xlog",
|
||||
"lzma"
|
||||
]
|
||||
revision = "0c6b41e72360850ca4f98dc341fd999726ea007f"
|
||||
version = "v0.5.4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/xenolf/lego"
|
||||
packages = ["acme"]
|
||||
revision = "67c86d860a797ce2483f50d9174d4ed24984bef2"
|
||||
version = "v0.4.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"bcrypt",
|
||||
"blowfish",
|
||||
"curve25519",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"hkdf",
|
||||
"ocsp"
|
||||
]
|
||||
revision = "49796115aa4b964c318aad4f3084fdb41e9aa067"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"bpf",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
"internal/iana",
|
||||
"internal/socket",
|
||||
"ipv4",
|
||||
"ipv6",
|
||||
"lex/httplex",
|
||||
"publicsuffix"
|
||||
]
|
||||
revision = "cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "88d2dcc510266da9f7f8c7f34e1940716cab5f5c"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"language",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable"
|
||||
]
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/natefinch/lumberjack.v2"
|
||||
packages = ["."]
|
||||
revision = "a96e63847dc3c67d17befa69c303767e2f84e54f"
|
||||
version = "v2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/square/go-jose.v1"
|
||||
packages = [
|
||||
".",
|
||||
"cipher",
|
||||
"json"
|
||||
]
|
||||
revision = "aa2e30fdd1fe9dd3394119af66451ae790d50e0d"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "7f97868eec74b32b0982dd158a51a446d1da7eb5"
|
||||
version = "v2.1.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "1f68bed4490068370884abaa296aa651c27b3f2e0d0451a1ab567f059d44f820"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
74
Gopkg.toml
Normal file
74
Gopkg.toml
Normal file
@@ -0,0 +1,74 @@
|
||||
[[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
|
||||
159
README.md
159
README.md
@@ -1,75 +1,84 @@
|
||||

|
||||
|
||||
# filebrowser
|
||||
|
||||
[](https://circleci.com/gh/filebrowser/filebrowser)
|
||||
[](https://goreportcard.com/report/github.com/filebrowser/filebrowser)
|
||||
[](http://godoc.org/github.com/filebrowser/filebrowser)
|
||||
[](https://github.com/filebrowser/filebrowser/releases/latest)
|
||||
|
||||
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.
|
||||
|
||||
# Table of contents
|
||||
|
||||
+ [Getting started](#getting-started)
|
||||
+ [Features](#features)
|
||||
- [Users](#users)
|
||||
- [Search](#search)
|
||||
+ [Contributing](#contributing)
|
||||
+ [Donate](#donate)
|
||||
|
||||
# Getting started
|
||||
|
||||
You can find the Getting Started guide on the [documentation](https://filebrowser.github.io/quick-start/).
|
||||
|
||||
# Features
|
||||
|
||||
Easy login system.
|
||||
|
||||

|
||||
|
||||
Listings of your files, available in two styles: mosaic and list. You can delete, move, rename, upload and create new files, as well as directories. Single files can be downloaded directly, and multiple files as *.zip*, *.tar*, *.tar.gz*, *.tar.bz2* or *.tar.xz*.
|
||||
|
||||

|
||||
|
||||
File Manager editor is powered by [Codemirror](https://codemirror.net/) and if you're working with markdown files with metadata, both parts will be separated from each other so you can focus on the content.
|
||||
|
||||

|
||||
|
||||
On the settings page, a regular user can set its own custom CSS to personalize the experience and change its password. For admins, they can manage the permissions of each user, set commands which can be executed when certain events are triggered (such as before saving and after saving) and change plugin's settings.
|
||||
|
||||

|
||||
|
||||
We also allow the users to search in the directories and execute commands if allowed.
|
||||
|
||||
## Users
|
||||
|
||||
We support multiple users and each user can have its own scope and custom stylesheet. The administrator is able to choose which permissions should be given to the users, as well as the commands they can execute. Each user also have a set of rules, in which he can be prevented or allowed to access some directories (regular expressions included!).
|
||||
|
||||

|
||||
|
||||
## Search
|
||||
|
||||
File Browser allows you to search through your files and it has some options. By default, your search will be something like this:
|
||||
|
||||
```
|
||||
this are keywords
|
||||
```
|
||||
|
||||
If you search for that it will look at every file that contains "this", "are" or "keywords" on their name. If you want to search for an exact term, you should surround your search by double quotes:
|
||||
|
||||
```
|
||||
"this is the name"
|
||||
```
|
||||
|
||||
That will search for any file that contains "this is the name" on its name. It won't search for each separated term this time.
|
||||
|
||||
By default, every search will be case sensitive. Although, you can make a case insensitive search by adding `case:insensitive` to the search terms, like this:
|
||||
|
||||
```
|
||||
this are keywords case:insensitive
|
||||
```
|
||||
|
||||
# Contributing
|
||||
|
||||
The contributing guidelines can be found [here](https://github.com/filebrowser/community).
|
||||
**In Q2 2018, this project was renamed from `filemanager` to `filebrowser`, a new GitHub organization was created and the main repo was moved from [hacdias/filemanager](https://github.com/hacdias/filemanager) to [filebrowser/filebrowser](https://github.com/filebrowser/filebrowser). Old refs will work, because permanent redirects are set up. However, we suggest every contributor to update the remote URLs in their local clones. See [Changing a remote's URL](https://help.github.com/articles/changing-a-remote-s-url/).**
|
||||
|
||||
**Moreover, the previously unique repo was split into multiple pieces: frontend, logo, caddy... At the same time, the official docker image was changed to [`filebrowser/filebrowser`](https://hub.docker.com/r/filebrowser/filebrowser/). Users are encouraged to check their sources and update them accordingly.**
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/filebrowser/logo/master/banner.png" width="550"/>
|
||||
</p>
|
||||
|
||||

|
||||
|
||||
# filebrowser
|
||||
|
||||
[](https://circleci.com/gh/filebrowser/filebrowser)
|
||||
[](https://goreportcard.com/report/github.com/filebrowser/filebrowser)
|
||||
[](http://godoc.org/github.com/filebrowser/filebrowser)
|
||||
[](https://github.com/filebrowser/filebrowser/releases/latest)
|
||||
[](http://webchat.freenode.net/?channels=%23filebrowser)
|
||||
|
||||
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.
|
||||
|
||||
# Table of contents
|
||||
|
||||
+ [Getting started](#getting-started)
|
||||
+ [Features](#features)
|
||||
- [Users](#users)
|
||||
- [Search](#search)
|
||||
+ [Contributing](#contributing)
|
||||
+ [Donate](#donate)
|
||||
|
||||
# Getting started
|
||||
|
||||
You can find the Getting Started guide on the [documentation](https://filebrowser.github.io/quick-start/).
|
||||
|
||||
# Features
|
||||
|
||||
Easy login system.
|
||||
|
||||

|
||||
|
||||
Listings of your files, available in two styles: mosaic and list. You can delete, move, rename, upload and create new files, as well as directories. Single files can be downloaded directly, and multiple files as *.zip*, *.tar*, *.tar.gz*, *.tar.bz2* or *.tar.xz*.
|
||||
|
||||

|
||||
|
||||
File Browser editor is powered by [Codemirror](https://codemirror.net/) and if you're working with markdown files with metadata, both parts will be separated from each other so you can focus on the content.
|
||||
|
||||

|
||||
|
||||
On the settings page, a regular user can set its own custom CSS to personalize the experience and change its password. For admins, they can manage the permissions of each user, set commands which can be executed when certain events are triggered (such as before saving and after saving) and change plugin's settings.
|
||||
|
||||

|
||||
|
||||
We also allow the users to search in the directories and execute commands if allowed.
|
||||
|
||||
## Users
|
||||
|
||||
We support multiple users and each user can have its own scope and custom stylesheet. The administrator is able to choose which permissions should be given to the users, as well as the commands they can execute. Each user also have a set of rules, in which he can be prevented or allowed to access some directories (regular expressions included!).
|
||||
|
||||

|
||||
|
||||
## Search
|
||||
|
||||
File Browser allows you to search through your files and it has some options. By default, your search will be something like this:
|
||||
|
||||
```
|
||||
this are keywords
|
||||
```
|
||||
|
||||
If you search for that it will look at every file that contains "this", "are" or "keywords" on their name. If you want to search for an exact term, you should surround your search by double quotes:
|
||||
|
||||
```
|
||||
"this is the name"
|
||||
```
|
||||
|
||||
That will search for any file that contains "this is the name" on its name. It won't search for each separated term this time.
|
||||
|
||||
By default, every search will be case insensitive. Although, you can make a case sensitive search by adding `case:sensitive` to the search terms, like this:
|
||||
|
||||
```
|
||||
this are keywords case:sensitive
|
||||
```
|
||||
|
||||
# Contributing
|
||||
|
||||
The contributing guidelines can be found [here](https://github.com/filebrowser/community).
|
||||
|
||||
@@ -2,7 +2,7 @@ package bolt
|
||||
|
||||
import (
|
||||
"github.com/asdine/storm"
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
)
|
||||
|
||||
// ConfigStore is a configuration store.
|
||||
@@ -14,7 +14,7 @@ type ConfigStore struct {
|
||||
func (c ConfigStore) Get(name string, to interface{}) error {
|
||||
err := c.DB.Get("config", name, to)
|
||||
if err == storm.ErrNotFound {
|
||||
return fm.ErrNotExist
|
||||
return fb.ErrNotExist
|
||||
}
|
||||
|
||||
return err
|
||||
|
||||
@@ -3,7 +3,7 @@ package bolt
|
||||
import (
|
||||
"github.com/asdine/storm"
|
||||
"github.com/asdine/storm/q"
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
)
|
||||
|
||||
// ShareStore is a shareable links store.
|
||||
@@ -12,55 +12,55 @@ type ShareStore struct {
|
||||
}
|
||||
|
||||
// Get gets a Share Link from an hash.
|
||||
func (s ShareStore) Get(hash string) (*fm.ShareLink, error) {
|
||||
var v fm.ShareLink
|
||||
func (s ShareStore) Get(hash string) (*fb.ShareLink, error) {
|
||||
var v fb.ShareLink
|
||||
err := s.DB.One("Hash", hash, &v)
|
||||
if err == storm.ErrNotFound {
|
||||
return nil, fm.ErrNotExist
|
||||
return nil, fb.ErrNotExist
|
||||
}
|
||||
|
||||
return &v, err
|
||||
}
|
||||
|
||||
// GetPermanent gets the permanent link from a path.
|
||||
func (s ShareStore) GetPermanent(path string) (*fm.ShareLink, error) {
|
||||
var v fm.ShareLink
|
||||
func (s ShareStore) GetPermanent(path string) (*fb.ShareLink, error) {
|
||||
var v fb.ShareLink
|
||||
err := s.DB.Select(q.Eq("Path", path), q.Eq("Expires", false)).First(&v)
|
||||
if err == storm.ErrNotFound {
|
||||
return nil, fm.ErrNotExist
|
||||
return nil, fb.ErrNotExist
|
||||
}
|
||||
|
||||
return &v, err
|
||||
}
|
||||
|
||||
// GetByPath gets all the links for a specific path.
|
||||
func (s ShareStore) GetByPath(hash string) ([]*fm.ShareLink, error) {
|
||||
var v []*fm.ShareLink
|
||||
func (s ShareStore) GetByPath(hash string) ([]*fb.ShareLink, error) {
|
||||
var v []*fb.ShareLink
|
||||
err := s.DB.Find("Path", hash, &v)
|
||||
if err == storm.ErrNotFound {
|
||||
return v, fm.ErrNotExist
|
||||
return v, fb.ErrNotExist
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Gets retrieves all the shareable links.
|
||||
func (s ShareStore) Gets() ([]*fm.ShareLink, error) {
|
||||
var v []*fm.ShareLink
|
||||
func (s ShareStore) Gets() ([]*fb.ShareLink, error) {
|
||||
var v []*fb.ShareLink
|
||||
err := s.DB.All(&v)
|
||||
if err == storm.ErrNotFound {
|
||||
return v, fm.ErrNotExist
|
||||
return v, fb.ErrNotExist
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Save stores a Share Link on the database.
|
||||
func (s ShareStore) Save(l *fm.ShareLink) error {
|
||||
func (s ShareStore) Save(l *fb.ShareLink) error {
|
||||
return s.DB.Save(l)
|
||||
}
|
||||
|
||||
// Delete deletes a Share Link from the database.
|
||||
func (s ShareStore) Delete(hash string) error {
|
||||
return s.DB.DeleteStruct(&fm.ShareLink{Hash: hash})
|
||||
return s.DB.DeleteStruct(&fb.ShareLink{Hash: hash})
|
||||
}
|
||||
|
||||
12
build/build.sh
Executable file
12
build/build.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
dep ensure -vendor-only
|
||||
|
||||
cd cmd/filebrowser
|
||||
CGO_ENABLED=0 go build -a
|
||||
cd ../..
|
||||
cp cmd/filebrowser/filebrowser ./
|
||||
47
build/build_all.sh
Executable file
47
build/build_all.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
if [ -d "rice-box.go" ]; then
|
||||
rm -rf rice-box.go
|
||||
fi
|
||||
|
||||
if [ "$USE_DOCKER" != "" ]; then
|
||||
if [ -d "frontend/dist" ]; then
|
||||
rm -rf frontend/dist
|
||||
fi;
|
||||
|
||||
if [ "$WDIR" = "" ]; then
|
||||
WDIR="/go/src/github.com/filebrowser/filebrowser"
|
||||
fi;
|
||||
|
||||
$(command -v winpty) docker run -it \
|
||||
--name filebrowser-tmp \
|
||||
-v /$(pwd):/src:z \
|
||||
-w /${WDIR} \
|
||||
filebrowser/dev \
|
||||
sh -c "\
|
||||
cp -r //src/* /$WDIR && \
|
||||
cd build && \
|
||||
dos2unix build_assets.sh && \
|
||||
dos2unix build.sh && \
|
||||
./build_assets.sh && \
|
||||
./build.sh \
|
||||
"
|
||||
exitcode=$?
|
||||
|
||||
if [ $exitcode -eq 0 ]; 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
|
||||
fi
|
||||
docker rm -f filebrowser-tmp
|
||||
else
|
||||
./build/build_assets.sh
|
||||
./build/build.sh
|
||||
fi
|
||||
18
build.sh → build/build_assets.sh
Normal file → Executable file
18
build.sh → build/build_assets.sh
Normal file → Executable file
@@ -1,14 +1,22 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
# Clean the dist folder and build the assets
|
||||
cd frontend
|
||||
if [ -d "dist" ]; then
|
||||
rm -rf dist/*
|
||||
fi;
|
||||
yarn install
|
||||
yarn build
|
||||
cd ..
|
||||
|
||||
# Install rice tool if not present
|
||||
if ! [ -x "$(command -v rice)" ]; then
|
||||
go get github.com/GeertJohan/go.rice/rice
|
||||
fi
|
||||
|
||||
# Clean the dist folder and build the assets
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
|
||||
# Embed the assets using rice
|
||||
rice embed-go
|
||||
BIN
build/deploy_key.enc
Normal file
BIN
build/deploy_key.enc
Normal file
Binary file not shown.
27
build/docker_login.sh
Executable file
27
build/docker_login.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# init key for pass
|
||||
gpg --batch --gen-key <<-EOF
|
||||
%echo Generating a standard key
|
||||
Key-Type: DSA
|
||||
Key-Length: 1024
|
||||
Subkey-Type: ELG-E
|
||||
Subkey-Length: 1024
|
||||
Name-Real: Meshuggah Rocks
|
||||
Name-Email: meshuggah@example.com
|
||||
Expire-Date: 0
|
||||
# Do a commit here, so that we can later print "done" :-)
|
||||
%commit
|
||||
%echo done
|
||||
EOF
|
||||
|
||||
key=$(gpg --no-auto-check-trustdb --list-secret-keys | grep ^sec | cut -d/ -f2 | cut -d" " -f1)
|
||||
pass init $key
|
||||
|
||||
if [ "$(command -v docker-credential-pass)" = "" ]; then
|
||||
docker run --rm -itv /usr/local/bin:/src filebrowser/dev sh -c "cp /go/bin/docker-credential-pass /src"
|
||||
fi
|
||||
|
||||
echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin
|
||||
14
build/push_images.sh
Executable file
14
build/push_images.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
./docker_login.sh
|
||||
|
||||
for tag in `echo $(docker images filebrowser/filebrowser* | awk -F ' ' '{print $1 ":" $2}') | cut -d ' ' -f2-`; do
|
||||
if [ "$tag" = "REPOSITORY:TAG" ]; then break; fi
|
||||
docker push $tag
|
||||
done
|
||||
|
||||
docker logout
|
||||
36
build/push_ricebox.sh
Executable file
36
build/push_ricebox.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
COMMIT_SHA="$(git rev-parse --verify HEAD | cut -c1-8)"
|
||||
|
||||
eval `ssh-agent -s`
|
||||
openssl aes-256-cbc -K $encrypted_9ca81b5594f5_key -iv $encrypted_9ca81b5594f5_iv -in ./deploy_key.enc -d | ssh-add -
|
||||
|
||||
git clone git@github.com:filebrowser/caddy caddy
|
||||
cd caddy
|
||||
cp ../../rice-box.go assets/
|
||||
git checkout -b update-rice-box origin/master
|
||||
git commit -am "update rice-box $COMMIT_SHA"
|
||||
|
||||
if [ $(git tag | grep "$TRAVIS_TAG" | wc -l) -ne 0 ]; then
|
||||
git tag -d "$TRAVIS_TAG"
|
||||
fi
|
||||
|
||||
git tag "$TRAVIS_TAG"
|
||||
|
||||
if [ "$(git ls-remote --heads origin update-rice-box)" != "" ]; then
|
||||
git push -u origin update-rice-box
|
||||
else
|
||||
git push origin +update-rice-box
|
||||
fi
|
||||
|
||||
if [ "$(git ls-remote --heads origin update-rice-box)" != "" ]; then
|
||||
git push origin "$TRAVIS_TAG"
|
||||
else
|
||||
git push origin :"$TRAVIS_TAG"
|
||||
git push origin "$TRAVIS_TAG"
|
||||
fi
|
||||
|
||||
55
build/release.sh
Executable file
55
build/release.sh
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
echo "> Checking semver format"
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "This release script requires a single argument corresponding to the semver to be released. See semver.org"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
semver=$(grep -P '^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)' <<< "$1")
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Not valid semver format. See semver.org"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "> Checking matching $semver in frontend submodule"
|
||||
|
||||
cd frontend
|
||||
git fetch --all
|
||||
|
||||
if [ $(git tag | grep "$semver" | wc -l) -eq 0 ]; then
|
||||
echo "Tag $semver does not exist in submodule 'frontend'. Tag it and run this script again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git rev-parse --verify --quiet release
|
||||
if [ $? -ne 0 ]; then
|
||||
git checkout -b release "$semver"
|
||||
else
|
||||
git checkout release
|
||||
git reset --hard "$semver"
|
||||
fi
|
||||
|
||||
cd ..
|
||||
|
||||
echo "> Updating submodule ref to $semver"
|
||||
|
||||
sed -i "s|(untracked)|$1|g" filebrowser.go
|
||||
git commit -am "chore: version $semver"
|
||||
git tag "$1"
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
echo "> Commiting untracked version notice..."
|
||||
|
||||
sed -i "s|$1|(untracked)|g" filebrowser.go
|
||||
git commit -am "chore: setting untracked version [ci skip]"
|
||||
git push
|
||||
|
||||
echo "> Done!"
|
||||
15
build/run_gometalinter.sh
Executable file
15
build/run_gometalinter.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
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 && \
|
||||
CGO_ENABLED=0 $dolint"
|
||||
else
|
||||
$dolint
|
||||
fi
|
||||
@@ -1,52 +0,0 @@
|
||||
package filemanager
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/filebrowser/filebrowser"
|
||||
"github.com/filebrowser/filebrowser/caddy/parser"
|
||||
h "github.com/filebrowser/filebrowser/http"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("filemanager", caddy.Plugin{
|
||||
ServerType: "http",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
type plugin struct {
|
||||
Next httpserver.Handler
|
||||
Configs []*filebrowser.FileBrowser
|
||||
}
|
||||
|
||||
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
|
||||
func (f plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
for i := range f.Configs {
|
||||
// Checks if this Path should be handled by File Manager.
|
||||
if !httpserver.Path(r.URL.Path).Matches(f.Configs[i].BaseURL) {
|
||||
continue
|
||||
}
|
||||
|
||||
h.Handler(f.Configs[i]).ServeHTTP(w, r)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return f.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// setup configures a new FileManager middleware instance.
|
||||
func setup(c *caddy.Controller) error {
|
||||
configs, err := parser.Parse(c, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
|
||||
return plugin{Configs: configs, Next: next}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/filebrowser/filebrowser"
|
||||
"github.com/filebrowser/filebrowser/caddy/parser"
|
||||
h "github.com/filebrowser/filebrowser/http"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("hugo", caddy.Plugin{
|
||||
ServerType: "http",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
type plugin struct {
|
||||
Next httpserver.Handler
|
||||
Configs []*filebrowser.FileBrowser
|
||||
}
|
||||
|
||||
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
|
||||
func (f plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
for i := range f.Configs {
|
||||
// Checks if this Path should be handled by File Manager.
|
||||
if !httpserver.Path(r.URL.Path).Matches(f.Configs[i].BaseURL) {
|
||||
continue
|
||||
}
|
||||
|
||||
h.Handler(f.Configs[i]).ServeHTTP(w, r)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return f.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// setup configures a new FileManager middleware instance.
|
||||
func setup(c *caddy.Controller) error {
|
||||
configs, err := parser.Parse(c, "hugo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
|
||||
return plugin{Configs: configs, Next: next}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package jekyll
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/filebrowser/filebrowser"
|
||||
"github.com/filebrowser/filebrowser/caddy/parser"
|
||||
h "github.com/filebrowser/filebrowser/http"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("jekyll", caddy.Plugin{
|
||||
ServerType: "http",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
type plugin struct {
|
||||
Next httpserver.Handler
|
||||
Configs []*filebrowser.FileBrowser
|
||||
}
|
||||
|
||||
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
|
||||
func (f plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
for i := range f.Configs {
|
||||
// Checks if this Path should be handled by File Manager.
|
||||
if !httpserver.Path(r.URL.Path).Matches(f.Configs[i].BaseURL) {
|
||||
continue
|
||||
}
|
||||
|
||||
h.Handler(f.Configs[i]).ServeHTTP(w, r)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return f.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// setup configures a new FileManager middleware instance.
|
||||
func setup(c *caddy.Controller) error {
|
||||
configs, err := parser.Parse(c, "jekyll")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
|
||||
return plugin{Configs: configs, Next: next}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/asdine/storm"
|
||||
"github.com/filebrowser/filebrowser"
|
||||
"github.com/filebrowser/filebrowser/bolt"
|
||||
"github.com/filebrowser/filebrowser/staticgen"
|
||||
"github.com/hacdias/fileutils"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
var databases = map[string]*storm.DB{}
|
||||
|
||||
// Parse ...
|
||||
func Parse(c *caddy.Controller, plugin string) ([]*filebrowser.FileBrowser, error) {
|
||||
var (
|
||||
configs []*filebrowser.FileBrowser
|
||||
err error
|
||||
)
|
||||
|
||||
for c.Next() {
|
||||
u := &filebrowser.User{
|
||||
Locale: "en",
|
||||
AllowCommands: true,
|
||||
AllowEdit: true,
|
||||
AllowNew: true,
|
||||
AllowPublish: true,
|
||||
Commands: []string{"git", "svn", "hg"},
|
||||
CSS: "",
|
||||
ViewMode: "mosaic",
|
||||
Rules: []*filebrowser.Rule{{
|
||||
Regex: true,
|
||||
Allow: false,
|
||||
Regexp: &filebrowser.Regexp{Raw: "\\/\\..+"},
|
||||
}},
|
||||
}
|
||||
|
||||
baseURL := "/"
|
||||
scope := "."
|
||||
database := ""
|
||||
noAuth := false
|
||||
reCaptchaKey := ""
|
||||
reCaptchaSecret := ""
|
||||
|
||||
if plugin != "" {
|
||||
baseURL = "/admin"
|
||||
}
|
||||
|
||||
// Get the baseURL and scope
|
||||
args := c.RemainingArgs()
|
||||
|
||||
if plugin == "" {
|
||||
if len(args) >= 1 {
|
||||
baseURL = args[0]
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
scope = args[1]
|
||||
}
|
||||
} else {
|
||||
if len(args) >= 1 {
|
||||
scope = args[0]
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
baseURL = args[1]
|
||||
}
|
||||
}
|
||||
|
||||
for c.NextBlock() {
|
||||
switch c.Val() {
|
||||
case "database":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
database = c.Val()
|
||||
case "locale":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
u.Locale = c.Val()
|
||||
case "allow_commands":
|
||||
if !c.NextArg() {
|
||||
u.AllowCommands = true
|
||||
continue
|
||||
}
|
||||
|
||||
u.AllowCommands, err = strconv.ParseBool(c.Val())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "allow_edit":
|
||||
if !c.NextArg() {
|
||||
u.AllowEdit = true
|
||||
continue
|
||||
}
|
||||
|
||||
u.AllowEdit, err = strconv.ParseBool(c.Val())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "allow_new":
|
||||
if !c.NextArg() {
|
||||
u.AllowNew = true
|
||||
continue
|
||||
}
|
||||
|
||||
u.AllowNew, err = strconv.ParseBool(c.Val())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "allow_publish":
|
||||
if !c.NextArg() {
|
||||
u.AllowPublish = true
|
||||
continue
|
||||
}
|
||||
|
||||
u.AllowPublish, err = strconv.ParseBool(c.Val())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "commands":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
u.Commands = strings.Split(c.Val(), " ")
|
||||
case "css":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
file := c.Val()
|
||||
css, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.CSS = string(css)
|
||||
case "view_mode":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
u.ViewMode = c.Val()
|
||||
if u.ViewMode != filebrowser.MosaicViewMode && u.ViewMode != filebrowser.ListViewMode {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
case "recaptcha_key":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
reCaptchaKey = c.Val()
|
||||
case "recaptcha_secret":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
reCaptchaSecret = c.Val()
|
||||
case "no_auth":
|
||||
if !c.NextArg() {
|
||||
noAuth = true
|
||||
continue
|
||||
}
|
||||
|
||||
noAuth, err = strconv.ParseBool(c.Val())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
caddyConf := httpserver.GetConfig(c)
|
||||
|
||||
path := filepath.Join(caddy.AssetsPath(), "filemanager")
|
||||
err := os.MkdirAll(path, 0700)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if there is a database path and it is not absolute,
|
||||
// it will be relative to Caddy folder.
|
||||
if !filepath.IsAbs(database) && database != "" {
|
||||
database = filepath.Join(path, database)
|
||||
}
|
||||
|
||||
// If there is no database path on the settings,
|
||||
// store one in .caddy/filemanager/name.db.
|
||||
if database == "" {
|
||||
// The name of the database is the hashed value of a string composed
|
||||
// by the host, address path and the baseurl of this File Manager
|
||||
// instance.
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(caddyConf.Addr.Host + caddyConf.Addr.Path + baseURL))
|
||||
sha := hex.EncodeToString(hasher.Sum(nil))
|
||||
database = filepath.Join(path, sha+".db")
|
||||
|
||||
fmt.Println("[WARNING] A database is going to be created for your File Manager instance at " + database +
|
||||
". It is highly recommended that you set the 'database' option to '" + sha + ".db'\n")
|
||||
}
|
||||
|
||||
u.Scope = scope
|
||||
u.FileSystem = fileutils.Dir(scope)
|
||||
|
||||
var db *storm.DB
|
||||
if stored, ok := databases[database]; ok {
|
||||
db = stored
|
||||
} else {
|
||||
db, err = storm.Open(database)
|
||||
databases[database] = db
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := &filebrowser.FileBrowser{
|
||||
NoAuth: noAuth,
|
||||
BaseURL: "",
|
||||
PrefixURL: "",
|
||||
ReCaptchaKey: reCaptchaKey,
|
||||
ReCaptchaSecret: reCaptchaSecret,
|
||||
DefaultUser: u,
|
||||
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 = m.Setup()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch plugin {
|
||||
case "hugo":
|
||||
// Initialize the default settings for Hugo.
|
||||
hugo := &staticgen.Hugo{
|
||||
Root: scope,
|
||||
Public: filepath.Join(scope, "public"),
|
||||
Args: []string{},
|
||||
CleanPublic: true,
|
||||
}
|
||||
|
||||
// Attaches Hugo plugin to this file manager instance.
|
||||
err = m.Attach(hugo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "jekyll":
|
||||
// Initialize the default settings for Jekyll.
|
||||
jekyll := &staticgen.Jekyll{
|
||||
Root: scope,
|
||||
Public: filepath.Join(scope, "_site"),
|
||||
Args: []string{},
|
||||
CleanPublic: true,
|
||||
}
|
||||
|
||||
// Attaches Hugo plugin to this file manager instance.
|
||||
err = m.Attach(jekyll)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.NoAuth = noAuth
|
||||
m.SetBaseURL(baseURL)
|
||||
m.SetPrefixURL(strings.TrimSuffix(caddyConf.Addr.Path, "/"))
|
||||
|
||||
configs = append(configs, m)
|
||||
}
|
||||
|
||||
return configs, nil
|
||||
}
|
||||
@@ -2,18 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/asdine/storm"
|
||||
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
|
||||
"github.com/filebrowser/filebrowser"
|
||||
"github.com/filebrowser/filebrowser/bolt"
|
||||
h "github.com/filebrowser/filebrowser/http"
|
||||
@@ -21,7 +10,14 @@ import (
|
||||
"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
|
||||
@@ -38,12 +34,17 @@ var (
|
||||
recaptchakey string
|
||||
recaptchasecret string
|
||||
port int
|
||||
noAuth bool
|
||||
allowCommands bool
|
||||
allowEdit bool
|
||||
allowNew bool
|
||||
allowPublish bool
|
||||
showVer bool
|
||||
auth struct {
|
||||
method string
|
||||
loginHeader string
|
||||
}
|
||||
noAuth bool
|
||||
allowCommands bool
|
||||
allowEdit bool
|
||||
allowNew bool
|
||||
allowPublish bool
|
||||
showVer bool
|
||||
alterRecaptcha bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -62,8 +63,11 @@ func init() {
|
||||
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")
|
||||
@@ -82,10 +86,13 @@ func setupViper() {
|
||||
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", "")
|
||||
|
||||
@@ -97,14 +104,17 @@ func setupViper() {
|
||||
viper.BindPFlag("Commands", flag.Lookup("commands"))
|
||||
viper.BindPFlag("AllowCommands", flag.Lookup("allow-commands"))
|
||||
viper.BindPFlag("AllowEdit", flag.Lookup("allow-edit"))
|
||||
viper.BindPFlag("AlowNew", flag.Lookup("allow-new"))
|
||||
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"))
|
||||
|
||||
@@ -164,6 +174,18 @@ func main() {
|
||||
})
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -186,10 +208,18 @@ func handler() http.Handler {
|
||||
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{
|
||||
|
||||
12
doc.go
12
doc.go
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Package filebrowser provides a web interface to access your files
|
||||
wherever you are. To use this package as a middleware for your app,
|
||||
you'll need to import both File Manager and File Manager HTTP packages.
|
||||
you'll need to import both File Browser and File Browser HTTP packages.
|
||||
|
||||
import (
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
@@ -16,6 +16,10 @@ to import "github.com/filebrowser/filebrowser/bolt".
|
||||
|
||||
m := &fm.FileBrowser{
|
||||
NoAuth: false,
|
||||
Auth: {
|
||||
Method: "default",
|
||||
LoginHeader: "X-Fowarded-User"
|
||||
},
|
||||
DefaultUser: &fm.User{
|
||||
AllowCommands: true,
|
||||
AllowEdit: true,
|
||||
@@ -49,11 +53,11 @@ functions:
|
||||
m.SetPrefixURL("/")
|
||||
|
||||
The Prefix URL is a part of the path that is already stripped from the
|
||||
r.URL.Path variable before the request arrives to File Manager's handler.
|
||||
r.URL.Path variable before the request arrives to File Browser's handler.
|
||||
This is a function that will rarely be used. You can see one example on Caddy
|
||||
filemanager plugin.
|
||||
|
||||
The Base URL is the URL path where you want File Manager to be available in. If
|
||||
The Base URL is the URL path where you want File Browser to be available in. If
|
||||
you want to be available at the root path, you should call:
|
||||
|
||||
m.SetBaseURL("/")
|
||||
@@ -62,7 +66,7 @@ But if you want to access it at '/admin', you would call:
|
||||
|
||||
m.SetBaseURL("/admin")
|
||||
|
||||
Now, that you already have a File Manager instance created, you just need to
|
||||
Now, that you already have a File Browser instance created, you just need to
|
||||
add it to your handlers using m.ServeHTTP which is compatible to http.Handler.
|
||||
We also have a m.ServeWithErrorsHTTP that returns the status code and an error.
|
||||
|
||||
|
||||
12
file.go
12
file.go
@@ -20,6 +20,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/maruel/natural"
|
||||
)
|
||||
|
||||
// File contains the information about a particular file or directory.
|
||||
@@ -134,13 +135,12 @@ func (i *File) GetListing(u *User, r *http.Request) error {
|
||||
}
|
||||
|
||||
if strings.HasPrefix(f.Mode().String(), "L") {
|
||||
// It's a symbolic link
|
||||
// The FileInfo from Readdir treats symbolic link as a file only.
|
||||
// It's a symbolic link. We try to follow it. If it doesn't work,
|
||||
// we stay with the link information instead if the target's.
|
||||
info, err := os.Stat(f.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
if err == nil {
|
||||
f = info
|
||||
}
|
||||
f = info
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
@@ -373,7 +373,7 @@ func (l byName) Less(i, j int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
|
||||
return natural.Less(l.Items[i].Name, l.Items[j].Name)
|
||||
}
|
||||
|
||||
// By Size
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// Version is the current File Manager version.
|
||||
Version = "1.5.2"
|
||||
// Version is the current File Browser version.
|
||||
Version = "v1.10.0"
|
||||
|
||||
ListViewMode = "list"
|
||||
MosaicViewMode = "mosaic"
|
||||
@@ -58,7 +58,7 @@ type FileBrowser struct {
|
||||
Store *Store
|
||||
|
||||
// PrefixURL is a part of the URL that is already trimmed from the request URL before it
|
||||
// arrives to our handlers. It may be useful when using File Manager as a middleware
|
||||
// arrives to our handlers. It may be useful when using File Browser as a middleware
|
||||
// such as in caddy-filemanager plugin. It is only useful in certain situations.
|
||||
PrefixURL string
|
||||
|
||||
@@ -71,7 +71,18 @@ type FileBrowser struct {
|
||||
// there will only exist one user, called "admin".
|
||||
NoAuth bool
|
||||
|
||||
// ReCaptcha Site key and secret.
|
||||
// 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
|
||||
|
||||
// ReCaptcha host, key and secret.
|
||||
ReCaptchaHost string
|
||||
ReCaptchaKey string
|
||||
ReCaptchaSecret string
|
||||
|
||||
@@ -114,11 +125,11 @@ type FSBuilder func(scope string) FileSystem
|
||||
|
||||
// Setup loads the configuration from the database and configures
|
||||
// the Assets and the Cron job. It must always be run after
|
||||
// creating a File Manager object.
|
||||
// creating a File Browser object.
|
||||
func (m *FileBrowser) Setup() error {
|
||||
// Creates a new File Manager instance with the Users
|
||||
// Creates a new File Browser instance with the Users
|
||||
// map and Assets box.
|
||||
m.Assets = rice.MustFindBox("./node_modules/filebrowser-frontend/dist")
|
||||
m.Assets = rice.MustFindBox("./frontend/dist")
|
||||
m.Cron = cron.New()
|
||||
|
||||
// Tries to get the encryption key from the database.
|
||||
@@ -220,7 +231,7 @@ func (m *FileBrowser) Setup() error {
|
||||
}
|
||||
|
||||
// RootURL returns the actual URL where
|
||||
// File Manager interface can be accessed.
|
||||
// File Browser interface can be accessed.
|
||||
func (m FileBrowser) RootURL() string {
|
||||
return m.PrefixURL + m.BaseURL
|
||||
}
|
||||
@@ -234,7 +245,7 @@ func (m *FileBrowser) SetPrefixURL(url string) {
|
||||
m.PrefixURL = strings.TrimSuffix(url, "/")
|
||||
}
|
||||
|
||||
// SetBaseURL updates the baseURL of a File Manager
|
||||
// SetBaseURL updates the baseURL of a File Browser
|
||||
// object.
|
||||
func (m *FileBrowser) SetBaseURL(url string) {
|
||||
url = strings.TrimPrefix(url, "/")
|
||||
@@ -243,7 +254,7 @@ func (m *FileBrowser) SetBaseURL(url string) {
|
||||
m.BaseURL = strings.TrimSuffix(url, "/")
|
||||
}
|
||||
|
||||
// Attach attaches a static generator to the current File Manager.
|
||||
// Attach attaches a static generator to the current File Browser.
|
||||
func (m *FileBrowser) Attach(s StaticGen) error {
|
||||
if reflect.TypeOf(s).Kind() != reflect.Ptr {
|
||||
return errors.New("data should be a pointer to interface, not interface")
|
||||
@@ -289,7 +300,7 @@ func (m FileBrowser) ShareCleaner() {
|
||||
func (m FileBrowser) Runner(event string, path string, destination string, user *User) error {
|
||||
commands := []string{}
|
||||
|
||||
// Get the commands from the File Manager instance itself.
|
||||
// Get the commands from the File Browser instance itself.
|
||||
if val, ok := m.Commands[event]; ok {
|
||||
commands = append(commands, val...)
|
||||
}
|
||||
|
||||
1
frontend
Submodule
1
frontend
Submodule
Submodule frontend added at 99740e3eab
52
http/auth.go
52
http/auth.go
@@ -9,10 +9,10 @@ import (
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/request"
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
)
|
||||
|
||||
const reCaptchaAPI = "https://www.google.com/recaptcha/api/siteverify"
|
||||
const reCaptchaAPI = "/recaptcha/api/siteverify"
|
||||
|
||||
type cred struct {
|
||||
Password string `json:"password"`
|
||||
@@ -21,14 +21,14 @@ type cred struct {
|
||||
}
|
||||
|
||||
// reCaptcha checks the reCaptcha code.
|
||||
func reCaptcha(secret string, response string) (bool, error) {
|
||||
func reCaptcha(host, secret, response string) (bool, error) {
|
||||
body := url.Values{}
|
||||
body.Set("secret", secret)
|
||||
body.Add("response", response)
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
resp, err := client.Post(reCaptchaAPI, "application/x-www-form-urlencoded", strings.NewReader(body.Encode()))
|
||||
resp, err := client.Post(host+reCaptchaAPI, "application/x-www-form-urlencoded", strings.NewReader(body.Encode()))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -50,26 +50,38 @@ func reCaptcha(secret string, response string) (bool, error) {
|
||||
}
|
||||
|
||||
// authHandler processes the authentication for the user.
|
||||
func authHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// NoAuth instances shouldn't call this method.
|
||||
func authHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if c.NoAuth {
|
||||
// NoAuth instances shouldn't call this method.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if c.AuthMethod == "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)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
c.User = u
|
||||
return printToken(c, w)
|
||||
}
|
||||
|
||||
// Receive the credentials from the request and unmarshal them.
|
||||
var cred cred
|
||||
|
||||
if r.Body == nil {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&cred)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, nil
|
||||
return http.StatusForbidden, err
|
||||
}
|
||||
|
||||
// If ReCaptcha is enabled, check the code.
|
||||
if len(c.ReCaptchaSecret) > 0 {
|
||||
ok, err := reCaptcha(c.ReCaptchaSecret, cred.ReCaptcha)
|
||||
ok, err := reCaptcha(c.ReCaptchaHost, c.ReCaptchaSecret, cred.ReCaptcha)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, err
|
||||
}
|
||||
@@ -86,7 +98,7 @@ func authHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, er
|
||||
}
|
||||
|
||||
// Checks if the password is correct.
|
||||
if !fm.CheckPasswordHash(cred.Password, u.Password) {
|
||||
if !fb.CheckPasswordHash(cred.Password, u.Password) {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
@@ -96,7 +108,7 @@ func authHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, er
|
||||
|
||||
// renewAuthHandler is used when the front-end already has a JWT token
|
||||
// and is checking if it is up to date. If so, updates its info.
|
||||
func renewAuthHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func renewAuthHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
ok, u := validateAuth(c, r)
|
||||
if !ok {
|
||||
return http.StatusForbidden, nil
|
||||
@@ -108,15 +120,15 @@ func renewAuthHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
|
||||
|
||||
// claims is the JWT claims.
|
||||
type claims struct {
|
||||
fm.User
|
||||
fb.User
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
// printToken prints the final JWT token to the user.
|
||||
func printToken(c *fm.Context, w http.ResponseWriter) (int, error) {
|
||||
func printToken(c *fb.Context, w http.ResponseWriter) (int, error) {
|
||||
// Creates a copy of the user and removes it password
|
||||
// hash so it never arrives to the user.
|
||||
u := fm.User{}
|
||||
u := fb.User{}
|
||||
u = *c.User
|
||||
u.Password = ""
|
||||
|
||||
@@ -125,7 +137,7 @@ func printToken(c *fm.Context, w http.ResponseWriter) (int, error) {
|
||||
u,
|
||||
jwt.StandardClaims{
|
||||
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
|
||||
Issuer: "File Manager",
|
||||
Issuer: "File Browser",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -165,12 +177,22 @@ 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 *fm.Context, r *http.Request) (bool, *fm.User) {
|
||||
func validateAuth(c *fb.Context, r *http.Request) (bool, *fb.User) {
|
||||
if c.NoAuth {
|
||||
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 err != nil {
|
||||
return false, nil
|
||||
}
|
||||
c.User = u
|
||||
return true, c.User
|
||||
}
|
||||
|
||||
keyFunc := func(token *jwt.Token) (interface{}, error) {
|
||||
return c.Key, nil
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
"github.com/hacdias/fileutils"
|
||||
"github.com/mholt/archiver"
|
||||
)
|
||||
|
||||
// downloadHandler creates an archive in one of the supported formats (zip, tar,
|
||||
// tar.gz or tar.bz2) and sends it to be downloaded.
|
||||
func downloadHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func downloadHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// If the file isn't a directory, serve it using http.ServeFile. We display it
|
||||
// inline if it is requested.
|
||||
if !c.File.IsDir {
|
||||
@@ -77,13 +77,12 @@ func downloadHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func downloadFileHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func downloadFileHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
file, err := os.Open(c.File.Path)
|
||||
defer file.Close()
|
||||
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
|
||||
47
http/http.go
47
http/http.go
@@ -10,13 +10,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
)
|
||||
|
||||
// Handler returns a function compatible with http.HandleFunc.
|
||||
func Handler(m *fm.FileBrowser) http.Handler {
|
||||
func Handler(m *fb.FileBrowser) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
code, err := serve(&fm.Context{
|
||||
code, err := serve(&fb.Context{
|
||||
FileBrowser: m,
|
||||
User: nil,
|
||||
File: nil,
|
||||
@@ -37,9 +37,9 @@ func Handler(m *fm.FileBrowser) http.Handler {
|
||||
}
|
||||
|
||||
// serve is the main entry point of this HTML application.
|
||||
func serve(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func serve(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Checks if the URL contains the baseURL and strips it. Otherwise, it just
|
||||
// returns a 404 fm.Error because we're not supposed to be here!
|
||||
// returns a 404 fb.Error because we're not supposed to be here!
|
||||
p := strings.TrimPrefix(r.URL.Path, c.BaseURL)
|
||||
|
||||
if len(p) >= len(r.URL.Path) && c.BaseURL != "" {
|
||||
@@ -93,7 +93,7 @@ func serve(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
}
|
||||
|
||||
// staticHandler handles the static assets path.
|
||||
func staticHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func staticHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if r.URL.Path != "/static/manifest.json" {
|
||||
http.FileServer(c.Assets.HTTPBox()).ServeHTTP(w, r)
|
||||
return 0, nil
|
||||
@@ -103,7 +103,7 @@ func staticHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int,
|
||||
}
|
||||
|
||||
// apiHandler is the main entry point for the /api endpoint.
|
||||
func apiHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func apiHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if r.URL.Path == "/auth/get" {
|
||||
return authHandler(c, w, r)
|
||||
}
|
||||
@@ -137,9 +137,9 @@ func apiHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Router == "checksum" || c.Router == "download" {
|
||||
if c.Router == "checksum" || c.Router == "download" || c.Router == "subtitle" || c.Router == "subtitles" {
|
||||
var err error
|
||||
c.File, err = fm.GetInfo(r.URL, c.FileBrowser, c.User)
|
||||
c.File, err = fb.GetInfo(r.URL, c.FileBrowser, c.User)
|
||||
if err != nil {
|
||||
return ErrorToHTTP(err, false), err
|
||||
}
|
||||
@@ -165,6 +165,10 @@ func apiHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, err
|
||||
code, err = settingsHandler(c, w, r)
|
||||
case "share":
|
||||
code, err = shareHandler(c, w, r)
|
||||
case "subtitles":
|
||||
code, err = subtitlesHandler(c, w, r)
|
||||
case "subtitle":
|
||||
code, err = subtitleHandler(c, w, r)
|
||||
default:
|
||||
code = http.StatusNotFound
|
||||
}
|
||||
@@ -173,11 +177,11 @@ func apiHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, err
|
||||
}
|
||||
|
||||
// serveChecksum calculates the hash of a file. Supports MD5, SHA1, SHA256 and SHA512.
|
||||
func checksumHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func checksumHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
query := r.URL.Query().Get("algo")
|
||||
|
||||
val, err := c.File.Checksum(query)
|
||||
if err == fm.ErrInvalidOption {
|
||||
if err == fb.ErrInvalidOption {
|
||||
return http.StatusBadRequest, err
|
||||
} else if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
@@ -205,7 +209,7 @@ func splitURL(path string) (string, string) {
|
||||
}
|
||||
|
||||
// renderFile renders a file using a template with some needed variables.
|
||||
func renderFile(c *fm.Context, w http.ResponseWriter, file string) (int, error) {
|
||||
func renderFile(c *fb.Context, w http.ResponseWriter, file string) (int, error) {
|
||||
tpl := template.Must(template.New("file").Parse(c.Assets.MustString(file)))
|
||||
|
||||
var contentType string
|
||||
@@ -223,12 +227,13 @@ func renderFile(c *fm.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,
|
||||
"Version": fm.Version,
|
||||
"CSS": template.CSS(c.CSS),
|
||||
"ReCaptcha": c.ReCaptchaKey != "" && c.ReCaptchaSecret != "",
|
||||
"ReCaptchaKey": c.ReCaptchaKey,
|
||||
"BaseURL": c.RootURL(),
|
||||
"NoAuth": c.NoAuth,
|
||||
"Version": fb.Version,
|
||||
"CSS": template.CSS(c.CSS),
|
||||
"ReCaptcha": c.ReCaptchaKey != "" && c.ReCaptchaSecret != "",
|
||||
"ReCaptchaHost": c.ReCaptchaHost,
|
||||
"ReCaptchaKey": c.ReCaptchaKey,
|
||||
}
|
||||
|
||||
if c.StaticGen != nil {
|
||||
@@ -245,9 +250,9 @@ func renderFile(c *fm.Context, w http.ResponseWriter, file string) (int, error)
|
||||
}
|
||||
|
||||
// sharePage build the share page.
|
||||
func sharePage(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func sharePage(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
s, err := c.Store.Share.Get(r.URL.Path)
|
||||
if err == fm.ErrNotExist {
|
||||
if err == fb.ErrNotExist {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return renderFile(c, w, "static/share/404.html")
|
||||
}
|
||||
@@ -270,7 +275,7 @@ func sharePage(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, erro
|
||||
return ErrorToHTTP(err, false), err
|
||||
}
|
||||
|
||||
c.File = &fm.File{
|
||||
c.File = &fb.File{
|
||||
Path: s.Path,
|
||||
Name: info.Name(),
|
||||
ModTime: info.ModTime(),
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
"github.com/hacdias/fileutils"
|
||||
)
|
||||
|
||||
@@ -27,7 +27,7 @@ func sanitizeURL(url string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
func resourceHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func resourceHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
r.URL.Path = sanitizeURL(r.URL.Path)
|
||||
|
||||
switch r.Method {
|
||||
@@ -62,9 +62,9 @@ func resourceHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
return http.StatusNotImplemented, nil
|
||||
}
|
||||
|
||||
func resourceGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func resourceGetHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Gets the information of the directory/file.
|
||||
f, err := fm.GetInfo(r.URL, c.FileBrowser, c.User)
|
||||
f, err := fb.GetInfo(r.URL, c.FileBrowser, c.User)
|
||||
if err != nil {
|
||||
return ErrorToHTTP(err, false), err
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func resourceGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (
|
||||
return renderJSON(w, f)
|
||||
}
|
||||
|
||||
func listingHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func listingHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
f := c.File
|
||||
f.Kind = "listing"
|
||||
|
||||
@@ -133,7 +133,7 @@ func listingHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int,
|
||||
return renderJSON(w, f)
|
||||
}
|
||||
|
||||
func resourceDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func resourceDeleteHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Prevent the removal of the root directory.
|
||||
if r.URL.Path == "/" || !c.User.AllowEdit {
|
||||
return http.StatusForbidden, nil
|
||||
@@ -158,7 +158,7 @@ func resourceDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func resourcePostPutHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if !c.User.AllowNew && r.Method == http.MethodPost {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
@@ -182,7 +182,7 @@ func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
// Otherwise we try to create the directory.
|
||||
err := c.User.FileSystem.Mkdir(r.URL.Path, 0776)
|
||||
err := c.User.FileSystem.Mkdir(r.URL.Path, 0775)
|
||||
return ErrorToHTTP(err, false), err
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
// Create/Open the file.
|
||||
f, err := c.User.FileSystem.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0776)
|
||||
f, err := c.User.FileSystem.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
|
||||
if err != nil {
|
||||
return ErrorToHTTP(err, false), err
|
||||
}
|
||||
@@ -240,7 +240,7 @@ func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Reques
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
func resourcePublishSchedule(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func resourcePublishSchedule(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
publish := r.Header.Get("Publish")
|
||||
schedule := r.Header.Get("Schedule")
|
||||
|
||||
@@ -271,7 +271,7 @@ func resourcePublishSchedule(c *fm.Context, w http.ResponseWriter, r *http.Reque
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
func resourcePublish(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func resourcePublish(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
path := filepath.Join(c.User.Scope, r.URL.Path)
|
||||
|
||||
// Before save command handler.
|
||||
@@ -293,7 +293,7 @@ func resourcePublish(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
}
|
||||
|
||||
// resourcePatchHandler is the entry point for resource handler.
|
||||
func resourcePatchHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func resourcePatchHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if !c.User.AllowEdit {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type modifySettingsRequest struct {
|
||||
*modifyRequest
|
||||
modifyRequest
|
||||
Data struct {
|
||||
CSS string `json:"css"`
|
||||
Commands map[string][]string `json:"commands"`
|
||||
@@ -28,7 +28,7 @@ type option struct {
|
||||
func parsePutSettingsRequest(r *http.Request) (*modifySettingsRequest, error) {
|
||||
// Checks if the request body is empty.
|
||||
if r.Body == nil {
|
||||
return nil, fm.ErrEmptyRequest
|
||||
return nil, fb.ErrEmptyRequest
|
||||
}
|
||||
|
||||
// Parses the request body and checks if it's well formed.
|
||||
@@ -40,13 +40,13 @@ func parsePutSettingsRequest(r *http.Request) (*modifySettingsRequest, error) {
|
||||
|
||||
// Checks if the request type is right.
|
||||
if mod.What != "settings" {
|
||||
return nil, fm.ErrWrongDataType
|
||||
return nil, fb.ErrWrongDataType
|
||||
}
|
||||
|
||||
return mod, nil
|
||||
}
|
||||
|
||||
func settingsHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func settingsHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if r.URL.Path != "" && r.URL.Path != "/" {
|
||||
return http.StatusNotFound, nil
|
||||
}
|
||||
@@ -67,7 +67,7 @@ type settingsGetRequest struct {
|
||||
StaticGen []option `json:"staticGen"`
|
||||
}
|
||||
|
||||
func settingsGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func settingsGetHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if !c.User.Admin {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
@@ -97,7 +97,7 @@ func settingsGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (
|
||||
return renderJSON(w, result)
|
||||
}
|
||||
|
||||
func settingsPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func settingsPutHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if !c.User.Admin {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
)
|
||||
|
||||
func shareHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func shareHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
r.URL.Path = sanitizeURL(r.URL.Path)
|
||||
|
||||
switch r.Method {
|
||||
@@ -26,10 +26,10 @@ func shareHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, e
|
||||
return http.StatusNotImplemented, nil
|
||||
}
|
||||
|
||||
func shareGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func shareGetHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
path := filepath.Join(c.User.Scope, r.URL.Path)
|
||||
s, err := c.Store.Share.GetByPath(path)
|
||||
if err == fm.ErrNotExist {
|
||||
if err == fb.ErrNotExist {
|
||||
return http.StatusNotFound, nil
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ func shareGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
return renderJSON(w, s)
|
||||
}
|
||||
|
||||
func sharePostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func sharePostHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
path := filepath.Join(c.User.Scope, r.URL.Path)
|
||||
|
||||
var s *fm.ShareLink
|
||||
var s *fb.ShareLink
|
||||
expire := r.URL.Query().Get("expires")
|
||||
unit := r.URL.Query().Get("unit")
|
||||
|
||||
@@ -67,14 +67,14 @@ func sharePostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
|
||||
}
|
||||
}
|
||||
|
||||
bytes, err := fm.GenerateRandomBytes(32)
|
||||
bytes, err := fb.GenerateRandomBytes(6)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
str := hex.EncodeToString(bytes)
|
||||
str := base64.URLEncoding.EncodeToString(bytes)
|
||||
|
||||
s = &fm.ShareLink{
|
||||
s = &fb.ShareLink{
|
||||
Path: path,
|
||||
Hash: str,
|
||||
Expires: expire != "",
|
||||
@@ -108,9 +108,9 @@ func sharePostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
|
||||
return renderJSON(w, s)
|
||||
}
|
||||
|
||||
func shareDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func shareDeleteHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
s, err := c.Store.Share.Get(strings.TrimPrefix(r.URL.Path, "/"))
|
||||
if err == fm.ErrNotExist {
|
||||
if err == fb.ErrNotExist {
|
||||
return http.StatusNotFound, nil
|
||||
}
|
||||
|
||||
|
||||
83
http/subtitle.go
Normal file
83
http/subtitle.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
)
|
||||
|
||||
func subtitlesHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
files, err := ReadDir(filepath.Dir(c.File.Path))
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
var 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["src"] = filepath.Dir(c.File.Path) + "/" + file.Name()
|
||||
sub["kind"] = "subtitles"
|
||||
sub["label"] = file.Name()
|
||||
subtitles = append(subtitles, sub)
|
||||
}
|
||||
}
|
||||
return renderJSON(w, subtitles)
|
||||
}
|
||||
|
||||
func subtitleHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
str, err := CleanSubtitle(c.File.Path)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
file, err := os.Open(c.File.Path)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Disposition", "inline")
|
||||
w.Header().Set("Content-Type", "text/vtt")
|
||||
http.ServeContent(w, r, stat.Name(), stat.ModTime(), bytes.NewReader([]byte(str)))
|
||||
|
||||
return 0, nil
|
||||
|
||||
}
|
||||
|
||||
func CleanSubtitle(filename string) (string, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
str := string(b) // convert content to a 'string'
|
||||
ext := filepath.Ext(filename)
|
||||
if ext == ".srt" {
|
||||
re := regexp.MustCompile("([0-9]{2}:[0-9]{2}:[0-9]{2}),([0-9]{3})")
|
||||
str = "WEBVTT\n\n" + re.ReplaceAllString(str, "$1.$2")
|
||||
}
|
||||
return str, err
|
||||
}
|
||||
|
||||
func ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||
f, err := os.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
)
|
||||
|
||||
type modifyRequest struct {
|
||||
@@ -18,13 +18,13 @@ type modifyRequest struct {
|
||||
}
|
||||
|
||||
type modifyUserRequest struct {
|
||||
*modifyRequest
|
||||
Data *fm.User `json:"data"`
|
||||
modifyRequest
|
||||
Data *fb.User `json:"data"`
|
||||
}
|
||||
|
||||
// usersHandler is the entry point of the users API. It's just a router
|
||||
// to send the request to its
|
||||
func usersHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func usersHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// If the user isn't admin and isn't making a PUT
|
||||
// request, then return forbidden.
|
||||
if !c.User.Admin && r.Method != http.MethodPut {
|
||||
@@ -47,7 +47,7 @@ func usersHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, e
|
||||
|
||||
// getUserID returns the id from the user which is present
|
||||
// in the request url. If the url is invalid and doesn't
|
||||
// contain a valid ID, it returns an fm.Error.
|
||||
// contain a valid ID, it returns an fb.Error.
|
||||
func getUserID(r *http.Request) (int, error) {
|
||||
// Obtains the ID in string from the URL and converts
|
||||
// it into an integer.
|
||||
@@ -63,11 +63,11 @@ func getUserID(r *http.Request) (int, error) {
|
||||
|
||||
// getUser returns the user which is present in the request
|
||||
// body. If the body is empty or the JSON is invalid, it
|
||||
// returns an fm.Error.
|
||||
func getUser(c *fm.Context, r *http.Request) (*fm.User, string, error) {
|
||||
// returns an fb.Error.
|
||||
func getUser(c *fb.Context, r *http.Request) (*fb.User, string, error) {
|
||||
// Checks if the request body is empty.
|
||||
if r.Body == nil {
|
||||
return nil, "", fm.ErrEmptyRequest
|
||||
return nil, "", fb.ErrEmptyRequest
|
||||
}
|
||||
|
||||
// Parses the request body and checks if it's well formed.
|
||||
@@ -79,14 +79,14 @@ func getUser(c *fm.Context, r *http.Request) (*fm.User, string, error) {
|
||||
|
||||
// Checks if the request type is right.
|
||||
if mod.What != "user" {
|
||||
return nil, "", fm.ErrWrongDataType
|
||||
return nil, "", fb.ErrWrongDataType
|
||||
}
|
||||
|
||||
mod.Data.FileSystem = c.NewFS(mod.Data.Scope)
|
||||
return mod.Data, mod.Which, nil
|
||||
}
|
||||
|
||||
func usersGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func usersGetHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Request for the default user data.
|
||||
if r.URL.Path == "/base" {
|
||||
return renderJSON(w, c.DefaultUser)
|
||||
@@ -118,7 +118,7 @@ func usersGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
}
|
||||
|
||||
u, err := c.Store.Users.Get(id, c.NewFS)
|
||||
if err == fm.ErrExist {
|
||||
if err == fb.ErrExist {
|
||||
return http.StatusNotFound, err
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func usersGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
return renderJSON(w, u)
|
||||
}
|
||||
|
||||
func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func usersPostHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if r.URL.Path != "/" {
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
@@ -142,22 +142,22 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
|
||||
|
||||
// Checks if username isn't empty.
|
||||
if u.Username == "" {
|
||||
return http.StatusBadRequest, fm.ErrEmptyUsername
|
||||
return http.StatusBadRequest, fb.ErrEmptyUsername
|
||||
}
|
||||
|
||||
// Checks if scope isn't empty.
|
||||
if u.Scope == "" {
|
||||
return http.StatusBadRequest, fm.ErrEmptyScope
|
||||
return http.StatusBadRequest, fb.ErrEmptyScope
|
||||
}
|
||||
|
||||
// Checks if password isn't empty.
|
||||
if u.Password == "" {
|
||||
return http.StatusBadRequest, fm.ErrEmptyPassword
|
||||
return http.StatusBadRequest, fb.ErrEmptyPassword
|
||||
}
|
||||
|
||||
// Initialize rules if they're not initialized.
|
||||
if u.Rules == nil {
|
||||
u.Rules = []*fm.Rule{}
|
||||
u.Rules = []*fb.Rule{}
|
||||
}
|
||||
|
||||
// If the view mode is empty, initialize with the default one.
|
||||
@@ -181,17 +181,17 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
|
||||
}
|
||||
|
||||
// Hashes the password.
|
||||
pw, err := fm.HashPassword(u.Password)
|
||||
pw, err := fb.HashPassword(u.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
u.Password = pw
|
||||
u.ViewMode = fm.MosaicViewMode
|
||||
u.ViewMode = fb.MosaicViewMode
|
||||
|
||||
// Saves the user to the database.
|
||||
err = c.Store.Users.Save(u)
|
||||
if err == fm.ErrExist {
|
||||
if err == fb.ErrExist {
|
||||
return http.StatusConflict, err
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ func checkFS(path string) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func usersDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func usersDeleteHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if r.URL.Path == "/" {
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
@@ -240,8 +240,8 @@ func usersDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (
|
||||
|
||||
// Deletes the user from the database.
|
||||
err = c.Store.Users.Delete(id)
|
||||
if err == fm.ErrNotExist {
|
||||
return http.StatusNotFound, fm.ErrNotExist
|
||||
if err == fb.ErrNotExist {
|
||||
return http.StatusNotFound, fb.ErrNotExist
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -251,7 +251,7 @@ func usersDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func usersPutHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// New users should be created on /api/users.
|
||||
if r.URL.Path == "/" {
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
@@ -298,14 +298,14 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
// Updates the Password.
|
||||
if which == "password" {
|
||||
if u.Password == "" {
|
||||
return http.StatusBadRequest, fm.ErrEmptyPassword
|
||||
return http.StatusBadRequest, fb.ErrEmptyPassword
|
||||
}
|
||||
|
||||
if id == c.User.ID && c.User.LockPassword {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
c.User.Password, err = fm.HashPassword(u.Password)
|
||||
c.User.Password, err = fb.HashPassword(u.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
@@ -320,17 +320,17 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
|
||||
// If can only be all.
|
||||
if which != "all" {
|
||||
return http.StatusBadRequest, fm.ErrInvalidUpdateField
|
||||
return http.StatusBadRequest, fb.ErrInvalidUpdateField
|
||||
}
|
||||
|
||||
// Checks if username isn't empty.
|
||||
if u.Username == "" {
|
||||
return http.StatusBadRequest, fm.ErrEmptyUsername
|
||||
return http.StatusBadRequest, fb.ErrEmptyUsername
|
||||
}
|
||||
|
||||
// Checks if filesystem isn't empty.
|
||||
if u.Scope == "" {
|
||||
return http.StatusBadRequest, fm.ErrEmptyScope
|
||||
return http.StatusBadRequest, fb.ErrEmptyScope
|
||||
}
|
||||
|
||||
// Checks if the scope exists.
|
||||
@@ -340,7 +340,7 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
|
||||
// Initialize rules if they're not initialized.
|
||||
if u.Rules == nil {
|
||||
u.Rules = []*fm.Rule{}
|
||||
u.Rules = []*fb.Rule{}
|
||||
}
|
||||
|
||||
// Initialize commands if not initialized.
|
||||
@@ -350,7 +350,7 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
|
||||
// Gets the current saved user from the in-memory map.
|
||||
suser, err := c.Store.Users.Get(id, c.NewFS)
|
||||
if err == fm.ErrNotExist {
|
||||
if err == fb.ErrNotExist {
|
||||
return http.StatusNotFound, nil
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
|
||||
|
||||
// Changes the password if the request wants it.
|
||||
if u.Password != "" {
|
||||
pw, err := fm.HashPassword(u.Password)
|
||||
pw, err := fb.HashPassword(u.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
@@ -27,8 +27,8 @@ var (
|
||||
)
|
||||
|
||||
// command handles the requests for VCS related commands: git, svn and mercurial
|
||||
func command(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Upgrades the connection to a websocket and checks for fm.Errors.
|
||||
func command(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Upgrades the connection to a websocket and checks for fb.Errors.
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -57,13 +57,14 @@ func command(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error)
|
||||
allowed := false
|
||||
|
||||
for _, cmd := range c.User.Commands {
|
||||
if cmd == command[0] {
|
||||
if regexp.MustCompile(cmd).MatchString(command[0]) {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
err = conn.WriteMessage(websocket.BinaryMessage, cmdNotAllowed)
|
||||
err = conn.WriteMessage(websocket.TextMessage, cmdNotAllowed)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
@@ -71,9 +72,9 @@ func command(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Check if the program is talled is installed on the computer.
|
||||
// Check if the program is installed on the computer.
|
||||
if _, err = exec.LookPath(command[0]); err != nil {
|
||||
err = conn.WriteMessage(websocket.BinaryMessage, cmdNotImplemented)
|
||||
err = conn.WriteMessage(websocket.TextMessage, cmdNotImplemented)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
@@ -92,7 +93,7 @@ func command(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error)
|
||||
cmd.Stderr = buff
|
||||
cmd.Stdout = buff
|
||||
|
||||
// Starts the command and checks for fm.Errors.
|
||||
// Starts the command and checks for fb.Errors.
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
@@ -146,9 +147,9 @@ var (
|
||||
type condition func(path string) bool
|
||||
|
||||
type searchOptions struct {
|
||||
CaseInsensitive bool
|
||||
Conditions []condition
|
||||
Terms []string
|
||||
CaseSensitive bool
|
||||
Conditions []condition
|
||||
Terms []string
|
||||
}
|
||||
|
||||
func extensionCondition(extension string) condition {
|
||||
@@ -180,9 +181,9 @@ func videoCondition(path string) bool {
|
||||
|
||||
func parseSearch(value string) *searchOptions {
|
||||
opts := &searchOptions{
|
||||
CaseInsensitive: strings.Contains(value, "case:insensitive"),
|
||||
Conditions: []condition{},
|
||||
Terms: []string{},
|
||||
CaseSensitive: strings.Contains(value, "case:sensitive"),
|
||||
Conditions: []condition{},
|
||||
Terms: []string{},
|
||||
}
|
||||
|
||||
// removes the options from the value
|
||||
@@ -214,7 +215,7 @@ func parseSearch(value string) *searchOptions {
|
||||
}
|
||||
|
||||
// If it's canse insensitive, put everything in lowercase.
|
||||
if opts.CaseInsensitive {
|
||||
if !opts.CaseSensitive {
|
||||
value = strings.ToLower(value)
|
||||
}
|
||||
|
||||
@@ -240,8 +241,8 @@ func parseSearch(value string) *searchOptions {
|
||||
}
|
||||
|
||||
// search searches for a file or directory.
|
||||
func search(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Upgrades the connection to a websocket and checks for fm.Errors.
|
||||
func search(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Upgrades the connection to a websocket and checks for fb.Errors.
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -275,7 +276,7 @@ func search(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error)
|
||||
scope = filepath.Clean(scope)
|
||||
|
||||
err = filepath.Walk(scope, func(path string, f os.FileInfo, err error) error {
|
||||
if search.CaseInsensitive {
|
||||
if !search.CaseSensitive {
|
||||
path = strings.ToLower(path)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": "filebrowser",
|
||||
"author": "File Browser contributors",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"filebrowser-frontend": "^1.0.3"
|
||||
}
|
||||
}
|
||||
21
publish.sh
21
publish.sh
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Building assets"
|
||||
./build.sh
|
||||
|
||||
echo "Updating version number to $1..."
|
||||
sed -i "s|(untracked)|$1|g" filebrowser.go
|
||||
git add -A
|
||||
git commit -m "chore: version $1"
|
||||
git tag "v$1"
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
echo "Commiting untracked version notice..."
|
||||
sed -i "s|$1|(untracked)|g" filebrowser.go
|
||||
git add -A
|
||||
git commit -m "chore: setting untracked version [ci skip]"
|
||||
git push
|
||||
|
||||
echo "Done!"
|
||||
@@ -1 +0,0 @@
|
||||
bf6bef64f1544a286116fe3a9366ac9addaa2dd5
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
"github.com/hacdias/varutils"
|
||||
)
|
||||
|
||||
@@ -64,7 +64,7 @@ func (h Hugo) Name() string {
|
||||
}
|
||||
|
||||
// Hook is the pre-api handler.
|
||||
func (h Hugo) Hook(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func (h Hugo) Hook(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// If we are not using HTTP Post, we shall return Method Not Allowed
|
||||
// since we are only working with this method.
|
||||
if r.Method != http.MethodPost {
|
||||
@@ -108,7 +108,7 @@ func (h Hugo) Hook(c *fm.Context, w http.ResponseWriter, r *http.Request) (int,
|
||||
}
|
||||
|
||||
// Publish publishes a post.
|
||||
func (h Hugo) Publish(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func (h Hugo) Publish(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
filename := filepath.Join(c.User.Scope, r.URL.Path)
|
||||
|
||||
// We only run undraft command if it is a file.
|
||||
@@ -125,7 +125,7 @@ func (h Hugo) Publish(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
|
||||
}
|
||||
|
||||
// Preview handles the preview path.
|
||||
func (h *Hugo) Preview(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func (h *Hugo) Preview(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Get a new temporary path if there is none.
|
||||
if h.previewPath == "" {
|
||||
path, err := ioutil.TempDir("", "")
|
||||
@@ -187,9 +187,6 @@ func (h Hugo) undraft(file string) error {
|
||||
// Setup sets up the plugin.
|
||||
func (h *Hugo) Setup() error {
|
||||
var err error
|
||||
if h.Exe, err = exec.LookPath("hugo"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
h.Exe, err = exec.LookPath("hugo")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
fm "github.com/filebrowser/filebrowser"
|
||||
fb "github.com/filebrowser/filebrowser"
|
||||
)
|
||||
|
||||
// Jekyll is the Jekyll static website generator.
|
||||
@@ -39,12 +39,12 @@ func (j Jekyll) SettingsPath() string {
|
||||
}
|
||||
|
||||
// Hook is the pre-api handler.
|
||||
func (j Jekyll) Hook(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func (j Jekyll) Hook(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Publish publishes a post.
|
||||
func (j Jekyll) Publish(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func (j Jekyll) Publish(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
filename := filepath.Join(c.User.Scope, r.URL.Path)
|
||||
|
||||
// We only run undraft command if it is a file.
|
||||
@@ -59,7 +59,7 @@ func (j Jekyll) Publish(c *fm.Context, w http.ResponseWriter, r *http.Request) (
|
||||
}
|
||||
|
||||
// Preview handles the preview path.
|
||||
func (j *Jekyll) Preview(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
func (j *Jekyll) Preview(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Get a new temporary path if there is none.
|
||||
if j.previewPath == "" {
|
||||
path, err := ioutil.TempDir("", "")
|
||||
|
||||
Reference in New Issue
Block a user