Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a950b98db | ||
|
|
033f0a9271 | ||
|
|
1f68fcb45f | ||
|
|
b81894b0cf | ||
|
|
cfaa27b981 | ||
|
|
ffcf9cc742 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -29,3 +29,4 @@ bindata_assetfs.go
|
|||||||
assets_vfsdata.go
|
assets_vfsdata.go
|
||||||
*.un~
|
*.un~
|
||||||
*.swp
|
*.swp
|
||||||
|
ghs-stats.json
|
||||||
@@ -109,6 +109,7 @@
|
|||||||
<th class="hidden-xs">
|
<th class="hidden-xs">
|
||||||
<span style="cursor: pointer" v-on:click='mtimeTypeFromNow = !mtimeTypeFromNow'>ModTime</span>
|
<span style="cursor: pointer" v-on:click='mtimeTypeFromNow = !mtimeTypeFromNow'>ModTime</span>
|
||||||
</th>
|
</th>
|
||||||
|
<th>Downloads</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -126,6 +127,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td><span v-if="f.type == 'dir'">~</span> {{f.size | formatBytes}}</td>
|
<td><span v-if="f.type == 'dir'">~</span> {{f.size | formatBytes}}</td>
|
||||||
<td class="hidden-xs">{{formatTime(f.mtime)}}</td>
|
<td class="hidden-xs">{{formatTime(f.mtime)}}</td>
|
||||||
|
<td class="hidden-xs">{{f.dldcnt}}</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="/-/zip/{{f.path}}">
|
<a class="btn btn-default btn-xs" href="/-/zip/{{f.path}}">
|
||||||
|
|||||||
107
db.go
Normal file
107
db.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func panicErr(e error) {
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Model struct {
|
||||||
|
Filename string
|
||||||
|
Data *map[string]int
|
||||||
|
LastSave time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var DBModel Model = Model{}
|
||||||
|
|
||||||
|
// var SaveInterval float64 = 60.0 // seconds
|
||||||
|
const SaveInterval = 60.0
|
||||||
|
|
||||||
|
func (model *Model) DBRead(filename string) *map[string]int {
|
||||||
|
data := make(map[string]int)
|
||||||
|
model.Filename = filename
|
||||||
|
model.Data = &data
|
||||||
|
|
||||||
|
// create if file not exist
|
||||||
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||||
|
f, cerr := os.Create(filename)
|
||||||
|
panicErr(cerr)
|
||||||
|
|
||||||
|
s, jerr := json.Marshal(data)
|
||||||
|
panicErr(jerr)
|
||||||
|
f.Write(s)
|
||||||
|
defer f.Close()
|
||||||
|
return &data
|
||||||
|
}
|
||||||
|
|
||||||
|
dat, err := ioutil.ReadFile(filename)
|
||||||
|
panicErr(err)
|
||||||
|
|
||||||
|
err = json.Unmarshal(dat, &data)
|
||||||
|
panicErr(err)
|
||||||
|
|
||||||
|
fmt.Println(data)
|
||||||
|
return &data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (model *Model) DBWrite() error {
|
||||||
|
fmt.Println("db writing")
|
||||||
|
dat, err := json.Marshal(model.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tmp := model.Filename + ".un~"
|
||||||
|
err = ioutil.WriteFile(tmp, dat, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Rename(tmp, model.Filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (model *Model) Incre(key string) {
|
||||||
|
(*model.Data)[key]++
|
||||||
|
fmt.Println(model.Data)
|
||||||
|
go model.AutoSave()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (model *Model) AutoSave() bool {
|
||||||
|
if time.Since(model.LastSave).Seconds() > SaveInterval {
|
||||||
|
model.DBWrite()
|
||||||
|
model.LastSave = time.Now()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type dldEntry struct {
|
||||||
|
ip string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DldCounter is used to validate duplicated download requests
|
||||||
|
type DldCounter struct {
|
||||||
|
IPHistory map[dldEntry]time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var DCounter DldCounter = DldCounter{}
|
||||||
|
var DldInterval float64 = 60.0 // seconds
|
||||||
|
|
||||||
|
func (counter *DldCounter) Validate(ip string, path string) bool {
|
||||||
|
if counter.IPHistory == nil {
|
||||||
|
counter.IPHistory = make(map[dldEntry]time.Time)
|
||||||
|
}
|
||||||
|
entry := dldEntry{ip, path}
|
||||||
|
if time.Since(counter.IPHistory[entry]).Seconds() > DldInterval {
|
||||||
|
counter.IPHistory[entry] = time.Now()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"mime"
|
"mime"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -50,12 +51,13 @@ type HTTPStaticServer struct {
|
|||||||
PlistProxy string
|
PlistProxy string
|
||||||
GoogleTrackerID string
|
GoogleTrackerID string
|
||||||
AuthType string
|
AuthType string
|
||||||
|
DBModel Model
|
||||||
|
|
||||||
indexes []IndexFileItem
|
indexes []IndexFileItem
|
||||||
m *mux.Router
|
m *mux.Router
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPStaticServer(root string) *HTTPStaticServer {
|
func NewHTTPStaticServer(root string, dbmodel Model) *HTTPStaticServer {
|
||||||
if root == "" {
|
if root == "" {
|
||||||
root = "./"
|
root = "./"
|
||||||
}
|
}
|
||||||
@@ -66,9 +68,10 @@ func NewHTTPStaticServer(root string) *HTTPStaticServer {
|
|||||||
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,
|
||||||
Theme: "black",
|
Theme: "black",
|
||||||
m: m,
|
m: m,
|
||||||
|
DBModel: dbmodel,
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -124,6 +127,7 @@ func (s *HTTPStaticServer) hIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
if r.FormValue("download") == "true" {
|
if r.FormValue("download") == "true" {
|
||||||
w.Header().Set("Content-Disposition", "attachment; filename="+strconv.Quote(filepath.Base(path)))
|
w.Header().Set("Content-Disposition", "attachment; filename="+strconv.Quote(filepath.Base(path)))
|
||||||
|
DldIncre(s.DBModel, r.RemoteAddr, path)
|
||||||
}
|
}
|
||||||
http.ServeFile(w, r, relPath)
|
http.ServeFile(w, r, relPath)
|
||||||
}
|
}
|
||||||
@@ -286,6 +290,7 @@ func (s *HTTPStaticServer) hInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (s *HTTPStaticServer) hZip(w http.ResponseWriter, r *http.Request) {
|
func (s *HTTPStaticServer) hZip(w http.ResponseWriter, r *http.Request) {
|
||||||
path := mux.Vars(r)["path"]
|
path := mux.Vars(r)["path"]
|
||||||
|
DldIncre(s.DBModel, r.RemoteAddr, path)
|
||||||
CompressToZip(w, filepath.Join(s.Root, path))
|
CompressToZip(w, filepath.Join(s.Root, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,6 +412,7 @@ type HTTPFileInfo struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
ModTime int64 `json:"mtime"`
|
ModTime int64 `json:"mtime"`
|
||||||
|
DldCnt int `json:"dldcnt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessTable struct {
|
type AccessTable struct {
|
||||||
@@ -541,6 +547,7 @@ func (s *HTTPStaticServer) hJSONList(w http.ResponseWriter, r *http.Request) {
|
|||||||
Name: info.Name(),
|
Name: info.Name(),
|
||||||
Path: path,
|
Path: path,
|
||||||
ModTime: info.ModTime().UnixNano() / 1e6,
|
ModTime: info.ModTime().UnixNano() / 1e6,
|
||||||
|
DldCnt: (*s.DBModel.Data)[path],
|
||||||
}
|
}
|
||||||
if search != "" {
|
if search != "" {
|
||||||
name, err := filepath.Rel(requestPath, path)
|
name, err := filepath.Rel(requestPath, path)
|
||||||
@@ -760,3 +767,10 @@ func checkFilename(name string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DldIncre(model Model, rAddress string, path string) {
|
||||||
|
ip, _, _ := net.SplitHostPort(rAddress)
|
||||||
|
if DCounter.Validate(ip, path) {
|
||||||
|
model.Incre(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
3
main.go
3
main.go
@@ -144,7 +144,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
||||||
|
|
||||||
ss := NewHTTPStaticServer(gcfg.Root)
|
DBModel.DBRead("ghs-stats.json")
|
||||||
|
ss := NewHTTPStaticServer(gcfg.Root, DBModel) // FIXME(ssx): should put DBModel inside HTTPStaticServer
|
||||||
ss.Theme = gcfg.Theme
|
ss.Theme = gcfg.Theme
|
||||||
ss.Title = gcfg.Title
|
ss.Title = gcfg.Title
|
||||||
ss.GoogleTrackerID = gcfg.GoogleTrackerID
|
ss.GoogleTrackerID = gcfg.GoogleTrackerID
|
||||||
|
|||||||
Reference in New Issue
Block a user