package repl import ( "context" "encoding/hex" "fmt" "os" "github.com/spf13/cobra" "gitlink.org.cn/cloudream/common/pkgs/logger" stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals" hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub" "gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/accesstoken" "gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db" cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types" "golang.org/x/crypto/bcrypt" "golang.org/x/term" "gorm.io/gorm" ) func init() { userCmd := &cobra.Command{ Use: "user", Short: "user command", } RootCmd.AddCommand(userCmd) createCmd := &cobra.Command{ Use: "create [account] [nickName]", Short: "create a new user account", Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { userCreate(GetCmdCtx(cmd), args[0], args[1]) }, } userCmd.AddCommand(createCmd) logoutCmd := &cobra.Command{ Use: "logout [account] [tokenID]", Short: "logout from a user account", Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { userLogout(GetCmdCtx(cmd), args[0], cortypes.AccessTokenID(args[1])) }, } userCmd.AddCommand(logoutCmd) } func userCreate(ctx *CommandContext, account string, nickName string) { _, err := ctx.repl.db.User().GetByAccount(ctx.repl.db.DefCtx(), account) if err == nil { fmt.Printf("user %s already exists\n", account) return } fmt.Printf("input account password: ") pass, err := term.ReadPassword(int(os.Stdin.Fd())) if err != nil { fmt.Println("error reading password:", err) return } passHash, err := bcrypt.GenerateFromPassword(pass, bcrypt.DefaultCost) if err != nil { fmt.Println("error hashing password:", err) return } user, err := db.DoTx02(ctx.repl.db, func(tx db.SQLContext) (cortypes.User, error) { return ctx.repl.db.User().Create(tx, account, hex.EncodeToString(passHash), nickName) }) if err != nil { fmt.Println("error creating user:", err) return } fmt.Printf("user %s created\n", user.Account) } func userLogout(ctx *CommandContext, account string, tokenID cortypes.AccessTokenID) { acc, err := ctx.repl.db.User().GetByAccount(ctx.repl.db.DefCtx(), account) if err != nil { fmt.Printf("user %s not found\n", account) return } log := logger.WithField("UserID", acc.UserID).WithField("TokenID", tokenID) d := ctx.repl.db loaded, err := db.DoTx02(d, func(tx db.SQLContext) ([]cortypes.LoadedAccessToken, error) { token, err := d.UserAccessToken().GetByID(tx, acc.UserID, tokenID) if err != nil { return nil, err } err = d.UserAccessToken().DeleteByID(tx, token.UserID, token.TokenID) if err != nil { return nil, err } loaded, err := d.LoadedAccessToken().GetByUserIDAndTokenID(tx, token.UserID, token.TokenID) if err != nil { return nil, err } err = d.LoadedAccessToken().DeleteAllByUserIDAndTokenID(tx, token.UserID, token.TokenID) if err != nil { return nil, err } return loaded, nil }) if err != nil { log.Warnf("delete access token: %v", err) if err == gorm.ErrRecordNotFound { return } return } ctx.repl.accessToken.NotifyTokenInvalid(accesstoken.CacheKey{ UserID: acc.UserID, TokenID: tokenID, }) var loadedHubIDs []cortypes.HubID for _, l := range loaded { loadedHubIDs = append(loadedHubIDs, l.HubID) } notifyLoadedHubs(ctx, acc.UserID, tokenID, loadedHubIDs) } func notifyLoadedHubs(ctx *CommandContext, userID cortypes.UserID, tokenID cortypes.AccessTokenID, loadedHubIDs []cortypes.HubID) { log := logger.WithField("UserID", userID).WithField("TokenID", tokenID) d := ctx.repl.db loadedHubs, err := d.Hub().BatchGetByID(d.DefCtx(), loadedHubIDs) if err != nil { log.Warnf("getting hubs: %v", err) return } for _, l := range loadedHubs { addr, ok := l.Address.(*cortypes.GRPCAddressInfo) if !ok { continue } cli := stgglb.HubRPCPool.Get(addr.ExternalIP, addr.ExternalGRPCPort) // 不关心返回值 cli.NotifyUserAccessTokenInvalid(context.Background(), &hubrpc.NotifyUserAccessTokenInvalid{ UserID: userID, TokenID: tokenID, }) cli.Release() } }