package common import ( "context" "fmt" "os" "runtime" "strings" "time" "git.lehouerou.net/laurent/sorare" "git.lehouerou.net/laurent/sorare/graphql" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/stdlib" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect/pgdialect" "github.com/uptrace/bun/extra/bundebug" ) func InitParams(cmd *cobra.Command) { cobra.OnInitialize(InitLog) viper.AutomaticEnv() viper.SetEnvPrefix("sorare") cmd.PersistentFlags().BoolP("verbose", "v", false, "Verbose output") _ = viper.BindPFlag("verbose", cmd.PersistentFlags().Lookup("verbose")) viper.SetDefault("verbose", false) cmd.PersistentFlags().String("jwttoken", "", "Sorare JWT Token") _ = viper.BindPFlag("jwttoken", cmd.PersistentFlags().Lookup("jwttoken")) viper.SetDefault("jwttoken", "") cmd.PersistentFlags().String("jwtaudience", "", "Sorare JWT Audience") _ = viper.BindPFlag("jwtaudience", cmd.PersistentFlags().Lookup("jwtaudience")) viper.SetDefault("jwtaudience", "") cmd.PersistentFlags().String("email", "", "Sorare Email") _ = viper.BindPFlag("email", cmd.PersistentFlags().Lookup("email")) viper.SetDefault("email", "") cmd.PersistentFlags().String("password", "", "Sorare Password") _ = viper.BindPFlag("password", cmd.PersistentFlags().Lookup("password")) viper.SetDefault("password", "") cmd.PersistentFlags().String("otp", "", "Sorare OTP") _ = viper.BindPFlag("otp", cmd.PersistentFlags().Lookup("otp")) viper.SetDefault("otp", "") cmd.PersistentFlags().String("dbhost", "", "Database Host") _ = viper.BindPFlag("dbhost", cmd.PersistentFlags().Lookup("dbhost")) viper.SetDefault("dbhost", "192.168.1.250") cmd.PersistentFlags().String("dbport", "", "Database Port") _ = viper.BindPFlag("dbport", cmd.PersistentFlags().Lookup("dbport")) viper.SetDefault("dbport", "5436") cmd.PersistentFlags().String("dbuser", "", "Database User") _ = viper.BindPFlag("dbuser", cmd.PersistentFlags().Lookup("dbuser")) viper.SetDefault("dbuser", "sorare") cmd.PersistentFlags().String("dbpass", "", "Database Password") _ = viper.BindPFlag("dbpass", cmd.PersistentFlags().Lookup("dbpass")) viper.SetDefault("dbpass", "sorare") cmd.PersistentFlags().String("dbname", "", "Database Name") _ = viper.BindPFlag("dbname", cmd.PersistentFlags().Lookup("dbname")) viper.SetDefault("dbname", "sorare") cmd.PersistentFlags().String("privatekey", "", "Sorare Private Key") _ = viper.BindPFlag("privatekey", cmd.PersistentFlags().Lookup("privatekey")) viper.SetDefault("privatekey", "") cmd.PersistentFlags().Bool("tracedb", false, "Trace Database Queries") _ = viper.BindPFlag("tracedb", cmd.PersistentFlags().Lookup("tracedb")) viper.SetDefault("tracedb", false) cmd.PersistentFlags().Bool("tracesorare", false, "Trace Sorare API Calls") _ = viper.BindPFlag("tracesorare", cmd.PersistentFlags().Lookup("tracesorare")) viper.SetDefault("tracesorare", false) cmd.PersistentFlags().String("cryptkey", "", "Crypt Key") _ = viper.BindPFlag("cryptkey", cmd.PersistentFlags().Lookup("cryptkey")) viper.SetDefault("cryptkey", "") cmd.PersistentFlags().String("jwtkey", "", "JWT Key") _ = viper.BindPFlag("jwtkey", cmd.PersistentFlags().Lookup("jwtkey")) viper.SetDefault("jwtkey", "") } func InitLog() { log.Logger = zerolog.New(zerolog.ConsoleWriter{ Out: os.Stdout, TimeFormat: "2006-01-02 | 15:04:05.000", FormatLevel: func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("| %-6s|", i)) }, FormatFieldName: func(i interface{}) string { return fmt.Sprintf("%s:", i) }, FormatFieldValue: func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("%s", i)) }, }).With().Timestamp().Logger() zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMicro zerolog.SetGlobalLevel(zerolog.InfoLevel) verbose := viper.GetBool("verbose") if verbose { log.Info().Msg("Verbose output enabled") zerolog.SetGlobalLevel(zerolog.DebugLevel) } } func InitSorare(ctx context.Context) (*sorare.Sorare, error) { audience := viper.GetString("jwtaudience") if audience == "" { return nil, errors.New("jwtaudience is required") } jwttoken := viper.GetString("jwttoken") s := sorare.New() tracesorare := viper.GetBool("tracesorare") if tracesorare { s.Debug() } if jwttoken != "" { s.SetJWTToken(graphql.JwtToken{ Token: jwttoken, ExpiredAt: time.Time{}, }, audience) } else { email := viper.GetString("email") password := viper.GetString("password") otp := viper.GetString("otp") err := s.Authenticate(ctx, email, password, audience, otp) if err != nil { return nil, errors.Wrap(err, "authenticating to sorare") } log.Info().Msgf("authentication to sorare successful. new token: %s", s.GetCurrentToken().Token) } cu, err := s.Users.CurrentUser.Get(ctx, graphql.EmptyParams{}) if err != nil { return nil, errors.Wrap(err, "getting current user") } log.Info().Msgf("authenticated on Sorare as %s (%s)", cu.Nickname, cu.Profile.ClubName) return s, nil } func InitDb(ctx context.Context) (*bun.DB, error) { host := viper.GetString("dbhost") if host == "" { return nil, errors.New("dbhost is required") } port := viper.GetString("dbport") if port == "" { return nil, errors.New("dbport is required") } user := viper.GetString("dbuser") if user == "" { return nil, errors.New("dbuser is required") } password := viper.GetString("dbpass") if password == "" { return nil, errors.New("dbpass is required") } dbname := viper.GetString("dbname") if dbname == "" { return nil, errors.New("dbname is required") } maxOpenConns := 4 * runtime.GOMAXPROCS(0) pgxconfig, err := pgx.ParseConfig( fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable", user, password, host, port, dbname), ) if err != nil { return nil, errors.Wrap(err, "parsing pgx config") } sqldb := stdlib.OpenDB(*pgxconfig) db := bun.NewDB(sqldb, pgdialect.New()) db.SetMaxOpenConns(maxOpenConns) tracedb := viper.GetBool("tracedb") if tracedb { db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true))) } // Test the connection ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := db.DB.PingContext(ctx); err != nil { return nil, errors.Wrap(err, "pinging database") } log.Info().Msgf("connected to database %s@%s:%s/%s", user, host, port, dbname) return db, nil } type contextKey string const ( SorareContextKey contextKey = "sorare" DbContextKey contextKey = "db" ) func CmdPreRunE(cmd *cobra.Command, _ []string) error { s, err := InitSorare(cmd.Context()) if err != nil { return errors.Wrap(err, "initializing sorare client") } cmd.SetContext(context.WithValue(cmd.Context(), SorareContextKey, s)) db, err := InitDb(cmd.Context()) if err != nil { return errors.Wrap(err, "initializing database") } cmd.SetContext(context.WithValue(cmd.Context(), DbContextKey, db)) return nil }