Add: external controller

This commit is contained in:
Dreamacro 2018-06-18 11:31:49 +08:00
parent bc4ca2818b
commit 79e5338113
5 changed files with 120 additions and 1 deletions

View File

@ -28,12 +28,16 @@ Unfortunately, there is no native elegant way to implement golang's daemon.
So we can use third-party daemon tools like pm2, supervisor, and so on. So we can use third-party daemon tools like pm2, supervisor, and so on.
In the case of pm2, we can start the daemon this way: In the case of [pm2](https://github.com/Unitech/pm2), we can start the daemon this way:
```sh ```sh
pm2 start clash pm2 start clash
``` ```
If you have Docker installed, you can run clash directly using `docker-compose`.
[Run clash in docker](https://github.com/Dreamacro/clash/wiki/Run-clash-in-docker)
## Config ## Config
Configuration file at `$HOME/.config/clash/config.ini` Configuration file at `$HOME/.config/clash/config.ini`
@ -45,6 +49,9 @@ Below is a simple demo configuration file:
port = 7890 port = 7890
socks-port = 7891 socks-port = 7891
# A RESTful API for clash
external-controller = 127.0.0.1:8080
[Proxy] [Proxy]
# name = ss, server, port, cipher, password # name = ss, server, port, cipher, password
# The types of cipher are consistent with go-shadowsocks2 # The types of cipher are consistent with go-shadowsocks2

83
hub/server.go Normal file
View File

@ -0,0 +1,83 @@
package hub
import (
"encoding/json"
"net/http"
"time"
"github.com/Dreamacro/clash/tunnel"
"github.com/go-chi/chi"
"github.com/go-chi/render"
log "github.com/sirupsen/logrus"
)
var (
tun = tunnel.GetInstance()
)
type Traffic struct {
Up int64 `json:"up"`
Down int64 `json:"down"`
}
type Log struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
type Error struct {
Error string `json:"error"`
}
func NewHub(addr string) {
r := chi.NewRouter()
r.Get("/traffic", traffic)
r.Get("/logs", getLogs)
err := http.ListenAndServe(addr, r)
if err != nil {
log.Fatalf("External controller error: %s", err.Error())
}
}
func traffic(w http.ResponseWriter, r *http.Request) {
render.Status(r, http.StatusOK)
tick := time.NewTicker(time.Second)
t := tun.Traffic()
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()
}
}
func getLogs(w http.ResponseWriter, r *http.Request) {
src := tun.Log()
sub, err := src.Subscribe()
defer src.UnSubscribe(sub)
if err != nil {
render.Status(r, http.StatusInternalServerError)
render.JSON(w, r, Error{
Error: err.Error(),
})
}
render.Status(r, http.StatusOK)
for elm := range sub {
log := elm.(tunnel.Log)
if err := json.NewEncoder(w).Encode(Log{
Type: log.Type(),
Payload: log.Payload,
}); err != nil {
break
}
w.(http.Flusher).Flush()
}
}

View File

@ -6,6 +6,7 @@ import (
"syscall" "syscall"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/hub"
"github.com/Dreamacro/clash/proxy/http" "github.com/Dreamacro/clash/proxy/http"
"github.com/Dreamacro/clash/proxy/socks" "github.com/Dreamacro/clash/proxy/socks"
"github.com/Dreamacro/clash/tunnel" "github.com/Dreamacro/clash/tunnel"
@ -37,6 +38,11 @@ func main() {
go http.NewHttpProxy(port) go http.NewHttpProxy(port)
go socks.NewSocksProxy(socksPort) go socks.NewSocksProxy(socksPort)
// Hub
if key, err := section.GetKey("external-controller"); err == nil {
go hub.NewHub(key.Value())
}
sigCh := make(chan os.Signal, 1) sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh <-sigCh

View File

@ -20,6 +20,21 @@ type Log struct {
Payload string Payload string
} }
func (l *Log) Type() string {
switch l.LogType {
case INFO:
return "Info"
case WARNING:
return "Warning"
case ERROR:
return "Error"
case DEBUG:
return "Debug"
default:
return "Unknow"
}
}
func print(data Log) { func print(data Log) {
switch data.LogType { switch data.LogType {
case INFO: case INFO:

View File

@ -34,6 +34,14 @@ func (t *Tunnel) Add(req C.ServerAdapter) {
t.queue.In() <- req t.queue.In() <- req
} }
func (t *Tunnel) Traffic() *C.Traffic {
return t.traffic
}
func (t *Tunnel) Log() *observable.Observable {
return t.observable
}
func (t *Tunnel) UpdateConfig() (err error) { func (t *Tunnel) UpdateConfig() (err error) {
cfg, err := C.GetConfig() cfg, err := C.GetConfig()
if err != nil { if err != nil {