{-# LANGUAGE FlexibleContexts, NoMonomorphismRestriction #-} module Main where import Control.Applicative import Control.Concurrent import Control.Concurrent.STM import Data.FileStore import Data.Maybe import Data.Record.Label import Network.Protocol.Http import Network.Salvia import Network.Salvia.Handler.ColorLog import Network.Salvia.Handler.ExtendedFileSystem import Network.Salvia.Handler.FileStore import Network.Salvia.Handler.StringTemplate import Network.Salvia.Handler.WebSocket import Network.Salvia.Handler.Login import Network.Salvia.Handler.Session import Network.Socket hiding (Socket, send) import Prelude hiding (read) import System.IO import qualified Control.Concurrent.ThreadManager as Tm import qualified Control.Monad.State as S import Paths_salvia_demo -- At the bottom to prevent warnings (?) import Control.Monad import Control.Monad.Trans main :: IO () main = do -- Index HTML template. let template tmpl = do s <- show . isJust . get sPayload <$> getSession u <- maybe "anonymous" (get username) <$> hGetUser c <- show . (+1) . unCounter <$> payload S.get hStringTemplate tmpl [ ("loggedin", s) , ("username", u) , ("counter", c) ] -- Handler environment to run handler in. let myHandlerEnv handler = do prolongSession (60 * 60) -- Expire after one hour of inactivity. hPortRouter [ ( 8080 , hVirtualHosting [ ("localhost", hRedirect "http://127.0.0.1:8080/") ] handler) ] (hCustomError Forbidden "Public service running on port 8080.") hColorLogWithCounter stdout -- Data directories packed in Cabal package. www <- getDataFileName "www" db <- getDataFileName "www/data/users.db" cgi <- getDataFileName "www/demo.cgi" idx <- getDataFileName "www/index.html" tm <- Tm.make counter <- atomically (newTVar (Counter 0)) ping <- atomically (newTMVar (0 :: Integer)) sessions <- atomically (newTVar mkSessions) :: IO (TVar (Sessions (UserPayload Bool))) userDB <- read (fileBackend db) >>= atomically . newTVar addr <- inet_addr "0.0.0.0" let filestore repo = hFileStore (gitFileStore repo) (Author "sebas" "sfvisser@cs.uu.nl") repo let ws = lift (forker tm (hSendTMVar 100 ping show)) >> hOnMessageUpdateTMVar 100 (const (+1)) ping let myHandler = (hDefaultEnv . myHandlerEnv) . hPrefixRouter [ ("/code", hExtendedFileSystem "src") , ("/store", filestore ".") ] . hPathRouter [ ("/", template idx) , ("/لغة عربية", hCustomError OK "arabic") , ("/Ελληνική", hCustomError OK "greek") , ("/Русский", hCustomError OK "russian") , ("/עִבְרִית", hCustomError OK "hebrew") , ("/ping", hWebSocket "myproto" (lift (hColorLogWithCounter stdout) >> ws)) , ("/favicon.ico", hError BadRequest) , ("/loginfo", loginfo) , ("/logout", logout >> hRedirect "/") , ("/login", login unauth (const $ hRedirect "/")) , ("/signup", whenWriteAccess (signup ["read-udb"] unauth (const $ hRedirect "/"))) , ("/users.db", whenReadAccess (hFileResource db)) , ("/sources", hCGI cgi) ] $ (hExtendedFileSystem www) where unauth = hCustomError Unauthorized "unauthorized, please login" whenReadAccess = authorized (Just "read-udb") unauth . const whenWriteAccess = authorized (Just "write-udb") unauth . const let myPayload = userDB & counter & sessions let myConfig = defaultConfig { listenOn = [ SockAddrInet 8080 addr , SockAddrInet 9090 addr ] } start myConfig myHandler myPayload forker :: (ForkM IO m, MonadIO m) => Tm.ThreadManager -> m () -> m ThreadId forker tm = forkM >=> liftIO . Tm.fork tm