{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TemplateHaskell #-} import Control.Lens import Data.Aeson hiding (defaultOptions) import Data.Map.Syntax ((##)) import Data.Monoid import Data.Proxy import Data.Text import GHC.Generics import qualified Heist.Interpreted as I import Snap.Core hiding (GET) import Snap.Util.CORS import Snap.Snaplet import Snap.Snaplet.Auth import Snap.Snaplet.Session import Snap.Snaplet.Session.Backends.CookieSession import Snap.Snaplet.Auth.Backends.JsonFile import Snap.Snaplet.Heist import Snap.Http.Server (defaultConfig) import Servant.API import qualified Servant.API.Stream as Stream import Servant (serveSnap, Server, serveDirectory) import qualified Servant.Types.SourceT as S -- * Example -- | A greet message data type newtype Greet = Greet { _msg :: Text } deriving (Generic, Show) instance FromJSON Greet instance ToJSON Greet -- API specification type TestApi = -- GET /hello/:name?capital={true, false} returns a Greet as JSON "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON] Greet :<|> "hellosnap" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON] Greet -- POST /greet with a Greet as JSON in the request body, -- returns a Greet as JSON :<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] Greet -- DELETE /greet/:greetid :<|> "greet" :> Capture "greetid" Text :> Delete '[JSON] () :<|> "stream" :> StreamGet NetstringFraming JSON (Stream.SourceIO Greet) :<|> "files" :> Raw :<|> "doraw" :> Raw -- Our application has some of the usual Snaplets data App = App { _heist :: Snaplet (Heist App) , _sess :: Snaplet SessionManager , _auth :: Snaplet (AuthManager App) } makeLenses ''App instance HasHeist App where heistLens = subSnaplet heist type AppHandler = Handler App App testApi :: Proxy TestApi testApi = Proxy -- Server-side handlers. -- -- There's one handler per endpoint, which, just like in the type -- that represents the API, are glued together using :<|>. -- -- Each handler runs in the 'AppHandler' monad. server :: Server TestApi '[] AppHandler server = helloH :<|> helloH' :<|> postGreetH :<|> deleteGreetH :<|> doStream :<|> serveDirectory "static" :<|> doRaw where helloH :: Text -> Maybe Bool -> AppHandler Greet helloH name Nothing = helloH name (Just False) helloH name (Just False) = return . Greet $ "Hello, " <> name helloH name (Just True) = return . Greet . toUpper $ "Hello, " <> name helloH' :: Text -> Maybe Bool -> (Handler App App) Greet helloH' name _ = with auth $ do cu <- currentUser return (Greet $ "Hi from snaplet, " <> name <> ". Login is " <> maybe "No login" (pack . show) cu) postGreetH :: Greet -> (Handler App App) Greet postGreetH greet = return greet deleteGreetH _ = return () doStream :: Handler App App (Stream.SourceIO Greet) doStream = return $ S.source [Greet "Hi", Greet "Tao", Greet "Howareya"] doRaw = with auth $ do u <- currentUser let spl = "tName" ## I.textSplice (maybe "NoLogin" (pack . show) u) renderWithSplices "test" spl initApp :: SnapletInit App App initApp = makeSnaplet "myapp" "An example app in servant" Nothing $ do h <- nestSnaplet "" heist $ heistInit "templates" s <- nestSnaplet "sess" sess $ initCookieSessionManager "site_key.txt" "sess" Nothing (Just 3600) a <- nestSnaplet "" auth $ initJsonFileAuthManager defAuthSettings sess "users.json" addRoutes [("api", applyCORS defaultOptions $ serveSnap testApi server) ,("", writeText "Hello")] return $ App h s a -- Run the server. main :: IO () main = serveSnaplet defaultConfig initApp