module Web.Wheb.Plugins.Redis (
runRedis
, initRedis
, initRedisCache
, RedisApp (..)
, RedisCacheApp (..)
, RedisContainer
, RedisCacheContainer
, module Database.Redis
) where
import Control.Monad (void, liftM)
import Data.Monoid ((<>))
import Data.ByteString
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Database.Redis hiding (runRedis)
import qualified Database.Redis as R
import Web.Wheb
import Web.Wheb.Plugins.Cache
import Web.Wheb.Plugins.Auth
import Web.Wheb.Plugins.Session
sessionPrefix = T.pack "session"
userPrefix = T.pack "user"
data RedisContainer = RedisContainer Connection
data RedisCacheContainer = RedisCacheContainer Connection
class RedisApp a where
getRedisContainer :: a -> RedisContainer
instance RedisApp a => SessionApp a where
getSessionContainer = SessionContainer . getRedisContainer
instance RedisApp a => AuthApp a where
getAuthContainer = AuthContainer . getRedisContainer
class RedisCacheApp a where
getRedisCacheContainer :: a -> RedisCacheContainer
instance RedisCacheApp a => CacheApp a where
getCacheContainer = CacheContainer . getRedisCacheContainer
instance CacheBackend RedisCacheContainer where
backendCachePut key content expr con = do
mvoid $ runWithCacheContainer con $ setex (T.encodeUtf8 key) expr content
backendCacheGet key con = do
e <- runWithCacheContainer con $ get (T.encodeUtf8 key)
return $ either (const Nothing) id e
backendCacheDelete key con = do
mvoid $ runWithCacheContainer con $ del [(T.encodeUtf8 key)]
instance SessionBackend RedisContainer where
backendSessionPut sessId key content mc = do
mvoid $ runWithContainer mc $ do
hset (makeKey sessionPrefix sessId) (T.encodeUtf8 key) (T.encodeUtf8 content)
backendSessionGet sessId key mc = do
runWithContainer mc $ do
e <- hget (makeKey sessionPrefix sessId) (T.encodeUtf8 key)
return $ either (const Nothing) (fmap T.decodeUtf8) e
backendSessionDelete sessId key mc = do
mvoid $ runWithContainer mc $
hdel (makeKey sessionPrefix sessId) [T.encodeUtf8 key]
backendSessionClear sessId mc = do
mvoid $ runWithContainer mc $ do
let sessk = (makeKey sessionPrefix sessId)
ek <- hkeys sessk
either (const $ return ()) (mvoid . hdel sessk) ek
instance AuthBackend RedisContainer where
backendGetUser name mc = do
runWithContainer mc $ do
n <- get (makeKey userPrefix name)
return $ either (const Nothing) (const $ Just $ AuthUser name) n
backendLogin name pw mc = do
passCheck <- runWithContainer mc $ do
n <- get (makeKey userPrefix name)
return $ either (const Nothing) (fmap (verifyPw pw . T.decodeUtf8)) n
case passCheck of
Just True -> return (Right $ AuthUser $ name)
Just False -> return (Left InvalidPassword)
Nothing -> return (Left UserDoesNotExist)
backendRegister user@(AuthUser name) pw mc = do
pwHash <- makePwHash pw
runWithContainer mc $ do
n <- get (makeKey userPrefix name)
case n of
Right Nothing -> do
set (makeKey userPrefix name) (T.encodeUtf8 pwHash)
return (Right $ user)
_ -> return (Left DuplicateUsername)
backendLogout _ = getUserSessionKey >>= deleteSessionValue
makeKey :: T.Text -> T.Text -> ByteString
makeKey a b = T.encodeUtf8 $ a <> (T.pack ":") <> b
mvoid :: Monad m => m a -> m ()
mvoid a = a >> return ()
runWithCacheContainer :: MonadIO m => RedisCacheContainer -> Redis a -> WhebT g s m a
runWithCacheContainer (RedisCacheContainer con) r = liftIO $ R.runRedis con r
runWithContainer :: MonadIO m => RedisContainer -> Redis a -> WhebT g s m a
runWithContainer (RedisContainer con) r = liftIO $ R.runRedis con r
runRedis :: (RedisApp g, MonadIO m) => Redis a -> WhebT g s m a
runRedis r = getWithApp getRedisContainer >>= flip runWithContainer r
initRedis :: MonadIO m => ConnectInfo -> InitM g s m RedisContainer
initRedis info = do
conn <- liftIO $ connect info
return $ RedisContainer conn
initRedisCache :: MonadIO m => ConnectInfo -> InitM g s m RedisCacheContainer
initRedisCache info = do
conn <- liftIO $ connect info
return $ RedisCacheContainer conn