support .ghs.yml add google analytics
This commit is contained in:
1
.fsw.yml
1
.fsw.yml
@@ -2,6 +2,7 @@ desc: Auto generated by fswatch [gohttp-vue]
|
|||||||
triggers:
|
triggers:
|
||||||
- name: ""
|
- name: ""
|
||||||
pattens:
|
pattens:
|
||||||
|
- '!.git/'
|
||||||
- '**/*.go'
|
- '**/*.go'
|
||||||
- '**/*.tmpl.html'
|
- '**/*.tmpl.html'
|
||||||
env:
|
env:
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -41,7 +41,7 @@ Upload size now limited to 1G
|
|||||||
1. [x] Hidden work `download` and `qrcode` in small screen
|
1. [x] Hidden work `download` and `qrcode` in small screen
|
||||||
1. [x] Theme select support
|
1. [x] Theme select support
|
||||||
1. [x] OK to working behide Nginx
|
1. [x] OK to working behide Nginx
|
||||||
1. [ ] \.htaccess support
|
1. [ ] \.ghs.yml support (like \.htaccess)
|
||||||
1. [ ] Calculate md5sum and sha
|
1. [ ] Calculate md5sum and sha
|
||||||
1. [ ] Folder upload
|
1. [ ] Folder upload
|
||||||
1. [ ] Support sort by size or modified time
|
1. [ ] Support sort by size or modified time
|
||||||
@@ -64,6 +64,25 @@ Listen port 8000 on all interface, and enable upload
|
|||||||
./gohttpserver -r ./ --addr :8000 --upload
|
./gohttpserver -r ./ --addr :8000 --upload
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Advanced usage
|
||||||
|
Support update access rule if there is a file named `.ghs.yml` under directory. `.ghs.yml` example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
upload: false
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if there is such file under directory `foo`, directory `foo` can not be uploaded, while `bar` can't.
|
||||||
|
|
||||||
|
```
|
||||||
|
root -
|
||||||
|
|-- foo
|
||||||
|
| |-- .ghs.yml
|
||||||
|
| `-- world.txt
|
||||||
|
`-- bar
|
||||||
|
`-- hello.txt
|
||||||
|
```
|
||||||
|
|
||||||
### ipa plist proxy
|
### ipa plist proxy
|
||||||
This is used for server which not https enabled. default use <https://plistproxy.herokuapp.com/plist>
|
This is used for server which not https enabled. default use <https://plistproxy.herokuapp.com/plist>
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-yaml/yaml"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ type HTTPStaticServer struct {
|
|||||||
Upload bool
|
Upload bool
|
||||||
Title string
|
Title string
|
||||||
PlistProxy string
|
PlistProxy string
|
||||||
|
GoogleTrackerId string
|
||||||
|
|
||||||
indexes []IndexFileItem
|
indexes []IndexFileItem
|
||||||
m *mux.Router
|
m *mux.Router
|
||||||
@@ -41,7 +43,7 @@ func NewHTTPStaticServer(root string) *HTTPStaticServer {
|
|||||||
if !strings.HasSuffix(root, "/") {
|
if !strings.HasSuffix(root, "/") {
|
||||||
root = root + "/"
|
root = root + "/"
|
||||||
}
|
}
|
||||||
log.Printf("Root path: %s\n", root)
|
log.Printf("root path: %s\n", root)
|
||||||
m := mux.NewRouter()
|
m := mux.NewRouter()
|
||||||
s := &HTTPStaticServer{
|
s := &HTTPStaticServer{
|
||||||
Root: root,
|
Root: root,
|
||||||
@@ -50,10 +52,12 @@ func NewHTTPStaticServer(root string) *HTTPStaticServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
for {
|
for {
|
||||||
log.Println("making fs index ...")
|
startTime := time.Now()
|
||||||
|
log.Println("Started making search index")
|
||||||
s.makeIndex()
|
s.makeIndex()
|
||||||
log.Println("indexing finished, next index after 10 minutes")
|
log.Printf("Completed search index in %v", time.Since(startTime))
|
||||||
//time.Sleep(time.Second * 1)
|
//time.Sleep(time.Second * 1)
|
||||||
time.Sleep(time.Minute * 10)
|
time.Sleep(time.Minute * 10)
|
||||||
}
|
}
|
||||||
@@ -101,6 +105,15 @@ func (s *HTTPStaticServer) hStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *HTTPStaticServer) hUpload(w http.ResponseWriter, req *http.Request) {
|
func (s *HTTPStaticServer) hUpload(w http.ResponseWriter, req *http.Request) {
|
||||||
|
path := mux.Vars(req)["path"]
|
||||||
|
|
||||||
|
// check auth
|
||||||
|
auth := s.readAccessConf(path)
|
||||||
|
if !auth.Upload {
|
||||||
|
http.Error(w, "Upload forbidden", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err := req.ParseMultipartForm(1 << 30) // max memory 1G
|
err := req.ParseMultipartForm(1 << 30) // max memory 1G
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
@@ -111,7 +124,6 @@ func (s *HTTPStaticServer) hUpload(w http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
path := mux.Vars(req)["path"]
|
|
||||||
dirpath := filepath.Join(s.Root, path)
|
dirpath := filepath.Join(s.Root, path)
|
||||||
|
|
||||||
for _, mfile := range req.MultipartForm.File["file"] {
|
for _, mfile := range req.MultipartForm.File["file"] {
|
||||||
@@ -263,6 +275,10 @@ type ListResponse struct {
|
|||||||
Size string `json:"size"`
|
Size string `json:"size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConf struct {
|
||||||
|
Upload bool `yaml:"upload" json:"upload"`
|
||||||
|
}
|
||||||
|
|
||||||
func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) {
|
func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) {
|
||||||
requestPath := mux.Vars(r)["path"]
|
requestPath := mux.Vars(r)["path"]
|
||||||
localPath := filepath.Join(s.Root, requestPath)
|
localPath := filepath.Join(s.Root, requestPath)
|
||||||
@@ -292,6 +308,7 @@ func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// turn file list -> json
|
||||||
lrs := make([]ListResponse, 0)
|
lrs := make([]ListResponse, 0)
|
||||||
for path, info := range fileInfoMap {
|
for path, info := range fileInfoMap {
|
||||||
lr := ListResponse{
|
lr := ListResponse{
|
||||||
@@ -318,7 +335,10 @@ func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) {
|
|||||||
lrs = append(lrs, lr)
|
lrs = append(lrs, lr)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, _ := json.Marshal(lrs)
|
data, _ := json.Marshal(map[string]interface{}{
|
||||||
|
"files": lrs,
|
||||||
|
"auth": s.readAccessConf(requestPath),
|
||||||
|
})
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
}
|
}
|
||||||
@@ -366,6 +386,29 @@ func (s *HTTPStaticServer) findIndex(text string) []IndexFileItem {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *HTTPStaticServer) defaultAccessConf() AccessConf {
|
||||||
|
return AccessConf{
|
||||||
|
Upload: s.Upload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HTTPStaticServer) readAccessConf(requestPath string) (ac AccessConf) {
|
||||||
|
ac = s.defaultAccessConf()
|
||||||
|
cfgFile := filepath.Join(s.Root, requestPath, ".ghs.yml")
|
||||||
|
data, err := ioutil.ReadFile(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Err read .ghs.yml: %v", err)
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(data, &ac)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Err format .ghs.yml: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func deepPath(basedir, name string) string {
|
func deepPath(basedir, name string) string {
|
||||||
isDir := true
|
isDir := true
|
||||||
// loop max 5, incase of for loop not finished
|
// loop max 5, incase of for loop not finished
|
||||||
|
|||||||
10
main.go
10
main.go
@@ -29,13 +29,14 @@ type Configure struct {
|
|||||||
Upload bool
|
Upload bool
|
||||||
PlistProxy *url.URL
|
PlistProxy *url.URL
|
||||||
Title string
|
Title string
|
||||||
|
GoogleTrackerId string
|
||||||
}
|
}
|
||||||
|
|
||||||
type logger struct {
|
type logger struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l logger) Log(record accesslog.LogRecord) {
|
func (l logger) Log(record accesslog.LogRecord) {
|
||||||
log.Printf("%s [code %d] %s", record.Method, record.Status, record.Uri)
|
log.Printf("%s - %s %d %s", record.Ip, record.Method, record.Status, record.Uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -83,6 +84,7 @@ func parseFlags() {
|
|||||||
kingpin.Flag("cors", "enable cross-site HTTP request").BoolVar(&gcfg.Cors)
|
kingpin.Flag("cors", "enable cross-site HTTP request").BoolVar(&gcfg.Cors)
|
||||||
kingpin.Flag("plistproxy", "plist proxy when server is not https").Default(defaultPlistProxy).Short('p').URLVar(&gcfg.PlistProxy)
|
kingpin.Flag("plistproxy", "plist proxy when server is not https").Default(defaultPlistProxy).Short('p').URLVar(&gcfg.PlistProxy)
|
||||||
kingpin.Flag("title", "server title").Default("Go HTTP File Server").StringVar(&gcfg.Title)
|
kingpin.Flag("title", "server title").Default("Go HTTP File Server").StringVar(&gcfg.Title)
|
||||||
|
kingpin.Flag("google-tracker-id", "set to empty to disable it").Default("UA-81205425-2").StringVar(&gcfg.GoogleTrackerId)
|
||||||
|
|
||||||
kingpin.Parse()
|
kingpin.Parse()
|
||||||
}
|
}
|
||||||
@@ -93,6 +95,7 @@ func main() {
|
|||||||
ss := NewHTTPStaticServer(gcfg.Root)
|
ss := NewHTTPStaticServer(gcfg.Root)
|
||||||
ss.Theme = gcfg.Theme
|
ss.Theme = gcfg.Theme
|
||||||
ss.Title = gcfg.Title
|
ss.Title = gcfg.Title
|
||||||
|
ss.GoogleTrackerId = gcfg.GoogleTrackerId
|
||||||
|
|
||||||
if gcfg.Upload {
|
if gcfg.Upload {
|
||||||
ss.EnableUpload()
|
ss.EnableUpload()
|
||||||
@@ -129,7 +132,10 @@ func main() {
|
|||||||
w.Write(data)
|
w.Write(data)
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Printf("Listening on addr: %s\n", strconv.Quote(gcfg.Addr))
|
if !strings.Contains(gcfg.Addr, ":") {
|
||||||
|
gcfg.Addr = ":" + gcfg.Addr
|
||||||
|
}
|
||||||
|
log.Printf("listening on %s\n", strconv.Quote(gcfg.Addr))
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if gcfg.Key != "" && gcfg.Cert != "" {
|
if gcfg.Key != "" && gcfg.Cert != "" {
|
||||||
|
|||||||
@@ -67,11 +67,9 @@
|
|||||||
<button class="btn btn-xs btn-default" v-on:click='toggleHidden()'>
|
<button class="btn btn-xs btn-default" v-on:click='toggleHidden()'>
|
||||||
Show hidden <i class="fa" v-bind:class='showHidden ? "fa-eye" : "fa-eye-slash"'></i>
|
Show hidden <i class="fa" v-bind:class='showHidden ? "fa-eye" : "fa-eye-slash"'></i>
|
||||||
</button>
|
</button>
|
||||||
[[ if .Upload ]]
|
<button class="btn btn-xs btn-default" v-if="auth.upload" data-toggle="modal" data-target="#upload-modal">
|
||||||
<button class="btn btn-xs btn-default" data-toggle="modal" data-target="#upload-modal">
|
|
||||||
Upload file <i class="fa fa-upload"></i>
|
Upload file <i class="fa fa-upload"></i>
|
||||||
</button>
|
</button>
|
||||||
[[ end ]]
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -179,6 +177,24 @@
|
|||||||
<script src="/-/res/js/dropzone.js"></script>
|
<script src="/-/res/js/dropzone.js"></script>
|
||||||
<script src="/-/res/bootstrap-3.3.5/js/bootstrap.min.js"></script>
|
<script src="/-/res/bootstrap-3.3.5/js/bootstrap.min.js"></script>
|
||||||
<script src="/-/res/js/index.js"></script>
|
<script src="/-/res/js/index.js"></script>
|
||||||
|
[[if .GoogleTrackerId ]]
|
||||||
|
<script>
|
||||||
|
(function(i, s, o, g, r, a, m) {
|
||||||
|
i['GoogleAnalyticsObject'] = r;
|
||||||
|
i[r] = i[r] || function() {
|
||||||
|
(i[r].q = i[r].q || []).push(arguments)
|
||||||
|
}, i[r].l = 1 * new Date();
|
||||||
|
a = s.createElement(o),
|
||||||
|
m = s.getElementsByTagName(o)[0];
|
||||||
|
a.async = 1;
|
||||||
|
a.src = g;
|
||||||
|
m.parentNode.insertBefore(a, m)
|
||||||
|
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
|
||||||
|
|
||||||
|
ga('create', '[[.GoogleTrackerId]]', 'auto');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
</script>
|
||||||
|
[[ end ]]
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ var vm = new Vue({
|
|||||||
showHidden: false,
|
showHidden: false,
|
||||||
previewFile: null,
|
previewFile: null,
|
||||||
version: "loading",
|
version: "loading",
|
||||||
|
auth: {},
|
||||||
search: getQueryString("search"),
|
search: getQueryString("search"),
|
||||||
files: [{
|
files: [{
|
||||||
name: "loading ...",
|
name: "loading ...",
|
||||||
@@ -200,13 +201,14 @@ function loadFileList(pathname) {
|
|||||||
dataType: "json",
|
dataType: "json",
|
||||||
cache: false,
|
cache: false,
|
||||||
success: function(res) {
|
success: function(res) {
|
||||||
res.sort(function(a, b) {
|
res.files.sort(function(a, b) {
|
||||||
var obj2n = function(v) {
|
var obj2n = function(v) {
|
||||||
return v.type == "dir" ? 0 : 1;
|
return v.type == "dir" ? 0 : 1;
|
||||||
}
|
}
|
||||||
return obj2n(a) - obj2n(b);
|
return obj2n(a) - obj2n(b);
|
||||||
})
|
})
|
||||||
vm.files = res;
|
vm.files = res.files;
|
||||||
|
vm.auth = res.auth;
|
||||||
},
|
},
|
||||||
error: function(err) {
|
error: function(err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|||||||
2
testdata/uploadable/.ghs.yml
vendored
Normal file
2
testdata/uploadable/.ghs.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
upload: true
|
||||||
Reference in New Issue
Block a user