support .ghs.yml add google analytics

This commit is contained in:
codeskyblue
2016-08-02 13:34:02 +08:00
parent 2a229b80ab
commit 43cb263853
7 changed files with 118 additions and 29 deletions

View File

@@ -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:

View File

@@ -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>

View File

@@ -14,6 +14,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/go-yaml/yaml"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@@ -23,11 +24,12 @@ type IndexFileItem struct {
} }
type HTTPStaticServer struct { type HTTPStaticServer struct {
Root string Root string
Theme string Theme string
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

32
main.go
View File

@@ -18,24 +18,25 @@ import (
) )
type Configure struct { type Configure struct {
Addr string Addr string
Root string Root string
HttpAuth string HttpAuth string
Cert string Cert string
Key string Key string
Cors bool Cors bool
Theme string Theme string
XHeaders bool XHeaders bool
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 != "" {

View File

@@ -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>

View File

@@ -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
View File

@@ -0,0 +1,2 @@
---
upload: true