chore(tazblog): Replace BlogDB with stubs for DNS-based storage

Removes acid-state specific code and the former BlogDB module, in its
stead the new BlogStorage module contains stubs for the functions that
will be filled in with DNS-based storage.

This code is unformatted and will not currently serve a working blog.
This commit is contained in:
Vincent Ambo 2019-08-20 00:17:23 +01:00
parent 1d5b53abf8
commit 11fcf62297
10 changed files with 116 additions and 561 deletions

View file

@ -1,11 +1,8 @@
-- | Main module for the blog's web server
module Main where
import BlogDB (initialBlogState)
import Control.Applicative (pure, (<$>), (<*>))
import Control.Applicative (pure, (<*>))
import Control.Exception (bracket)
import Data.Acid
import Data.Acid.Remote
import Data.Word (Word16)
import Locales (version)
import Network (HostName, PortID (..))
@ -13,18 +10,12 @@ import Options
import Server
data MainOptions = MainOptions {
dbHost :: String,
dbPort :: Word16,
blogPort :: Int,
resourceDir :: String
}
instance Options MainOptions where
defineOptions = pure MainOptions
<*> simpleOption "dbHost" "localhost"
"Remote acid-state database host. Default is localhost"
<*> simpleOption "dbPort" 8070
"Remote acid-state database port. Default is 8070"
<*> simpleOption "blogPort" 8000
"Port to serve the blog on. Default is 8000."
<*> simpleOption "resourceDir" "/opt/tazblog/static"
@ -34,8 +25,4 @@ main :: IO()
main = do
putStrLn ("TazBlog " ++ version ++ " in Haskell starting")
runCommand $ \opts _ ->
let port = PortNumber $ fromIntegral $ dbPort opts
in openRemoteState skipAuthenticationPerform (dbHost opts) port >>=
(\acid -> runBlog acid (blogPort opts) (resourceDir opts))
runBlog (blogPort opts) (resourceDir opts)

View file

@ -1,34 +0,0 @@
-- | Main module for the database server
module Main where
import BlogDB (initialBlogState)
import Control.Applicative (pure, (<$>), (<*>))
import Control.Exception (bracket)
import Data.Acid
import Data.Acid.Local (createCheckpointAndClose)
import Data.Acid.Remote
import Data.Word
import Network (PortID (..))
import Options
data DBOptions = DBOptions {
dbPort :: Word16,
stateDirectory :: String
}
instance Options DBOptions where
defineOptions = pure DBOptions
<*> simpleOption "dbport" 8070
"Port to serve acid-state on remotely."
<*> simpleOption "state" "/var/tazblog/state"
"Directory in which the acid-state is located."
main :: IO ()
main = do
putStrLn ("Launching TazBlog database server ...")
runCommand $ \opts args ->
bracket (openState opts) createCheckpointAndClose
(acidServer skipAuthenticationCheck $ getPort opts)
where
openState o = openLocalStateFrom (stateDirectory o) initialBlogState
getPort = PortNumber . fromIntegral . dbPort

View file

@ -11,9 +11,9 @@
module Blog where
import BlogDB
import BlogStore
import Data.Maybe (fromJust)
import Data.Text (Text, append, empty, pack)
import Data.Text (Text, empty, pack)
import Data.Text.Lazy (fromStrict)
import Data.Time
import Locales
@ -75,9 +75,9 @@ isEntryMarkdown e = edate e > markdownCutoff
renderEntryMarkdown :: Text -> Html
renderEntryMarkdown = markdown def {msXssProtect = False} . fromStrict
renderEntries :: Bool -> [Entry] -> Maybe Html -> Html
renderEntries showAll entries pageLinks = [shamlet|
$forall entry <- toDisplay
renderEntries :: [Entry] -> Maybe Html -> Html
renderEntries entries pageLinks = [shamlet|
$forall entry <- entries
<article>
<h2 .inline>
<a href=#{linkElems entry} .unstyled-link>
@ -97,10 +97,9 @@ $maybe links <- pageLinks
^{links}
|]
where
toDisplay = if showAll then entries else (take 6 entries)
linkElems Entry{..} = concat $ ["/", show lang, "/", show entryId]
showLinks :: Maybe Int -> BlogLang -> Html
showLinks :: Maybe Integer -> BlogLang -> Html
showLinks (Just i) lang = [shamlet|
$if ((>) i 1)
<div .navigation>
@ -135,103 +134,6 @@ renderEntry e@Entry{..} = [shamlet|
<hr>
|]
{- Administration pages -}
adminTemplate :: Text -> Html -> Html
adminTemplate title body = [shamlet|
$doctype 5
<head>
<link rel="stylesheet" type="text/css" href="/static/admin.css" media="all">
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
<title>#{append "TazBlog Admin: " title}
<body>
^{body}
|]
adminLogin :: Html
adminLogin = adminTemplate "Login" $ [shamlet|
<div class="loginBox">
<div class="loginBoxTop">TazBlog Admin: Login
<div class="loginBoxMiddle">
<form action="/admin" method="POST">
<p>Account ID
<p><input type="text" style="font-size:2;" name="account" value="tazjin" readonly="1">
<p>Passwort
<p><input type="password" style="font-size:2;" name="password">
<p><input alt="Anmelden" type="image" src="/static/signin.gif">
|]
adminIndex :: Text -> Html
adminIndex sUser = adminTemplate "Index" $ [shamlet|
<div style="float:center;">
<form action="/admin/entry" method="POST">
<table>
<tr>
<thead><td>Title:
<td><input type="text" name="title">
<tr>
<thead><td>Language:
<td><select name="lang">
<option value="en">English
<option value="de">Deutsch
<tr>
<thead><td>Text:
<td>
<textarea name="btext" cols="100" rows="15">
<tr>
<thead>
<td style="vertical-align:top;">Read more:
<td>
<textarea name="mtext" cols="100" rows="15">
<input type="hidden" name="author" value=#{sUser}>
<input style="margin-left:20px;" type="submit" value="Submit">
^{adminFooter}
|]
adminFooter :: Html
adminFooter = [shamlet|
<a href="/">Front page
\ -- #
<a href="/admin">New article
\ -- Entry list: #
<a href="/admin/entrylist/en">EN
\ & #
<a href="/admin/entrylist/de">DE
|]
adminEntryList :: [Entry] -> Html
adminEntryList entries = adminTemplate "EntryList" $ [shamlet|
<div style="float: center;">
<table>
$forall entry <- entries
<tr>
<td><a href=#{append "/admin/entry/" (show' $ entryId entry)}>#{title entry}
<td>#{formatPostDate $ edate entry}
|]
where
formatPostDate = formatTime defaultTimeLocale "[On %D at %H:%M]"
editPage :: Entry -> Html
editPage (Entry{..}) = adminTemplate "Index" $ [shamlet|
<div style="float:center;">
<form action=#{append "/admin/entry/" (show' entryId)} method="POST">
<table>
<tr>
<td>Title:
<td>
<input type="text" name="title" value=#{title}>
<tr>
<td style="vertical-align:top;">Text:
<td>
<textarea name="btext" cols="100" rows="15">#{btext}
<tr>
<td style="vertical-align:top;">Read more:
<td>
<textarea name="mtext" cols="100" rows="15">#{mtext}
<input type="submit" style="margin-left:20px;" value="Submit">
<p>^{adminFooter}
|]
showError :: BlogError -> BlogLang -> Html
showError NotFound l = blogTemplate l (T.append ": " $ notFoundTitle l) $ [shamlet|
<p>:(

View file

@ -1,241 +0,0 @@
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module BlogDB where
import Control.Monad.Reader (ask)
import Control.Monad.State (get, put)
import Data.Acid
import Data.Acid.Advanced
import Data.Acid.Remote
import Data.ByteString (ByteString)
import Data.Data (Data, Typeable)
import Data.IxSet (Indexable (..), IxSet, Proxy (..),
getOne, ixFun, ixSet, (@=))
import Data.SafeCopy (base, deriveSafeCopy)
import Data.Text (Text, pack)
import Data.Time
import Network (PortID (..))
import System.Environment (getEnv)
import qualified Crypto.Hash.SHA512 as SHA (hash)
import qualified Data.ByteString.Base64 as B64 (encode)
import qualified Data.ByteString.Char8 as B
import qualified Data.IxSet as IxSet
newtype EntryId = EntryId { unEntryId :: Integer }
deriving (Eq, Ord, Data, Enum, Typeable)
$(deriveSafeCopy 2 'base ''EntryId)
instance Show EntryId where
show = show . unEntryId
data BlogLang = EN | DE
deriving (Eq, Ord, Data, Typeable)
instance Show BlogLang where
show DE = "de"
show EN = "en"
$(deriveSafeCopy 0 'base ''BlogLang)
data Entry = Entry {
entryId :: EntryId,
lang :: BlogLang,
author :: Text,
title :: Text,
btext :: Text,
mtext :: Text,
edate :: UTCTime
} deriving (Eq, Ord, Show, Data, Typeable)
$(deriveSafeCopy 2 'base ''Entry)
-- ixSet requires different datatypes for field indexes, so let's define some
newtype Author = Author Text deriving (Eq, Ord, Data, Typeable)
newtype Title = Title Text deriving (Eq, Ord, Data, Typeable)
newtype BText = BText Text deriving (Eq, Ord, Data, Typeable) -- standard text
newtype MText = MText Text deriving (Eq, Ord, Data, Typeable) -- "read more" text
newtype Tag = Tag Text deriving (Eq, Ord, Data, Typeable)
newtype EDate = EDate UTCTime deriving (Eq, Ord, Data, Typeable)
newtype SDate = SDate UTCTime deriving (Eq, Ord, Data, Typeable)
newtype Username = Username Text deriving (Eq, Ord, Data, Typeable)
newtype SessionID = SessionID Text deriving (Eq, Ord, Data, Typeable)
$(deriveSafeCopy 2 'base ''Author)
$(deriveSafeCopy 2 'base ''Title)
$(deriveSafeCopy 2 'base ''BText)
$(deriveSafeCopy 2 'base ''MText)
$(deriveSafeCopy 2 'base ''Tag)
$(deriveSafeCopy 2 'base ''EDate)
$(deriveSafeCopy 2 'base ''SDate)
$(deriveSafeCopy 2 'base ''Username)
$(deriveSafeCopy 2 'base ''SessionID)
instance Indexable Entry where
empty = ixSet [ ixFun $ \e -> [ entryId e]
, ixFun $ (:[]) . lang
, ixFun $ \e -> [ Author $ author e ]
, ixFun $ \e -> [ Title $ title e]
, ixFun $ \e -> [ BText $ btext e]
, ixFun $ \e -> [ MText $ mtext e]
, ixFun $ \e -> [ EDate $ edate e]
]
data User = User {
username :: Text,
password :: ByteString
} deriving (Eq, Ord, Data, Typeable)
$(deriveSafeCopy 0 'base ''User)
data Session = Session {
sessionID :: Text,
user :: User,
sdate :: UTCTime
} deriving (Eq, Ord, Data, Typeable)
$(deriveSafeCopy 0 'base ''Session)
instance Indexable User where
empty = ixSet [ ixFun $ \u -> [Username $ username u]
, ixFun $ (:[]) . password
]
instance Indexable Session where
empty = ixSet [ ixFun $ \s -> [SessionID $ sessionID s]
, ixFun $ (:[]) . user
, ixFun $ \s -> [SDate $ sdate s]
]
data Blog = Blog {
blogSessions :: IxSet Session,
blogUsers :: IxSet User,
blogEntries :: IxSet Entry
} deriving (Data, Typeable)
$(deriveSafeCopy 0 'base ''Blog)
initialBlogState :: Blog
initialBlogState =
Blog { blogSessions = empty
, blogUsers = empty
, blogEntries = empty }
-- acid-state database functions (purity is necessary!)
insertEntry :: Entry -> Update Blog Entry
insertEntry e =
do b@Blog{..} <- get
put $ b { blogEntries = IxSet.insert e blogEntries }
return e
updateEntry :: Entry -> Update Blog Entry
updateEntry e =
do b@Blog{..} <- get
put $ b { blogEntries = IxSet.updateIx (entryId e) e blogEntries }
return e
deleteEntry :: EntryId -> Update Blog EntryId
deleteEntry entry =
do b@Blog{..} <- get
put $ b { blogEntries = IxSet.deleteIx entry blogEntries }
return entry
getEntry :: EntryId -> Query Blog (Maybe Entry)
getEntry eId =
do Blog{..} <- ask
return $ getOne $ blogEntries @= eId
latestEntries :: BlogLang -> Query Blog [Entry]
latestEntries lang =
do Blog{..} <- ask
return $ IxSet.toDescList (Proxy :: Proxy EDate) $ blogEntries @= lang
addSession :: Session -> Update Blog Session
addSession nSession =
do b@Blog{..} <- get
put $ b { blogSessions = IxSet.insert nSession blogSessions}
return nSession
getSession :: SessionID -> Query Blog (Maybe Session)
getSession sId =
do Blog{..} <- ask
return $ getOne $ blogSessions @= sId
clearSessions :: Update Blog [Session]
clearSessions =
do b@Blog{..} <- get
put $ b { blogSessions = empty }
return []
addUser :: Text -> String -> Update Blog User
addUser un pw =
do b@Blog{..} <- get
let u = User un $ hashString pw
put $ b { blogUsers = IxSet.insert u blogUsers}
return u
getUser :: Username -> Query Blog (Maybe User)
getUser uN =
do Blog{..} <- ask
return $ getOne $ blogUsers @= uN
checkUser :: Username -> String -> Query Blog Bool
checkUser uN pw =
do Blog{..} <- ask
let user = getOne $ blogUsers @= uN
case user of
Nothing -> return False
(Just u) -> return $ password u == hashString pw
-- various functions
hashString :: String -> ByteString
hashString = B64.encode . SHA.hash . B.pack
$(makeAcidic ''Blog
[ 'insertEntry
, 'updateEntry
, 'deleteEntry
, 'getEntry
, 'latestEntries
, 'addSession
, 'getSession
, 'addUser
, 'getUser
, 'checkUser
, 'clearSessions
])
interactiveUserAdd :: String -> IO ()
interactiveUserAdd dbHost = do
acid <- openRemoteState skipAuthenticationPerform dbHost (PortNumber 8070)
putStrLn "Username:"
un <- getLine
putStrLn "Password:"
pw <- getLine
update' acid (AddUser (pack un) pw)
closeAcidState acid
flushSessions :: IO ()
flushSessions = do
tbDir <- getEnv "TAZBLOG"
acid <- openLocalStateFrom (tbDir ++ "/BlogState") initialBlogState
update' acid ClearSessions
closeAcidState acid
archiveState :: IO ()
archiveState = do
tbDir <- getEnv "TAZBLOG"
acid <- openLocalStateFrom (tbDir ++ "/BlogState") initialBlogState
createArchive acid
closeAcidState acid

View file

@ -0,0 +1,54 @@
-- |This module implements fetching of individual blog entries from
-- DNS. Yes, you read that correctly.
--
-- Each blog post is stored as a set of records in a designated DNS
-- zone. For the production blog, this zone is `blog.tazj.in.`.
--
-- A top-level record at `_posts` contains a list of all published
-- post IDs.
--
-- For each of these post IDs, there is a record at `_meta.$postID`
-- that contains the title and number of post chunks.
--
-- For each post chunk, there is a record at `_$chunkID.$postID` that
-- contains a base64-encoded post fragment.
--
-- This module implements logic for assembling a post out of these
-- fragments and caching it based on the TTL of its `_meta` record.
module BlogStore where
import Data.Text (Text)
import Locales (BlogLang(..))
import Data.Time (UTCTime)
import Control.Monad.IO.Class (MonadIO)
newtype EntryId = EntryId { unEntryId :: Integer }
deriving (Eq, Ord)
instance Show EntryId where
show = show . unEntryId
data Entry = Entry {
entryId :: EntryId,
lang :: BlogLang,
author :: Text,
title :: Text,
btext :: Text,
mtext :: Text,
edate :: UTCTime
} deriving (Eq, Ord, Show)
data BlogCache
type Offset = Integer
type Count = Integer
newCache :: String -> IO BlogCache
newCache zone = undefined
listEntries :: MonadIO m => BlogCache -> Offset -> Count -> m [Entry]
listEntries cache offset count = undefined
getEntry :: MonadIO m => BlogCache -> EntryId -> m (Maybe Entry)
getEntry cache eId = undefined

View file

@ -1,15 +1,21 @@
{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
module Locales where
import BlogDB (BlogLang (..))
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import Network.URI
data BlogLang = EN | DE
deriving (Eq, Ord)
instance Show BlogLang where
show DE = "de"
show EN = "en"
data BlogError = NotFound | UnknownError
version = "5.1.2"
version = "6.0.0"
blogTitle :: BlogLang -> Text -> Text
blogTitle DE s = T.concat ["Tazjins blog", s]

View file

@ -9,7 +9,7 @@ import Data.Time (UTCTime, getCurrentTime)
import Network.URI
import Text.RSS
import BlogDB hiding (Title)
import BlogStore
import Locales
createChannel :: BlogLang -> UTCTime -> [ChannelElem]

View file

@ -2,20 +2,15 @@
module Server where
import Control.Applicative (optional)
import Control.Monad (msum, mzero, unless)
import Control.Monad (msum)
import Control.Monad.IO.Class (liftIO)
import Data.Acid
import Data.Acid.Advanced
import Data.ByteString.Char8 (unpack)
import Data.Char (toLower)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time
import Happstack.Server hiding (Session)
import Data.Maybe (fromJust)
import Data.Maybe (maybe)
import Blog
import BlogDB hiding (updateEntry)
import BlogStore
import Locales
import RSS
@ -26,32 +21,32 @@ instance FromReqURI BlogLang where
"en" -> Just EN
_ -> Nothing
pageSize :: Integer
pageSize = 3
tmpPolicy :: BodyPolicy
tmpPolicy = defaultBodyPolicy "/tmp" 0 200000 1000
runBlog :: AcidState Blog -> Int -> String -> IO ()
runBlog acid port respath =
simpleHTTP nullConf {port = port} $ tazBlog acid respath
runBlog :: Int -> String -> IO ()
runBlog port respath = do
cache <- newCache "blog.tazj.in."
simpleHTTP nullConf {port = port} $ tazBlog cache respath
tazBlog :: AcidState Blog -> String -> ServerPart Response
tazBlog acid resDir = do
msum [ path $ \(lang :: BlogLang) -> blogHandler acid lang
, dir "admin" $ msum [
adminHandler acid -- this checks auth
, method GET >> (ok $ toResponse adminLogin)
, method POST >> processLogin acid ]
tazBlog :: BlogCache -> String -> ServerPart Response
tazBlog cache resDir = do
msum [ path $ \(lang :: BlogLang) -> blogHandler cache lang
, dir "static" $ staticHandler resDir
, blogHandler acid EN
, blogHandler cache EN
, staticHandler resDir
, notFound $ toResponse $ showError NotFound DE
]
blogHandler :: AcidState Blog -> BlogLang -> ServerPart Response
blogHandler acid lang =
msum [ path $ \(eId :: Integer) -> showEntry acid lang $ EntryId eId
, nullDir >> showIndex acid lang
, dir "rss" $ nullDir >> showRSS acid lang
, dir "rss.xml" $ nullDir >> showRSS acid lang
blogHandler :: BlogCache -> BlogLang -> ServerPart Response
blogHandler cache lang =
msum [ path $ \(eId :: Integer) -> showEntry cache lang $ EntryId eId
, nullDir >> showIndex cache lang
, dir "rss" $ nullDir >> showRSS cache lang
, dir "rss.xml" $ nullDir >> showRSS cache lang
, notFound $ toResponse $ showError NotFound lang
]
@ -61,20 +56,9 @@ staticHandler resDir = do
setHeaderM "expires" "Tue, 20 Jan 2037 04:20:42 GMT"
serveDirectory DisableBrowsing [] resDir
adminHandler :: AcidState Blog -> ServerPart Response
adminHandler acid = do
guardSession acid
msum [ dir "entry" $ method POST >> postEntry acid
, dir "entry" $ path $ \(entry :: Integer) -> msum [
method GET >> editEntry acid entry
, method POST >> updateEntry acid entry ]
, dir "entrylist" $ path $ \(lang :: BlogLang) -> entryList acid lang
, ok $ toResponse $ adminIndex "tazjin"
]
showEntry :: AcidState Blog -> BlogLang -> EntryId -> ServerPart Response
showEntry acid lang eId = do
entry <- query' acid (GetEntry eId)
showEntry :: BlogCache -> BlogLang -> EntryId -> ServerPart Response
showEntry cache lang eId = do
entry <- getEntry cache eId
tryEntry entry lang
tryEntry :: Maybe Entry -> BlogLang -> ServerPart Response
@ -84,107 +68,19 @@ tryEntry (Just entry) _ = ok $ toResponse $ blogTemplate eLang eTitle $ renderEn
eTitle = T.append ": " (title entry)
eLang = lang entry
showIndex :: AcidState Blog -> BlogLang -> ServerPart Response
showIndex acid lang = do
entries <- query' acid (LatestEntries lang)
(page :: Maybe Int) <- optional $ lookRead "page"
ok $ toResponse $ blogTemplate lang "" $
renderEntries False (eDrop page entries) (Just $ showLinks page lang)
where
eDrop :: Maybe Int -> [a] -> [a]
eDrop (Just i) = drop ((i-1) * 6)
eDrop Nothing = drop 0
offset :: Maybe Integer -> Integer
offset = maybe 0 ((*) pageSize)
showRSS :: AcidState Blog -> BlogLang -> ServerPart Response
showRSS acid lang = do
entries <- query' acid (LatestEntries lang)
feed <- liftIO $ renderFeed lang $ take 6 entries
showIndex :: BlogCache -> BlogLang -> ServerPart Response
showIndex cache lang = do
(page :: Maybe Integer) <- optional $ lookRead "page"
entries <- listEntries cache (offset page) pageSize
ok $ toResponse $ blogTemplate lang "" $
renderEntries entries (Just $ showLinks page lang)
showRSS :: BlogCache -> BlogLang -> ServerPart Response
showRSS cache lang = do
entries <- listEntries cache 0 4
feed <- liftIO $ renderFeed lang entries
setHeaderM "content-type" "text/xml"
ok $ toResponse feed
{- ADMIN stuff -}
postEntry :: AcidState Blog -> ServerPart Response
postEntry acid = do
nullDir
decodeBody tmpPolicy
now <- liftIO getCurrentTime
let eId = timeToId now
lang <- look "lang"
nBtext <- lookText' "btext"
nMtext <- lookText' "mtext"
nEntry <- Entry <$> pure eId
<*> getLang lang
<*> readCookieValue "sUser"
<*> lookText' "title"
<*> pure nBtext
<*> pure nMtext
<*> pure now
update' acid (InsertEntry nEntry)
seeOther ("/" ++ lang ++ "/" ++ show eId) (toResponse())
where
timeToId :: UTCTime -> EntryId
timeToId t = EntryId . read $ formatTime defaultTimeLocale "%s" t
getLang :: String -> ServerPart BlogLang
getLang "de" = return DE
getLang _ = return EN -- English is default
entryList :: AcidState Blog -> BlogLang -> ServerPart Response
entryList acid lang = do
entries <- query' acid (LatestEntries lang)
ok $ toResponse $ adminEntryList entries
editEntry :: AcidState Blog -> Integer -> ServerPart Response
editEntry acid entryId = do
entry <- query' acid (GetEntry $ EntryId entryId)
ok $ toResponse $ editPage $ fromJust entry
updateEntry :: AcidState Blog -> Integer -> ServerPart Response
updateEntry acid entryId = do
decodeBody tmpPolicy
entry <- query' acid (GetEntry $ EntryId entryId)
nTitle <- lookText' "title"
nBtext <- lookText' "btext"
nMtext <- lookText' "mtext"
let newEntry = (fromJust entry)
{ title = nTitle
, btext = nBtext
, mtext = nMtext}
update' acid (UpdateEntry newEntry)
seeOther (concat $ ["/", show $ lang newEntry, "/", show entryId])
(toResponse ())
guardSession :: AcidState Blog -> ServerPartT IO ()
guardSession acid = do
(sId :: Text) <- readCookieValue "session"
(uName :: Text) <- readCookieValue "sUser"
now <- liftIO getCurrentTime
mS <- query' acid (GetSession $ SessionID sId)
case mS of
Nothing -> mzero
(Just Session{..}) -> unless ((uName == username user) && sessionTimeDiff now sdate)
mzero
where
sessionTimeDiff :: UTCTime -> UTCTime -> Bool
sessionTimeDiff now sdate = diffUTCTime now sdate < 43200
processLogin :: AcidState Blog -> ServerPart Response
processLogin acid = do
decodeBody tmpPolicy
account <- lookText' "account"
password <- look "password"
login <- query' acid (CheckUser (Username account) password)
if login
then createSession account
else unauthorized $ toResponse adminLogin
where
createSession account = do
now <- liftIO getCurrentTime
let sId = hashString $ show now
addCookie (MaxAge 43200) (mkCookie "session" $ unpack sId)
addCookie (MaxAge 43200) (mkCookie "sUser" $ T.unpack account)
user <- query' acid (GetUser $ Username account)
let nSession = Session (T.pack $ unpack sId) (fromJust user) now
update' acid (AddSession nSession)
seeOther ("/admin?do=login" :: Text) (toResponse())

View file

@ -1,9 +1,9 @@
Name: tazblog
Version: 5.1.3
Version: 6.0.0
Synopsis: Tazjin's Blog
License: MIT
Author: Vincent Ambo
Maintainer: tazjin@gmail.com
Maintainer: mail@tazj.in
Category: Web blog
Build-type: Simple
cabal-version: >= 1.10
@ -12,7 +12,7 @@ library
hs-source-dirs: src
default-language: Haskell2010
ghc-options: -W
exposed-modules: Blog, BlogDB, Locales, Server, RSS
exposed-modules: Blog, BlogStore, Locales, Server, RSS
build-depends: base,
bytestring,
happstack-server,
@ -24,9 +24,6 @@ library
old-locale,
time,
base64-bytestring,
acid-state,
ixset,
safecopy,
mtl,
transformers,
network,
@ -53,18 +50,6 @@ executable tazblog
default-language: Haskell2010
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base,
acid-state,
tazblog,
options,
network
executable tazblog-db
hs-source-dirs: db
main-is: Main.hs
default-language: Haskell2010
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base,
acid-state,
tazblog,
options,
network

View file

@ -6,17 +6,17 @@
}:
mkDerivation {
pname = "tazblog";
version = "5.1.3";
version = "6.0.0";
src = ./.;
isLibrary = true;
isExecutable = true;
libraryHaskellDepends = [
acid-state base base64-bytestring blaze-html blaze-markup
bytestring crypto-api cryptohash hamlet happstack-server ixset
markdown mtl network network-uri old-locale rss safecopy
shakespeare text time transformers
base base64-bytestring blaze-html blaze-markup bytestring
crypto-api cryptohash hamlet happstack-server markdown mtl
network network-uri old-locale rss shakespeare text time
transformers
];
executableHaskellDepends = [ acid-state base network options ];
executableHaskellDepends = [ base network options ];
description = "Tazjin's Blog";
license = stdenv.lib.licenses.mit;
}