{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-}
module IHP.Controller.Session
(
SessionError (..)
, setSession
, getSession
, getSessionEither
, deleteSession
, getSessionAndClear
, sessionVaultKey
) where
import IHP.Prelude
import IHP.Controller.RequestContext
import IHP.Controller.Context
import IHP.ModelSupport
import qualified Data.UUID as UUID
import qualified Data.Vault.Lazy as Vault
import qualified Network.Wai as Wai
import qualified Data.Serialize as Serialize
import Data.Serialize (Serialize)
import Data.Serialize.Text ()
import qualified Network.Wai.Session
import System.IO.Unsafe (unsafePerformIO)
data SessionError
= NotFoundError
| ParseError String
deriving (Int -> SessionError -> ShowS
[SessionError] -> ShowS
SessionError -> String
(Int -> SessionError -> ShowS)
-> (SessionError -> String)
-> ([SessionError] -> ShowS)
-> Show SessionError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SessionError -> ShowS
showsPrec :: Int -> SessionError -> ShowS
$cshow :: SessionError -> String
show :: SessionError -> String
$cshowList :: [SessionError] -> ShowS
showList :: [SessionError] -> ShowS
Show, SessionError -> SessionError -> Bool
(SessionError -> SessionError -> Bool)
-> (SessionError -> SessionError -> Bool) -> Eq SessionError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SessionError -> SessionError -> Bool
== :: SessionError -> SessionError -> Bool
$c/= :: SessionError -> SessionError -> Bool
/= :: SessionError -> SessionError -> Bool
Eq)
setSession :: (?context :: ControllerContext, Serialize value)
=> ByteString -> value -> IO ()
setSession :: forall value.
(?context::ControllerContext, Serialize value) =>
ByteString -> value -> IO ()
setSession ByteString
name value
value = (?context::ControllerContext) => ByteString -> ByteString -> IO ()
ByteString -> ByteString -> IO ()
sessionInsert ByteString
name (value -> ByteString
forall a. Serialize a => a -> ByteString
Serialize.encode value
value)
{-# INLINABLE setSession #-}
getSession :: forall value
. (?context :: ControllerContext, Serialize value)
=> ByteString -> IO (Maybe value)
getSession :: forall value.
(?context::ControllerContext, Serialize value) =>
ByteString -> IO (Maybe value)
getSession ByteString
name = ByteString -> IO (Either SessionError value)
forall value.
(?context::ControllerContext, Serialize value) =>
ByteString -> IO (Either SessionError value)
getSessionEither ByteString
name IO (Either SessionError value)
-> (Either SessionError value -> IO (Maybe value))
-> IO (Maybe value)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Left SessionError
_ -> Maybe value -> IO (Maybe value)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe value
forall a. Maybe a
Nothing
Right value
result -> Maybe value -> IO (Maybe value)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (value -> Maybe value
forall a. a -> Maybe a
Just value
result)
{-# INLINABLE getSession #-}
getSessionEither :: forall value
. (?context :: ControllerContext, Serialize value)
=> ByteString -> IO (Either SessionError value)
getSessionEither :: forall value.
(?context::ControllerContext, Serialize value) =>
ByteString -> IO (Either SessionError value)
getSessionEither ByteString
name = (?context::ControllerContext) =>
ByteString -> IO (Maybe ByteString)
ByteString -> IO (Maybe ByteString)
sessionLookup ByteString
name IO (Maybe ByteString)
-> (Maybe ByteString -> IO (Either SessionError value))
-> IO (Either SessionError value)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe ByteString
Nothing -> Either SessionError value -> IO (Either SessionError value)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either SessionError value -> IO (Either SessionError value))
-> Either SessionError value -> IO (Either SessionError value)
forall a b. (a -> b) -> a -> b
$ SessionError -> Either SessionError value
forall a b. a -> Either a b
Left SessionError
NotFoundError
Just ByteString
"" -> Either SessionError value -> IO (Either SessionError value)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either SessionError value -> IO (Either SessionError value))
-> Either SessionError value -> IO (Either SessionError value)
forall a b. (a -> b) -> a -> b
$ SessionError -> Either SessionError value
forall a b. a -> Either a b
Left SessionError
NotFoundError
Just ByteString
stringValue -> case ByteString -> Either String value
forall a. Serialize a => ByteString -> Either String a
Serialize.decode ByteString
stringValue of
Left String
error -> Either SessionError value -> IO (Either SessionError value)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either SessionError value -> IO (Either SessionError value))
-> (SessionError -> Either SessionError value)
-> SessionError
-> IO (Either SessionError value)
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. SessionError -> Either SessionError value
forall a b. a -> Either a b
Left (SessionError -> IO (Either SessionError value))
-> SessionError -> IO (Either SessionError value)
forall a b. (a -> b) -> a -> b
$ String -> SessionError
ParseError String
error
Right value
value -> Either SessionError value -> IO (Either SessionError value)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either SessionError value -> IO (Either SessionError value))
-> Either SessionError value -> IO (Either SessionError value)
forall a b. (a -> b) -> a -> b
$ value -> Either SessionError value
forall a b. b -> Either a b
Right value
value
{-# INLINABLE getSessionEither #-}
deleteSession :: (?context :: ControllerContext) => ByteString -> IO ()
deleteSession :: (?context::ControllerContext) => ByteString -> IO ()
deleteSession ByteString
name = (?context::ControllerContext) => ByteString -> ByteString -> IO ()
ByteString -> ByteString -> IO ()
sessionInsert ByteString
name ByteString
""
getSessionAndClear :: forall value
. (?context :: ControllerContext, Serialize value)
=> ByteString -> IO (Maybe value)
getSessionAndClear :: forall value.
(?context::ControllerContext, Serialize value) =>
ByteString -> IO (Maybe value)
getSessionAndClear ByteString
name = do
Maybe value
value <- forall value.
(?context::ControllerContext, Serialize value) =>
ByteString -> IO (Maybe value)
getSession @value ByteString
name
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe value -> Bool
forall a. Maybe a -> Bool
isJust Maybe value
value) ((?context::ControllerContext) => ByteString -> IO ()
ByteString -> IO ()
deleteSession ByteString
name)
Maybe value -> IO (Maybe value)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe value
value
{-# INLINABLE getSessionAndClear #-}
instance (PrimaryKey table ~ UUID) => Serialize (Id' table) where
put :: Putter (Id' table)
put (Id PrimaryKey table
value) = Putter ByteString
forall t. Serialize t => Putter t
Serialize.put (UUID -> ByteString
UUID.toASCIIBytes UUID
PrimaryKey table
value)
get :: Get (Id' table)
get = do
Maybe UUID
maybeUUID <- ByteString -> Maybe UUID
UUID.fromASCIIBytes (ByteString -> Maybe UUID) -> Get ByteString -> Get (Maybe UUID)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get ByteString
forall t. Serialize t => Get t
Serialize.get
case Maybe UUID
maybeUUID of
Maybe UUID
Nothing -> String -> Get (Id' table)
forall a. String -> Get a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Failed to parse UUID"
Just UUID
uuid -> Id' table -> Get (Id' table)
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (PrimaryKey table -> Id' table
forall (table :: Symbol). PrimaryKey table -> Id' table
Id UUID
PrimaryKey table
uuid)
sessionInsert :: (?context :: ControllerContext) => ByteString -> ByteString -> IO ()
sessionInsert :: (?context::ControllerContext) => ByteString -> ByteString -> IO ()
sessionInsert = (ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
-> ByteString -> ByteString -> IO ()
forall a b. (a, b) -> b
snd (ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
(?context::ControllerContext) =>
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
sessionVault
sessionLookup :: (?context :: ControllerContext) => ByteString -> IO (Maybe ByteString)
sessionLookup :: (?context::ControllerContext) =>
ByteString -> IO (Maybe ByteString)
sessionLookup = (ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
-> ByteString -> IO (Maybe ByteString)
forall a b. (a, b) -> a
fst (ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
(?context::ControllerContext) =>
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
sessionVault
sessionVault :: (?context :: ControllerContext) => (ByteString -> IO (Maybe ByteString), ByteString -> ByteString -> IO ())
sessionVault :: (?context::ControllerContext) =>
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
sessionVault = case Maybe
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
vaultLookup of
Just (ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
session -> (ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
session
Maybe
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
Nothing -> Text
-> (ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
forall a. Text -> a
error Text
"sessionInsert: The session vault is missing in the request"
where
RequestContext { Request
request :: Request
request :: RequestContext -> Request
request } = ?context::ControllerContext
ControllerContext
?context.requestContext
vaultLookup :: Maybe
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
vaultLookup = Key
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
-> Vault
-> Maybe
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
forall a. Key a -> Vault -> Maybe a
Vault.lookup Key
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
sessionVaultKey Request
request.vault
sessionVaultKey :: Vault.Key (Network.Wai.Session.Session IO ByteString ByteString)
sessionVaultKey :: Key
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
sessionVaultKey = IO
(Key
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ()))
-> Key
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ())
forall a. IO a -> a
unsafePerformIO IO
(Key
(ByteString -> IO (Maybe ByteString),
ByteString -> ByteString -> IO ()))
forall a. IO (Key a)
Vault.newKey
{-# NOINLINE sessionVaultKey #-}