Clarify IO usage via Applicative interface in Handlers, whitespace cleanup, remove dedicated source file for 404 view
This commit is contained in:
parent
bf47f02282
commit
754302e543
@ -1,6 +1,6 @@
|
|||||||
# the sampu Haskell blog engine
|
# the sampu Haskell blog engine
|
||||||
|
|
||||||
https://eversole.co (not live yet!)
|
https://eversole.co
|
||||||
|
|
||||||
a _work-in-progress_ blog engine using simple flat-file Markdown content storage
|
a _work-in-progress_ blog engine using simple flat-file Markdown content storage
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
cabal-version: 3.0
|
cabal-version: 3.0
|
||||||
name: sampu
|
name: sampu
|
||||||
version: 0.9.1
|
version: 0.9.2
|
||||||
license: ISC
|
license: ISC
|
||||||
author: James Eversole
|
author: James Eversole
|
||||||
maintainer: james@eversole.co
|
maintainer: james@eversole.co
|
||||||
@ -38,6 +38,5 @@ executable sampu
|
|||||||
Core.HTTP
|
Core.HTTP
|
||||||
Core.Rendering
|
Core.Rendering
|
||||||
Fragments.Base
|
Fragments.Base
|
||||||
Fragments.NotFound
|
|
||||||
Fragments.Styles
|
Fragments.Styles
|
||||||
default-language: GHC2021
|
default-language: GHC2021
|
||||||
|
@ -25,7 +25,7 @@ renderFeed = fromJust . Export.textFeedWith def{rsPretty = True} . AtomFeed
|
|||||||
|
|
||||||
-- Convert a Post to an Atom Entry
|
-- Convert a Post to an Atom Entry
|
||||||
toEntry :: Post -> Atom.Entry
|
toEntry :: Post -> Atom.Entry
|
||||||
toEntry (Post date url title content) = (Atom.nullEntry url (Atom.TextString title) date)
|
toEntry (Post date url title content) = (Atom.nullEntry url (Atom.TextString title) date)
|
||||||
{ Atom.entryLinks = [Atom.nullLink url]
|
{ Atom.entryLinks = [Atom.nullLink url]
|
||||||
, Atom.entryContent = Just (Atom.HTMLContent content)
|
, Atom.entryContent = Just (Atom.HTMLContent content)
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,10 @@ import Web.Twain
|
|||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
port <- Conf.port
|
port <- Conf.port
|
||||||
let app = preProcessors
|
let app = preProcessors
|
||||||
++ routes
|
++ routes
|
||||||
++ postProcessors
|
++ postProcessors
|
||||||
run (read port) $
|
run (read port) $
|
||||||
foldr ($) (notFound Handle.missing) app
|
foldr ($) (notFound Handle.missing) app
|
||||||
|
|
||||||
-- These Middlewares are executed before any routes are reached.
|
-- These Middlewares are executed before any routes are reached.
|
||||||
@ -36,11 +36,11 @@ postProcessors = []
|
|||||||
-- Core routes expressed as a list of WAI Middlewares.
|
-- Core routes expressed as a list of WAI Middlewares.
|
||||||
routes :: [Middleware]
|
routes :: [Middleware]
|
||||||
routes =
|
routes =
|
||||||
[ get "/" Handle.index
|
[ get "/" Handle.index
|
||||||
, get "/style.css" Handle.theme
|
, get "/style.css" Handle.theme
|
||||||
, get "/posts" Handle.postsIndex
|
, get "/posts" Handle.postsIndex
|
||||||
, get "/posts/:name" Handle.posts
|
, get "/posts/:name" Handle.posts
|
||||||
, get "/contact" Handle.contact
|
, get "/contact" Handle.contact
|
||||||
, get "/atom.xml" Handle.feed
|
, get "/atom.xml" Handle.feed
|
||||||
, get "/feed" Handle.feed
|
, get "/feed" Handle.feed
|
||||||
]
|
]
|
||||||
|
@ -2,9 +2,8 @@ module Core.Handlers where
|
|||||||
|
|
||||||
import qualified Core.Configuration as Conf
|
import qualified Core.Configuration as Conf
|
||||||
import Core.Feed (Post(..), autoFeed, renderFeed)
|
import Core.Feed (Post(..), autoFeed, renderFeed)
|
||||||
import Core.Rendering
|
import Core.Rendering
|
||||||
import Fragments.Base
|
import Fragments.Base
|
||||||
import Fragments.NotFound
|
|
||||||
import Fragments.Styles as S
|
import Fragments.Styles as S
|
||||||
|
|
||||||
import qualified Text.Atom.Feed as Atom
|
import qualified Text.Atom.Feed as Atom
|
||||||
@ -22,28 +21,27 @@ import Web.Twain hiding (fileName)
|
|||||||
|
|
||||||
-- A ResponoderM capable of lifting to IO monad; constructs response to clients
|
-- A ResponoderM capable of lifting to IO monad; constructs response to clients
|
||||||
index :: ResponderM a
|
index :: ResponderM a
|
||||||
index = do
|
index = do
|
||||||
-- Query the system environment for the BLOGTITLE environment variable
|
(title, homeMd, footerMd) <- liftIO $ (,,)
|
||||||
title <- liftIO Conf.title
|
<$> Conf.title
|
||||||
-- Read a Commonmark Markdown file and process it to HTML
|
<*> mdFileToLucid "./data/posts/home.md"
|
||||||
homeMd <- liftIO $ mdFileToLucid "./data/posts/home.md"
|
<*> mdFileToLucid "./data/posts/footer.md"
|
||||||
-- Get the Footer content from Markdown
|
|
||||||
footerMd <- liftIO $ mdFileToLucid "./data/posts/footer.md"
|
|
||||||
-- Respond to request with fragments compositionally to create a home page
|
-- Respond to request with fragments compositionally to create a home page
|
||||||
sendLucidFragment $ basePage title (baseHome homeMd) footerMd
|
sendLucidFragment $ basePage title (baseHome homeMd) footerMd
|
||||||
|
|
||||||
-- Responds with processed Commonmark -> HTML for posts
|
-- Responds with processed Commonmark -> HTML for posts
|
||||||
posts :: ResponderM a
|
posts :: ResponderM a
|
||||||
posts = do
|
posts = do
|
||||||
postName <- param "name"
|
postName <- param "name"
|
||||||
postValid <- liftIO $ postExists postName
|
postValid <- liftIO $ postExists postName
|
||||||
case postValid of
|
case postValid of
|
||||||
False -> missing
|
False -> missing
|
||||||
True -> do
|
True -> do
|
||||||
title <- liftIO Conf.title
|
(title, footerMd, postMd) <- liftIO $ (,,)
|
||||||
footerMd <- liftIO $ mdFileToLucid "./data/posts/footer.md"
|
<$> Conf.title
|
||||||
postMd <- liftIO $ mdFileToLucid $ postPath postName
|
<*> mdFileToLucid "./data/posts/footer.md"
|
||||||
sendLucidFragment $ basePage title (basePost postMd) footerMd
|
<*> (mdFileToLucid $ postPath postName)
|
||||||
|
sendLucidFragment $ basePage title (basePost postMd) footerMd
|
||||||
where
|
where
|
||||||
postExists :: T.Text -> IO Bool
|
postExists :: T.Text -> IO Bool
|
||||||
postExists postName = doesFileExist $ postPath postName
|
postExists postName = doesFileExist $ postPath postName
|
||||||
@ -54,22 +52,23 @@ posts = do
|
|||||||
-- Builds an index of all posts
|
-- Builds an index of all posts
|
||||||
postsIndex :: ResponderM a
|
postsIndex :: ResponderM a
|
||||||
postsIndex = do
|
postsIndex = do
|
||||||
postNames <- liftIO mdPostNames
|
(postNames, title, footerMd) <- liftIO $ (,,)
|
||||||
title <- liftIO Conf.title
|
<$> mdPostNames
|
||||||
footerMd <- liftIO $ mdFileToLucid "./data/posts/footer.md"
|
<*> Conf.title
|
||||||
sendLucidFragment $ basePage title (postIndex postNames) footerMd
|
<*> mdFileToLucid "./data/posts/footer.md"
|
||||||
|
sendLucidFragment $ basePage title (postIndex postNames) footerMd
|
||||||
|
|
||||||
-- Generates the XML feed at /feed
|
-- Generates the XML feed at /feed
|
||||||
feed :: ResponderM a
|
feed :: ResponderM a
|
||||||
feed = do
|
feed = do
|
||||||
postNames <- liftIO mdPostNames
|
(postNames, title, baseUrl, time) <- liftIO $ (,,,)
|
||||||
title <- liftIO Conf.title
|
<$> mdPostNames
|
||||||
baseUrl <- liftIO Conf.baseUrl
|
<*> Conf.title
|
||||||
time <- liftIO $ fmap (\x -> timeFormat x) getCurrentTime
|
<*> Conf.baseUrl
|
||||||
-- Create Atom [Post] to populate the feed
|
<*> fmap (\x -> timeFormat x) getCurrentTime
|
||||||
feedData <- liftIO $ mapM (makePost baseUrl) postNames
|
feedData <- liftIO $ mapM (makePost baseUrl) postNames
|
||||||
-- Send an XML response with an automatically populated Atom feed
|
-- Send an XML response with an automatically populated Atom feed
|
||||||
send $ xml $ TLE.encodeUtf8 $ renderFeed
|
send $ xml $ TLE.encodeUtf8 $ renderFeed
|
||||||
$ autoFeed (baseFeed title time baseUrl) feedData
|
$ autoFeed (baseFeed title time baseUrl) feedData
|
||||||
where
|
where
|
||||||
-- Base feed data structure which we populate with entries
|
-- Base feed data structure which we populate with entries
|
||||||
@ -81,12 +80,12 @@ feed = do
|
|||||||
|
|
||||||
-- Create an Atom Post for each markdown post present
|
-- Create an Atom Post for each markdown post present
|
||||||
makePost :: String -> FilePath -> IO (Post)
|
makePost :: String -> FilePath -> IO (Post)
|
||||||
makePost baseUrl postName = do
|
makePost baseUrl postName = do
|
||||||
date <- getModificationTime $ "./data/posts/" ++ postName ++ ".md"
|
date <- getModificationTime $ "./data/posts/" ++ postName ++ ".md"
|
||||||
postContent <- mdFileToText $ "./data/posts/" ++ postName ++ ".md"
|
postContent <- mdFileToText $ "./data/posts/" ++ postName ++ ".md"
|
||||||
return $ Post
|
return $ Post
|
||||||
(T.pack $ (timeFormat date) ++ " UTC")
|
(T.pack $ (timeFormat date) ++ " UTC")
|
||||||
(T.pack $ baseUrl ++ "/posts/" ++ postName)
|
(T.pack $ baseUrl ++ "/posts/" ++ postName)
|
||||||
(T.pack $ show postName)
|
(T.pack $ show postName)
|
||||||
(postContent)
|
(postContent)
|
||||||
|
|
||||||
@ -97,16 +96,17 @@ feed = do
|
|||||||
-- Refer to index comments
|
-- Refer to index comments
|
||||||
contact :: ResponderM a
|
contact :: ResponderM a
|
||||||
contact = do
|
contact = do
|
||||||
title <- liftIO Conf.title
|
(title, contactMd, footerMd) <- liftIO $ (,,)
|
||||||
contactMd <- liftIO $ mdFileToLucid "./data/posts/contact.md"
|
<$> Conf.title
|
||||||
footerMd <- liftIO $ mdFileToLucid "./data/posts/footer.md"
|
<*> mdFileToLucid "./data/posts/contact.md"
|
||||||
|
<*> mdFileToLucid "./data/posts/footer.md"
|
||||||
sendLucidFragment $ basePage title (baseContact contactMd) footerMd
|
sendLucidFragment $ basePage title (baseContact contactMd) footerMd
|
||||||
|
|
||||||
-- Respond with primary processed CSS
|
-- Respond with primary processed CSS
|
||||||
theme :: ResponderM a
|
theme :: ResponderM a
|
||||||
theme = send $ css $ TLE.encodeUtf8 $ S.cssRender S.composedStyles
|
theme = send $ css $ TLE.encodeUtf8 $ S.cssRender S.composedStyles
|
||||||
|
|
||||||
-- Helper function for responding in ResponderM from Html
|
-- Helper function for responding in ResponderM from Html
|
||||||
sendLucidFragment :: Html () -> ResponderM a
|
sendLucidFragment :: Html () -> ResponderM a
|
||||||
sendLucidFragment x = send $ html $ lucidToTwain x
|
sendLucidFragment x = send $ html $ lucidToTwain x
|
||||||
|
|
||||||
|
@ -21,6 +21,6 @@ mdFileToLucid :: FilePath -> IO (LU.Html ())
|
|||||||
mdFileToLucid path = fmap (mdToLucid . decodeUtf8) (B.readFile path)
|
mdFileToLucid path = fmap (mdToLucid . decodeUtf8) (B.readFile path)
|
||||||
|
|
||||||
mdFileToText :: FilePath -> IO (Text)
|
mdFileToText :: FilePath -> IO (Text)
|
||||||
mdFileToText path = do
|
mdFileToText path = do
|
||||||
htmlContent <- mdFileToLucid path
|
htmlContent <- mdFileToLucid path
|
||||||
return $ toStrict $ LU.renderText htmlContent
|
return $ toStrict $ LU.renderText htmlContent
|
||||||
|
@ -44,5 +44,10 @@ postIndex postNames = div_ [class_ "postList"] $ do
|
|||||||
(\x -> li_ $ a_ [href_ (pack $ "/posts/" ++ x)] (fromString x))
|
(\x -> li_ $ a_ [href_ (pack $ "/posts/" ++ x)] (fromString x))
|
||||||
postNames
|
postNames
|
||||||
|
|
||||||
|
pageNotFound :: Html ()
|
||||||
|
pageNotFound = baseDoc "404" baseNav <>
|
||||||
|
(div_ [class_ "notFound"] $ h1_ "404 NOT FOUND")
|
||||||
|
|
||||||
none :: Text
|
none :: Text
|
||||||
none = mempty
|
none = mempty
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
module Fragments.NotFound where
|
|
||||||
|
|
||||||
import Fragments.Base
|
|
||||||
|
|
||||||
import Lucid
|
|
||||||
|
|
||||||
pageNotFound :: Html ()
|
|
||||||
pageNotFound = baseDoc "404" baseNav <>
|
|
||||||
(div_ [class_ "notFound"] $ h1_ "404 NOT FOUND")
|
|
@ -61,7 +61,7 @@ footer_ = do
|
|||||||
margin auto (em 0) (em 0) (em 0)
|
margin auto (em 0) (em 0) (em 0)
|
||||||
width $ pct 100
|
width $ pct 100
|
||||||
backgroundColor terColor
|
backgroundColor terColor
|
||||||
textAlign center
|
textAlign center
|
||||||
padding (em 1) (em 0) (em 1) (em 0)
|
padding (em 1) (em 0) (em 1) (em 0)
|
||||||
boxSizing borderBox
|
boxSizing borderBox
|
||||||
p ? do
|
p ? do
|
||||||
@ -92,7 +92,7 @@ notFound_ :: Css
|
|||||||
notFound_ = do
|
notFound_ = do
|
||||||
".notFound" ? do
|
".notFound" ? do
|
||||||
margin (em 0) auto (em 0) auto
|
margin (em 0) auto (em 0) auto
|
||||||
textAlign center
|
textAlign center
|
||||||
h1 ? do
|
h1 ? do
|
||||||
fontSize $ pct 500
|
fontSize $ pct 500
|
||||||
fontWeight $ weight 200
|
fontWeight $ weight 200
|
||||||
|
@ -5,5 +5,5 @@ import qualified Core.Configuration as Conf
|
|||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
Conf.main
|
Conf.main
|
||||||
HTTP.main
|
HTTP.main
|
||||||
|
Loading…
x
Reference in New Issue
Block a user