Support for Lucid HTML, add static file middleware
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,6 @@ | ||||
| WD | ||||
| bin/ | ||||
| data | ||||
| /result | ||||
| /Dockerfile | ||||
| /docker-stack.yml | ||||
|  | ||||
| @ -25,10 +25,12 @@ common global | ||||
|     , mtl | ||||
|     , postgresql-simple | ||||
|     , resource-pool | ||||
|     , servant-lucid | ||||
|     , servant-server | ||||
|     , text | ||||
|     , time | ||||
|     , utf8-string | ||||
|     , wai-middleware-static | ||||
|     , warp | ||||
|   default-extensions: | ||||
|       BlockArguments | ||||
| @ -58,6 +60,7 @@ executable Main | ||||
|     Handlers | ||||
|     Logger | ||||
|     Routes | ||||
|     Views | ||||
|   build-depends:  | ||||
|     HELPS | ||||
|  | ||||
| @ -65,4 +68,4 @@ library | ||||
|   import: global | ||||
|   hs-source-dirs: | ||||
|     src | ||||
|   exposed-modules: Core, Database, Handlers, Logger, Routes | ||||
|   exposed-modules: Core, Database, Handlers, Logger, Routes, Views, Main | ||||
|  | ||||
| @ -15,6 +15,7 @@ import Effectful.Error.Static               (Error, throwError) | ||||
| import Effectful.Reader.Static              (Reader) | ||||
| import GHC.Generics                         (Generic) | ||||
| import Servant hiding                       ((:>), throwError) | ||||
| import Servant.HTML.Lucid | ||||
|  | ||||
| -- | ||||
| -- Core data types | ||||
|  | ||||
| @ -4,6 +4,7 @@ import Core | ||||
|  | ||||
| import Control.Exception            (IOException) | ||||
| import Data.Aeson                   (ToJSON) | ||||
| import Data.ByteString              (ByteString) | ||||
| import Data.Maybe                   (listToMaybe) | ||||
| import Data.Pool | ||||
| import qualified Data.Text          as T | ||||
| @ -91,9 +92,9 @@ runDatabaseDebug = interpret $ \_ -> \case | ||||
|      ++ "\nValues:\n" | ||||
|      ++ show values | ||||
|  | ||||
| createConnectionPool :: IO (Pool Connection) | ||||
| createConnectionPool = newPool $ defaultPoolConfig | ||||
|   (connectPostgreSQL "host=localhost dbname=demo") | ||||
| createConnectionPool :: ByteString -> IO (Pool Connection) | ||||
| createConnectionPool connectString = newPool $ defaultPoolConfig | ||||
|   (connectPostgreSQL connectString) | ||||
|   close | ||||
|   60 | ||||
|   10 | ||||
|  | ||||
| @ -3,26 +3,35 @@ module Handlers where | ||||
| import Core | ||||
| import Database | ||||
| import Logger | ||||
| import Views                              as V | ||||
|  | ||||
| import qualified Data.ByteString.Char8    as C | ||||
| import Data.List | ||||
| import qualified Data.Text                as T | ||||
| import Effectful | ||||
| import Effectful.Error.Static             (Error, throwError) | ||||
| import Lucid                              (Html) | ||||
| import Servant hiding                     ((:>), throwError) | ||||
| import qualified Servant                  as S | ||||
| import Servant.HTML.Lucid | ||||
|  | ||||
| -- Type synonym for common CRUD constraints like interacting with a database, | ||||
| -- logging, and the possibility to throw an error. | ||||
| type CRUD es = ( Database :> es, Logger :> es, Error ServerError :> es ) | ||||
|  | ||||
| rootHandler :: (Logger :> es, Error ServerError :> es)  | ||||
|             => Eff es T.Text | ||||
| rootHandler = (writeLog Info "Hit the root!") >>= \_ -> | ||||
|   return "Hello, World!" | ||||
| -- Remember to drop effect constraints you don't need! | ||||
| rootHandler :: (Logger :> es, Error ServerError :> es) | ||||
|             => Eff es (Html ()) | ||||
| rootHandler = (writeLog Info "Hit the root!") | ||||
|     >>= \_ -> return V.root | ||||
|  | ||||
| userListHandler :: CRUD es  | ||||
|                 => Eff es [User] | ||||
| userListHandler = (writeLog Info "Selected all users!") >>= \_ -> | ||||
|   databaseRead_ "SELECT id, name FROM users" | ||||
|                 => Eff es (Html ()) | ||||
| userListHandler = do | ||||
|   writeLog Info "Selected all users!" | ||||
|   users <- databaseRead_ "SELECT id, name FROM users" | ||||
|   return $ V.baseDoc $ case users of | ||||
|     [] -> warning "No users found" | ||||
|     _  -> foldMap userHtml users | ||||
|  | ||||
| userGetHandler :: CRUD es  | ||||
|                => UserId -> Eff es User | ||||
|  | ||||
							
								
								
									
										18
									
								
								src/Main.hs
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/Main.hs
									
									
									
									
									
								
							| @ -6,24 +6,25 @@ import Handlers | ||||
| import Logger | ||||
| import Routes | ||||
|  | ||||
| import Control.Monad.Except     (ExceptT (ExceptT)) | ||||
| import Control.Monad.Except          (ExceptT (ExceptT)) | ||||
| import Data.List | ||||
| import Effectful | ||||
| import Effectful.Error.Static   (Error, runErrorNoCallStack, throwError) | ||||
| import Effectful.Error.Static        (Error, runErrorNoCallStack, throwError) | ||||
| import Effectful.Reader.Static | ||||
| import Network.Wai.Handler.Warp (run) | ||||
| import Servant hiding           ((:>), throwError) | ||||
| import qualified Servant        as S | ||||
| import Network.Wai.Handler.Warp      (run) | ||||
| import Network.Wai.Middleware.Static ((>->), staticPolicy, noDots, addBase) | ||||
| import Servant hiding                ((:>), throwError) | ||||
| import qualified Servant             as S | ||||
|  | ||||
| -- | ||||
| -- Application | ||||
| -- | ||||
| main :: IO () | ||||
| main = do | ||||
|   pool   <- createConnectionPool  | ||||
|   pool   <- createConnectionPool "host=localhost dbname=demo" | ||||
|   let env = AppEnv { pool = pool } | ||||
|   runAppEff env $ databaseInit | ||||
|   run port . serve proxy $ app env | ||||
|   run port . middleware . serve proxy $ app env | ||||
|  | ||||
| app :: AppEnv -> Server AppAPI | ||||
| app env = hoistServer proxy (Handler . ExceptT . runAppEff env) handlers | ||||
| @ -35,6 +36,9 @@ handlers = rootHandler | ||||
|       :<|> userPostHandler | ||||
|       :<|> userDeleteHandler | ||||
|  | ||||
| middleware :: Application -> Application | ||||
| middleware = staticPolicy (noDots >-> addBase "data/assets/public") | ||||
|  | ||||
| runAppEff :: AppEnv -> AppEff a -> IO (Either ServerError a) | ||||
| runAppEff env = runEff | ||||
|               . runErrorNoCallStack | ||||
|  | ||||
| @ -3,15 +3,17 @@ module Routes where | ||||
| import Core | ||||
|    | ||||
| import qualified Data.Text as T | ||||
| import Lucid                  (Html) | ||||
| import Servant | ||||
| import Servant.HTML.Lucid | ||||
|  | ||||
| -- | ||||
| -- Routes | ||||
| -- | ||||
| type Root = Get '[PlainText] T.Text | ||||
| type Root = Get '[HTML] (Html ()) | ||||
|  | ||||
| type UserList = "user" | ||||
|   :> Get '[JSON] [User] | ||||
|   :> Get '[HTML] (Html ()) | ||||
|  | ||||
| type UserGet = "user" | ||||
|   :> Capture "userId" UserId  | ||||
|  | ||||
							
								
								
									
										34
									
								
								src/Views.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/Views.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| module Views where | ||||
|  | ||||
| import Core | ||||
|  | ||||
| import Data.Text | ||||
| import Data.String    (fromString) | ||||
| import Effectful | ||||
| import Lucid | ||||
|  | ||||
| baseDoc :: Html () -> Html () | ||||
| baseDoc a = doctypehtml_ $ do | ||||
|   head_ $ do | ||||
|     meta_ [ name_ "viewport", content_ "width=device-width, initial-scale=1.0" ] | ||||
|     title_ "HELPS Template!" | ||||
|     link_ [rel_ "stylesheet", type_ "text/css", href_ "/style.css"] | ||||
|     body_ $ do | ||||
|       main_ [ class_ "container" ] a | ||||
|  | ||||
| root :: Html () | ||||
| root = baseDoc $ do | ||||
|   h1_ "Welcome to HELPS!" | ||||
|   p_  "Haskell, Effectful, Lucid, PostgreSQL, Servant" | ||||
|   p_  "You can get started by reviewing the README.md for directions on using \ | ||||
|      \ this template for your own projects." | ||||
|  | ||||
| userHtml :: User -> Html () | ||||
| userHtml user = div_ [] $ do | ||||
|   ul_ $ do | ||||
|     li_ $ do | ||||
|       "Username: " >> toHtml (show $ userName user) | ||||
|       ul_ $ li_ $ "User ID: " >> toHtml (show $ userId user) | ||||
|  | ||||
| warning :: String -> Html () | ||||
| warning s = p_ [class_ "warning"] (toHtml s) | ||||
		Reference in New Issue
	
	Block a user
	 James Eversole
					James Eversole