espial-0.0.3: src/Foundation.hs
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
module Foundation where
import Import.NoFoundation
import Database.Persist.Sql (ConnectionPool, runSqlPool)
import Text.Hamlet (hamletFile)
import Text.Jasmine (minifym)
import PathPiece()
-- import Yesod.Auth.Dummy
import Yesod.Default.Util (addStaticContentExternal)
import Yesod.Core.Types
import Yesod.Auth.Message
import qualified Yesod.Core.Unsafe as Unsafe
import qualified Data.CaseInsensitive as CI
import qualified Data.Text.Encoding as TE
data App = App
{ appSettings :: AppSettings
, appStatic :: Static -- ^ Settings for static file serving.
, appConnPool :: ConnectionPool -- ^ Database connection pool.
, appHttpManager :: Manager
, appLogger :: Logger
}
mkYesodData "App" $(parseRoutesFile "config/routes")
-- YesodPersist
instance YesodPersist App where
type YesodPersistBackend App = SqlBackend
runDB action = do
master <- getYesod
runSqlPool action (appConnPool master)
instance YesodPersistRunner App where
getDBRunner = defaultGetDBRunner appConnPool
-- Yesod
instance Yesod App where
approot = ApprootRequest $ \app req ->
case appRoot (appSettings app) of
Nothing -> getApprootText guessApproot app req
Just root -> root
makeSessionBackend _ = Just <$> defaultClientSessionBackend
10080 -- min (7 days)
"config/client_session_key.aes"
yesodMiddleware = defaultYesodMiddleware . defaultCsrfMiddleware
defaultLayout widget = do
req <- getRequest
master <- getYesod
urlrender <- getUrlRender
mmsg <- getMessage
musername <- maybeAuthUsername
mcurrentRoute <- getCurrentRoute
pc <- widgetToPageContent $ do
setTitle "Espial"
addAppScripts
addStylesheet (StaticR css_main_css)
$(widgetFile "default-layout")
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
addStaticContent ext mime content = do
master <- getYesod
let staticDir = appStaticDir (appSettings master)
addStaticContentExternal
minifym
genFileName
staticDir
(StaticR . flip StaticRoute [])
ext
mime
content
where
genFileName lbs = "autogen-" ++ base64md5 lbs
shouldLog app _source level =
appShouldLogAll (appSettings app)
|| level == LevelWarn
|| level == LevelError
makeLogger = return . appLogger
authRoute _ = Just (AuthR LoginR)
isAuthorized (AuthR _) _ = pure Authorized
isAuthorized _ _ = pure Authorized
isAuthenticated :: Handler AuthResult
isAuthenticated = maybe AuthenticationRequired (const Authorized) <$> maybeAuthId
addAppScripts :: (MonadWidget m, HandlerSite m ~ App) => m ()
addAppScripts = do
addScript (StaticR js_js_cookie_2_2_0_min_js)
addScript (StaticR js_moment_min_js)
toWidget $(juliusFile "templates/app.julius")
addScript (StaticR js_app_js)
-- popupLayout
popupLayout :: Maybe Widget -> Widget -> Handler Html
popupLayout malert widget = do
req <- getRequest
master <- getYesod
mmsg <- getMessage
musername <- maybeAuthUsername
mcurrentRoute <- getCurrentRoute
pc <- widgetToPageContent $ do
addAppScripts
addStylesheet (StaticR css_popup_css)
$(widgetFile "popup-layout")
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
-- YesodAuth
instance YesodAuth App where
type AuthId App = UserId
authHttpManager = getHttpManager
authPlugins _ = [dbAuthPlugin]
authenticate = authenticateCreds
loginDest = const HomeR
logoutDest = const HomeR
onLogin = maybeAuth >>= \case
Nothing -> cpprint ("onLogin: could not find user" :: Text)
Just (Entity _ uname) -> setSession userNameKey (userName uname)
onLogout =
deleteSession userNameKey
redirectToReferer = const True
instance YesodAuthPersist App
-- session keys
maybeAuthUsername :: Handler (Maybe Text)
maybeAuthUsername = do
lookupSession userNameKey
ultDestKey :: Text
ultDestKey = "_ULT"
userNameKey :: Text
userNameKey = "_UNAME"
-- dbAuthPlugin
dbAuthPluginName :: Text
dbAuthPluginName = "db"
dbAuthPlugin :: AuthPlugin App
dbAuthPlugin = AuthPlugin dbAuthPluginName dbDispatch dbLoginHandler
where
dbDispatch "POST" ["login"] = dbPostLoginR >>= sendResponse
dbDispatch _ _ = notFound
dbLoginHandler toParent = do
req <- getRequest
lookupSession ultDestKey >>= \case
Just dest | "logout" `isInfixOf` dest -> deleteSession ultDestKey
_ -> pure ()
setTitle "Espial | Log In"
$(widgetFile "login")
dbLoginR :: AuthRoute
dbLoginR = PluginR dbAuthPluginName ["login"]
dbPostLoginR :: AuthHandler master TypedContent
dbPostLoginR = do
mresult <- lift $ runInputPostResult (dbLoginCreds
<$> ireq textField "username"
<*> ireq textField "password")
case mresult of
FormSuccess creds -> lift $ setCredsRedirect creds
_ -> loginErrorMessageI LoginR InvalidUsernamePass
dbLoginCreds :: Text -> Text -> Creds master
dbLoginCreds username password =
Creds
{ credsPlugin = dbAuthPluginName
, credsIdent = username
, credsExtra = [("password", password)]
}
authenticateCreds
:: (AuthId master ~ UserId)
=> Creds master -> Handler (AuthenticationResult App)
authenticateCreds creds =
runDB $
authenticatePW
(credsIdent creds)
(fromMaybe "" (lookup "password" (credsExtra creds))) >>=
\case
Nothing -> pure (UserError InvalidUsernamePass)
Just (Entity uid _) -> pure (Authenticated uid)
-- Util
instance RenderMessage App FormMessage where
renderMessage _ _ = defaultFormMessage
instance HasHttpManager App where
getHttpManager = appHttpManager
unsafeHandler :: App -> Handler a -> IO a
unsafeHandler = Unsafe.fakeHandlerGetLogger appLogger