github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/components/config/setting.go (about)

     1  package config
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"runtime"
     7  	"runtime/debug"
     8  	"sync"
     9  
    10  	"github.com/Asutorufa/yuhaiin/internal/version"
    11  	"github.com/Asutorufa/yuhaiin/pkg/protos/config"
    12  	gc "github.com/Asutorufa/yuhaiin/pkg/protos/config/grpc"
    13  	"github.com/Asutorufa/yuhaiin/pkg/protos/config/listener"
    14  	"github.com/Asutorufa/yuhaiin/pkg/utils/jsondb"
    15  	"google.golang.org/protobuf/proto"
    16  	"google.golang.org/protobuf/types/known/emptypb"
    17  )
    18  
    19  type Setting interface {
    20  	gc.ConfigServiceServer
    21  	AddObserver(Observer)
    22  }
    23  
    24  type Observer interface {
    25  	Update(*config.Setting)
    26  }
    27  
    28  type ObserverFunc func(*config.Setting)
    29  
    30  func (o ObserverFunc) Update(s *config.Setting) { o(s) }
    31  
    32  type setting struct {
    33  	gc.UnimplementedConfigServiceServer
    34  
    35  	db *jsondb.DB[*config.Setting]
    36  
    37  	os []Observer
    38  
    39  	mu sync.RWMutex
    40  }
    41  
    42  func NewConfig(path string) Setting {
    43  	return &setting{db: jsondb.Open(path, defaultSetting(path))}
    44  }
    45  
    46  func (c *setting) Info(context.Context, *emptypb.Empty) (*config.Info, error) { return Info(), nil }
    47  
    48  func Info() *config.Info {
    49  	var build []string
    50  	info, ok := debug.ReadBuildInfo()
    51  	if ok {
    52  		for _, v := range info.Settings {
    53  			build = append(build, fmt.Sprintf("%s=%s", v.Key, v.Value))
    54  		}
    55  	}
    56  
    57  	return &config.Info{
    58  		Version:   version.Version,
    59  		Commit:    version.GitCommit,
    60  		BuildTime: version.BuildTime,
    61  		GoVersion: runtime.Version(),
    62  		Platform:  runtime.GOOS + "/" + runtime.GOARCH,
    63  		Compiler:  runtime.Compiler,
    64  		Arch:      runtime.GOARCH,
    65  		Os:        runtime.GOOS,
    66  		Build:     build,
    67  	}
    68  }
    69  
    70  func (c *setting) Load(context.Context, *emptypb.Empty) (*config.Setting, error) {
    71  	c.mu.RLock()
    72  	defer c.mu.RUnlock()
    73  	return c.db.Data, nil
    74  }
    75  
    76  func (c *setting) Save(_ context.Context, s *config.Setting) (*emptypb.Empty, error) {
    77  	c.mu.Lock()
    78  	defer c.mu.Unlock()
    79  
    80  	c.db.Data = proto.Clone(s).(*config.Setting)
    81  	for k, v := range c.db.Data.Server.Servers {
    82  		key := "server-" + k
    83  		_, ok := c.db.Data.Server.Inbounds[key]
    84  		if ok {
    85  			continue
    86  		}
    87  
    88  		if c.db.Data.Server.Inbounds == nil {
    89  			c.db.Data.Server.Inbounds = make(map[string]*listener.Inbound)
    90  		}
    91  
    92  		c.db.Data.Server.Inbounds[key] = v.ToInbound()
    93  		delete(c.db.Data.Server.Servers, k)
    94  	}
    95  
    96  	if err := c.db.Save(); err != nil {
    97  		return &emptypb.Empty{}, fmt.Errorf("save settings failed: %w", err)
    98  	}
    99  
   100  	wg := sync.WaitGroup{}
   101  	for i := range c.os {
   102  		wg.Add(1)
   103  		go func(o Observer) {
   104  			defer wg.Done()
   105  			o.Update(proto.Clone(c.db.Data).(*config.Setting))
   106  		}(c.os[i])
   107  	}
   108  	wg.Wait()
   109  
   110  	return &emptypb.Empty{}, nil
   111  }
   112  
   113  func (c *setting) AddObserver(o Observer) {
   114  	if o == nil {
   115  		return
   116  	}
   117  	c.mu.Lock()
   118  	defer c.mu.Unlock()
   119  
   120  	c.os = append(c.os, o)
   121  	o.Update(c.db.Data)
   122  }