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:
- name: ""
pattens:
- '!.git/'
- '**/*.go'
- '**/*.tmpl.html'
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] 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 <https://plistproxy.herokuapp.com/plist>

View file

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

32
main.go
View file

@ -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 != "" {

View file

@ -67,11 +67,9 @@
<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>
</button>
[[ if .Upload ]]
<button class="btn btn-xs btn-default" data-toggle="modal" data-target="#upload-modal">
<button class="btn btn-xs btn-default" v-if="auth.upload" data-toggle="modal" data-target="#upload-modal">
Upload file <i class="fa fa-upload"></i>
</button>
[[ end ]]
</td>
</tr>
<tr>
@ -179,6 +177,24 @@
<script src="/-/res/js/dropzone.js"></script>
<script src="/-/res/bootstrap-3.3.5/js/bootstrap.min.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>
</html>

View file

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

2
testdata/uploadable/.ghs.yml vendored Normal file
View file

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