diff --git a/miku/go.mod b/miku/go.mod index 3efb0e9..b01a21a 100644 --- a/miku/go.mod +++ b/miku/go.mod @@ -4,6 +4,14 @@ go 1.24.2 replace miku/users => ../users/ -require miku/users v0.0.0-00010101000000-000000000000 +require ( + github.com/urfave/cli/v3 v3.3.2 + miku/supporter v0.0.0-00010101000000-000000000000 +) -require github.com/google/uuid v1.6.0 // indirect +require ( + github.com/google/uuid v1.6.0 // indirect + miku/users v0.0.0-00010101000000-000000000000 // indirect +) + +replace miku/supporter => ../supporter diff --git a/miku/main.go b/miku/main.go index 7257d90..c742f4f 100644 --- a/miku/main.go +++ b/miku/main.go @@ -1,28 +1,142 @@ package main import ( + "context" "encoding/json" + "errors" "fmt" "log" - "miku/users" "os" + "path/filepath" + + "miku/supporter" + + "github.com/urfave/cli/v3" ) func main() { - var confFile map[string]interface{} - file, err := os.ReadFile("../testconfig.json") - if err != nil { - fmt.Println(err) + cmd := &cli.Command{ + Name: "miku", + Usage: "Управление пользователями Vless в конфиге sing-box", + Commands: []*cli.Command{ + { + Name: "add", + Aliases: []string{"a"}, + Usage: "Добавить пользователя в config.json", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + Value: "/etc/sing-box/config.json", + Usage: "Указывает местоположение конфига", + }, + &cli.StringFlag{ + Name: "domain", + Aliases: []string{"d"}, + Value: "vpn.catgirls.asia", + Usage: "Указывает доменное имя, по которому клиенты могут выполнять подключение", + }, + }, + Action: func(ctx context.Context, c *cli.Command) error { + var confFile map[string]any + configPath, ok := c.Flags[0].Get().(string) + if !ok { + return errors.New("не удалось преобразовать тип") + } + fullpath, err := filepath.Abs(configPath) + if err != nil { + return nil + } + fmt.Println(fullpath) + file, err := os.ReadFile(fullpath) + if err != nil { + return err + } + json.Unmarshal(file, &confFile) + vlessUsers, err := supporter.MapToStruct(confFile) + if err != nil { + return err + } + vlessUsers, err = vlessUsers.Add(c.Args().First()) + if err != nil { + return err + } + fileToWrite, err := os.OpenFile(fullpath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644) + if err != nil { + return err + } + newConfig, err := supporter.StructToConfig(vlessUsers, confFile) + if err != nil { + return err + } + newJson, err := json.MarshalIndent(newConfig, "", "\t") + if err != nil { + return nil + } + fileToWrite.Write(newJson) + fmt.Printf("Пользователь %s добавлен в конфиг %s", c.Args().First(), fullpath) + return nil + }, + }, + { + Name: "delete", + Aliases: []string{"del", "d"}, + Usage: "Удаление пользователя из конфига Vless", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + Value: "/etc/sing-box/config.json", + Usage: "Указывает местоположение конфига", + }, + &cli.StringFlag{ + Name: "domain", + Aliases: []string{"d"}, + Value: "vpn.catgirls.asia", + Usage: "Указывает доменное имя, по которому клиенты могут выполнять подключение", + }, + }, + Action: func(ctx context.Context, c *cli.Command) error { + var confFile map[string]any + configPath, ok := c.Flags[0].Get().(string) + if !ok { + return errors.New("не удалось преобразовать тип") + } + fullpath, err := filepath.Abs(configPath) + if err != nil { + return nil + } + fmt.Println(fullpath) + file, err := os.ReadFile(fullpath) + if err != nil { + return err + } + json.Unmarshal(file, &confFile) + vlessUsers, err := supporter.MapToStruct(confFile) + if err != nil { + return err + } + vlessUsers = vlessUsers.Del(c.Args().First()) + fileToWrite, err := os.OpenFile(fullpath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644) + if err != nil { + return err + } + newConfig, err := supporter.StructToConfig(vlessUsers, confFile) + if err != nil { + return err + } + newJson, err := json.MarshalIndent(newConfig, "", "\t") + if err != nil { + return nil + } + fileToWrite.Write(newJson) + fmt.Printf("Пользователь %s удален из конфига %s", c.Args().First(), fullpath) + return nil + }, + }, + }, } - err = json.Unmarshal(file, &confFile) - if err != nil { - fmt.Println(err) + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) } - vlessUsers := users.MapToStruct(confFile) - newUsers := vlessUsers.Add("test") - newConfFile, err := users.StructToConfig(newUsers, confFile) - if err != nil { - log.Println(err) - } - log.Println(newConfFile) } diff --git a/supporter/go.mod b/supporter/go.mod new file mode 100644 index 0000000..bdb4dae --- /dev/null +++ b/supporter/go.mod @@ -0,0 +1,10 @@ +module miku/supporter + +go 1.24.2 + +require ( + github.com/google/uuid v1.6.0 + miku/users v0.0.0-00010101000000-000000000000 +) + +replace miku/users => ../users diff --git a/supporter/supporter.go b/supporter/supporter.go new file mode 100644 index 0000000..074be67 --- /dev/null +++ b/supporter/supporter.go @@ -0,0 +1,52 @@ +package supporter + +import ( + "encoding/json" + "errors" + + "miku/users" + + "github.com/google/uuid" +) + +func MapToStruct(toStructMap map[string]any) (users.VLESSUsers, error) { + var vlessUsers users.VLESSUsers + inbounds, ok := toStructMap["inbounds"].([]any) + if !ok || len(inbounds) == 0 { + return nil, errors.New("нет inbounds") + } + firstInbound, ok := inbounds[0].(map[string]any) + if !ok || len(firstInbound) == 0 { + return nil, errors.New("неизвестный формат inbound") + } + usedMap, ok := firstInbound["users"].([]any) + if !ok || len(usedMap) == 0 { + return nil, errors.New("неизвестный формат пользователей") + } + for _, curretMap := range usedMap { + userMap := curretMap.(map[string]any) + vlessUsers = append(vlessUsers, struct { + Name string "json:\"name\"" + UUID uuid.UUID "json:\"uuid\"" + }{ + Name: userMap["name"].(string), + UUID: uuid.MustParse(userMap["uuid"].(string)), + }) + } + return vlessUsers, nil +} + +func StructToConfig(vlessUsers users.VLESSUsers, configMap map[string]any) (map[string]any, error) { + var toInterface []map[string]any + newConfigMap := configMap + structJson, err := json.Marshal(vlessUsers) + if err != nil { + return nil, err + } + err = json.Unmarshal(structJson, &toInterface) + if err != nil { + return nil, err + } + newConfigMap["inbounds"].([]any)[0].(map[string]any)["users"] = toInterface + return newConfigMap, nil +} diff --git a/users/users.go b/users/users.go index 3256c26..4b45aa3 100644 --- a/users/users.go +++ b/users/users.go @@ -1,19 +1,20 @@ package users import ( - "encoding/json" - "log" + "errors" "github.com/google/uuid" ) -type VLESSUsers []struct { +type User struct { Name string `json:"name"` UUID uuid.UUID `json:"uuid"` } +type VLESSUsers []User + func (u VLESSUsers) Del(user string) VLESSUsers { - var newU VLESSUsers + var newU VLESSUsers = make(VLESSUsers, 0, len(u)) for _, v := range u { if v.Name != user { newU = append(newU, v) @@ -22,52 +23,28 @@ func (u VLESSUsers) Del(user string) VLESSUsers { return newU } -func (u VLESSUsers) Add(user string) VLESSUsers { - var userStruct struct { - Name string - UUID uuid.UUID +func (u VLESSUsers) checkInConfig(userName string) bool { + for _, v := range u { + if v.Name == userName { + return true + } } - userStruct.Name = user - randUUID, err := uuid.NewUUID() - if err != nil { - log.Println(err) - } - userStruct.UUID = randUUID - - newU := append(u, struct { - Name string "json:\"name\"" - UUID uuid.UUID "json:\"uuid\"" - }(userStruct)) - return newU + return false } -func MapToStruct(toStructMap map[string]interface{}) VLESSUsers { - var vlessUsers VLESSUsers - usedMap := toStructMap["inbounds"].([]interface{})[0].(map[string]interface{})["users"].([]interface{}) - for _, curretMap := range usedMap { - userMap := curretMap.(map[string]interface{}) - vlessUsers = append(vlessUsers, struct { - Name string "json:\"name\"" - UUID uuid.UUID "json:\"uuid\"" - }{ - Name: userMap["name"].(string), - UUID: uuid.MustParse(userMap["uuid"].(string)), - }) - } - return vlessUsers -} +func (u VLESSUsers) Add(userName string) (VLESSUsers, error) { -func StructToConfig(vlessUsers VLESSUsers, configMap map[string]interface{}) (map[string]interface{}, error) { - var toInterface []map[string]interface{} - newConfigMap := configMap - structJson, err := json.Marshal(vlessUsers) - if err != nil { - return map[string]interface{}{}, err + if u.checkInConfig(userName) { + return nil, errors.New("пользователь уже добавлен в конфиг") } - err = json.Unmarshal(structJson, &toInterface) + + newUUID, err := uuid.NewRandom() if err != nil { - return map[string]interface{}{}, err + return nil, err } - newConfigMap["inbounds"].([]interface{})[0].(map[string]interface{})["users"] = toInterface - return newConfigMap, nil + newUser := User{ + Name: userName, + UUID: newUUID, + } + return append(u, newUser), nil }