1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | module Types ( Env(..) , Config(..) , ReqInfo(..) , Ctx(..) , EnvCtx(..) , EnvCtxAuth(..) , App(..) , Handler(..) , HandlerAuth(..) , HasEnv(..) , HasCtx(..) , HasUser(..) , HasAuth(..) , CanLog(..) , Server , Action , ActionAuth ) where import qualified Database.SQLite.Simple.Internal as SQL (Connection) import Web.Scotty.Trans (ActionT, ScottyT) import Control.Monad.Reader (MonadReader, ReaderT, asks) import Control.Monad.IO.Unlift (MonadUnliftIO(..)) import Colog (LogAction(..)) import Models import qualified Data.Text as T import qualified Data.ByteString as BS import Control.Monad.IO.Class (liftIO, MonadIO) import GHC.Stack import Data.Aeson as JSON import qualified Data.Aeson.Types as JSON import Utils (logJson) import qualified Data.Vault.Lazy as V data Env = Env { db :: SQL.Connection , port :: Int , logAction :: LogAction IO BS.ByteString } data Config = Config { env :: Env , reqInfoKey :: V.Key ReqInfo } data ReqInfo = ReqInfo { reqId :: T.Text , user :: Maybe User } data Ctx = Ctx { reqId :: T.Text , csrf :: T.Text , path :: T.Text } data EnvCtx = EnvCtx { env :: Env , ctx :: Ctx , user :: Maybe User } data EnvCtxAuth = EnvCtxAuth { env :: Env , ctx :: Ctx , user :: User } newtype App a = App { runApp :: ReaderT Config IO a } deriving (Applicative, Functor, Monad, MonadIO, MonadReader Config, MonadUnliftIO) newtype Handler a = Handler { runHandler :: ReaderT EnvCtx IO a } deriving (Applicative, Functor, Monad, MonadIO, MonadReader EnvCtx, MonadUnliftIO) newtype HandlerAuth a = HandlerAuth { runHandler :: ReaderT EnvCtxAuth IO a } deriving (Applicative, Functor, Monad, MonadIO, MonadReader EnvCtxAuth, MonadUnliftIO) sourceLoc :: CallStack -> T.Text sourceLoc cs = showCallStack where showCallStack :: T.Text showCallStack = case getCallStack cs of [] -> "<unknown loc>" [(name, loc)] -> showLoc name loc (_, _) : (callerName, loc) : _ -> showLoc callerName loc showLoc :: String -> SrcLoc -> T.Text showLoc name loc = T.pack loc.srcLocModule <> "." <> T.pack name <> ":" <> T.pack (show loc.srcLocStartLine) class HasEnv m where getEnv :: m Env instance HasEnv App where getEnv = asks (.env) instance HasEnv Handler where getEnv = asks (.env) instance HasEnv HandlerAuth where getEnv = asks (.env) class Monad m => HasCtx m where getCtx :: m Ctx instance HasCtx Handler where getCtx = asks (.ctx) instance HasCtx HandlerAuth where getCtx = asks (.ctx) class Monad m => HasUser m where getUser :: m (Maybe User) getUserId :: m (Maybe T.Text) getUserId = fmap (.id) <$> getUser instance HasUser Handler where getUser = asks (.user) instance HasUser HandlerAuth where getUser = do user <- asks (.user) return $ Just user class (Monad m) => HasAuth m where getAuthUser :: m User getAuthUserId :: m T.Text getAuthUserId = fmap (.id) getAuthUser instance HasAuth HandlerAuth where getAuthUser = asks (.user) class (HasEnv m, MonadIO m) => CanLog m where logXd :: HasCallStack => T.Text -> m () logXd msg = withFrozenCallStack $ do logger <- fmap (.logAction) getEnv ctx <- logCtx liftIO $ logJson logger $ ctx ++ [ ("loc", JSON.toJSON $ sourceLoc callStack) , ("msg", JSON.toJSON msg) ] logCtx :: m [JSON.Pair] logCtx = return [] instance CanLog App instance CanLog Handler where logCtx = logHandlerCtx instance CanLog HandlerAuth where logCtx = logHandlerCtx logHandlerCtx :: (HasCtx m, HasUser m) => m [JSON.Pair] logHandlerCtx = do ctx <- getCtx userId <- getUserId return [ ("path", JSON.toJSON ctx.path) , ("reqId", JSON.toJSON ctx.reqId) , ("userId", JSON.toJSON userId) ] type Server = ScottyT App () type Action a = ActionT Handler a type ActionAuth a = ActionT HandlerAuth a |