mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-11-04 13:35:46 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			175 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package middleware
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/json"
 | 
						|
	. "github.com/mickael-kerjean/filestash/server/common"
 | 
						|
	"net/http"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
type Middleware func(func(*App, http.ResponseWriter, *http.Request)) func(*App, http.ResponseWriter, *http.Request)
 | 
						|
 | 
						|
func NewMiddlewareChain(fn func(*App, http.ResponseWriter, *http.Request), m []Middleware, app App) http.HandlerFunc {
 | 
						|
 | 
						|
	return func(res http.ResponseWriter, req *http.Request) {
 | 
						|
		var resw ResponseWriter = NewResponseWriter(res)
 | 
						|
		var f func(*App, http.ResponseWriter, *http.Request) = fn
 | 
						|
		for i := len(m) - 1; i >= 0; i-- {
 | 
						|
			f = m[i](f)
 | 
						|
		}
 | 
						|
		app.Context = req.Context()
 | 
						|
		f(&app, &resw, req)
 | 
						|
		if req.Body != nil {
 | 
						|
			req.Body.Close()
 | 
						|
		}
 | 
						|
		go Logger(app, &resw, req)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type ResponseWriter struct {
 | 
						|
	http.ResponseWriter
 | 
						|
	status int
 | 
						|
	start  time.Time
 | 
						|
}
 | 
						|
 | 
						|
func NewResponseWriter(res http.ResponseWriter) ResponseWriter {
 | 
						|
	return ResponseWriter{
 | 
						|
		ResponseWriter: res,
 | 
						|
		start:          time.Now(),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (w *ResponseWriter) WriteHeader(status int) {
 | 
						|
	w.status = status
 | 
						|
	w.ResponseWriter.WriteHeader(status)
 | 
						|
}
 | 
						|
 | 
						|
func (w *ResponseWriter) Write(b []byte) (int, error) {
 | 
						|
	if w.status == 0 {
 | 
						|
		w.status = 200
 | 
						|
	}
 | 
						|
	return w.ResponseWriter.Write(b)
 | 
						|
}
 | 
						|
 | 
						|
type LogEntry struct {
 | 
						|
	Host       string  `json:"host"`
 | 
						|
	Method     string  `json:"method"`
 | 
						|
	RequestURI string  `json:"pathname"`
 | 
						|
	Proto      string  `json:"proto"`
 | 
						|
	Status     int     `json:"status"`
 | 
						|
	Scheme     string  `json:"scheme"`
 | 
						|
	UserAgent  string  `json:"userAgent"`
 | 
						|
	Ip         string  `json:"ip"`
 | 
						|
	Referer    string  `json:"referer"`
 | 
						|
	Duration   float64 `json:"responseTime"`
 | 
						|
	Version    string  `json:"version"`
 | 
						|
	Backend    string  `json:"backend"`
 | 
						|
	Share      string  `json:"share"`
 | 
						|
	License    string  `json:"license"`
 | 
						|
	Session    string  `json:"session"`
 | 
						|
	RequestID  string  `json:"requestID"`
 | 
						|
}
 | 
						|
 | 
						|
func Logger(ctx App, res http.ResponseWriter, req *http.Request) {
 | 
						|
	if obj, ok := res.(*ResponseWriter); ok && req.RequestURI != "/about" {
 | 
						|
		point := LogEntry{
 | 
						|
			Version:    APP_VERSION + "." + BUILD_DATE,
 | 
						|
			License:    LICENSE,
 | 
						|
			Scheme:     req.URL.Scheme,
 | 
						|
			Host:       req.Host,
 | 
						|
			Method:     req.Method,
 | 
						|
			RequestURI: req.RequestURI,
 | 
						|
			Proto:      req.Proto,
 | 
						|
			Status:     obj.status,
 | 
						|
			UserAgent:  req.Header.Get("User-Agent"),
 | 
						|
			Ip:         req.RemoteAddr,
 | 
						|
			Referer:    req.Referer(),
 | 
						|
			Duration:   float64(time.Now().Sub(obj.start)) / (1000 * 1000),
 | 
						|
			Backend: func() string {
 | 
						|
				if ctx.Session["type"] == "" {
 | 
						|
					return "null"
 | 
						|
				}
 | 
						|
				return ctx.Session["type"]
 | 
						|
			}(),
 | 
						|
			Share: func() string {
 | 
						|
				if ctx.Share.Id == "" {
 | 
						|
					return "null"
 | 
						|
				}
 | 
						|
				return ctx.Share.Id
 | 
						|
			}(),
 | 
						|
			Session: func() string {
 | 
						|
				if ctx.Session["type"] == "" {
 | 
						|
					return "null"
 | 
						|
				}
 | 
						|
				return GenerateID(&ctx)
 | 
						|
			}(),
 | 
						|
			RequestID: func() string {
 | 
						|
				defer func() string {
 | 
						|
					if r := recover(); r != nil {
 | 
						|
						Log.Debug("middleware::index get header '%s'", r)
 | 
						|
					}
 | 
						|
					return "null"
 | 
						|
				}()
 | 
						|
				return res.Header().Get("X-Request-ID")
 | 
						|
			}(),
 | 
						|
		}
 | 
						|
		if Config.Get("log.telemetry").Bool() {
 | 
						|
			telemetry.Record(point)
 | 
						|
		}
 | 
						|
		if Config.Get("log.enable").Bool() {
 | 
						|
			Log.Stdout("HTTP %3d %3s %6.1fms %s", point.Status, point.Method, point.Duration, point.RequestURI)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type Telemetry struct {
 | 
						|
	Data []LogEntry
 | 
						|
	mu   sync.Mutex
 | 
						|
}
 | 
						|
 | 
						|
func (this *Telemetry) Record(point LogEntry) {
 | 
						|
	this.mu.Lock()
 | 
						|
	this.Data = append(this.Data, point)
 | 
						|
	this.mu.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
func (this *Telemetry) Flush() {
 | 
						|
	if len(this.Data) == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	this.mu.Lock()
 | 
						|
	pts := this.Data
 | 
						|
	this.Data = make([]LogEntry, 0)
 | 
						|
	this.mu.Unlock()
 | 
						|
 | 
						|
	body, err := json.Marshal(pts)
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	r, err := http.NewRequest("POST", "https://downloads.filestash.app/event", bytes.NewReader(body))
 | 
						|
	r.Header.Set("Connection", "Close")
 | 
						|
	r.Header.Set("Content-Type", "application/json")
 | 
						|
	r.Close = true
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	resp, err := HTTP.Do(r)
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	resp.Body.Close()
 | 
						|
}
 | 
						|
 | 
						|
var telemetry Telemetry = Telemetry{Data: make([]LogEntry, 0)}
 | 
						|
 | 
						|
func init() {
 | 
						|
	go func() {
 | 
						|
		for {
 | 
						|
			time.Sleep(10 * time.Second)
 | 
						|
			telemetry.Flush()
 | 
						|
		}
 | 
						|
	}()
 | 
						|
}
 |