{-# LANGUAGE CPP, DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, TemplateHaskell, TypeFamilies #-}
module Clckwrks.Acid where
import Clckwrks.NavBar.Acid (NavBarState , initialNavBarState)
import Clckwrks.ProfileData.Acid (ProfileDataState, initialProfileDataState)
import Clckwrks.Types (UUID)
import Clckwrks.URL (ClckURL)
import Control.Applicative ((<$>))
import Control.Exception (bracket, catch, throw)
import Control.Lens ((?=), (.=), (^.), (.~), makeLenses, view, set)
import Control.Lens.At (IxValue(..), Ixed(..), Index(..), At(at))
import Control.Concurrent (killThread, forkIO)
import Control.Monad.Reader (ask)
import Control.Monad.State (modify, put)
import Data.Acid (AcidState, Query, Update, createArchive, makeAcidic)
import Data.Acid.Local (openLocalStateFrom, createCheckpointAndClose)
#if MIN_VERSION_acid_state (0,16,0)
import Data.Acid.Remote (acidServerSockAddr, skipAuthenticationCheck)
import Network.Socket (SockAddr(SockAddrUnix))
#else
import Data.Acid.Remote (acidServer, skipAuthenticationCheck)
import Network (PortID(UnixSocket))
#endif
import Data.Data (Data, Typeable)
import Data.Maybe (fromMaybe)
import Data.SafeCopy (Migrate(..), base, deriveSafeCopy, extension)
import Data.Text (Text)
import Happstack.Authenticate.Core (AuthenticateState, SimpleAddress(..))
import Happstack.Authenticate.Password.Core (PasswordState)
import Prelude hiding (catch)
import System.Directory (removeFile)
import System.FilePath ((</>))
import System.IO.Error (isDoesNotExistError)
import HSP.Google.Analytics (UACCT)
data CoreState_v0 = CoreState_v0
{ coreUACCT_v0 :: Maybe UACCT
, coreRootRedirect_v0 :: Maybe Text
}
deriving (Eq, Data, Typeable, Show)
$(deriveSafeCopy 0 'base ''CoreState_v0)
data CoreState_1 = CoreState_1
{ coreSiteName_1 :: Maybe Text
, coreUACCT_1 :: Maybe UACCT
, coreRootRedirect_1 :: Maybe Text
, coreLoginRedirect_1 :: Maybe Text
}
deriving (Eq, Data, Typeable, Show)
$(deriveSafeCopy 1 'extension ''CoreState_1)
instance Migrate CoreState_1 where
type MigrateFrom CoreState_1 = CoreState_v0
migrate (CoreState_v0 ua rr) = CoreState_1 Nothing ua rr Nothing
data CoreState = CoreState
{ _coreSiteName :: Maybe Text
, _coreUACCT :: Maybe UACCT
, _coreRootRedirect :: Maybe Text
, _coreLoginRedirect :: Maybe Text
, _coreFromAddress :: Maybe SimpleAddress
, _coreReplyToAddress :: Maybe SimpleAddress
, _coreSendmailPath :: Maybe FilePath
, _coreEnableOpenId :: Bool
}
deriving (Eq, Data, Typeable, Show)
$(deriveSafeCopy 2 'extension ''CoreState)
makeLenses ''CoreState
instance Migrate CoreState where
type MigrateFrom CoreState = CoreState_1
migrate (CoreState_1 sn ua rr lr) = CoreState sn ua rr lr Nothing Nothing Nothing True
initialCoreState :: CoreState
initialCoreState = CoreState
{ _coreSiteName = Nothing
, _coreUACCT = Nothing
, _coreRootRedirect = Nothing
, _coreLoginRedirect = Nothing
, _coreFromAddress = Nothing
, _coreReplyToAddress = Nothing
, _coreSendmailPath = Nothing
, _coreEnableOpenId = True
}
getSiteName :: Query CoreState (Maybe Text)
getSiteName = view coreSiteName
setSiteName :: Maybe Text -> Update CoreState ()
setSiteName name = coreSiteName .= name
getUACCT :: Query CoreState (Maybe UACCT)
getUACCT = view coreUACCT
setUACCT :: Maybe UACCT -> Update CoreState ()
setUACCT mua = coreUACCT .= mua
getRootRedirect :: Query CoreState (Maybe Text)
getRootRedirect = view coreRootRedirect
setRootRedirect :: Maybe Text -> Update CoreState ()
setRootRedirect path = coreRootRedirect .= path
getLoginRedirect :: Query CoreState (Maybe Text)
getLoginRedirect = view coreLoginRedirect
setLoginRedirect :: Maybe Text -> Update CoreState ()
setLoginRedirect path = coreLoginRedirect .= path
getFromAddress :: Query CoreState (Maybe SimpleAddress)
getFromAddress = view coreFromAddress
setFromAddress :: Maybe SimpleAddress -> Update CoreState ()
setFromAddress addr = coreFromAddress .= addr
getReplyToAddress :: Query CoreState (Maybe SimpleAddress)
getReplyToAddress = view coreReplyToAddress
setReplyToAddress :: Maybe SimpleAddress -> Update CoreState ()
setReplyToAddress addr = coreReplyToAddress .= addr
getSendmailPath :: Query CoreState (Maybe FilePath)
getSendmailPath = view coreSendmailPath
setSendmailPath :: Maybe FilePath -> Update CoreState ()
setSendmailPath path = coreSendmailPath .= path
getEnableOpenId :: Query CoreState Bool
getEnableOpenId = view coreEnableOpenId
setEnableOpenId :: Bool -> Update CoreState ()
setEnableOpenId b = coreEnableOpenId .= b
getCoreState :: Query CoreState CoreState
getCoreState = ask
setCoreState :: CoreState -> Update CoreState ()
setCoreState = put
$(makeAcidic ''CoreState
[ 'getUACCT
, 'setUACCT
, 'getRootRedirect
, 'setRootRedirect
, 'getLoginRedirect
, 'setLoginRedirect
, 'getSiteName
, 'setSiteName
, 'getFromAddress
, 'setFromAddress
, 'getReplyToAddress
, 'setReplyToAddress
, 'getSendmailPath
, 'setSendmailPath
, 'setEnableOpenId
, 'getEnableOpenId
, 'getCoreState
, 'setCoreState
])
data Acid = Acid
{
acidProfileData :: AcidState ProfileDataState
, acidCore :: AcidState CoreState
, acidNavBar :: AcidState NavBarState
}
class GetAcidState m st where
getAcidState :: m (AcidState st)
withAcid :: Maybe FilePath -> (Acid -> IO a) -> IO a
withAcid mBasePath f =
let basePath = fromMaybe "_state" mBasePath in
bracket (openLocalStateFrom (basePath </> "core") initialCoreState) (createArchiveCheckpointAndClose) $ \core ->
bracket (openLocalStateFrom (basePath </> "profileData") initialProfileDataState) (createArchiveCheckpointAndClose) $ \profileData ->
bracket (openLocalStateFrom (basePath </> "navBar") initialNavBarState) (createArchiveCheckpointAndClose) $ \navBar ->
#if MIN_VERSION_acid_state (0,16,0)
bracket (forkIO (tryRemoveFile (basePath </> "core_socket") >> acidServerSockAddr skipAuthenticationCheck (SockAddrUnix $ basePath </> "core_socket") profileData))
(\tid -> killThread tid >> tryRemoveFile (basePath </> "core_socket")) $ const $
#else
bracket (forkIO (tryRemoveFile (basePath </> "core_socket") >> acidServer skipAuthenticationCheck (UnixSocket $ basePath </> "core_socket") profileData))
(\tid -> killThread tid >> tryRemoveFile (basePath </> "core_socket")) $ const $
#endif
#if MIN_VERSION_acid_state (0,16,0)
bracket (forkIO (tryRemoveFile (basePath </> "profileData_socket") >> acidServerSockAddr skipAuthenticationCheck (SockAddrUnix $ basePath </> "profileData_socket") profileData))
(\tid -> killThread tid >> tryRemoveFile (basePath </> "profileData_socket"))
#else
bracket (forkIO (tryRemoveFile (basePath </> "profileData_socket") >> acidServer skipAuthenticationCheck (UnixSocket $ basePath </> "profileData_socket") profileData))
(\tid -> killThread tid >> tryRemoveFile (basePath </> "profileData_socket"))
#endif
(const $ f (Acid profileData core navBar))
where
tryRemoveFile fp = removeFile fp `catch` (\e -> if isDoesNotExistError e then return () else throw e)
createArchiveCheckpointAndClose acid =
do createArchive acid
createCheckpointAndClose acid