{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
module Application where
import Breve.Generator
import Breve.UrlTable
import Views
import Control.Monad.IO.Class (liftIO)
import qualified Data.Text.IO as T
import Data.Text (Text)
import Data.Aeson (ToJSON)
import GHC.Generics (Generic)
import Text.Blaze.Html5 (Html)
import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
import Servant
import Servant.HTML.Blaze (HTML)
import Web.FormUrlEncoded (FromForm(..), parseUnique)
data ApiReply = ApiReply
{ link :: Url
, name :: Name
, original :: Url
} deriving Generic
instance ToJSON ApiReply
newtype UrlForm = UrlForm Text
instance FromForm UrlForm where
fromForm f = UrlForm <$> parseUnique "url" f
type Breve = API :<|> App
type App =
Get '[HTML] Html
:<|> "static" :> Raw
:<|> Capture "name" Name :> Redirect
:<|> ReqBody '[FormUrlEncoded] UrlForm :> Post '[HTML] Html
type API =
"api" :> ReqBody '[FormUrlEncoded] UrlForm :> Post '[JSON] ApiReply
breve :: FilePath
-> Url
-> UrlTable
-> Application
breve static url table = serve (Proxy :: Proxy Breve) (breveServer static url table)
emptyApp :: Application
emptyApp = serve (Proxy :: Proxy EmptyAPI) emptyServer
breveServer :: FilePath -> Url -> UrlTable -> Server Breve
breveServer static url table =
api url table :<|> app
where app = homepage :<|>
serveDirectoryWebApp static :<|>
resolver table :<|>
uploader url table
homepage :: Handler Html
homepage = pure index
resolver :: UrlTable -> Name -> Handler Redirection
resolver table name = do
url <- liftIO (extract table name)
case url of
Nothing ->
throwError $ err404 { errBody = renderHtml (message "404: not found") }
Just url -> do
logStr ("Resolved " <> name <> " -> " <> url)
pure (addHeader url NoContent)
uploader :: Url -> UrlTable -> UrlForm -> Handler Html
uploader bindUrl table (UrlForm url) = do
name <- liftIO (insert table url)
logStr ("Registered " <> url <> " -> " <> name)
pure (done $ bindUrl <> name)
api :: Url -> UrlTable -> UrlForm -> Handler ApiReply
api bindUrl table (UrlForm url) = do
name <- liftIO (insert table url)
logStr ("Registered " <> url <> " -> " <> name)
pure $ ApiReply { link = (bindUrl <> name)
, name = name
, original = url
}
logStr :: Text -> Handler ()
logStr = liftIO . T.putStrLn . ("[breve] " <>)
type Redirect =
Verb 'GET 302 '[PlainText] Redirection
type Redirection =
Headers '[Header "Location" Text] NoContent