github.com/DapperCollectives/CAST/backend@v0.0.0-20230921221157-1350c8be7c96/main/server/app.go (about) 1 package server 2 3 import ( 4 // "errors" 5 6 "context" 7 "encoding/json" 8 "flag" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "os" 13 "strings" 14 15 "github.com/DapperCollectives/CAST/backend/main/middleware" 16 "github.com/DapperCollectives/CAST/backend/main/models" 17 "github.com/DapperCollectives/CAST/backend/main/shared" 18 "github.com/DapperCollectives/CAST/backend/main/strategies" 19 "github.com/axiomzen/envconfig" 20 "github.com/gorilla/mux" 21 "github.com/jackc/pgx/v4/pgxpool" 22 23 "github.com/rs/zerolog" 24 "github.com/rs/zerolog/log" 25 ) 26 27 type Database shared.Database 28 type IpfsClient shared.IpfsClient 29 type Allowlist shared.Allowlist 30 type Vote models.Vote 31 type Proposal models.Proposal 32 type Community models.Community 33 type Balance models.Balance 34 type List models.List 35 type ListRequest models.ListPayload 36 type ListUpdatePayload models.ListUpdatePayload 37 type CommunityUser models.CommunityUser 38 type CommunityUserPayload models.CommunityUserPayload 39 type UserCommunity models.UserCommunity 40 41 type TxOptionsAddresses []string 42 43 type App struct { 44 Router *mux.Router 45 DB *shared.Database 46 IpfsClient *shared.IpfsClient 47 FlowAdapter *shared.FlowAdapter 48 49 TxOptionsAddresses []string 50 Env string 51 AdminAllowlist shared.Allowlist 52 CommunityBlocklist shared.Allowlist 53 Config shared.Config 54 } 55 56 type Strategy interface { 57 TallyVotes(votes []*models.VoteWithBalance, p *models.ProposalResults, proposal *models.Proposal) (models.ProposalResults, error) 58 GetVotes(votes []*models.VoteWithBalance, proposal *models.Proposal) ([]*models.VoteWithBalance, error) 59 GetVoteWeightForBalance(vote *models.VoteWithBalance, proposal *models.Proposal) (float64, error) 60 InitStrategy(f *shared.FlowAdapter, db *shared.Database) 61 FetchBalance(b *models.Balance, p *models.Proposal) (*models.Balance, error) 62 RequiresSnapshot() bool 63 } 64 65 var strategyMap = map[string]Strategy{ 66 "token-weighted-default": &strategies.TokenWeightedDefault{}, 67 "total-token-weighted-default": &strategies.TotalTokenWeightedDefault{}, 68 "staked-token-weighted-default": &strategies.StakedTokenWeightedDefault{}, 69 "one-address-one-vote": &strategies.OneAddressOneVote{}, 70 "balance-of-nfts": &strategies.BalanceOfNfts{}, 71 "float-nfts": &strategies.FloatNFTs{}, 72 "custom-script": &strategies.CustomScript{}, 73 } 74 75 var customScripts []shared.CustomScript 76 77 var helpers Helpers 78 79 ////////////////////// 80 // INSTANCE METHODS // 81 ////////////////////// 82 83 func (a *App) Initialize() { 84 log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}) 85 86 // Env 87 env := os.Getenv("APP_ENV") 88 if flag.Lookup("test.v") != nil { 89 env = "TEST" 90 os.Setenv("APP_ENV", "TEST") 91 } else if len(env) == 0 { 92 env = "PROD" 93 } 94 a.Env = strings.TrimSpace(env) 95 96 // Set log level based on env 97 if a.Env == "PROD" { 98 log.Logger = log.Logger.Level(zerolog.InfoLevel) 99 log.Info().Msgf("Log level: %s for APP_ENV=%s", "INFO", a.Env) 100 } else { 101 log.Logger = log.Logger.Level(zerolog.DebugLevel) 102 log.Info().Msgf("Log level: %s for APP_ENV=%s", "DEBUG", a.Env) 103 } 104 105 // Set App-wide Config 106 err := envconfig.Process("FVT", &a.Config) 107 if err != nil { 108 log.Error().Err(err).Msg("Error Reading Configuration.") 109 os.Exit(1) 110 } 111 112 //////////// 113 // Clients 114 //////////// 115 116 // when running "make proposals" sets db to dev not test 117 arg := flag.String("db", "", "database type") 118 flag.Int("port", 5001, "port") 119 flag.Int("amount", 4, "Amount of proposals to create") 120 121 flag.Parse() 122 if *arg == "local" { 123 os.Setenv("APP_ENV", "DEV") 124 } 125 126 // Postgres 127 dbname := os.Getenv("DB_NAME") 128 129 // IPFS 130 if os.Getenv("APP_ENV") == "TEST" || os.Getenv("APP_ENV") == "DEV" { 131 flag.Bool("ipfs-override", true, "overrides ipfs call") 132 } else { 133 flag.Bool("ipfs-override", false, "overrides ipfs call") 134 } 135 136 // TEST Env 137 if os.Getenv("APP_ENV") == "TEST" { 138 dbname = os.Getenv("TEST_DB_NAME") 139 } 140 141 // Postgres 142 a.ConnectDB( 143 os.Getenv("DB_USERNAME"), 144 os.Getenv("DB_PASSWORD"), 145 os.Getenv("DB_HOST"), 146 os.Getenv("DB_PORT"), 147 dbname, 148 ) 149 150 // IPFS 151 a.IpfsClient = shared.NewIpfsClient(os.Getenv("IPFS_KEY"), os.Getenv("IPFS_SECRET")) 152 153 // Flow 154 155 // Load custom scripts for strategies 156 scripts, err := ioutil.ReadFile("./main/cadence/scripts/custom/scripts.json") 157 if err != nil { 158 log.Error().Err(err).Msg("Error Reading Custom Strategy scripts.") 159 } 160 161 err = json.Unmarshal(scripts, &customScripts) 162 if err != nil { 163 log.Error().Err(err).Msg("Error during Unmarshalling custom scripts") 164 } 165 166 // Create Map for Flow Adaptor to look up when voting 167 var customScriptsMap = make(map[string]shared.CustomScript) 168 for _, script := range customScripts { 169 customScriptsMap[script.Key] = script 170 } 171 172 if os.Getenv("FLOW_ENV") == "" { 173 os.Setenv("FLOW_ENV", "emulator") 174 } 175 a.FlowAdapter = shared.NewFlowClient(os.Getenv("FLOW_ENV"), customScriptsMap) 176 177 // Snapshot 178 a.TxOptionsAddresses = strings.Fields(os.Getenv("TX_OPTIONS_ADDRS")) 179 180 // Router 181 a.Router = mux.NewRouter() 182 a.initializeRoutes() 183 184 // Middlewares 185 a.Router.Use(mux.CORSMethodMiddleware(a.Router)) 186 a.Router.Use(middleware.Logger) 187 a.Router.Use(middleware.UseCors(a.Config)) 188 189 helpers.Initialize(a) 190 } 191 192 func (a *App) Run() { 193 addr := fmt.Sprintf(":%s", os.Getenv("API_PORT")) 194 log.Info().Msgf("Starting server on %s ...", addr) 195 log.Fatal().Err(http.ListenAndServe(addr, a.Router)).Msgf("Server at %s crashed!", addr) 196 } 197 198 func (a *App) ConnectDB(username, password, host, port, dbname string) { 199 var database shared.Database 200 var err error 201 202 database.Context = context.Background() 203 database.Name = dbname 204 205 connectionString := 206 fmt.Sprintf("postgres://%s:%s@%s:%s/%s", username, password, host, port, dbname) 207 208 pconf, confErr := pgxpool.ParseConfig(connectionString) 209 if confErr != nil { 210 log.Fatal().Err(err).Msg("Unable to parse database config url") 211 } 212 213 if os.Getenv("APP_ENV") == "TEST" { 214 log.Info().Msg("Setting MIN/MAX connections to 1") 215 pconf.MinConns = 1 216 pconf.MaxConns = 1 217 } 218 219 database.Conn, err = pgxpool.ConnectConfig(database.Context, pconf) 220 221 database.Env = &a.Env 222 if err != nil { 223 log.Fatal().Err(err).Msg("Error creating Postsgres conn pool") 224 } else { 225 a.DB = &database 226 log.Info().Msgf("Successfully created Postgres conn pool") 227 } 228 }