From 43cb2638539ff3d4a5466b593fbfe79c836797ba Mon Sep 17 00:00:00 2001 From: codeskyblue Date: Tue, 2 Aug 2016 13:34:02 +0800 Subject: [PATCH] support .ghs.yml add google analytics --- .fsw.yml | 1 + README.md | 21 +++++++++++- httpstaticserver.go | 63 ++++++++++++++++++++++++++++++------ main.go | 32 ++++++++++-------- res/index.tmpl.html | 22 +++++++++++-- res/js/index.js | 6 ++-- testdata/uploadable/.ghs.yml | 2 ++ 7 files changed, 118 insertions(+), 29 deletions(-) create mode 100644 testdata/uploadable/.ghs.yml diff --git a/.fsw.yml b/.fsw.yml index a60a618..889418c 100644 --- a/.fsw.yml +++ b/.fsw.yml @@ -2,6 +2,7 @@ desc: Auto generated by fswatch [gohttp-vue] triggers: - name: "" pattens: + - '!.git/' - '**/*.go' - '**/*.tmpl.html' env: diff --git a/README.md b/README.md index 38f54fa..c7fc42b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Upload size now limited to 1G 1. [x] Hidden work `download` and `qrcode` in small screen 1. [x] Theme select support 1. [x] OK to working behide Nginx -1. [ ] \.htaccess support +1. [ ] \.ghs.yml support (like \.htaccess) 1. [ ] Calculate md5sum and sha 1. [ ] Folder upload 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 ``` +## 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 This is used for server which not https enabled. default use diff --git a/httpstaticserver.go b/httpstaticserver.go index 4787f6f..568113d 100644 --- a/httpstaticserver.go +++ b/httpstaticserver.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "github.com/go-yaml/yaml" "github.com/gorilla/mux" ) @@ -23,11 +24,12 @@ type IndexFileItem struct { } type HTTPStaticServer struct { - Root string - Theme string - Upload bool - Title string - PlistProxy string + Root string + Theme string + Upload bool + Title string + PlistProxy string + GoogleTrackerId string indexes []IndexFileItem m *mux.Router @@ -41,7 +43,7 @@ func NewHTTPStaticServer(root string) *HTTPStaticServer { if !strings.HasSuffix(root, "/") { root = root + "/" } - log.Printf("Root path: %s\n", root) + log.Printf("root path: %s\n", root) m := mux.NewRouter() s := &HTTPStaticServer{ Root: root, @@ -50,10 +52,12 @@ func NewHTTPStaticServer(root string) *HTTPStaticServer { } go func() { + time.Sleep(1 * time.Second) for { - log.Println("making fs index ...") + startTime := time.Now() + log.Println("Started making search index") 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.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) { + 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 if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -111,7 +124,6 @@ func (s *HTTPStaticServer) hUpload(w http.ResponseWriter, req *http.Request) { return } - path := mux.Vars(req)["path"] dirpath := filepath.Join(s.Root, path) for _, mfile := range req.MultipartForm.File["file"] { @@ -263,6 +275,10 @@ type ListResponse struct { Size string `json:"size"` } +type AccessConf struct { + Upload bool `yaml:"upload" json:"upload"` +} + func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) { requestPath := mux.Vars(r)["path"] 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) for path, info := range fileInfoMap { lr := ListResponse{ @@ -318,7 +335,10 @@ func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) { 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.Write(data) } @@ -366,6 +386,29 @@ func (s *HTTPStaticServer) findIndex(text string) []IndexFileItem { 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 { isDir := true // loop max 5, incase of for loop not finished diff --git a/main.go b/main.go index f8ec7df..c7f7abc 100644 --- a/main.go +++ b/main.go @@ -18,24 +18,25 @@ import ( ) type Configure struct { - Addr string - Root string - HttpAuth string - Cert string - Key string - Cors bool - Theme string - XHeaders bool - Upload bool - PlistProxy *url.URL - Title string + Addr string + Root string + HttpAuth string + Cert string + Key string + Cors bool + Theme string + XHeaders bool + Upload bool + PlistProxy *url.URL + Title string + GoogleTrackerId string } type logger struct { } 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 ( @@ -83,6 +84,7 @@ func parseFlags() { 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("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() } @@ -93,6 +95,7 @@ func main() { ss := NewHTTPStaticServer(gcfg.Root) ss.Theme = gcfg.Theme ss.Title = gcfg.Title + ss.GoogleTrackerId = gcfg.GoogleTrackerId if gcfg.Upload { ss.EnableUpload() @@ -129,7 +132,10 @@ func main() { 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 if gcfg.Key != "" && gcfg.Cert != "" { diff --git a/res/index.tmpl.html b/res/index.tmpl.html index a81cb6f..5647658 100644 --- a/res/index.tmpl.html +++ b/res/index.tmpl.html @@ -67,11 +67,9 @@ - [[ if .Upload ]] - - [[ end ]] @@ -179,6 +177,24 @@ + [[if .GoogleTrackerId ]] + + [[ end ]] diff --git a/res/js/index.js b/res/js/index.js index ac11847..5642a6e 100644 --- a/res/js/index.js +++ b/res/js/index.js @@ -27,6 +27,7 @@ var vm = new Vue({ showHidden: false, previewFile: null, version: "loading", + auth: {}, search: getQueryString("search"), files: [{ name: "loading ...", @@ -200,13 +201,14 @@ function loadFileList(pathname) { dataType: "json", cache: false, success: function(res) { - res.sort(function(a, b) { + res.files.sort(function(a, b) { var obj2n = function(v) { return v.type == "dir" ? 0 : 1; } return obj2n(a) - obj2n(b); }) - vm.files = res; + vm.files = res.files; + vm.auth = res.auth; }, error: function(err) { console.error(err) diff --git a/testdata/uploadable/.ghs.yml b/testdata/uploadable/.ghs.yml new file mode 100644 index 0000000..d0d1665 --- /dev/null +++ b/testdata/uploadable/.ghs.yml @@ -0,0 +1,2 @@ +--- +upload: true