clash/hub/route/server.go

167 lines
3.4 KiB
Go
Raw Normal View History

2018-11-21 05:47:46 +00:00
package route
2018-06-18 03:31:49 +00:00
import (
"encoding/json"
"net/http"
2018-10-06 05:15:02 +00:00
"strings"
2018-06-18 03:31:49 +00:00
"time"
2018-11-21 05:47:46 +00:00
"github.com/Dreamacro/clash/log"
2018-07-15 14:23:20 +00:00
T "github.com/Dreamacro/clash/tunnel"
2018-06-18 03:31:49 +00:00
"github.com/go-chi/chi"
2018-07-19 01:31:53 +00:00
"github.com/go-chi/cors"
2018-06-18 03:31:49 +00:00
"github.com/go-chi/render"
)
2018-11-21 05:47:46 +00:00
var (
serverSecret = ""
serverAddr = ""
2018-12-19 17:29:13 +00:00
uiPath = ""
2018-11-21 05:47:46 +00:00
)
2018-10-06 05:15:02 +00:00
2018-06-18 03:31:49 +00:00
type Traffic struct {
Up int64 `json:"up"`
Down int64 `json:"down"`
}
2018-12-19 17:29:13 +00:00
func SetUIPath(path string) {
uiPath = path
}
2018-11-21 05:47:46 +00:00
func Start(addr string, secret string) {
if serverAddr != "" {
return
}
2018-11-21 05:47:46 +00:00
serverAddr = addr
serverSecret = secret
2018-06-18 03:31:49 +00:00
r := chi.NewRouter()
2018-07-19 01:31:53 +00:00
cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
2018-12-29 06:11:54 +00:00
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
2018-10-06 05:15:02 +00:00
AllowedHeaders: []string{"Content-Type", "Authorization"},
2018-07-19 01:31:53 +00:00
MaxAge: 300,
})
2018-12-29 06:11:54 +00:00
root := chi.NewRouter().With(jsonContentType)
root.Get("/traffic", traffic)
root.Get("/logs", getLogs)
r.Get("/", hello)
r.Group(func(r chi.Router) {
r.Use(cors.Handler, authentication)
2018-12-29 06:11:54 +00:00
r.Mount("/", root)
r.Mount("/configs", configRouter())
r.Mount("/proxies", proxyRouter())
r.Mount("/rules", ruleRouter())
})
2018-06-18 03:31:49 +00:00
2018-12-19 17:29:13 +00:00
if uiPath != "" {
r.Group(func(r chi.Router) {
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(uiPath)))
r.Get("/ui", http.RedirectHandler("/ui/", 301).ServeHTTP)
r.Get("/ui/*", func(w http.ResponseWriter, r *http.Request) {
fs.ServeHTTP(w, r)
})
2018-12-19 17:29:13 +00:00
})
}
2018-11-21 05:47:46 +00:00
log.Infoln("RESTful API listening at: %s", addr)
2018-06-18 03:31:49 +00:00
err := http.ListenAndServe(addr, r)
if err != nil {
2018-11-21 05:47:46 +00:00
log.Errorln("External controller error: %s", err.Error())
2018-06-18 03:31:49 +00:00
}
}
func jsonContentType(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
2018-10-06 05:15:02 +00:00
func authentication(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
header := r.Header.Get("Authorization")
text := strings.SplitN(header, " ", 2)
2018-11-21 05:47:46 +00:00
if serverSecret == "" {
2018-10-06 05:15:02 +00:00
next.ServeHTTP(w, r)
return
}
hasUnvalidHeader := text[0] != "Bearer"
2018-11-21 05:47:46 +00:00
hasUnvalidSecret := len(text) == 2 && text[1] != serverSecret
2018-10-06 05:15:02 +00:00
if hasUnvalidHeader || hasUnvalidSecret {
2018-12-10 03:33:37 +00:00
render.Status(r, http.StatusUnauthorized)
render.JSON(w, r, ErrUnauthorized)
2018-10-06 05:15:02 +00:00
return
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
2018-12-29 06:11:54 +00:00
func hello(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, render.M{"hello": "clash"})
}
2018-06-18 03:31:49 +00:00
func traffic(w http.ResponseWriter, r *http.Request) {
2018-12-10 03:33:37 +00:00
render.Status(r, http.StatusOK)
2018-06-18 03:31:49 +00:00
tick := time.NewTicker(time.Second)
2018-11-21 05:47:46 +00:00
t := T.Instance().Traffic()
2018-06-18 03:31:49 +00:00
for range tick.C {
up, down := t.Now()
if err := json.NewEncoder(w).Encode(Traffic{
Up: up,
Down: down,
}); err != nil {
break
}
w.(http.Flusher).Flush()
}
}
2018-06-22 05:15:20 +00:00
type Log struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
2018-06-18 03:31:49 +00:00
func getLogs(w http.ResponseWriter, r *http.Request) {
2018-08-12 11:35:13 +00:00
levelText := r.URL.Query().Get("level")
if levelText == "" {
levelText = "info"
2018-06-22 05:15:20 +00:00
}
2018-11-21 05:47:46 +00:00
level, ok := log.LogLevelMapping[levelText]
2018-06-22 05:15:20 +00:00
if !ok {
2018-12-10 03:33:37 +00:00
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, ErrBadRequest)
2018-06-22 05:15:20 +00:00
return
}
2018-11-21 05:47:46 +00:00
sub := log.Subscribe()
2018-06-18 03:31:49 +00:00
render.Status(r, http.StatusOK)
for elm := range sub {
2018-11-21 05:47:46 +00:00
log := elm.(*log.Event)
if log.LogLevel < level {
2018-06-22 05:15:20 +00:00
continue
}
2018-06-18 03:31:49 +00:00
if err := json.NewEncoder(w).Encode(Log{
Type: log.Type(),
Payload: log.Payload,
}); err != nil {
break
}
w.(http.Flusher).Flush()
}
}