Browse Source

Commands: Add adu/rmu inbound user management to API (#4943)

Co-authored-by: nobody <[email protected]>
nobody 10 months ago
parent
commit
3fe02a658a

+ 2 - 0
main/commands/all/api/api.go

@@ -23,6 +23,8 @@ var CmdAPI = &base.Command{
 		cmdRemoveOutbounds,
 		cmdListInbounds,
 		cmdListOutbounds,
+		cmdAddInboundUsers,
+		cmdRemoveInboundUsers,
 		cmdInboundUser,
 		cmdInboundUserCount,
 		cmdAddRules,

+ 144 - 0
main/commands/all/api/inbound_user_add.go

@@ -0,0 +1,144 @@
+package api
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/xtls/xray-core/common/protocol"
+
+	handlerService "github.com/xtls/xray-core/app/proxyman/command"
+	cserial "github.com/xtls/xray-core/common/serial"
+
+	"github.com/xtls/xray-core/core"
+	"github.com/xtls/xray-core/infra/conf"
+	"github.com/xtls/xray-core/infra/conf/serial"
+	"github.com/xtls/xray-core/proxy/shadowsocks"
+	"github.com/xtls/xray-core/proxy/shadowsocks_2022"
+	"github.com/xtls/xray-core/proxy/trojan"
+	vlessin "github.com/xtls/xray-core/proxy/vless/inbound"
+	vmessin "github.com/xtls/xray-core/proxy/vmess/inbound"
+
+	"github.com/xtls/xray-core/main/commands/base"
+)
+
+var cmdAddInboundUsers = &base.Command{
+	CustomFlags: true,
+	UsageLine:   "{{.Exec}} api adu [--server=127.0.0.1:8080] <c1.json> [c2.json]...",
+	Short:       "Add users to inbounds",
+	Long: `
+Add users to inbounds.
+Arguments:
+	-s, -server
+		The API server address. Default 127.0.0.1:8080
+	-t, -timeout
+		Timeout seconds to call API. Default 3
+Example:
+    {{.Exec}} {{.LongName}} --server=127.0.0.1:8080  c1.json c2.json
+`,
+	Run: executeAddInboundUsers,
+}
+
+func executeAddInboundUsers(cmd *base.Command, args []string) {
+	setSharedFlags(cmd)
+	cmd.Flag.Parse(args)
+	unnamedArgs := cmd.Flag.Args()
+	inbs := extractInboundsConfig(unnamedArgs)
+
+	conn, ctx, close := dialAPIServer()
+	defer close()
+	client := handlerService.NewHandlerServiceClient(conn)
+
+	success := 0
+	for _, inb := range inbs {
+		success += executeInboundUserAction(ctx, client, inb, addInboundUserAction)
+	}
+	fmt.Println("Added", success, "user(s) in total.")
+}
+
+func addInboundUserAction(ctx context.Context, client handlerService.HandlerServiceClient, tag string, user *protocol.User) error {
+	fmt.Println("add user:", user.Email)
+	_, err := client.AlterInbound(ctx, &handlerService.AlterInboundRequest{
+		Tag: tag,
+		Operation: cserial.ToTypedMessage(
+			&handlerService.AddUserOperation{
+				User: user,
+			}),
+	})
+	return err
+}
+
+func extractInboundUsers(inb *core.InboundHandlerConfig) []*protocol.User {
+	if inb == nil {
+		return nil
+	}
+	inst, err := inb.ProxySettings.GetInstance()
+	if err != nil || inst == nil {
+		fmt.Println("failed to get inbound instance:", err)
+		return nil
+	}
+	switch ty := inst.(type) {
+	case *vmessin.Config:
+		return ty.User
+	case *vlessin.Config:
+		return ty.Clients
+	case *trojan.ServerConfig:
+		return ty.Users
+	case *shadowsocks.ServerConfig:
+		return ty.Users
+	case *shadowsocks_2022.MultiUserServerConfig:
+		return ty.Users
+	default:
+		fmt.Println("unsupported inbound type")
+	}
+	return nil
+}
+
+func extractInboundsConfig(unnamedArgs []string) []conf.InboundDetourConfig {
+	ins := make([]conf.InboundDetourConfig, 0)
+	for _, arg := range unnamedArgs {
+		r, err := loadArg(arg)
+		if err != nil {
+			base.Fatalf("failed to load %s: %s", arg, err)
+		}
+		conf, err := serial.DecodeJSONConfig(r)
+		if err != nil {
+			base.Fatalf("failed to decode %s: %s", arg, err)
+		}
+		ins = append(ins, conf.InboundConfigs...)
+	}
+	return ins
+}
+
+func executeInboundUserAction(ctx context.Context, client handlerService.HandlerServiceClient, inb conf.InboundDetourConfig, action func(ctx context.Context, client handlerService.HandlerServiceClient, tag string, user *protocol.User) error) int {
+	success := 0
+
+	tag := inb.Tag
+	if len(tag) < 1 {
+		return success
+	}
+
+	fmt.Println("processing inbound:", tag)
+	built, err := inb.Build()
+	if err != nil {
+		fmt.Println("failed to build config:", err)
+		return success
+	}
+
+	users := extractInboundUsers(built)
+	if users == nil {
+		return success
+	}
+
+	for _, user := range users {
+		if len(user.Email) < 1 {
+			continue
+		}
+		if err := action(ctx, client, inb.Tag, user); err == nil {
+			fmt.Println("result: ok")
+			success += 1
+		} else {
+			fmt.Println(err)
+		}
+	}
+	return success
+}

+ 62 - 0
main/commands/all/api/inbound_user_remove.go

@@ -0,0 +1,62 @@
+package api
+
+import (
+	"fmt"
+
+	handlerService "github.com/xtls/xray-core/app/proxyman/command"
+	cserial "github.com/xtls/xray-core/common/serial"
+
+	"github.com/xtls/xray-core/main/commands/base"
+)
+
+var cmdRemoveInboundUsers = &base.Command{
+	CustomFlags: true,
+	UsageLine:   "{{.Exec}} api rmu [--server=127.0.0.1:8080] -tag=tag <email1> [email2]...",
+	Short:       "Remove users from inbounds",
+	Long: `
+Remove users from inbounds.
+Arguments:
+	-s, -server
+		The API server address. Default 127.0.0.1:8080
+	-t, -timeout
+		Timeout seconds to call API. Default 3
+	-tag
+		Inbound tag
+Example:
+    {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="vless-in" "[email protected]" ...
+`,
+	Run: executeRemoveUsers,
+}
+
+func executeRemoveUsers(cmd *base.Command, args []string) {
+	setSharedFlags(cmd)
+	var tag string
+	cmd.Flag.StringVar(&tag, "tag", "", "")
+	cmd.Flag.Parse(args)
+	emails := cmd.Flag.Args()
+	if len(tag) < 1 {
+		base.Fatalf("inbound tag not specified")
+	}
+
+	conn, ctx, close := dialAPIServer()
+	defer close()
+	client := handlerService.NewHandlerServiceClient(conn)
+
+	success := 0
+	for _, email := range emails {
+		fmt.Println("remove user:", email)
+		_, err := client.AlterInbound(ctx, &handlerService.AlterInboundRequest{
+			Tag: tag,
+			Operation: cserial.ToTypedMessage(
+				&handlerService.RemoveUserOperation{
+					Email: email,
+				}),
+		})
+		if err == nil {
+			success += 1
+		} else {
+			fmt.Println(err)
+		}
+	}
+	fmt.Println("Removed", success, "user(s) in total.")
+}