Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdd6ff9435 | ||
|
|
29bd44ac0c | ||
|
|
4b9be2fd12 | ||
|
|
2d7a0f1aaf | ||
|
|
23f70c844a | ||
|
|
6d8af0af8f | ||
|
|
189100b3af | ||
|
|
75eb27f9c9 | ||
|
|
e31775af89 | ||
|
|
2a0e1f7400 | ||
|
|
dbd0e06c73 | ||
|
|
bd2d79953d | ||
|
|
dd8692b656 | ||
|
|
eacf2d1dc3 | ||
|
|
08e2cf764d | ||
|
|
a400a028ff | ||
|
|
f2fb06ae18 | ||
|
|
71957e4947 | ||
|
|
5fab10ed10 | ||
|
|
30e4b86595 | ||
|
|
5bcbcd0bac | ||
|
|
2a5610bb74 | ||
|
|
b86e0e623e | ||
|
|
bca7b842f5 | ||
|
|
ea7272437d | ||
|
|
6de7ca4676 | ||
|
|
88433ea55e | ||
|
|
d874ba59cf | ||
|
|
5dc0040375 | ||
|
|
034215a031 | ||
|
|
80c9e79869 | ||
|
|
2adc57be6c | ||
|
|
6829892be4 | ||
|
|
26545513de | ||
|
|
342d9cc14c | ||
|
|
ed85a9687b | ||
|
|
fe8dcd76c1 | ||
|
|
230bc3d182 | ||
|
|
28a90b90dd | ||
|
|
e607da8ec2 | ||
|
|
e47bdd1272 | ||
|
|
ff821dcb63 | ||
|
|
8887f4ef69 | ||
|
|
bd1a35541a | ||
|
|
56045c6c8e | ||
|
|
b5bd6779f9 | ||
|
|
8665999fc0 | ||
|
|
677c5a7cec | ||
|
|
41981a3d8a | ||
|
|
47789eec68 | ||
|
|
4c880193ce | ||
|
|
86aeecfac2 | ||
|
|
e8af97872c | ||
|
|
d7590729f1 | ||
|
|
1258ac759f | ||
|
|
a60a9c4811 |
34
.github/workflows/release.yml
vendored
Normal file
34
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: goreleaser
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# run only against tags
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
# packages: write
|
||||||
|
# issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Fetch all tags
|
||||||
|
run: git fetch --force --tags
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.18
|
||||||
|
- name: Run GoReleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v2
|
||||||
|
with:
|
||||||
|
# either 'goreleaser' (default) or 'goreleaser-pro'
|
||||||
|
distribution: goreleaser
|
||||||
|
version: latest
|
||||||
|
args: release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -31,3 +31,4 @@ assets_vfsdata.go
|
|||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
dist/
|
dist/
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
|
version: 2
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
- go generate .
|
|
||||||
builds:
|
builds:
|
||||||
- env:
|
- env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
main: .
|
main: .
|
||||||
flags:
|
|
||||||
- -tags=vfs
|
|
||||||
ldflags: -s -w -X main.VERSION={{.Version}} -X main.BUILDTIME={{.Date}} -X main.GITCOMMIT={{.Commit}}
|
ldflags: -s -w -X main.VERSION={{.Version}} -X main.BUILDTIME={{.Date}} -X main.GITCOMMIT={{.Commit}}
|
||||||
goos:
|
goos:
|
||||||
- linux
|
- linux
|
||||||
@@ -19,8 +17,16 @@ builds:
|
|||||||
- "386"
|
- "386"
|
||||||
goarm:
|
goarm:
|
||||||
- "6"
|
- "6"
|
||||||
brews:
|
|
||||||
- tap:
|
archives:
|
||||||
owner: codeskyblue
|
- format: tar.gz
|
||||||
name: homebrew-tap
|
# use zip for windows archives
|
||||||
folder: Formula
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
|
||||||
|
# brews:
|
||||||
|
# - tap:
|
||||||
|
# owner: codeskyblue
|
||||||
|
# name: homebrew-tap
|
||||||
|
# folder: Formula
|
||||||
|
|||||||
25
.travis.yml
25
.travis.yml
@@ -1,25 +0,0 @@
|
|||||||
services:
|
|
||||||
- docker
|
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- "1.16"
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
script:
|
|
||||||
- go test -v
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- docker-ce
|
|
||||||
deploy:
|
|
||||||
- provider: script
|
|
||||||
skip_cleanup: true
|
|
||||||
script: curl -sL https://git.io/goreleaser | bash
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
condition: $TRAVIS_OS_NAME = linux
|
|
||||||
#- provider: script
|
|
||||||
# skip_cleanup: true
|
|
||||||
# script: bash docker/push_images && bash docker/push_manifest
|
|
||||||
# on:
|
|
||||||
# branch: master
|
|
||||||
68
README.md
68
README.md
@@ -58,10 +58,8 @@ Tested with go-1.16
|
|||||||
1. [x] Support unzip zip file when upload(with form: unzip=true)
|
1. [x] Support unzip zip file when upload(with form: unzip=true)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
```
|
```bash
|
||||||
go get -v github.com/codeskyblue/gohttpserver
|
$ go install github.com/codeskyblue/gohttpserver@latest
|
||||||
cd $GOPATH/src/github.com/codeskyblue/gohttpserver
|
|
||||||
go build && ./gohttpserver
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Or download binaries from [github releases](https://github.com/codeskyblue/gohttpserver/releases)
|
Or download binaries from [github releases](https://github.com/codeskyblue/gohttpserver/releases)
|
||||||
@@ -69,14 +67,14 @@ Or download binaries from [github releases](https://github.com/codeskyblue/gohtt
|
|||||||
If you are using Mac, simply run command
|
If you are using Mac, simply run command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew install codeskyblue/tap/gohttpserver
|
$ brew install codeskyblue/tap/gohttpserver
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
Listen on port 8000 of all interfaces, and enable file uploading.
|
Listen on port 8000 of all interfaces, and enable file uploading.
|
||||||
|
|
||||||
```
|
```
|
||||||
./gohttpserver -r ./ --port 8000 --upload
|
$ gohttpserver -r ./ --port 8000 --upload
|
||||||
```
|
```
|
||||||
|
|
||||||
Use command `gohttpserver --help` to see more usage.
|
Use command `gohttpserver --help` to see more usage.
|
||||||
@@ -85,21 +83,21 @@ Use command `gohttpserver --help` to see more usage.
|
|||||||
share current directory
|
share current directory
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --rm -p 8000:8000 -v $PWD:/app/public --name gohttpserver codeskyblue/gohttpserver
|
$ docker run -it --rm -p 8000:8000 -v $PWD:/app/public --name gohttpserver codeskyblue/gohttpserver
|
||||||
```
|
```
|
||||||
|
|
||||||
Share current directory with http basic auth
|
Share current directory with http basic auth
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --rm -p 8000:8000 -v $PWD:/app/public --name gohttpserver \
|
$ docker run -it --rm -p 8000:8000 -v $PWD:/app/public --name gohttpserver \
|
||||||
codeskyblue/gohttpserver \
|
codeskyblue/gohttpserver \
|
||||||
--auth-type http --auth-http username:password
|
--auth-type http --auth-http username1:password1 --auth-http username2:password2
|
||||||
```
|
```
|
||||||
|
|
||||||
Share current directory with openid auth. (Works only in netease company.)
|
Share current directory with openid auth. (Works only in netease company.)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --rm -p 8000:8000 -v $PWD:/app/public --name gohttpserver \
|
$ docker run -it --rm -p 8000:8000 -v $PWD:/app/public --name gohttpserver \
|
||||||
codeskyblue/gohttpserver \
|
codeskyblue/gohttpserver \
|
||||||
--auth-type openid
|
--auth-type openid
|
||||||
```
|
```
|
||||||
@@ -107,15 +105,15 @@ docker run -it --rm -p 8000:8000 -v $PWD:/app/public --name gohttpserver \
|
|||||||
To build image yourself, please change the PWD to the root of this repo.
|
To build image yourself, please change the PWD to the root of this repo.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd gohttpserver/
|
$ cd gohttpserver/
|
||||||
docker build -t codeskyblue/gohttpserver -f docker/Dockerfile .
|
$ docker build -t codeskyblue/gohttpserver -f docker/Dockerfile .
|
||||||
```
|
```
|
||||||
|
|
||||||
## Authentication options
|
## Authentication options
|
||||||
- Enable basic http authentication
|
- Enable basic http authentication
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ gohttpserver --auth-type http --auth-http username:password
|
$ gohttpserver --auth-type http --auth-http username1:password1 --auth-http username2:password2
|
||||||
```
|
```
|
||||||
|
|
||||||
- Use openid auth
|
- Use openid auth
|
||||||
@@ -132,7 +130,7 @@ docker build -t codeskyblue/gohttpserver -f docker/Dockerfile .
|
|||||||
You can configure to let a http reverse proxy handling authentication.
|
You can configure to let a http reverse proxy handling authentication.
|
||||||
When using oauth2-proxy, the backend will use identification info from request headers `X-Auth-Request-Email` as userId and `X-Auth-Request-Fullname` as user's display name.
|
When using oauth2-proxy, the backend will use identification info from request headers `X-Auth-Request-Email` as userId and `X-Auth-Request-Fullname` as user's display name.
|
||||||
Please config your oauth2 reverse proxy yourself.
|
Please config your oauth2 reverse proxy yourself.
|
||||||
More about [oauth2-proxy](https://github.com/bitly/oauth2_proxy).
|
More about [oauth2-proxy](https://github.com/oauth2-proxy/oauth2-proxy).
|
||||||
|
|
||||||
All required headers list as following.
|
All required headers list as following.
|
||||||
|
|
||||||
@@ -198,8 +196,8 @@ accessTables:
|
|||||||
### ipa plist proxy
|
### ipa plist proxy
|
||||||
This is used for server on which https is enabled. default use <https://plistproxy.herokuapp.com/plist>
|
This is used for server on which https is enabled. default use <https://plistproxy.herokuapp.com/plist>
|
||||||
|
|
||||||
```
|
```bash
|
||||||
./gohttpserver --plistproxy=https://someproxyhost.com/
|
$ gohttpserver --plistproxy=https://someproxyhost.com/
|
||||||
```
|
```
|
||||||
|
|
||||||
Test if proxy works:
|
Test if proxy works:
|
||||||
@@ -264,6 +262,35 @@ gohttpserver should started with `--xheaders` argument when behide nginx.
|
|||||||
|
|
||||||
Refs: <http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size>
|
Refs: <http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size>
|
||||||
|
|
||||||
|
gohttpserver also support `--prefix` flag which will help to when meet `/` is occupied by other service. relative issue <https://github.com/codeskyblue/gohttpserver/issues/105>
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# for gohttpserver
|
||||||
|
$ gohttpserver --prefix /foo --addr :8200 --xheaders
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nginx settigns**
|
||||||
|
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name your-domain-name.com;
|
||||||
|
|
||||||
|
location /foo {
|
||||||
|
proxy_pass http://127.0.0.1:8200; # here need to change
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
client_max_body_size 0; # disable upload limit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
- [How to generate self signed certificate with openssl](http://stackoverflow.com/questions/10175812/how-to-create-a-self-signed-certificate-with-openssl)
|
- [How to generate self signed certificate with openssl](http://stackoverflow.com/questions/10175812/how-to-create-a-self-signed-certificate-with-openssl)
|
||||||
|
|
||||||
@@ -279,14 +306,13 @@ Depdencies are managed by [govendor](https://github.com/kardianos/govendor)
|
|||||||
1. Build develop version. **assets** directory must exists
|
1. Build develop version. **assets** directory must exists
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
go build
|
$ go build
|
||||||
./gohttpserver
|
$ ./gohttpserver
|
||||||
```
|
```
|
||||||
2. Build single binary release
|
2. Build single binary release
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
go generate .
|
$ go build
|
||||||
go build -tags vfs
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Theme are defined in [assets/themes](assets/themes) directory. Now only two themes are available, "black" and "green".
|
Theme are defined in [assets/themes](assets/themes) directory. Now only two themes are available, "black" and "green".
|
||||||
@@ -306,7 +332,7 @@ Theme are defined in [assets/themes](assets/themes) directory. Now only two them
|
|||||||
|
|
||||||
**Go Libraries**
|
**Go Libraries**
|
||||||
|
|
||||||
* [vfsgen](https://github.com/shurcooL/vfsgen)
|
* [vfsgen](https://github.com/shurcooL/vfsgen) Not using now
|
||||||
* [go-bindata-assetfs](https://github.com/elazarl/go-bindata-assetfs) Not using now
|
* [go-bindata-assetfs](https://github.com/elazarl/go-bindata-assetfs) Not using now
|
||||||
* <http://www.gorillatoolkit.org/pkg/handlers>
|
* <http://www.gorillatoolkit.org/pkg/handlers>
|
||||||
|
|
||||||
|
|||||||
12
assets.go
Normal file
12
assets.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed assets
|
||||||
|
var assetsFS embed.FS
|
||||||
|
|
||||||
|
// Assets contains project assets.
|
||||||
|
var Assets = http.FS(assetsFS)
|
||||||
@@ -64,6 +64,8 @@
|
|||||||
</template>
|
</template>
|
||||||
[[end]]
|
[[end]]
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
[[if not .NoIndex ]]
|
||||||
<form class="navbar-form navbar-right">
|
<form class="navbar-form navbar-right">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" name="search" class="form-control" placeholder="Search text" v-bind:value="search"
|
<input type="text" name="search" class="form-control" placeholder="Search text" v-bind:value="search"
|
||||||
@@ -75,6 +77,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
[[end]]
|
||||||
<ul id="nav-right-bar" class="nav navbar-nav navbar-right">
|
<ul id="nav-right-bar" class="nav navbar-nav navbar-right">
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,7 +132,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="f in computedFiles">
|
<tr v-for="f in computedFiles">
|
||||||
<td>
|
<td>
|
||||||
<a v-on:click='clickFileOrDir(f, $event)' href="{{getEncodePath(f)}}">
|
<a v-on:click='clickFileOrDir(f, $event)' href="{{getEncodePath(f.name)}}">
|
||||||
<!-- ?raw=false -->
|
<!-- ?raw=false -->
|
||||||
<i style="padding-right: 0.5em" class="fa" v-bind:class='genFileClass(f)'></i> {{f.name}}
|
<i style="padding-right: 0.5em" class="fa" v-bind:class='genFileClass(f)'></i> {{f.name}}
|
||||||
</a>
|
</a>
|
||||||
@@ -142,7 +145,7 @@
|
|||||||
<td class="hidden-xs">{{formatTime(f.mtime)}}</td>
|
<td class="hidden-xs">{{formatTime(f.mtime)}}</td>
|
||||||
<td style="text-align: left">
|
<td style="text-align: left">
|
||||||
<template v-if="f.type == 'dir'">
|
<template v-if="f.type == 'dir'">
|
||||||
<a class="btn btn-default btn-xs" href="{{getEncodePath(f)}}/?op=archive">
|
<a class="btn btn-default btn-xs" href="{{getEncodePath(f.name)}}/?op=archive">
|
||||||
<span class="hidden-xs">Archive</span> Zip
|
<span class="hidden-xs">Archive</span> Zip
|
||||||
<span class="glyphicon glyphicon-download-alt"></span>
|
<span class="glyphicon glyphicon-download-alt"></span>
|
||||||
</a>
|
</a>
|
||||||
@@ -256,8 +259,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div id="footer" class="pull-right" style="margin: 2em 1em">
|
<div id="footer" class="pull-right" style="margin: 2em 1em">
|
||||||
<a href="https://github.com/codeskyblue/gohttpserver">gohttpserver (ver:{{version}})</a>, written by <a href="https://github.com/codeskyblue">codeskyblue</a>.
|
<a href="https://github.com/codeskyblue/gohttpserver">gohttpserver ({{version}})</a>, by <a href="https://github.com/codeskyblue">codeskyblue</a>.
|
||||||
Copyright 2016-2021. go1.16
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ function showErrorMessage(jqXHR) {
|
|||||||
console.error(errMsg)
|
console.error(errMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var vm = new Vue({
|
var vm = new Vue({
|
||||||
el: "#app",
|
el: "#app",
|
||||||
data: {
|
data: {
|
||||||
@@ -141,8 +140,8 @@ var vm = new Vue({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getEncodePath: function (f) {
|
getEncodePath: function (filepath) {
|
||||||
return pathJoin([location.pathname, encodeURIComponent(f.name)]);
|
return pathJoin([location.pathname].concat(filepath.split("/").map(v => encodeURIComponent(v))))
|
||||||
},
|
},
|
||||||
formatTime: function (timestamp) {
|
formatTime: function (timestamp) {
|
||||||
var m = moment(timestamp);
|
var m = moment(timestamp);
|
||||||
@@ -191,7 +190,7 @@ var vm = new Vue({
|
|||||||
genDownloadURL: function (f) {
|
genDownloadURL: function (f) {
|
||||||
var search = location.search;
|
var search = location.search;
|
||||||
var sep = search == "" ? "?" : "&"
|
var sep = search == "" ? "?" : "&"
|
||||||
return location.origin + this.getEncodePath(f) + location.search + sep + "download=true";
|
return location.origin + this.getEncodePath(f.name) + location.search + sep + "download=true";
|
||||||
},
|
},
|
||||||
shouldHaveQrcode: function (name) {
|
shouldHaveQrcode: function (name) {
|
||||||
return ['apk', 'ipa'].indexOf(getExtention(name)) !== -1;
|
return ['apk', 'ipa'].indexOf(getExtention(name)) !== -1;
|
||||||
@@ -237,10 +236,18 @@ var vm = new Vue({
|
|||||||
return "fa-file-text-o"
|
return "fa-file-text-o"
|
||||||
},
|
},
|
||||||
clickFileOrDir: function (f, e) {
|
clickFileOrDir: function (f, e) {
|
||||||
var reqPath = pathJoin([location.pathname, encodeURIComponent(f.name)]);
|
var reqPath = this.getEncodePath(f.name)
|
||||||
// TODO: fix here tomorrow
|
// TODO: fix here tomorrow
|
||||||
if (f.type == "file") {
|
if (f.type == "file") {
|
||||||
window.location.href = reqPath;
|
// check whether the file is video
|
||||||
|
var videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
||||||
|
var fileExtension = getExtention(f.name).toLowerCase();
|
||||||
|
if (videoExtensions.includes(fileExtension)) {
|
||||||
|
window.location.href = '/-/video-player' + reqPath;
|
||||||
|
} else {
|
||||||
|
window.location.href = reqPath;
|
||||||
|
}
|
||||||
|
e.preventDefault()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loadFileOrDir(reqPath);
|
loadFileOrDir(reqPath);
|
||||||
@@ -253,7 +260,7 @@ var vm = new Vue({
|
|||||||
showInfo: function (f) {
|
showInfo: function (f) {
|
||||||
console.log(f);
|
console.log(f);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: pathJoin(["/", location.pathname, encodeURIComponent(f.name)]),
|
url: this.getEncodePath(f.name),
|
||||||
data: {
|
data: {
|
||||||
op: "info",
|
op: "info",
|
||||||
},
|
},
|
||||||
@@ -280,7 +287,7 @@ var vm = new Vue({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: pathJoin(["/", location.pathname, "/", encodeURIComponent(name)]),
|
url: this.getEncodePath(name),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
console.log(res)
|
console.log(res)
|
||||||
@@ -294,12 +301,12 @@ var vm = new Vue({
|
|||||||
deletePathConfirm: function (f, e) {
|
deletePathConfirm: function (f, e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!e.altKey) { // skip confirm when alt pressed
|
if (!e.altKey) { // skip confirm when alt pressed
|
||||||
if (!window.confirm("Delete " + location.pathname + "/" + f.name + " ?")) {
|
if (!window.confirm("Delete " + f.name + " ?")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: pathJoin([location.pathname, encodeURIComponent(f.name)]),
|
url: this.getEncodePath(f.name),
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
loadFileList()
|
loadFileList()
|
||||||
@@ -434,7 +441,7 @@ $(function () {
|
|||||||
loadFileList(location.pathname + location.search)
|
loadFileList(location.pathname + location.search)
|
||||||
|
|
||||||
// update version
|
// update version
|
||||||
$.getJSON(URL_PREFIX + "/-/sysinfo", function (res) {
|
$.getJSON("/-/sysinfo", function (res) {
|
||||||
vm.version = res.version;
|
vm.version = res.version;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
49
assets/video-player.html
Normal file
49
assets/video-player.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Video Player - [[.FileName]]</title>
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
.video-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="video-container">
|
||||||
|
<video id="videoPlayer" controls autoplay>
|
||||||
|
<source src="[[.VideoURL]]" type="video/[[.Extension]]">
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var video = document.getElementById('videoPlayer');
|
||||||
|
video.focus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// +build !vfs
|
|
||||||
//go:generate go run assets_generate.go
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
// Assets contains project assets.
|
|
||||||
var Assets http.FileSystem = http.Dir("assets")
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// +build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/shurcooL/vfsgen"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var fs http.FileSystem = http.Dir("assets")
|
|
||||||
|
|
||||||
err := vfsgen.Generate(fs, vfsgen.Options{
|
|
||||||
PackageName: "main",
|
|
||||||
BuildTags: "vfs",
|
|
||||||
VariableName: "Assets",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3
build.sh
3
build.sh
@@ -23,13 +23,10 @@ fi
|
|||||||
build() {
|
build() {
|
||||||
echo "$1 $2 ..."
|
echo "$1 $2 ..."
|
||||||
GOOS=$1 GOARCH=$2 go build \
|
GOOS=$1 GOARCH=$2 go build \
|
||||||
-tags vfs \
|
|
||||||
-ldflags "$LDFLAGS" \
|
-ldflags "$LDFLAGS" \
|
||||||
-o dist/gohttpserver-${3:-""}
|
-o dist/gohttpserver-${3:-""}
|
||||||
}
|
}
|
||||||
|
|
||||||
go generate .
|
|
||||||
|
|
||||||
build linux arm linux-arm
|
build linux arm linux-arm
|
||||||
build darwin amd64 mac-amd64
|
build darwin amd64 mac-amd64
|
||||||
build linux amd64 linux-amd64
|
build linux amd64 linux-amd64
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ WORKDIR /app/gohttpserver
|
|||||||
ADD . /app/gohttpserver
|
ADD . /app/gohttpserver
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags '-X main.VERSION=docker' -o gohttpserver
|
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags '-X main.VERSION=docker' -o gohttpserver
|
||||||
|
|
||||||
FROM debian:stretch
|
FROM debian:stable
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN mkdir -p /app/public
|
RUN mkdir -p /app/public
|
||||||
RUN apt-get update && apt-get install -y ca-certificates
|
RUN apt-get update && apt-get install -y ca-certificates
|
||||||
|
|||||||
19
docker/Dockerfile.alpine
Normal file
19
docker/Dockerfile.alpine
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
FROM golang:1.16.0-alpine as build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY go.mod .
|
||||||
|
COPY go.sum .
|
||||||
|
RUN go mod download
|
||||||
|
# Binary Compression
|
||||||
|
RUN apk add upx --no-cache
|
||||||
|
|
||||||
|
# Build app and pack using upx
|
||||||
|
ADD . /app
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-s -w -X main.VERSION=docker' -o gohttpserver && upx gohttpserver
|
||||||
|
|
||||||
|
FROM alpine:3
|
||||||
|
WORKDIR /app
|
||||||
|
ADD assets /usr/local/bin/assets
|
||||||
|
COPY --from=build /app/gohttpserver /usr/local/bin/gohttpserver
|
||||||
|
EXPOSE 8000
|
||||||
|
ENTRYPOINT [ "/usr/local/bin/gohttpserver" ]
|
||||||
6
go.mod
6
go.mod
@@ -17,11 +17,9 @@ require (
|
|||||||
github.com/gorilla/sessions v1.1.3
|
github.com/gorilla/sessions v1.1.3
|
||||||
github.com/pkg/errors v0.8.0 // indirect
|
github.com/pkg/errors v0.8.0 // indirect
|
||||||
github.com/shogo82148/androidbinary v0.0.0-20180627093851-01c4bfa8b3b5
|
github.com/shogo82148/androidbinary v0.0.0-20180627093851-01c4bfa8b3b5
|
||||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 // indirect
|
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca
|
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
github.com/stretchr/testify v1.3.0
|
github.com/stretchr/testify v1.3.0
|
||||||
golang.org/x/text v0.3.3
|
golang.org/x/net v0.23.0 // indirect
|
||||||
golang.org/x/tools v0.1.0 // indirect
|
golang.org/x/text v0.14.0
|
||||||
howett.net/plist v0.0.0-20201203080718-1454fab16a06 // indirect
|
howett.net/plist v0.0.0-20201203080718-1454fab16a06 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
56
go.sum
56
go.sum
@@ -44,10 +44,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/shogo82148/androidbinary v0.0.0-20180627093851-01c4bfa8b3b5 h1:bXRaUWl3Afe3F9YR5NU1U3UB5zjCHlu4im5p3J/LUYk=
|
github.com/shogo82148/androidbinary v0.0.0-20180627093851-01c4bfa8b3b5 h1:bXRaUWl3Afe3F9YR5NU1U3UB5zjCHlu4im5p3J/LUYk=
|
||||||
github.com/shogo82148/androidbinary v0.0.0-20180627093851-01c4bfa8b3b5/go.mod h1:05AjXWPWLdTIl9+REKhSmTeoJ6Wz5e9ir0Q0NRxCIKo=
|
github.com/shogo82148/androidbinary v0.0.0-20180627093851-01c4bfa8b3b5/go.mod h1:05AjXWPWLdTIl9+REKhSmTeoJ6Wz5e9ir0Q0NRxCIKo=
|
||||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 h1:SWV2fHctRpRrp49VXJ6UZja7gU9QLHwRpIPBN89SKEo=
|
|
||||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca h1:3fECS8atRjByijiI8yYiuwLwQ2ZxXobW7ua/8GRB3pI=
|
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
@@ -55,33 +51,53 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||||
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -41,22 +42,30 @@ type IndexFileItem struct {
|
|||||||
Info os.FileInfo
|
Info os.FileInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Directory struct {
|
||||||
|
size map[string]int64
|
||||||
|
mutex *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
type HTTPStaticServer struct {
|
type HTTPStaticServer struct {
|
||||||
Root string
|
Root string
|
||||||
Prefix string
|
Prefix string
|
||||||
Upload bool
|
Upload bool
|
||||||
Delete bool
|
Delete bool
|
||||||
Title string
|
Title string
|
||||||
Theme string
|
Theme string
|
||||||
PlistProxy string
|
PlistProxy string
|
||||||
GoogleTrackerID string
|
GoogleTrackerID string
|
||||||
AuthType string
|
AuthType string
|
||||||
|
DeepPathMaxDepth int
|
||||||
|
NoIndex bool
|
||||||
|
|
||||||
indexes []IndexFileItem
|
indexes []IndexFileItem
|
||||||
m *mux.Router
|
m *mux.Router
|
||||||
|
bufPool sync.Pool // use sync.Pool caching buf to reduce gc ratio
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPStaticServer(root string) *HTTPStaticServer {
|
func NewHTTPStaticServer(root string, noIndex bool) *HTTPStaticServer {
|
||||||
// if root == "" {
|
// if root == "" {
|
||||||
// root = "./"
|
// root = "./"
|
||||||
// }
|
// }
|
||||||
@@ -71,23 +80,30 @@ func NewHTTPStaticServer(root string) *HTTPStaticServer {
|
|||||||
Root: root,
|
Root: root,
|
||||||
Theme: "black",
|
Theme: "black",
|
||||||
m: m,
|
m: m,
|
||||||
|
bufPool: sync.Pool{
|
||||||
|
New: func() interface{} { return make([]byte, 32*1024) },
|
||||||
|
},
|
||||||
|
NoIndex: noIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
if !noIndex {
|
||||||
time.Sleep(1 * time.Second)
|
go func() {
|
||||||
for {
|
time.Sleep(1 * time.Second)
|
||||||
startTime := time.Now()
|
for {
|
||||||
log.Println("Started making search index")
|
startTime := time.Now()
|
||||||
s.makeIndex()
|
log.Println("Started making search index")
|
||||||
log.Printf("Completed search index in %v", time.Since(startTime))
|
s.makeIndex()
|
||||||
//time.Sleep(time.Second * 1)
|
log.Printf("Completed search index in %v", time.Since(startTime))
|
||||||
time.Sleep(time.Minute * 10)
|
//time.Sleep(time.Second * 1)
|
||||||
}
|
time.Sleep(time.Minute * 10)
|
||||||
}()
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// routers for Apple *.ipa
|
// routers for Apple *.ipa
|
||||||
m.HandleFunc("/-/ipa/plist/{path:.*}", s.hPlist)
|
m.HandleFunc("/-/ipa/plist/{path:.*}", s.hPlist)
|
||||||
m.HandleFunc("/-/ipa/link/{path:.*}", s.hIpaLink)
|
m.HandleFunc("/-/ipa/link/{path:.*}", s.hIpaLink)
|
||||||
|
m.HandleFunc("/-/video-player/{path:.*}", s.hVideoPlayer)
|
||||||
|
|
||||||
m.HandleFunc("/{path:.*}", s.hIndex).Methods("GET", "HEAD")
|
m.HandleFunc("/{path:.*}", s.hIndex).Methods("GET", "HEAD")
|
||||||
m.HandleFunc("/{path:.*}", s.hUploadOrMkdir).Methods("POST")
|
m.HandleFunc("/{path:.*}", s.hUploadOrMkdir).Methods("POST")
|
||||||
@@ -137,7 +153,7 @@ func (s *HTTPStaticServer) hIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
if r.Method == "HEAD" {
|
if r.Method == "HEAD" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
renderHTML(w, "index.html", s)
|
renderHTML(w, "assets/index.html", s)
|
||||||
} else {
|
} else {
|
||||||
if filepath.Base(path) == YAMLCONF {
|
if filepath.Base(path) == YAMLCONF {
|
||||||
auth := s.readAccessConf(realPath)
|
auth := s.readAccessConf(realPath)
|
||||||
@@ -244,7 +260,12 @@ func (s *HTTPStaticServer) hUploadOrMkdir(w http.ResponseWriter, req *http.Reque
|
|||||||
http.Error(w, "File create "+err.Error(), http.StatusInternalServerError)
|
http.Error(w, "File create "+err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, copyErr = io.Copy(dst, file)
|
|
||||||
|
// Note: very large size file might cause poor performance
|
||||||
|
// _, copyErr = io.Copy(dst, file)
|
||||||
|
buf := s.bufPool.Get().([]byte)
|
||||||
|
defer s.bufPool.Put(buf)
|
||||||
|
_, copyErr = io.CopyBuffer(dst, file, buf)
|
||||||
dst.Close()
|
dst.Close()
|
||||||
// }
|
// }
|
||||||
if copyErr != nil {
|
if copyErr != nil {
|
||||||
@@ -413,7 +434,7 @@ func (s *HTTPStaticServer) hIpaLink(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html")
|
w.Header().Set("Content-Type", "text/html")
|
||||||
log.Println("PlistURL:", plistUrl)
|
log.Println("PlistURL:", plistUrl)
|
||||||
renderHTML(w, "ipa-install.html", map[string]string{
|
renderHTML(w, "assets/ipa-install.html", map[string]string{
|
||||||
"Name": filepath.Base(path),
|
"Name": filepath.Base(path),
|
||||||
"PlistLink": plistUrl,
|
"PlistLink": plistUrl,
|
||||||
})
|
})
|
||||||
@@ -556,6 +577,7 @@ func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) {
|
|||||||
auth := s.readAccessConf(realPath)
|
auth := s.readAccessConf(realPath)
|
||||||
auth.Upload = auth.canUpload(r)
|
auth.Upload = auth.canUpload(r)
|
||||||
auth.Delete = auth.canDelete(r)
|
auth.Delete = auth.canDelete(r)
|
||||||
|
maxDepth := s.DeepPathMaxDepth
|
||||||
|
|
||||||
// path string -> info os.FileInfo
|
// path string -> info os.FileInfo
|
||||||
fileInfoMap := make(map[string]os.FileInfo, 0)
|
fileInfoMap := make(map[string]os.FileInfo, 0)
|
||||||
@@ -600,7 +622,7 @@ func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) {
|
|||||||
lr.Name = filepath.ToSlash(name) // fix for windows
|
lr.Name = filepath.ToSlash(name) // fix for windows
|
||||||
}
|
}
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
name := deepPath(realPath, info.Name())
|
name := deepPath(realPath, info.Name(), maxDepth)
|
||||||
lr.Name = name
|
lr.Name = name
|
||||||
lr.Path = filepath.Join(filepath.Dir(path), name)
|
lr.Path = filepath.Join(filepath.Dir(path), name)
|
||||||
lr.Type = "dir"
|
lr.Type = "dir"
|
||||||
@@ -620,7 +642,7 @@ func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Write(data)
|
w.Write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
var dirSizeMap = make(map[string]int64)
|
var dirInfoSize = Directory{size: make(map[string]int64), mutex: &sync.RWMutex{}}
|
||||||
|
|
||||||
func (s *HTTPStaticServer) makeIndex() error {
|
func (s *HTTPStaticServer) makeIndex() error {
|
||||||
var indexes = make([]IndexFileItem, 0)
|
var indexes = make([]IndexFileItem, 0)
|
||||||
@@ -640,21 +662,28 @@ func (s *HTTPStaticServer) makeIndex() error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
s.indexes = indexes
|
s.indexes = indexes
|
||||||
dirSizeMap = make(map[string]int64)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HTTPStaticServer) historyDirSize(dir string) int64 {
|
func (s *HTTPStaticServer) historyDirSize(dir string) int64 {
|
||||||
var size int64
|
dirInfoSize.mutex.RLock()
|
||||||
if size, ok := dirSizeMap[dir]; ok {
|
size, ok := dirInfoSize.size[dir]
|
||||||
|
dirInfoSize.mutex.RUnlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fitem := range s.indexes {
|
for _, fitem := range s.indexes {
|
||||||
if filepath.HasPrefix(fitem.Path, dir) {
|
if filepath.HasPrefix(fitem.Path, dir) {
|
||||||
size += fitem.Info.Size()
|
size += fitem.Info.Size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dirSizeMap[dir] = size
|
|
||||||
|
dirInfoSize.mutex.Lock()
|
||||||
|
dirInfoSize.size[dir] = size
|
||||||
|
dirInfoSize.mutex.Unlock()
|
||||||
|
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -718,11 +747,9 @@ func (s *HTTPStaticServer) readAccessConf(realPath string) (ac AccessConf) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func deepPath(basedir, name string) string {
|
func deepPath(basedir, name string, maxDepth int) string {
|
||||||
isDir := true
|
|
||||||
// loop max 5, incase of for loop not finished
|
// loop max 5, incase of for loop not finished
|
||||||
maxDepth := 5
|
for depth := 0; depth <= maxDepth; depth += 1 {
|
||||||
for depth := 0; depth <= maxDepth && isDir; depth += 1 {
|
|
||||||
finfos, err := ioutil.ReadDir(filepath.Join(basedir, name))
|
finfos, err := ioutil.ReadDir(filepath.Join(basedir, name))
|
||||||
if err != nil || len(finfos) != 1 {
|
if err != nil || len(finfos) != 1 {
|
||||||
break
|
break
|
||||||
@@ -774,7 +801,7 @@ var (
|
|||||||
_tmpls = make(map[string]*template.Template)
|
_tmpls = make(map[string]*template.Template)
|
||||||
)
|
)
|
||||||
|
|
||||||
func executeTemplate(w http.ResponseWriter, name string, v interface{}) {
|
func renderHTML(w http.ResponseWriter, name string, v interface{}) {
|
||||||
if t, ok := _tmpls[name]; ok {
|
if t, ok := _tmpls[name]; ok {
|
||||||
t.Execute(w, v)
|
t.Execute(w, v)
|
||||||
return
|
return
|
||||||
@@ -784,19 +811,34 @@ func executeTemplate(w http.ResponseWriter, name string, v interface{}) {
|
|||||||
t.Execute(w, v)
|
t.Execute(w, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderHTML(w http.ResponseWriter, name string, v interface{}) {
|
|
||||||
if _, ok := Assets.(http.Dir); ok {
|
|
||||||
log.Println("Hot load", name)
|
|
||||||
t := template.Must(template.New(name).Funcs(funcMap).Delims("[[", "]]").Parse(assetsContent(name)))
|
|
||||||
t.Execute(w, v)
|
|
||||||
} else {
|
|
||||||
executeTemplate(w, name, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkFilename(name string) error {
|
func checkFilename(name string) error {
|
||||||
if strings.ContainsAny(name, "\\/:*<>|") {
|
if strings.ContainsAny(name, "\\/:*<>|") {
|
||||||
return errors.New("Name should not contains \\/:*<>|")
|
return errors.New("Name should not contains \\/:*<>|")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *HTTPStaticServer) hVideoPlayer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
path := mux.Vars(r)["path"]
|
||||||
|
realPath := s.getRealPath(r)
|
||||||
|
extension := strings.ToLower(strings.TrimPrefix(filepath.Ext(path), "."))
|
||||||
|
|
||||||
|
if _, err := os.Stat(realPath); os.IsNotExist(err) {
|
||||||
|
http.Error(w, "File not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := filepath.Base(path)
|
||||||
|
|
||||||
|
scheme := "http"
|
||||||
|
if r.TLS != nil {
|
||||||
|
scheme = "https"
|
||||||
|
}
|
||||||
|
videoURL := fmt.Sprintf("%s://%s/%s", scheme, r.Host, path)
|
||||||
|
|
||||||
|
renderHTML(w, "assets/video-player.html", map[string]interface{}{
|
||||||
|
"FileName": fileName,
|
||||||
|
"VideoURL": videoURL,
|
||||||
|
"Extension": extension,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
90
main.go
90
main.go
@@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/subtle"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -22,7 +24,6 @@ import (
|
|||||||
"github.com/goji/httpauth"
|
"github.com/goji/httpauth"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
_ "github.com/shurcooL/vfsgen"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Configure struct {
|
type Configure struct {
|
||||||
@@ -34,7 +35,6 @@ type Configure struct {
|
|||||||
HTTPAuth string `yaml:"httpauth"`
|
HTTPAuth string `yaml:"httpauth"`
|
||||||
Cert string `yaml:"cert"`
|
Cert string `yaml:"cert"`
|
||||||
Key string `yaml:"key"`
|
Key string `yaml:"key"`
|
||||||
Cors bool `yaml:"cors"`
|
|
||||||
Theme string `yaml:"theme"`
|
Theme string `yaml:"theme"`
|
||||||
XHeaders bool `yaml:"xheaders"`
|
XHeaders bool `yaml:"xheaders"`
|
||||||
Upload bool `yaml:"upload"`
|
Upload bool `yaml:"upload"`
|
||||||
@@ -44,12 +44,14 @@ type Configure struct {
|
|||||||
Debug bool `yaml:"debug"`
|
Debug bool `yaml:"debug"`
|
||||||
GoogleTrackerID string `yaml:"google-tracker-id"`
|
GoogleTrackerID string `yaml:"google-tracker-id"`
|
||||||
Auth struct {
|
Auth struct {
|
||||||
Type string `yaml:"type"` // openid|http|github
|
Type string `yaml:"type"` // openid|http|github
|
||||||
OpenID string `yaml:"openid"`
|
OpenID string `yaml:"openid"`
|
||||||
HTTP string `yaml:"http"`
|
HTTP []string `yaml:"http"`
|
||||||
ID string `yaml:"id"` // for oauth2
|
ID string `yaml:"id"` // for oauth2
|
||||||
Secret string `yaml:"secret"` // for oauth2
|
Secret string `yaml:"secret"` // for oauth2
|
||||||
} `yaml:"auth"`
|
} `yaml:"auth"`
|
||||||
|
DeepPathMaxDepth int `yaml:"deep-path-max-depth"`
|
||||||
|
NoIndex bool `yaml:"no-index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpLogger struct{}
|
type httpLogger struct{}
|
||||||
@@ -100,6 +102,8 @@ func parseFlags() error {
|
|||||||
gcfg.Auth.OpenID = defaultOpenID
|
gcfg.Auth.OpenID = defaultOpenID
|
||||||
gcfg.GoogleTrackerID = "UA-81205425-2"
|
gcfg.GoogleTrackerID = "UA-81205425-2"
|
||||||
gcfg.Title = "Go HTTP File Server"
|
gcfg.Title = "Go HTTP File Server"
|
||||||
|
gcfg.DeepPathMaxDepth = 5
|
||||||
|
gcfg.NoIndex = false
|
||||||
|
|
||||||
kingpin.HelpFlag.Short('h')
|
kingpin.HelpFlag.Short('h')
|
||||||
kingpin.Version(versionMessage())
|
kingpin.Version(versionMessage())
|
||||||
@@ -111,17 +115,18 @@ func parseFlags() error {
|
|||||||
kingpin.Flag("cert", "tls cert.pem path").StringVar(&gcfg.Cert)
|
kingpin.Flag("cert", "tls cert.pem path").StringVar(&gcfg.Cert)
|
||||||
kingpin.Flag("key", "tls key.pem path").StringVar(&gcfg.Key)
|
kingpin.Flag("key", "tls key.pem path").StringVar(&gcfg.Key)
|
||||||
kingpin.Flag("auth-type", "Auth type <http|openid>").StringVar(&gcfg.Auth.Type)
|
kingpin.Flag("auth-type", "Auth type <http|openid>").StringVar(&gcfg.Auth.Type)
|
||||||
kingpin.Flag("auth-http", "HTTP basic auth (ex: user:pass)").StringVar(&gcfg.Auth.HTTP)
|
kingpin.Flag("auth-http", "HTTP basic auth (ex: user:pass)").StringsVar(&gcfg.Auth.HTTP)
|
||||||
kingpin.Flag("auth-openid", "OpenID auth identity url").StringVar(&gcfg.Auth.OpenID)
|
kingpin.Flag("auth-openid", "OpenID auth identity url").StringVar(&gcfg.Auth.OpenID)
|
||||||
kingpin.Flag("theme", "web theme, one of <black|green>").StringVar(&gcfg.Theme)
|
kingpin.Flag("theme", "web theme, one of <black|green>").StringVar(&gcfg.Theme)
|
||||||
kingpin.Flag("upload", "enable upload support").BoolVar(&gcfg.Upload)
|
kingpin.Flag("upload", "enable upload support").BoolVar(&gcfg.Upload)
|
||||||
kingpin.Flag("delete", "enable delete support").BoolVar(&gcfg.Delete)
|
kingpin.Flag("delete", "enable delete support").BoolVar(&gcfg.Delete)
|
||||||
kingpin.Flag("xheaders", "used when behide nginx").BoolVar(&gcfg.XHeaders)
|
kingpin.Flag("xheaders", "used when behide nginx").BoolVar(&gcfg.XHeaders)
|
||||||
kingpin.Flag("cors", "enable cross-site HTTP request").BoolVar(&gcfg.Cors)
|
|
||||||
kingpin.Flag("debug", "enable debug mode").BoolVar(&gcfg.Debug)
|
kingpin.Flag("debug", "enable debug mode").BoolVar(&gcfg.Debug)
|
||||||
kingpin.Flag("plistproxy", "plist proxy when server is not https").Short('p').StringVar(&gcfg.PlistProxy)
|
kingpin.Flag("plistproxy", "plist proxy when server is not https").Short('p').StringVar(&gcfg.PlistProxy)
|
||||||
kingpin.Flag("title", "server title").StringVar(&gcfg.Title)
|
kingpin.Flag("title", "server title").StringVar(&gcfg.Title)
|
||||||
kingpin.Flag("google-tracker-id", "set to empty to disable it").StringVar(&gcfg.GoogleTrackerID)
|
kingpin.Flag("google-tracker-id", "set to empty to disable it").StringVar(&gcfg.GoogleTrackerID)
|
||||||
|
kingpin.Flag("deep-path-max-depth", "set to -1 to not combine dirs").IntVar(&gcfg.DeepPathMaxDepth)
|
||||||
|
kingpin.Flag("no-index", "disable indexing").BoolVar(&gcfg.NoIndex)
|
||||||
|
|
||||||
kingpin.Parse() // first parse conf
|
kingpin.Parse() // first parse conf
|
||||||
|
|
||||||
@@ -149,6 +154,41 @@ func fixPrefix(prefix string) string {
|
|||||||
return prefix
|
return prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cors(next http.Handler) http.Handler {
|
||||||
|
// access control and CORS middleware
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
w.Header().Set("Access-Control-Allow-Methods", "*")
|
||||||
|
w.Header().Set("Access-Control-Allow-Headers", "*")
|
||||||
|
if r.Method == "OPTIONS" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func multiBasicAuth(auths []string) func(http.Handler) http.Handler {
|
||||||
|
userPassMap := make(map[string]string)
|
||||||
|
for _, auth := range auths {
|
||||||
|
userpass := strings.SplitN(auth, ":", 2)
|
||||||
|
if len(userpass) == 2 {
|
||||||
|
userPassMap[userpass[0]] = userpass[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return httpauth.BasicAuth(httpauth.AuthOptions{
|
||||||
|
Realm: "Restricted",
|
||||||
|
AuthFunc: func(user, pass string, request *http.Request) bool {
|
||||||
|
password, ok := userPassMap[user]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
givenPass := sha256.Sum256([]byte(pass))
|
||||||
|
requiredPass := sha256.Sum256([]byte(password))
|
||||||
|
return subtle.ConstantTimeCompare(givenPass[:], requiredPass[:]) == 1
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := parseFlags(); err != nil {
|
if err := parseFlags(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -165,7 +205,7 @@ func main() {
|
|||||||
log.Printf("url prefix: %s", gcfg.Prefix)
|
log.Printf("url prefix: %s", gcfg.Prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
ss := NewHTTPStaticServer(gcfg.Root)
|
ss := NewHTTPStaticServer(gcfg.Root, gcfg.NoIndex)
|
||||||
ss.Prefix = gcfg.Prefix
|
ss.Prefix = gcfg.Prefix
|
||||||
ss.Theme = gcfg.Theme
|
ss.Theme = gcfg.Theme
|
||||||
ss.Title = gcfg.Title
|
ss.Title = gcfg.Title
|
||||||
@@ -173,6 +213,7 @@ func main() {
|
|||||||
ss.Upload = gcfg.Upload
|
ss.Upload = gcfg.Upload
|
||||||
ss.Delete = gcfg.Delete
|
ss.Delete = gcfg.Delete
|
||||||
ss.AuthType = gcfg.Auth.Type
|
ss.AuthType = gcfg.Auth.Type
|
||||||
|
ss.DeepPathMaxDepth = gcfg.DeepPathMaxDepth
|
||||||
|
|
||||||
if gcfg.PlistProxy != "" {
|
if gcfg.PlistProxy != "" {
|
||||||
u, err := url.Parse(gcfg.PlistProxy)
|
u, err := url.Parse(gcfg.PlistProxy)
|
||||||
@@ -191,13 +232,9 @@ func main() {
|
|||||||
hdlr = accesslog.NewLoggingHandler(hdlr, logger)
|
hdlr = accesslog.NewLoggingHandler(hdlr, logger)
|
||||||
|
|
||||||
// HTTP Basic Authentication
|
// HTTP Basic Authentication
|
||||||
userpass := strings.SplitN(gcfg.Auth.HTTP, ":", 2)
|
|
||||||
switch gcfg.Auth.Type {
|
switch gcfg.Auth.Type {
|
||||||
case "http":
|
case "http":
|
||||||
if len(userpass) == 2 {
|
hdlr = multiBasicAuth(gcfg.Auth.HTTP)(hdlr)
|
||||||
user, pass := userpass[0], userpass[1]
|
|
||||||
hdlr = httpauth.SimpleBasicAuth(user, pass)(hdlr)
|
|
||||||
}
|
|
||||||
case "openid":
|
case "openid":
|
||||||
handleOpenID(gcfg.Auth.OpenID, false) // FIXME(ssx): set secure default to false
|
handleOpenID(gcfg.Auth.OpenID, false) // FIXME(ssx): set secure default to false
|
||||||
// case "github":
|
// case "github":
|
||||||
@@ -207,25 +244,24 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CORS
|
// CORS
|
||||||
if gcfg.Cors {
|
hdlr = cors(hdlr)
|
||||||
hdlr = handlers.CORS()(hdlr)
|
|
||||||
}
|
|
||||||
if gcfg.XHeaders {
|
if gcfg.XHeaders {
|
||||||
hdlr = handlers.ProxyHeaders(hdlr)
|
hdlr = handlers.ProxyHeaders(hdlr)
|
||||||
}
|
}
|
||||||
|
|
||||||
mainRooter := mux.NewRouter()
|
mainRouter := mux.NewRouter()
|
||||||
rooter := mainRooter
|
router := mainRouter
|
||||||
if gcfg.Prefix != "" {
|
if gcfg.Prefix != "" {
|
||||||
rooter = mainRooter.PathPrefix(gcfg.Prefix).Subrouter()
|
router = mainRouter.PathPrefix(gcfg.Prefix).Subrouter()
|
||||||
mainRooter.Handle(gcfg.Prefix, hdlr)
|
mainRouter.Handle(gcfg.Prefix, hdlr)
|
||||||
mainRooter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
mainRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, gcfg.Prefix, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, gcfg.Prefix, http.StatusTemporaryRedirect)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
rooter.PathPrefix("/-/assets/").Handler(http.StripPrefix(gcfg.Prefix+"/-/assets/", http.FileServer(Assets)))
|
router.PathPrefix("/-/assets/").Handler(http.StripPrefix(gcfg.Prefix+"/-/", http.FileServer(Assets)))
|
||||||
rooter.HandleFunc("/-/sysinfo", func(w http.ResponseWriter, r *http.Request) {
|
router.HandleFunc("/-/sysinfo", func(w http.ResponseWriter, r *http.Request) {
|
||||||
data, _ := json.Marshal(map[string]interface{}{
|
data, _ := json.Marshal(map[string]interface{}{
|
||||||
"version": VERSION,
|
"version": VERSION,
|
||||||
})
|
})
|
||||||
@@ -233,7 +269,7 @@ func main() {
|
|||||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
})
|
})
|
||||||
rooter.PathPrefix("/").Handler(hdlr)
|
router.PathPrefix("/").Handler(hdlr)
|
||||||
|
|
||||||
if gcfg.Addr == "" {
|
if gcfg.Addr == "" {
|
||||||
gcfg.Addr = fmt.Sprintf(":%d", gcfg.Port)
|
gcfg.Addr = fmt.Sprintf(":%d", gcfg.Port)
|
||||||
@@ -245,7 +281,7 @@ func main() {
|
|||||||
log.Printf("listening on %s, local address http://%s:%s\n", strconv.Quote(gcfg.Addr), getLocalIP(), port)
|
log.Printf("listening on %s, local address http://%s:%s\n", strconv.Quote(gcfg.Addr), getLocalIP(), port)
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Handler: mainRooter,
|
Handler: mainRouter,
|
||||||
Addr: gcfg.Addr,
|
Addr: gcfg.Addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
testdata/filetypes/script.js
vendored
Normal file
1
testdata/filetypes/script.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
document.write("Hello world!")
|
||||||
Reference in New Issue
Block a user