2023-02-03 22:40:28 +00:00
|
|
|
{-# LANGUAGE QuasiQuotes #-}
|
|
|
|
{-# LANGUAGE DataKinds #-}
|
2023-02-27 18:16:29 +00:00
|
|
|
{-# LANGUAGE TypeOperators #-}
|
2023-02-03 13:45:16 +00:00
|
|
|
module Main where
|
|
|
|
|
2023-03-05 11:16:56 +00:00
|
|
|
import Database.SQLite.Simple hiding ((:.))
|
2023-02-03 22:40:28 +00:00
|
|
|
import Database.SQLite.Simple.QQ
|
|
|
|
import Network.Wai.Handler.Warp
|
|
|
|
import Data.Proxy
|
|
|
|
import Servant
|
|
|
|
import Control.Monad.IO.Class
|
2023-03-05 09:38:02 +00:00
|
|
|
import Servant.HTML.Blaze
|
2023-03-05 11:16:56 +00:00
|
|
|
import qualified Data.Text as T
|
2023-04-11 09:02:42 +00:00
|
|
|
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
|
2023-03-05 11:16:56 +00:00
|
|
|
import Crypto.Argon2
|
|
|
|
import Data.Text.Short (fromText, ShortText)
|
|
|
|
import Data.ByteString (ByteString)
|
2023-04-11 09:02:42 +00:00
|
|
|
import Data.ByteString.Lazy (fromStrict)
|
2023-02-03 22:40:28 +00:00
|
|
|
|
2023-02-27 18:16:29 +00:00
|
|
|
import Api.Types
|
|
|
|
import qualified Parsers.KOReader as KO
|
2023-04-11 09:02:42 +00:00
|
|
|
import qualified Parsers.Readwise as RW
|
2023-03-05 09:38:02 +00:00
|
|
|
import Config
|
|
|
|
import Options.Applicative
|
2023-02-03 13:45:16 +00:00
|
|
|
|
2023-03-05 11:16:56 +00:00
|
|
|
data User = User
|
|
|
|
{ username :: T.Text
|
|
|
|
, password :: ByteString
|
|
|
|
} deriving (Show, Eq)
|
|
|
|
|
2023-03-05 09:38:02 +00:00
|
|
|
type API = Get '[HTML] Quote
|
|
|
|
:<|> "quotes" :> Get '[JSON] [Quote]
|
2023-02-27 18:16:29 +00:00
|
|
|
:<|> "quote" :> "random" :> Get '[JSON] Quote
|
2023-03-05 09:38:02 +00:00
|
|
|
:<|> "today" :> Get '[HTML] Quote
|
2023-04-11 09:02:42 +00:00
|
|
|
:<|> BasicAuth "update data" User :> ( "koreader" :> ReqBody '[JSON] KO.KoHighlight :> Post '[JSON] NoContent
|
|
|
|
:<|> "readwise" :> ReqBody '[PlainText] T.Text :> Post '[JSON] NoContent)
|
2023-03-05 11:16:56 +00:00
|
|
|
|
2023-02-03 22:40:28 +00:00
|
|
|
api :: Proxy API
|
|
|
|
api = Proxy
|
|
|
|
|
2023-03-05 11:16:56 +00:00
|
|
|
checkBasicAuth :: T.Text -> ShortText -> BasicAuthCheck User
|
|
|
|
checkBasicAuth user passhash = BasicAuthCheck $ \authData ->
|
|
|
|
let username = decodeUtf8 (basicAuthUsername authData)
|
|
|
|
password = basicAuthPassword authData
|
|
|
|
in
|
|
|
|
case user == username of
|
|
|
|
False -> return NoSuchUser
|
|
|
|
True -> case verifyEncoded passhash password of
|
|
|
|
Argon2Ok -> return $ Authorized $ User username password
|
|
|
|
_ -> return Unauthorized
|
|
|
|
|
|
|
|
|
2023-02-03 22:40:28 +00:00
|
|
|
initDb :: FilePath -> IO ()
|
|
|
|
initDb dbFile = withConnection dbFile $ \conn ->
|
|
|
|
execute_ conn
|
2023-02-27 18:16:29 +00:00
|
|
|
[sql|CREATE TABLE IF NOT EXISTS quotes ( quote text non null
|
|
|
|
, author text
|
|
|
|
, title text
|
|
|
|
, page text
|
|
|
|
, chapter text
|
|
|
|
, created_on integer);|]
|
2023-02-03 22:40:28 +00:00
|
|
|
|
2023-02-27 18:16:29 +00:00
|
|
|
-- | TODO: readerT
|
2023-02-03 22:40:28 +00:00
|
|
|
server :: FilePath -> Server API
|
2023-04-11 09:02:42 +00:00
|
|
|
server dbf = randomQuote dbf
|
|
|
|
:<|> listQuotes dbf
|
|
|
|
:<|> randomQuote dbf
|
|
|
|
:<|> randomQuote dbf
|
|
|
|
:<|> (\_ -> (addKoReader dbf :<|> addReadwise dbf))
|
2023-03-05 11:16:56 +00:00
|
|
|
|
2023-02-27 18:16:29 +00:00
|
|
|
-- | API begins here
|
|
|
|
randomQuote :: FilePath -> Handler Quote
|
|
|
|
randomQuote db = do
|
2023-03-05 11:16:56 +00:00
|
|
|
qts <- liftIO $ withConnection db $ \c -> query_ c qry
|
|
|
|
case length qts of
|
2023-02-27 18:16:29 +00:00
|
|
|
0 -> undefined
|
|
|
|
_ -> pure (head qts)
|
|
|
|
where
|
|
|
|
qry = [sql|SELECT * FROM quotes ORDER BY RANDOM();|]
|
2023-02-03 22:40:28 +00:00
|
|
|
|
|
|
|
listQuotes :: FilePath -> Handler [Quote]
|
|
|
|
listQuotes db = liftIO $ withConnection db $ \conn -> query_ conn [sql|SELECT * FROM quotes;|]
|
|
|
|
|
2023-02-27 18:16:29 +00:00
|
|
|
addKoReader :: FilePath -> KO.KoHighlight -> Handler NoContent
|
|
|
|
addKoReader db hl = do
|
|
|
|
liftIO $ withConnection db $ \c ->
|
|
|
|
executeMany c qry qts
|
|
|
|
pure NoContent
|
|
|
|
where
|
|
|
|
qry = [sql|INSERT INTO quotes VALUES (?,?,?,?,?,?);|]
|
|
|
|
qts = KO.parse hl
|
|
|
|
|
2023-04-11 09:02:42 +00:00
|
|
|
addReadwise :: FilePath -> T.Text -> Handler NoContent
|
|
|
|
addReadwise db hl = do
|
|
|
|
let
|
|
|
|
qts = RW.parse (fromStrict $ encodeUtf8 hl)
|
|
|
|
liftIO $ print $ show qts
|
|
|
|
pure NoContent
|
|
|
|
|
|
|
|
|
2023-03-05 09:38:02 +00:00
|
|
|
runApp :: AppConfig -> IO ()
|
2023-03-05 11:16:56 +00:00
|
|
|
runApp c = run (appPort c) (serveWithContext api ctx $ server (appDbFile c))
|
|
|
|
where
|
|
|
|
ctx = checkBasicAuth (appUser c) (fromText $ appPassHash c):. EmptyContext
|
2023-03-05 09:38:02 +00:00
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = do
|
|
|
|
conf <- execParser parserOpts
|
|
|
|
putStrLn $ "running with conf" <> show conf
|
|
|
|
initDb (appDbFile conf)
|
|
|
|
runApp conf
|