Chore: improve code architecture
This commit is contained in:
296
config/config.go
296
config/config.go
@ -5,44 +5,30 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/common/observable"
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
R "github.com/Dreamacro/clash/rules"
|
||||
T "github.com/Dreamacro/clash/tunnel"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
config *Config
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// General config
|
||||
type General struct {
|
||||
Port int
|
||||
SocksPort int
|
||||
RedirPort int
|
||||
AllowLan bool
|
||||
Mode Mode
|
||||
LogLevel C.LogLevel
|
||||
Port int `json:"port"`
|
||||
SocksPort int `json:"socks-port"`
|
||||
RedirPort int `json:"redir-port"`
|
||||
AllowLan bool `json:"allow-lan"`
|
||||
Mode T.Mode `json:"mode"`
|
||||
LogLevel log.LogLevel `json:"log-level"`
|
||||
ExternalController string `json:"external-controller,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
// ProxyConfig is update proxy schema
|
||||
type ProxyConfig struct {
|
||||
Port *int
|
||||
SocksPort *int
|
||||
RedirPort *int
|
||||
AllowLan *bool
|
||||
}
|
||||
|
||||
// RawConfig is raw config struct
|
||||
type RawConfig struct {
|
||||
type rawConfig struct {
|
||||
Port int `yaml:"port"`
|
||||
SocksPort int `yaml:"socks-port"`
|
||||
RedirPort int `yaml:"redir-port"`
|
||||
@ -59,38 +45,16 @@ type RawConfig struct {
|
||||
|
||||
// Config is clash config manager
|
||||
type Config struct {
|
||||
general *General
|
||||
rules []C.Rule
|
||||
proxies map[string]C.Proxy
|
||||
lastUpdate time.Time
|
||||
|
||||
event chan<- interface{}
|
||||
reportCh chan interface{}
|
||||
observable *observable.Observable
|
||||
General *General
|
||||
Rules []C.Rule
|
||||
Proxies map[string]C.Proxy
|
||||
}
|
||||
|
||||
// Event is event of clash config
|
||||
type Event struct {
|
||||
Type string
|
||||
Payload interface{}
|
||||
}
|
||||
|
||||
// Subscribe config stream
|
||||
func (c *Config) Subscribe() observable.Subscription {
|
||||
sub, _ := c.observable.Subscribe()
|
||||
return sub
|
||||
}
|
||||
|
||||
// Report return a channel for collecting report message
|
||||
func (c *Config) Report() chan<- interface{} {
|
||||
return c.reportCh
|
||||
}
|
||||
|
||||
func (c *Config) readConfig() (*RawConfig, error) {
|
||||
if _, err := os.Stat(C.Path.Config()); os.IsNotExist(err) {
|
||||
func readConfig(path string) (*rawConfig, error) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadFile(C.Path.Config())
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -100,10 +64,10 @@ func (c *Config) readConfig() (*RawConfig, error) {
|
||||
}
|
||||
|
||||
// config with some default value
|
||||
rawConfig := &RawConfig{
|
||||
rawConfig := &rawConfig{
|
||||
AllowLan: false,
|
||||
Mode: Rule.String(),
|
||||
LogLevel: C.INFO.String(),
|
||||
Mode: T.Rule.String(),
|
||||
LogLevel: log.INFO.String(),
|
||||
Rule: []string{},
|
||||
Proxy: []map[string]interface{}{},
|
||||
ProxyGroup: []map[string]interface{}{},
|
||||
@ -113,131 +77,69 @@ func (c *Config) readConfig() (*RawConfig, error) {
|
||||
}
|
||||
|
||||
// Parse config
|
||||
func (c *Config) Parse() error {
|
||||
cfg, err := c.readConfig()
|
||||
func Parse(path string) (*Config, error) {
|
||||
config := &Config{}
|
||||
|
||||
rawCfg, err := readConfig(path)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.parseGeneral(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.parseProxies(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.parseRules(cfg)
|
||||
}
|
||||
|
||||
// Proxies return proxies of clash
|
||||
func (c *Config) Proxies() map[string]C.Proxy {
|
||||
return c.proxies
|
||||
}
|
||||
|
||||
// Rules return rules of clash
|
||||
func (c *Config) Rules() []C.Rule {
|
||||
return c.rules
|
||||
}
|
||||
|
||||
// SetMode change mode of clash
|
||||
func (c *Config) SetMode(mode Mode) {
|
||||
c.general.Mode = mode
|
||||
c.event <- &Event{Type: "mode", Payload: mode}
|
||||
}
|
||||
|
||||
// SetLogLevel change log level of clash
|
||||
func (c *Config) SetLogLevel(level C.LogLevel) {
|
||||
c.general.LogLevel = level
|
||||
c.event <- &Event{Type: "log-level", Payload: level}
|
||||
}
|
||||
|
||||
// General return clash general config
|
||||
func (c *Config) General() General {
|
||||
return *c.general
|
||||
}
|
||||
|
||||
// UpdateRules is a function for hot reload rules
|
||||
func (c *Config) UpdateRules() error {
|
||||
cfg, err := c.readConfig()
|
||||
general, err := parseGeneral(rawCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
config.General = general
|
||||
|
||||
return c.parseRules(cfg)
|
||||
proxies, err := parseProxies(rawCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Proxies = proxies
|
||||
|
||||
rules, err := parseRules(rawCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Rules = rules
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (c *Config) parseGeneral(cfg *RawConfig) error {
|
||||
func parseGeneral(cfg *rawConfig) (*General, error) {
|
||||
port := cfg.Port
|
||||
socksPort := cfg.SocksPort
|
||||
redirPort := cfg.RedirPort
|
||||
allowLan := cfg.AllowLan
|
||||
logLevelString := cfg.LogLevel
|
||||
modeString := cfg.Mode
|
||||
externalController := cfg.ExternalController
|
||||
secret := cfg.Secret
|
||||
|
||||
mode, exist := ModeMapping[modeString]
|
||||
mode, exist := T.ModeMapping[modeString]
|
||||
if !exist {
|
||||
return fmt.Errorf("General.mode value invalid")
|
||||
return nil, fmt.Errorf("General.mode value invalid")
|
||||
}
|
||||
|
||||
logLevel, exist := C.LogLevelMapping[logLevelString]
|
||||
logLevel, exist := log.LogLevelMapping[logLevelString]
|
||||
if !exist {
|
||||
return fmt.Errorf("General.log-level value invalid")
|
||||
return nil, fmt.Errorf("General.log-level value invalid")
|
||||
}
|
||||
|
||||
c.general = &General{
|
||||
Port: port,
|
||||
SocksPort: socksPort,
|
||||
RedirPort: redirPort,
|
||||
AllowLan: allowLan,
|
||||
Mode: mode,
|
||||
LogLevel: logLevel,
|
||||
general := &General{
|
||||
Port: port,
|
||||
SocksPort: socksPort,
|
||||
RedirPort: redirPort,
|
||||
AllowLan: allowLan,
|
||||
Mode: mode,
|
||||
LogLevel: logLevel,
|
||||
ExternalController: externalController,
|
||||
Secret: secret,
|
||||
}
|
||||
|
||||
if restAddr := cfg.ExternalController; restAddr != "" {
|
||||
c.event <- &Event{Type: "external-controller", Payload: restAddr}
|
||||
c.event <- &Event{Type: "secret", Payload: cfg.Secret}
|
||||
}
|
||||
|
||||
c.UpdateGeneral(*c.general)
|
||||
return nil
|
||||
return general, nil
|
||||
}
|
||||
|
||||
// UpdateGeneral dispatch update event
|
||||
func (c *Config) UpdateGeneral(general General) {
|
||||
c.UpdateProxy(ProxyConfig{
|
||||
Port: &general.Port,
|
||||
SocksPort: &general.SocksPort,
|
||||
RedirPort: &general.RedirPort,
|
||||
AllowLan: &general.AllowLan,
|
||||
})
|
||||
c.event <- &Event{Type: "mode", Payload: general.Mode}
|
||||
c.event <- &Event{Type: "log-level", Payload: general.LogLevel}
|
||||
}
|
||||
|
||||
// UpdateProxy dispatch update proxy event
|
||||
func (c *Config) UpdateProxy(pc ProxyConfig) {
|
||||
if pc.AllowLan != nil {
|
||||
c.general.AllowLan = *pc.AllowLan
|
||||
}
|
||||
|
||||
c.general.Port = *or(pc.Port, &c.general.Port)
|
||||
if c.general.Port != 0 && (pc.AllowLan != nil || pc.Port != nil) {
|
||||
c.event <- &Event{Type: "http-addr", Payload: genAddr(c.general.Port, c.general.AllowLan)}
|
||||
}
|
||||
|
||||
c.general.SocksPort = *or(pc.SocksPort, &c.general.SocksPort)
|
||||
if c.general.SocksPort != 0 && (pc.AllowLan != nil || pc.SocksPort != nil) {
|
||||
c.event <- &Event{Type: "socks-addr", Payload: genAddr(c.general.SocksPort, c.general.AllowLan)}
|
||||
}
|
||||
|
||||
c.general.RedirPort = *or(pc.RedirPort, &c.general.RedirPort)
|
||||
if c.general.RedirPort != 0 && (pc.AllowLan != nil || pc.RedirPort != nil) {
|
||||
c.event <- &Event{Type: "redir-addr", Payload: genAddr(c.general.RedirPort, c.general.AllowLan)}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
func parseProxies(cfg *rawConfig) (map[string]C.Proxy, error) {
|
||||
proxies := make(map[string]C.Proxy)
|
||||
proxiesConfig := cfg.Proxy
|
||||
groupsConfig := cfg.ProxyGroup
|
||||
@ -251,7 +153,7 @@ func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
for idx, mapping := range proxiesConfig {
|
||||
proxyType, existType := mapping["type"].(string)
|
||||
if !existType {
|
||||
return fmt.Errorf("Proxy %d missing type", idx)
|
||||
return nil, fmt.Errorf("Proxy %d missing type", idx)
|
||||
}
|
||||
|
||||
var proxy C.Proxy
|
||||
@ -279,15 +181,15 @@ func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
}
|
||||
proxy, err = adapters.NewVmess(*vmessOption)
|
||||
default:
|
||||
return fmt.Errorf("Unsupport proxy type: %s", proxyType)
|
||||
return nil, fmt.Errorf("Unsupport proxy type: %s", proxyType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Proxy [%d]: %s", idx, err.Error())
|
||||
return nil, fmt.Errorf("Proxy [%d]: %s", idx, err.Error())
|
||||
}
|
||||
|
||||
if _, exist := proxies[proxy.Name()]; exist {
|
||||
return fmt.Errorf("Proxy %s is the duplicate name", proxy.Name())
|
||||
return nil, fmt.Errorf("Proxy %s is the duplicate name", proxy.Name())
|
||||
}
|
||||
proxies[proxy.Name()] = proxy
|
||||
}
|
||||
@ -297,11 +199,11 @@ func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
groupType, existType := mapping["type"].(string)
|
||||
groupName, existName := mapping["name"].(string)
|
||||
if !existType && existName {
|
||||
return fmt.Errorf("ProxyGroup %d: missing type or name", idx)
|
||||
return nil, fmt.Errorf("ProxyGroup %d: missing type or name", idx)
|
||||
}
|
||||
|
||||
if _, exist := proxies[groupName]; exist {
|
||||
return fmt.Errorf("ProxyGroup %s: the duplicate name", groupName)
|
||||
return nil, fmt.Errorf("ProxyGroup %s: the duplicate name", groupName)
|
||||
}
|
||||
var group C.Proxy
|
||||
var err error
|
||||
@ -315,7 +217,7 @@ func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
|
||||
ps, err := getProxies(proxies, urlTestOption.Proxies)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
}
|
||||
group, err = adapters.NewURLTest(*urlTestOption, ps)
|
||||
case "select":
|
||||
@ -327,7 +229,7 @@ func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
|
||||
ps, err := getProxies(proxies, selectorOption.Proxies)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
}
|
||||
group, err = adapters.NewSelector(selectorOption.Name, ps)
|
||||
case "fallback":
|
||||
@ -339,12 +241,12 @@ func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
|
||||
ps, err := getProxies(proxies, fallbackOption.Proxies)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
}
|
||||
group, err = adapters.NewFallback(*fallbackOption, ps)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Proxy %s: %s", groupName, err.Error())
|
||||
return nil, fmt.Errorf("Proxy %s: %s", groupName, err.Error())
|
||||
}
|
||||
proxies[groupName] = group
|
||||
}
|
||||
@ -357,7 +259,7 @@ func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
proxies["GLOBAL"], _ = adapters.NewSelector("GLOBAL", ps)
|
||||
|
||||
// close old goroutine
|
||||
for _, proxy := range c.proxies {
|
||||
for _, proxy := range proxies {
|
||||
switch raw := proxy.(type) {
|
||||
case *adapters.URLTest:
|
||||
raw.Close()
|
||||
@ -365,12 +267,10 @@ func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||
raw.Close()
|
||||
}
|
||||
}
|
||||
c.proxies = proxies
|
||||
c.event <- &Event{Type: "proxies", Payload: proxies}
|
||||
return nil
|
||||
return proxies, nil
|
||||
}
|
||||
|
||||
func (c *Config) parseRules(cfg *RawConfig) error {
|
||||
func parseRules(cfg *rawConfig) ([]C.Rule, error) {
|
||||
rules := []C.Rule{}
|
||||
|
||||
rulesConfig := cfg.Rule
|
||||
@ -397,55 +297,5 @@ func (c *Config) parseRules(cfg *RawConfig) error {
|
||||
}
|
||||
}
|
||||
|
||||
c.rules = rules
|
||||
c.event <- &Event{Type: "rules", Payload: rules}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) handleResponseMessage() {
|
||||
for elm := range c.reportCh {
|
||||
event := elm.(*Event)
|
||||
switch event.Type {
|
||||
case "http-addr":
|
||||
if event.Payload.(bool) == false {
|
||||
log.Errorf("Listening HTTP proxy at %d error", c.general.Port)
|
||||
c.general.Port = 0
|
||||
}
|
||||
case "socks-addr":
|
||||
if event.Payload.(bool) == false {
|
||||
log.Errorf("Listening SOCKS proxy at %d error", c.general.SocksPort)
|
||||
c.general.SocksPort = 0
|
||||
}
|
||||
case "redir-addr":
|
||||
if event.Payload.(bool) == false {
|
||||
log.Errorf("Listening Redir proxy at %d error", c.general.RedirPort)
|
||||
c.general.RedirPort = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newConfig() *Config {
|
||||
event := make(chan interface{})
|
||||
reportCh := make(chan interface{})
|
||||
config := &Config{
|
||||
general: &General{},
|
||||
proxies: make(map[string]C.Proxy),
|
||||
rules: []C.Rule{},
|
||||
lastUpdate: time.Now(),
|
||||
|
||||
event: event,
|
||||
reportCh: reportCh,
|
||||
observable: observable.NewObservable(event),
|
||||
}
|
||||
go config.handleResponseMessage()
|
||||
return config
|
||||
}
|
||||
|
||||
// Instance return singleton instance of Config
|
||||
func Instance() *Config {
|
||||
once.Do(func() {
|
||||
config = newConfig()
|
||||
})
|
||||
return config
|
||||
return rules, nil
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package config
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -54,15 +55,15 @@ func downloadMMDB(path string) (err error) {
|
||||
}
|
||||
|
||||
// Init prepare necessary files
|
||||
func Init() {
|
||||
func Init(dir string) error {
|
||||
// initial homedir
|
||||
if _, err := os.Stat(C.Path.HomeDir()); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(C.Path.HomeDir(), 0777); err != nil {
|
||||
log.Fatalf("Can't create config directory %s: %s", C.Path.HomeDir(), err.Error())
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(dir, 0777); err != nil {
|
||||
return fmt.Errorf("Can't create config directory %s: %s", dir, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// initial config.ini
|
||||
// initial config.yml
|
||||
if _, err := os.Stat(C.Path.Config()); os.IsNotExist(err) {
|
||||
log.Info("Can't find config, create a empty file")
|
||||
os.OpenFile(C.Path.Config(), os.O_CREATE|os.O_WRONLY, 0644)
|
||||
@ -73,7 +74,8 @@ func Init() {
|
||||
log.Info("Can't find MMDB, start download")
|
||||
err := downloadMMDB(C.Path.MMDB())
|
||||
if err != nil {
|
||||
log.Fatalf("Can't download MMDB: %s", err.Error())
|
||||
return fmt.Errorf("Can't download MMDB: %s", err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
package config
|
||||
|
||||
type Mode int
|
||||
|
||||
var (
|
||||
// ModeMapping is a mapping for Mode enum
|
||||
ModeMapping = map[string]Mode{
|
||||
"Global": Global,
|
||||
"Rule": Rule,
|
||||
"Direct": Direct,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
Global Mode = iota
|
||||
Rule
|
||||
Direct
|
||||
)
|
||||
|
||||
func (m Mode) String() string {
|
||||
switch m {
|
||||
case Global:
|
||||
return "Global"
|
||||
case Rule:
|
||||
return "Rule"
|
||||
case Direct:
|
||||
return "Direct"
|
||||
default:
|
||||
return "Unknow"
|
||||
}
|
||||
}
|
@ -14,13 +14,6 @@ func trimArr(arr []string) (r []string) {
|
||||
return
|
||||
}
|
||||
|
||||
func genAddr(port int, allowLan bool) string {
|
||||
if allowLan {
|
||||
return fmt.Sprintf(":%d", port)
|
||||
}
|
||||
return fmt.Sprintf("127.0.0.1:%d", port)
|
||||
}
|
||||
|
||||
func getProxies(mapping map[string]C.Proxy, list []string) ([]C.Proxy, error) {
|
||||
var ps []C.Proxy
|
||||
for _, name := range list {
|
||||
|
Reference in New Issue
Block a user