cqrs-example-0.8.0: src/CQRSExample/Main.hs
import Blaze.ByteString.Builder (Builder)
import Blaze.ByteString.Builder.Char8 (fromText)
import Control.Concurrent (forkIO, threadDelay)
import Control.Concurrent.Chan (Chan, newChan, writeList2Chan)
import Control.Monad (void)
import Data.CQRS
import Data.CQRS.EventStore.Backend.Sqlite3 (openSqliteEventStore)
import qualified Data.Set as S
import Data.Text (Text)
import qualified Data.Text as T
import Database.HDBC (IConnection)
import Database.HDBC.Sqlite3 (connectSqlite3)
import Network.Wai.EventSource (ServerEvent(..))
import Network.Wai.Handler.Warp (runSettings, defaultSettings, settingsPort)
import Prelude hiding (mapM)
import CQRSExample.Command
import CQRSExample.Events
import CQRSExample.Instances ()
import CQRSExample.Query
import CQRSExample.Wai
import CQRSExample.WaiAuth
-- Source of refresh events to the browser.
eventSourcingThread :: IConnection c => c -> Chan ServerEvent -> EventStore Event -> IO ()
eventSourcingThread connection jsonEvents eventStore = do
loop
where
loop = do
putStrLn "Sourcing events..."
refresh <- reactToUnseenEvents connection eventStore
-- Issue refresh events to browser.
writeList2Chan jsonEvents (map (toServerEvent . fromText . convertRefresh) $ S.toList refresh)
-- Wait 1 second
threadDelay 1000000
-- Go again.
loop
-- Convert refresh values to JSON for browser.
convertRefresh :: Refresh -> Text
convertRefresh RefreshProjects = "refresh-projects"
convertRefresh RefreshTasks = "refresh-tasks"
convertRefresh RefreshTimesheet = "refresh-timesheet"
-- Convert a value to a server event.
toServerEvent :: Builder -> ServerEvent
toServerEvent j = ServerEvent Nothing Nothing [j]
-- Check user.
checkUser :: IConnection c => c -> String -> String -> IO (Maybe AuthenticatedUser)
checkUser c u p = do
user <- qFindUser c $ T.pack u
case user of
Just (userId, p') ->
return $ if p == T.unpack p' then Just (AuthenticatedUser u userId) else Nothing
Nothing ->
return Nothing
-- Start serving the application.
startServing :: IO ()
startServing =
do
let queryDbFile = "query.db"
let sqliteFile = "example.db"
-- Connect to query database
conn <- connectSqlite3 queryDbFile
setupQueryDatabase conn -- Perform any necessary setup.
-- Queue of json events to send to browser.
jsonEvents <- newChan
-- Start sourcing events.
_ <- forkIO $ do
withEventStore (openSqliteEventStore sqliteFile) $ do
eventSourcingThread conn jsonEvents
-- Web serving thread.
let mySettings = defaultSettings { settingsPort = 8000 }
void $ forkIO $ do
withEventStore (openSqliteEventStore sqliteFile) $ \eventStore ->
runSettings mySettings $
basicAuth "Clockwork" (checkUser conn) $
exampleWaiApplication conn eventStore jsonEvents
-- Perform site initialization if necessary.
withEventStore (openSqliteEventStore sqliteFile) $ \eventStore -> do
runTransactionT eventStore $ do
void $ siteInitialization
main :: IO ()
main = do
putStrLn "Starting..."
startServing
putStrLn "Press <Enter> to quit"
_ <- getLine
return ()