module Web.Spock.Simple
(
spock, SpockM
, spockT, spockLimT, SpockT
, SpockRoute, (<//>)
, subcomponent
, get, post, getpost, head, put, delete, patch, hookRoute, hookAny
, Http.StdMethod (..)
, middleware
, SafeAction (..)
, safeActionPath
, module Web.Spock.Shared
)
where
import Web.Spock.Shared
import Web.Spock.Internal.Types
import qualified Web.Spock.Internal.Core as C
import Control.Applicative
import Control.Monad.Trans
import Data.Monoid
import Data.String
import Data.Word
import Network.HTTP.Types.Method
import Prelude hiding (head)
import Web.Routing.TextRouting
import qualified Data.Text as T
import qualified Network.HTTP.Types as Http
import qualified Network.Wai as Wai
type SpockM conn sess st a = SpockT (WebStateM conn sess st) a
newtype SpockT m a
= SpockT { runSpockT :: C.SpockAllT (TextRouter (ActionT m) ()) m a
} deriving (Monad, Functor, Applicative, MonadIO)
instance MonadTrans SpockT where
lift = SpockT . lift
newtype SpockRoute
= SpockRoute { _unSpockRoute :: T.Text }
deriving (Eq, Ord, Show, Read)
instance IsString SpockRoute where
fromString str = SpockRoute $ combineRoute (T.pack str) ""
spock :: SpockCfg conn sess st -> SpockM conn sess st () -> IO Wai.Middleware
spock cfg spockAppl =
C.spockAll TextRouter cfg (runSpockT spockAppl')
where
spockAppl' =
do hookSafeActions
spockAppl
spockT :: (MonadIO m)
=> (forall a. m a -> IO a)
-> SpockT m ()
-> IO Wai.Middleware
spockT = spockLimT Nothing
spockLimT :: (MonadIO m)
=> Maybe Word64
-> (forall a. m a -> IO a)
-> SpockT m ()
-> IO Wai.Middleware
spockLimT mSizeLimit liftFun (SpockT app) =
C.spockAllT mSizeLimit TextRouter liftFun app
(<//>) :: SpockRoute -> SpockRoute -> SpockRoute
(SpockRoute t) <//> (SpockRoute t') = SpockRoute $ combineRoute t t'
get :: MonadIO m => SpockRoute -> ActionT m () -> SpockT m ()
get = hookRoute GET
post :: MonadIO m => SpockRoute -> ActionT m () -> SpockT m ()
post = hookRoute POST
getpost :: MonadIO m => SpockRoute -> ActionT m () -> SpockT m ()
getpost r a = hookRoute POST r a >> hookRoute GET r a
head :: MonadIO m => SpockRoute -> ActionT m () -> SpockT m ()
head = hookRoute HEAD
put :: MonadIO m => SpockRoute -> ActionT m () -> SpockT m ()
put = hookRoute PUT
delete :: MonadIO m => SpockRoute -> ActionT m () -> SpockT m ()
delete = hookRoute DELETE
patch :: MonadIO m => SpockRoute -> ActionT m () -> SpockT m ()
patch = hookRoute PATCH
hookRoute :: Monad m => StdMethod -> SpockRoute -> ActionT m () -> SpockT m ()
hookRoute m (SpockRoute path) action = SpockT $ C.hookRoute m (TextRouterPath path) (TAction action)
hookAny :: Monad m => StdMethod -> ([T.Text] -> ActionT m ()) -> SpockT m ()
hookAny m action = SpockT $ C.hookAny m action
subcomponent :: Monad m => SpockRoute -> SpockT m () -> SpockT m ()
subcomponent (SpockRoute p) (SpockT subapp) = SpockT $ C.subcomponent (TextRouterPath p) subapp
middleware :: Monad m => Wai.Middleware -> SpockT m ()
middleware = SpockT . C.middleware
safeActionPath :: forall conn sess st a.
( SafeAction conn sess st a
, HasSpock(SpockAction conn sess st)
, SpockConn (SpockAction conn sess st) ~ conn
, SpockSession (SpockAction conn sess st) ~ sess
, SpockState (SpockAction conn sess st) ~ st)
=> a
-> SpockAction conn sess st T.Text
safeActionPath safeAction =
do mgr <- getSessMgr
hash <- sm_addSafeAction mgr (PackedSafeAction safeAction)
return $ "/h/" <> hash
hookSafeActions :: forall conn sess st.
( HasSpock (SpockAction conn sess st)
, SpockConn (SpockAction conn sess st) ~ conn
, SpockSession (SpockAction conn sess st) ~ sess
, SpockState (SpockAction conn sess st) ~ st)
=> SpockM conn sess st ()
hookSafeActions =
getpost ("h" <//> ":spock-csurf-protection") run
where
run =
do Just h <- param "spock-csurf-protection"
mgr <- getSessMgr
mAction <- sm_lookupSafeAction mgr h
case mAction of
Nothing ->
do setStatus Http.status404
text "File not found"
Just p@(PackedSafeAction action) ->
do runSafeAction action
sm_removeSafeAction mgr p