github.com/supabase/cli@v1.168.1/internal/utils/flags/db_url.go (about) 1 package flags 2 3 import ( 4 "crypto/rand" 5 "fmt" 6 "math/big" 7 "os" 8 "strings" 9 10 "github.com/go-errors/errors" 11 "github.com/jackc/pgconn" 12 "github.com/spf13/afero" 13 "github.com/spf13/pflag" 14 "github.com/spf13/viper" 15 "github.com/supabase/cli/internal/utils" 16 "github.com/supabase/cli/internal/utils/credentials" 17 "github.com/supabase/cli/pkg/api" 18 ) 19 20 type connection int 21 22 const ( 23 unknown connection = iota 24 direct 25 local 26 linked 27 proxy 28 ) 29 30 var DbConfig pgconn.Config 31 32 func ParseDatabaseConfig(flagSet *pflag.FlagSet, fsys afero.Fs) error { 33 // Changed flags take precedence over default values 34 var connType connection 35 if flag := flagSet.Lookup("db-url"); flag != nil && flag.Changed { 36 connType = direct 37 } else if flag := flagSet.Lookup("local"); flag != nil && flag.Changed { 38 connType = local 39 } else if flag := flagSet.Lookup("linked"); flag != nil && flag.Changed { 40 connType = linked 41 } else if flag := flagSet.Lookup("proxy"); flag != nil && flag.Changed { 42 connType = proxy 43 } else if value, err := flagSet.GetBool("local"); err == nil && value { 44 connType = local 45 } else if value, err := flagSet.GetBool("linked"); err == nil && value { 46 connType = linked 47 } else if value, err := flagSet.GetBool("proxy"); err == nil && value { 48 connType = proxy 49 } 50 // Update connection config 51 switch connType { 52 case direct: 53 if flag := flagSet.Lookup("db-url"); flag != nil { 54 config, err := pgconn.ParseConfig(flag.Value.String()) 55 if err != nil { 56 return errors.Errorf("failed to parse connection string: %w", err) 57 } 58 DbConfig = *config 59 } 60 case local: 61 if err := utils.LoadConfigFS(fsys); err != nil { 62 return err 63 } 64 // Ignore other PG settings 65 DbConfig.Host = utils.Config.Hostname 66 DbConfig.Port = utils.Config.Db.Port 67 DbConfig.User = "postgres" 68 DbConfig.Password = utils.Config.Db.Password 69 DbConfig.Database = "postgres" 70 case linked: 71 if err := utils.LoadConfigFS(fsys); err != nil { 72 return err 73 } 74 projectRef, err := LoadProjectRef(fsys) 75 if err != nil { 76 return err 77 } 78 DbConfig = NewDbConfigWithPassword(projectRef) 79 case proxy: 80 token, err := utils.LoadAccessTokenFS(fsys) 81 if err != nil { 82 return err 83 } 84 projectRef, err := LoadProjectRef(fsys) 85 if err != nil { 86 return err 87 } 88 DbConfig.Host = utils.GetSupabaseAPIHost() 89 DbConfig.Port = 443 90 DbConfig.User = "postgres" 91 DbConfig.Password = token 92 DbConfig.Database = projectRef 93 } 94 return nil 95 } 96 97 func NewDbConfigWithPassword(projectRef string) pgconn.Config { 98 config := getDbConfig(projectRef) 99 config.Password = getPassword(projectRef) 100 return config 101 } 102 103 func getPassword(projectRef string) string { 104 if password := viper.GetString("DB_PASSWORD"); len(password) > 0 { 105 return password 106 } 107 if password, err := credentials.Get(projectRef); err == nil { 108 return password 109 } 110 fmt.Fprint(os.Stderr, "Enter your database password: ") 111 return credentials.PromptMasked(os.Stdin) 112 } 113 114 const PASSWORD_LENGTH = 16 115 116 func PromptPassword(stdin *os.File) string { 117 fmt.Fprint(os.Stderr, "Enter your database password (or leave blank to generate one): ") 118 if input := credentials.PromptMasked(stdin); len(input) > 0 { 119 return input 120 } 121 // Generate a password, see ./Settings/Database/DatabaseSettings/ResetDbPassword.tsx#L83 122 var password []byte 123 charset := string(api.AbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891) 124 charset = strings.ReplaceAll(charset, ":", "") 125 maxRange := big.NewInt(int64(len(charset))) 126 for i := 0; i < PASSWORD_LENGTH; i++ { 127 random, err := rand.Int(rand.Reader, maxRange) 128 if err != nil { 129 fmt.Fprintln(os.Stderr, "Failed to randomise password:", err) 130 continue 131 } 132 password = append(password, charset[random.Int64()]) 133 } 134 return string(password) 135 } 136 137 func getDbConfig(projectRef string) pgconn.Config { 138 if poolerConfig := utils.GetPoolerConfig(projectRef); poolerConfig != nil { 139 return *poolerConfig 140 } 141 return pgconn.Config{ 142 Host: utils.GetSupabaseDbHost(projectRef), 143 Port: 5432, 144 User: "postgres", 145 Database: "postgres", 146 } 147 } 148 149 func GetDbConfigOptionalPassword(projectRef string) pgconn.Config { 150 config := getDbConfig(projectRef) 151 config.Password = viper.GetString("DB_PASSWORD") 152 if config.Password == "" { 153 fmt.Fprint(os.Stderr, "Enter your database password (or leave blank to skip): ") 154 config.Password = credentials.PromptMasked(os.Stdin) 155 } 156 return config 157 }