Register new users
This commit is contained in:
		@@ -19,9 +19,11 @@ executable ebook-manager
 | 
				
			|||||||
  main-is:             Main.hs
 | 
					  main-is:             Main.hs
 | 
				
			||||||
  other-modules:       Devel.Main
 | 
					  other-modules:       Devel.Main
 | 
				
			||||||
                     , API
 | 
					                     , API
 | 
				
			||||||
 | 
					                     , API.Users
 | 
				
			||||||
                     , Configuration
 | 
					                     , Configuration
 | 
				
			||||||
                     , Database
 | 
					                     , Database
 | 
				
			||||||
                     , Database.Schema
 | 
					                     , Database.Schema
 | 
				
			||||||
 | 
					                     , Database.User
 | 
				
			||||||
                     , Server
 | 
					                     , Server
 | 
				
			||||||
                     , Types
 | 
					                     , Types
 | 
				
			||||||
  -- other-extensions:
 | 
					  -- other-extensions:
 | 
				
			||||||
@@ -55,5 +57,11 @@ executable ebook-manager
 | 
				
			|||||||
                     , selda
 | 
					                     , selda
 | 
				
			||||||
                     , selda-postgresql
 | 
					                     , selda-postgresql
 | 
				
			||||||
                     , process
 | 
					                     , process
 | 
				
			||||||
 | 
					                     , aeson
 | 
				
			||||||
 | 
					                     , http-api-data
 | 
				
			||||||
  hs-source-dirs:      src
 | 
					  hs-source-dirs:      src
 | 
				
			||||||
 | 
					  default-extensions:  DeriveGeneric
 | 
				
			||||||
 | 
					                     , NoImplicitPrelude
 | 
				
			||||||
 | 
					                     , OverloadedStrings
 | 
				
			||||||
 | 
					                     , RecordWildCards
 | 
				
			||||||
  default-language:    Haskell2010
 | 
					  default-language:    Haskell2010
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,8 @@ import qualified Lucid.Html5 as H
 | 
				
			|||||||
import Types
 | 
					import Types
 | 
				
			||||||
import Control.Monad.Logger
 | 
					import Control.Monad.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import qualified API.Users as Users
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- XXX: Temporary
 | 
					-- XXX: Temporary
 | 
				
			||||||
import Database.Schema
 | 
					import Database.Schema
 | 
				
			||||||
import Database
 | 
					import Database
 | 
				
			||||||
@@ -44,9 +46,13 @@ instance ToHtml Index where
 | 
				
			|||||||
  toHtmlRaw = toHtml
 | 
					  toHtmlRaw = toHtml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type API = Get '[HTML] Index
 | 
					type API = Get '[HTML] Index
 | 
				
			||||||
 | 
					      :<|> Users.API
 | 
				
			||||||
 | 
					
 | 
				
			||||||
handler :: ServerT API AppM
 | 
					handler :: ServerT API AppM
 | 
				
			||||||
handler = do
 | 
					handler = indexHandler :<|> Users.handler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					indexHandler :: AppM Index
 | 
				
			||||||
 | 
					indexHandler = do
 | 
				
			||||||
  u <- runDB $ do
 | 
					  u <- runDB $ do
 | 
				
			||||||
    query $ select $ gen users
 | 
					    query $ select $ gen users
 | 
				
			||||||
  $logInfo $ "users: " <> (pack . show $ u)
 | 
					  $logInfo $ "users: " <> (pack . show $ u)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										62
									
								
								src/API/Users.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/API/Users.hs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					{-# Language DataKinds #-}
 | 
				
			||||||
 | 
					{-# Language TypeFamilies #-}
 | 
				
			||||||
 | 
					{-# Language OverloadedStrings #-}
 | 
				
			||||||
 | 
					{-# Language NoImplicitPrelude #-}
 | 
				
			||||||
 | 
					{-# Language TypeOperators #-}
 | 
				
			||||||
 | 
					{-# Language DuplicateRecordFields #-}
 | 
				
			||||||
 | 
					module API.Users (API, handler) where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Servant
 | 
				
			||||||
 | 
					import ClassyPrelude
 | 
				
			||||||
 | 
					import Types
 | 
				
			||||||
 | 
					import Data.Aeson
 | 
				
			||||||
 | 
					import Web.FormUrlEncoded
 | 
				
			||||||
 | 
					import Database (runDB)
 | 
				
			||||||
 | 
					import Database.User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data LoginForm = LoginForm { username :: Text
 | 
				
			||||||
 | 
					                           , password :: Text }
 | 
				
			||||||
 | 
					               deriving (Generic, Show)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data RegisterForm = RegisterForm { username :: Text
 | 
				
			||||||
 | 
					                                 , email :: Text
 | 
				
			||||||
 | 
					                                 , password :: Text
 | 
				
			||||||
 | 
					                                 , passwordAgain :: Text }
 | 
				
			||||||
 | 
					                  deriving (Generic, Show)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data LoginStatus = LoginStatus deriving Generic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data RegisterStatus = RegisterStatus deriving Generic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					instance FromJSON LoginForm
 | 
				
			||||||
 | 
					instance ToJSON LoginForm
 | 
				
			||||||
 | 
					instance ToJSON LoginStatus
 | 
				
			||||||
 | 
					instance FromJSON LoginStatus
 | 
				
			||||||
 | 
					instance FromForm LoginForm
 | 
				
			||||||
 | 
					instance ToForm LoginForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					instance FromJSON RegisterForm
 | 
				
			||||||
 | 
					instance ToJSON RegisterForm
 | 
				
			||||||
 | 
					instance ToJSON RegisterStatus
 | 
				
			||||||
 | 
					instance FromJSON RegisterStatus
 | 
				
			||||||
 | 
					instance FromForm RegisterForm
 | 
				
			||||||
 | 
					instance ToForm RegisterForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type API = "login" :> ReqBody '[JSON, FormUrlEncoded] LoginForm :> Post '[JSON] LoginStatus
 | 
				
			||||||
 | 
					      :<|> "register" :> ReqBody '[JSON, FormUrlEncoded] RegisterForm :> Post '[JSON] RegisterStatus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					handler :: ServerT API AppM
 | 
				
			||||||
 | 
					handler = loginHandler :<|> registerHandler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					loginHandler :: LoginForm -> AppM LoginStatus
 | 
				
			||||||
 | 
					loginHandler LoginForm{..} = throwM err403
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					registerHandler :: RegisterForm -> AppM RegisterStatus
 | 
				
			||||||
 | 
					registerHandler RegisterForm{..} =
 | 
				
			||||||
 | 
					  case () of
 | 
				
			||||||
 | 
					       () | password /= passwordAgain -> noMatch
 | 
				
			||||||
 | 
					          | otherwise ->
 | 
				
			||||||
 | 
					              either (const alreadyExists) (const (pure RegisterStatus)) =<< runDB (insertUser username email (PlainPassword password))
 | 
				
			||||||
 | 
					  where
 | 
				
			||||||
 | 
					    noMatch = throwM err403{errBody = "passwords don't match"}
 | 
				
			||||||
 | 
					    alreadyExists = throwM err403{errBody = "User already exists"}
 | 
				
			||||||
@@ -7,7 +7,10 @@ module Database
 | 
				
			|||||||
  , runDB
 | 
					  , runDB
 | 
				
			||||||
  , query
 | 
					  , query
 | 
				
			||||||
  , select
 | 
					  , select
 | 
				
			||||||
  , gen )
 | 
					  , gen
 | 
				
			||||||
 | 
					  , fromRel
 | 
				
			||||||
 | 
					  , toRel
 | 
				
			||||||
 | 
					  , SeldaT )
 | 
				
			||||||
  where
 | 
					  where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Data.Generics.Product
 | 
					import Data.Generics.Product
 | 
				
			||||||
@@ -15,7 +18,7 @@ import Control.Lens (view)
 | 
				
			|||||||
import Data.Pool (Pool, withResource)
 | 
					import Data.Pool (Pool, withResource)
 | 
				
			||||||
import Database.Selda.Backend (SeldaConnection, runSeldaT, SeldaT)
 | 
					import Database.Selda.Backend (SeldaConnection, runSeldaT, SeldaT)
 | 
				
			||||||
import Database.Selda (query, select)
 | 
					import Database.Selda (query, select)
 | 
				
			||||||
import Database.Selda.Generic (gen)
 | 
					import Database.Selda.Generic (gen, fromRel, toRel)
 | 
				
			||||||
import ClassyPrelude
 | 
					import ClassyPrelude
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DBLike r m = (MonadIO m, MonadReader r m, MonadBaseControl IO m, MonadMask m, HasField' "database" r (Pool SeldaConnection))
 | 
					type DBLike r m = (MonadIO m, MonadReader r m, MonadBaseControl IO m, MonadMask m, HasField' "database" r (Pool SeldaConnection))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,29 @@
 | 
				
			|||||||
{-# Language NoImplicitPrelude #-}
 | 
					{-# Language NoImplicitPrelude #-}
 | 
				
			||||||
{-# Language DeriveGeneric #-}
 | 
					{-# Language DeriveGeneric #-}
 | 
				
			||||||
{-# Language OverloadedStrings #-}
 | 
					{-# Language OverloadedStrings #-}
 | 
				
			||||||
 | 
					{-# Language DuplicateRecordFields #-}
 | 
				
			||||||
module Database.Schema where
 | 
					module Database.Schema where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ClassyPrelude
 | 
					import ClassyPrelude
 | 
				
			||||||
import Database.Selda.Generic
 | 
					import Database.Selda.Generic
 | 
				
			||||||
 | 
					import Database.Selda
 | 
				
			||||||
 | 
					import Database.Selda.Backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
data User = User { email :: Text
 | 
					data User pass = User { email :: Text
 | 
				
			||||||
                      , username :: Text
 | 
					                      , username :: Text
 | 
				
			||||||
                 , password :: ByteString }
 | 
					                      , role :: Role
 | 
				
			||||||
 | 
					                      , password :: pass }
 | 
				
			||||||
          deriving (Show, Generic)
 | 
					          deriving (Show, Generic)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
users :: GenTable User
 | 
					data Role = UserRole | AdminRole deriving (Show, Read, Enum, Bounded, Typeable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					instance SqlType Role where
 | 
				
			||||||
 | 
					  mkLit = LCustom . LText . pack . show
 | 
				
			||||||
 | 
					  fromSql sql = case sql of
 | 
				
			||||||
 | 
					                     SqlString x -> fromMaybe (error "fromSql: Not a valid role") . readMay . unpack $ x
 | 
				
			||||||
 | 
					                     _ -> error "fromSql: Not a valid role"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defaultValue = mkLit minBound
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					users :: GenTable (User ByteString)
 | 
				
			||||||
users = genTable "users" [ email :- primaryGen ]
 | 
					users = genTable "users" [ email :- primaryGen ]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										54
									
								
								src/Database/User.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/Database/User.hs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					{-# Language LambdaCase #-}
 | 
				
			||||||
 | 
					{-# Language TypeApplications #-}
 | 
				
			||||||
 | 
					{-# Language DataKinds #-}
 | 
				
			||||||
 | 
					{-# Language TemplateHaskell #-}
 | 
				
			||||||
 | 
					module Database.User where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ClassyPrelude
 | 
				
			||||||
 | 
					import Database
 | 
				
			||||||
 | 
					import Database.Schema
 | 
				
			||||||
 | 
					import Database.Selda
 | 
				
			||||||
 | 
					import Control.Lens (over, _Just)
 | 
				
			||||||
 | 
					import Data.Generics.Product
 | 
				
			||||||
 | 
					import Crypto.KDF.BCrypt
 | 
				
			||||||
 | 
					import Crypto.Random.Types (MonadRandom)
 | 
				
			||||||
 | 
					import Control.Monad.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data UserExistsError = UserExistsError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					newtype PlainPassword = PlainPassword Text
 | 
				
			||||||
 | 
					newtype HashedPassword = HashedPassword {unHashed :: ByteString}
 | 
				
			||||||
 | 
					data NoPassword = NoPassword
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					insertUser :: (MonadLogger m, MonadIO m, MonadMask m, MonadRandom m) => Text -> Text -> PlainPassword -> SeldaT m (Either UserExistsError (User NoPassword))
 | 
				
			||||||
 | 
					insertUser username email (PlainPassword password) =
 | 
				
			||||||
 | 
					  getUser' username >>= maybe insert' (const (return $ Left UserExistsError))
 | 
				
			||||||
 | 
					  where
 | 
				
			||||||
 | 
					    insert' = adminExists >>= \e -> Right <$> if e then insertAs UserRole else insertAs AdminRole
 | 
				
			||||||
 | 
					    insertAs role = do
 | 
				
			||||||
 | 
					      lift $ $logInfo $ "Inserting new user as " <> pack (show role)
 | 
				
			||||||
 | 
					      let bytePass = encodeUtf8 password
 | 
				
			||||||
 | 
					      user <- User email username role . HashedPassword <$> lift (hashPassword 12 bytePass)
 | 
				
			||||||
 | 
					      insert_ (gen users) [toRel (over (field @"password") unHashed user)] >> return (over (field @"password") (const NoPassword) user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					adminExists :: (MonadMask m, MonadLogger m, MonadIO m) => SeldaT m Bool
 | 
				
			||||||
 | 
					adminExists = do
 | 
				
			||||||
 | 
					  r <- query q
 | 
				
			||||||
 | 
					  lift $ $logInfo $ "Admin users: " <> (pack (show r))
 | 
				
			||||||
 | 
					  return $ maybe False (> 0) . listToMaybe $ r
 | 
				
			||||||
 | 
					  where
 | 
				
			||||||
 | 
					    q = aggregate $ do
 | 
				
			||||||
 | 
					      (_ :*: _ :*: r :*: _) <- select (gen users)
 | 
				
			||||||
 | 
					      restrict (r .== literal AdminRole)
 | 
				
			||||||
 | 
					      return (count r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					getUser :: (MonadMask m, MonadIO m) => Text -> SeldaT m (Maybe (User NoPassword))
 | 
				
			||||||
 | 
					getUser name = over (_Just . field @"password") (const NoPassword) <$> getUser' name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					getUser' :: (MonadMask m, MonadIO m) => Text -> SeldaT m (Maybe ( User HashedPassword ))
 | 
				
			||||||
 | 
					getUser' name = over (_Just . field @"password") HashedPassword . listToMaybe . fmap fromRel <$> query q
 | 
				
			||||||
 | 
					  where
 | 
				
			||||||
 | 
					    q = do
 | 
				
			||||||
 | 
					      u@(username :*: _ :*: _ :*: _) <- select (gen users)
 | 
				
			||||||
 | 
					      restrict (username .== literal name)
 | 
				
			||||||
 | 
					      return u
 | 
				
			||||||
@@ -1,7 +1,8 @@
 | 
				
			|||||||
{-# Language OverloadedStrings #-}
 | 
					 | 
				
			||||||
module Devel.Main where
 | 
					module Devel.Main where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Main (defaultMain)
 | 
					import Prelude
 | 
				
			||||||
 | 
					import Control.Monad.Trans.Reader (runReaderT)
 | 
				
			||||||
 | 
					import Main (withApp, defaultMain)
 | 
				
			||||||
import Control.Concurrent
 | 
					import Control.Concurrent
 | 
				
			||||||
import Control.Monad (void)
 | 
					import Control.Monad (void)
 | 
				
			||||||
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
 | 
					import Data.IORef (IORef, newIORef, readIORef, writeIORef)
 | 
				
			||||||
@@ -9,6 +10,10 @@ import Foreign.Store (Store(..), lookupStore, readStore, storeAction, withStore)
 | 
				
			|||||||
import GHC.Word (Word32)
 | 
					import GHC.Word (Word32)
 | 
				
			||||||
import Dhall (input, auto)
 | 
					import Dhall (input, auto)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Database.Schema
 | 
				
			||||||
 | 
					import Database.Selda (tryCreateTable)
 | 
				
			||||||
 | 
					import Database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
update :: IO ()
 | 
					update :: IO ()
 | 
				
			||||||
update = do
 | 
					update = do
 | 
				
			||||||
  lookupStore tidStoreNum >>= maybe setupNew restart
 | 
					  lookupStore tidStoreNum >>= maybe setupNew restart
 | 
				
			||||||
@@ -25,7 +30,17 @@ update = do
 | 
				
			|||||||
      withStore doneStore takeMVar
 | 
					      withStore doneStore takeMVar
 | 
				
			||||||
      readStore doneStore >>= start
 | 
					      readStore doneStore >>= start
 | 
				
			||||||
    start :: MVar () -> IO ThreadId
 | 
					    start :: MVar () -> IO ThreadId
 | 
				
			||||||
    start done = forkFinally (input auto "./config/devel.dhall" >>= defaultMain) (\_ -> putMVar done ())
 | 
					    start done = forkFinally develMain (\_ -> putMVar done ())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					develMain :: IO ()
 | 
				
			||||||
 | 
					develMain = do
 | 
				
			||||||
 | 
					  conf <- input auto "./config/devel.dhall"
 | 
				
			||||||
 | 
					  withApp conf $ \app -> do
 | 
				
			||||||
 | 
					    void $ runReaderT (runDB migrate) app
 | 
				
			||||||
 | 
					    defaultMain app
 | 
				
			||||||
 | 
					  where
 | 
				
			||||||
 | 
					    migrate = do
 | 
				
			||||||
 | 
					      tryCreateTable (gen users)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
 | 
					modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
 | 
				
			||||||
modifyStoredIORef store f = withStore store $ \ref -> do
 | 
					modifyStoredIORef store f = withStore store $ \ref -> do
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								src/Main.hs
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/Main.hs
									
									
									
									
									
								
							@@ -17,28 +17,20 @@ import Data.Generics.Product
 | 
				
			|||||||
import Data.Pool (createPool)
 | 
					import Data.Pool (createPool)
 | 
				
			||||||
import Database.Selda.PostgreSQL (PGConnectInfo(..), pgOpen, seldaClose)
 | 
					import Database.Selda.PostgreSQL (PGConnectInfo(..), pgOpen, seldaClose)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defaultMain :: Config -> IO ()
 | 
					defaultMain :: App -> IO ()
 | 
				
			||||||
defaultMain config = do
 | 
					defaultMain = run 8080 . server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					withApp :: Config -> (App -> IO ()) -> IO ()
 | 
				
			||||||
 | 
					withApp config f = do
 | 
				
			||||||
  let pgHost = view (field @"database" . field @"host") config
 | 
					  let pgHost = view (field @"database" . field @"host") config
 | 
				
			||||||
      pgPort = 5432
 | 
					      pgPort = 5432
 | 
				
			||||||
      pgDatabase = view (field @"database" . field @"database") config
 | 
					      pgDatabase = view (field @"database" . field @"database") config
 | 
				
			||||||
      pgUsername = Just (view (field @"database" . field @"username") config)
 | 
					      pgUsername = Just (view (field @"database" . field @"username") config)
 | 
				
			||||||
      pgPassword = Just (view (field @"database" . field @"password") config)
 | 
					      pgPassword = Just (view (field @"database" . field @"password") config)
 | 
				
			||||||
  database <- createPool (pgOpen (PGConnectInfo{..})) seldaClose 10 2 5
 | 
					  database <- createPool (pgOpen (PGConnectInfo{..})) seldaClose 10 2 5
 | 
				
			||||||
  let app = App{..}
 | 
					  f App{..}
 | 
				
			||||||
  run 8080 (server app)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- migrate :: Pg -> IO ()
 | 
					 | 
				
			||||||
-- migrate Pg{..} = do
 | 
					 | 
				
			||||||
--   -- Credentials visible on ps
 | 
					 | 
				
			||||||
--   -- XXX: Modify this to write the credentials to a temporary file or something
 | 
					 | 
				
			||||||
--   callProcess "flyway" $ fmap unpack [ "migrate"
 | 
					 | 
				
			||||||
--                                      , "-locations=filesystem:migrations/"
 | 
					 | 
				
			||||||
--                                      , "-url=jdbc:postgresql://" <> host <> "/" <> database
 | 
					 | 
				
			||||||
--                                      , "-user=" <> username
 | 
					 | 
				
			||||||
--                                      , "-password=" <> password]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
main :: IO ()
 | 
					main :: IO ()
 | 
				
			||||||
main = do
 | 
					main = do
 | 
				
			||||||
  c <- input auto "./config/config.dhall"
 | 
					  c <- input auto "./config/config.dhall"
 | 
				
			||||||
  defaultMain c
 | 
					  withApp c defaultMain
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
{-# Language NoImplicitPrelude #-}
 | 
					{-# Language NoImplicitPrelude #-}
 | 
				
			||||||
{-# Language DeriveGeneric #-}
 | 
					{-# Language DeriveGeneric #-}
 | 
				
			||||||
 | 
					{-# Language TypeSynonymInstances #-}
 | 
				
			||||||
 | 
					{-# Language FlexibleInstances #-}
 | 
				
			||||||
module Types where
 | 
					module Types where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ClassyPrelude
 | 
					import ClassyPrelude
 | 
				
			||||||
@@ -7,9 +9,13 @@ import Control.Monad.Logger
 | 
				
			|||||||
import Configuration
 | 
					import Configuration
 | 
				
			||||||
import Data.Pool (Pool)
 | 
					import Data.Pool (Pool)
 | 
				
			||||||
import Database.Selda.Backend (SeldaConnection)
 | 
					import Database.Selda.Backend (SeldaConnection)
 | 
				
			||||||
 | 
					import Crypto.Random.Types (MonadRandom(..))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
data App = App { config :: Config
 | 
					data App = App { config :: Config
 | 
				
			||||||
               , database :: Pool SeldaConnection }
 | 
					               , database :: Pool SeldaConnection }
 | 
				
			||||||
         deriving (Generic)
 | 
					         deriving (Generic)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type AppM = LoggingT (ReaderT App IO)
 | 
					type AppM = LoggingT (ReaderT App IO)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					instance MonadRandom AppM where
 | 
				
			||||||
 | 
					  getRandomBytes = lift . lift . getRandomBytes
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user