add host to useragent

This commit is contained in:
Jon Schoning 2020-07-20 12:43:03 -05:00
parent 26d43109dd
commit 1786cf558e
13 changed files with 82 additions and 64 deletions

1
.cf-clearance Normal file
View file

@ -0,0 +1 @@
cf_clearance=96f0dfc0741239f896f9443f721c94c8fafa82b0-1595277120-GJDOCYQO

View file

@ -1,10 +1,10 @@
cabal-version: 1.12 cabal-version: 1.12
-- This file has been generated from package.yaml by hpack version 0.31.2. -- This file has been generated from package.yaml by hpack version 0.33.0.
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
-- --
-- hash: b6f4cdc1abf4e7a6b5bbc1382963c22cc62bbbe8c4ae5e5394cf6fe5fc1ff9cb -- hash: 1e6238118c05016ee28c808f6f736f03c5c719ed69576390d95900d6a5e85cee
name: espial name: espial
version: 0.0.8 version: 0.0.8
@ -123,7 +123,7 @@ library
Paths_espial Paths_espial
hs-source-dirs: hs-source-dirs:
src src
default-extensions: BangPatterns CPP ConstraintKinds DataKinds DeriveDataTypeable DeriveGeneric DerivingStrategies EmptyDataDecls FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds PolymorphicComponents QuasiQuotes Rank2Types RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators TypeSynonymInstances UndecidableInstances ViewPatterns default-extensions: BangPatterns BlockArguments CPP ConstraintKinds DataKinds DeriveDataTypeable DeriveGeneric DerivingStrategies EmptyDataDecls FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds PolymorphicComponents QuasiQuotes Rank2Types RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators TypeSynonymInstances UndecidableInstances ViewPatterns
build-depends: build-depends:
aeson >=1.4 aeson >=1.4
, attoparsec , attoparsec
@ -194,7 +194,7 @@ executable espial
Paths_espial Paths_espial
hs-source-dirs: hs-source-dirs:
app app
default-extensions: BangPatterns CPP ConstraintKinds DataKinds DeriveDataTypeable DeriveGeneric DerivingStrategies EmptyDataDecls FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds PolymorphicComponents QuasiQuotes Rank2Types RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators TypeSynonymInstances UndecidableInstances ViewPatterns default-extensions: BangPatterns BlockArguments CPP ConstraintKinds DataKinds DeriveDataTypeable DeriveGeneric DerivingStrategies EmptyDataDecls FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds PolymorphicComponents QuasiQuotes Rank2Types RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators TypeSynonymInstances UndecidableInstances ViewPatterns
ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: build-depends:
aeson >=1.4 aeson >=1.4
@ -263,7 +263,7 @@ executable migration
Paths_espial Paths_espial
hs-source-dirs: hs-source-dirs:
app/migration app/migration
default-extensions: BangPatterns CPP ConstraintKinds DataKinds DeriveDataTypeable DeriveGeneric DerivingStrategies EmptyDataDecls FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds PolymorphicComponents QuasiQuotes Rank2Types RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators TypeSynonymInstances UndecidableInstances ViewPatterns default-extensions: BangPatterns BlockArguments CPP ConstraintKinds DataKinds DeriveDataTypeable DeriveGeneric DerivingStrategies EmptyDataDecls FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds PolymorphicComponents QuasiQuotes Rank2Types RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators TypeSynonymInstances UndecidableInstances ViewPatterns
ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: build-depends:
aeson >=1.4 aeson >=1.4
@ -337,7 +337,7 @@ test-suite test
Paths_espial Paths_espial
hs-source-dirs: hs-source-dirs:
test test
default-extensions: BangPatterns CPP ConstraintKinds DataKinds DeriveDataTypeable DeriveGeneric DerivingStrategies EmptyDataDecls FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds PolymorphicComponents QuasiQuotes Rank2Types RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators TypeSynonymInstances UndecidableInstances ViewPatterns default-extensions: BangPatterns BlockArguments CPP ConstraintKinds DataKinds DeriveDataTypeable DeriveGeneric DerivingStrategies EmptyDataDecls FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds PolymorphicComponents QuasiQuotes Rank2Types RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators TypeSynonymInstances UndecidableInstances ViewPatterns
ghc-options: -Wall ghc-options: -Wall
build-depends: build-depends:
aeson >=1.4 aeson >=1.4

View file

@ -42,6 +42,7 @@ extra-source-files:
default-extensions: default-extensions:
- BangPatterns - BangPatterns
- BlockArguments
- CPP - CPP
- ConstraintKinds - ConstraintKinds
- DataKinds - DataKinds

View file

@ -139,8 +139,8 @@ develMain = develMainHelper getApplicationDev
-- forkEKG :: App -> IO () -- forkEKG :: App -> IO ()
-- forkEKG foundation = -- forkEKG foundation =
-- let settings = appSettings foundation in -- let settings = appSettings foundation in
-- for_ (appEkgHost settings) $ \ekgHost -> -- for_ (appEkgHost settings) \ekgHost ->
-- for_ (appEkgPort settings) $ \ekgPort -> -- for_ (appEkgPort settings) \ekgPort ->
-- EKG.forkServerWith -- EKG.forkServerWith
-- (appMetrics foundation ^. MM.metricsStore) -- (appMetrics foundation ^. MM.metricsStore)
-- (encodeUtf8 ekgHost) -- (encodeUtf8 ekgHost)

View file

@ -49,7 +49,7 @@ instance YesodPersistRunner App where
-- Yesod -- Yesod
instance Yesod App where instance Yesod App where
approot = ApprootRequest $ \app req -> approot = ApprootRequest \app req ->
case appRoot (appSettings app) of case appRoot (appSettings app) of
Nothing -> getApprootText guessApproot app req Nothing -> getApprootText guessApproot app req
Just root -> root Just root -> root
@ -71,7 +71,7 @@ instance Yesod App where
mcurrentRoute <- getCurrentRoute mcurrentRoute <- getCurrentRoute
-- void $ mapM (incrementRouteEKG req) mcurrentRoute -- void $ mapM (incrementRouteEKG req) mcurrentRoute
let msourceCodeUri = appSourceCodeUri (appSettings master) let msourceCodeUri = appSourceCodeUri (appSettings master)
pc <- widgetToPageContent $ do pc <- widgetToPageContent do
setTitle "Espial" setTitle "Espial"
addAppScripts addAppScripts
addStylesheet (StaticR css_tachyons_min_css) addStylesheet (StaticR css_tachyons_min_css)
@ -132,7 +132,7 @@ popupLayout widget = do
mmsg <- getMessage mmsg <- getMessage
musername <- maybeAuthUsername musername <- maybeAuthUsername
let msourceCodeUri = appSourceCodeUri (appSettings master) let msourceCodeUri = appSourceCodeUri (appSettings master)
pc <- widgetToPageContent $ do pc <- widgetToPageContent do
addAppScripts addAppScripts
addStylesheet (StaticR css_tachyons_min_css) addStylesheet (StaticR css_tachyons_min_css)
addStylesheet (StaticR css_popup_css) addStylesheet (StaticR css_popup_css)

View file

@ -8,7 +8,7 @@ getAccountSettingsR = do
(_, user) <- requireAuthPair (_, user) <- requireAuthPair
let accountSettingsEl = "accountSettings" :: Text let accountSettingsEl = "accountSettings" :: Text
let accountSettings = toAccountSettingsForm user let accountSettings = toAccountSettingsForm user
defaultLayout $ do defaultLayout do
$(widgetFile "user-settings") $(widgetFile "user-settings")
toWidgetBody [julius| toWidgetBody [julius|
app.userR = "@{UserR (UserNameP $ userName user)}"; app.userR = "@{UserR (UserNameP $ userName user)}";

View file

@ -16,7 +16,7 @@ getAddViewR = do
let renderEl = "addForm" :: Text let renderEl = "addForm" :: Text
popupLayout $ do popupLayout do
toWidget [whamlet| toWidget [whamlet|
<div id="#{ renderEl }"> <div id="#{ renderEl }">
|] |]

View file

@ -15,6 +15,7 @@ import qualified Web.FormUrlEncoded as WH
-- import qualified Control.Monad.Metrics as MM -- import qualified Control.Monad.Metrics as MM
import HTMLEntities.Decoder (htmlEncodedText) import HTMLEntities.Decoder (htmlEncodedText)
import Data.Text.Lazy.Builder (toLazyText) import Data.Text.Lazy.Builder (toLazyText)
import Network.Wai (requestHeaderHost)
shouldArchiveBookmark :: User -> Key Bookmark -> Handler Bool shouldArchiveBookmark :: User -> Key Bookmark -> Handler Bool
shouldArchiveBookmark user kbid = do shouldArchiveBookmark user kbid = do
@ -35,7 +36,7 @@ archiveBookmarkUrl kbid url =
$(logError) (pack e) $(logError) (pack e)
Right submitInfo -> do Right submitInfo -> do
userId <- requireAuthId userId <- requireAuthId
let req = _buildArchiveSubmitRequest submitInfo url req <- _buildArchiveSubmitRequest submitInfo url
-- MM.increment "archive.submit" -- MM.increment "archive.submit"
res <- liftIO $ NH.httpLbs req =<< NH.getGlobalManager res <- liftIO $ NH.httpLbs req =<< NH.getGlobalManager
let status = NH.responseStatus res let status = NH.responseStatus res
@ -71,32 +72,19 @@ _parseRefreshHeaderUrl h = do
then Just $ decodeUtf8 u then Just $ decodeUtf8 u
else Nothing else Nothing
_buildArchiveSubmitRequest :: (String, String) -> String -> NH.Request
_buildArchiveSubmitRequest (action, submitId) href =
NH.parseRequest_ ("POST " <> action) & \r ->
r { NH.requestHeaders =
[ ("User-Agent", _archiveUserAgent)
, ("Content-Type", "application/x-www-form-urlencoded")
]
, NH.requestBody = NH.RequestBodyLBS $ WH.urlEncodeAsForm ((
[ ("submitid" , submitId)
, ("url", href)
]) :: [(String, String)])
, NH.redirectCount = 0
}
_fetchArchiveSubmitInfo :: Handler (Either String (String , String)) _fetchArchiveSubmitInfo :: Handler (Either String (String , String))
_fetchArchiveSubmitInfo = do _fetchArchiveSubmitInfo = do
-- MM.increment "archive.fetchSubmitId" -- MM.increment "archive.fetchSubmitId"
res <- liftIO $ NH.httpLbs (buildSimpleRequest "https://archive.li/") =<< NH.getGlobalManager req <- buildRequest "https://archive.li/"
res <- liftIO $ NH.httpLbs req =<< NH.getGlobalManager
-- MM.increment ("archive.fetchSubmitId_status_" <> (pack.show) (NH.statusCode (NH.responseStatus res))) -- MM.increment ("archive.fetchSubmitId_status_" <> (pack.show) (NH.statusCode (NH.responseStatus res)))
let body = LBS.toStrict (responseBody res) let body = LBS.toStrict (responseBody res)
action = _parseSubstring (AP8.string "action=\"") (AP8.notChar '"') body action = _parseSubstring (AP8.string "action=\"") (AP8.notChar '"') body
submitId = _parseSubstring (AP8.string "submitid\" value=\"") (AP8.notChar '"') body submitId = _parseSubstring (AP8.string "submitid\" value=\"") (AP8.notChar '"') body
pure $ (,) <$> action <*> submitId if statusCode (responseStatus res) == 200
then pure $ (,) <$> action <*> submitId
_archiveUserAgent :: ByteString else pure $ Left $ "Invalid statusCode: " <> show (responseStatus res)
_archiveUserAgent = "espial"
_parseSubstring :: AP8.Parser ByteString -> AP8.Parser Char -> BS.ByteString -> Either String String _parseSubstring :: AP8.Parser ByteString -> AP8.Parser Char -> BS.ByteString -> Either String String
_parseSubstring start inner res = do _parseSubstring start inner res = do
@ -109,7 +97,8 @@ fetchPageTitle :: String -> Handler (Either String Text)
fetchPageTitle url = fetchPageTitle url =
do do
-- MM.increment "fetchPageTitle" -- MM.increment "fetchPageTitle"
res <- liftIO $ NH.httpLbs (buildSimpleRequest url) =<< NH.getGlobalManager req <- buildRequest url
res <- liftIO $ NH.httpLbs req =<< NH.getGlobalManager
let body = LBS.toStrict (responseBody res) let body = LBS.toStrict (responseBody res)
pure (decodeHtmlBs <$> parseTitle body) pure (decodeHtmlBs <$> parseTitle body)
`catch` (\(e :: SomeException) -> do `catch` (\(e :: SomeException) -> do
@ -118,7 +107,7 @@ fetchPageTitle url =
pure (Left (show e))) pure (Left (show e)))
where where
parseTitle bs = parseTitle bs =
(flip AP.parseOnly) bs $ do (flip AP.parseOnly) bs do
_ <- skipAnyTill (AP.string "<title") _ <- skipAnyTill (AP.string "<title")
_ <- skipAnyTill (AP.string ">") _ <- skipAnyTill (AP.string ">")
let lt = toEnum (ord '<') let lt = toEnum (ord '<')
@ -126,7 +115,34 @@ fetchPageTitle url =
decodeHtmlBs = toStrict . toLazyText . htmlEncodedText . decodeUtf8 decodeHtmlBs = toStrict . toLazyText . htmlEncodedText . decodeUtf8
skipAnyTill end = go where go = end *> pure () <|> AP.anyWord8 *> go skipAnyTill end = go where go = end *> pure () <|> AP.anyWord8 *> go
buildSimpleRequest :: String -> Request _buildArchiveSubmitRequest :: (String, String) -> String -> Handler NH.Request
buildSimpleRequest url = _buildArchiveSubmitRequest (action, submitId) href = do
NH.parseRequest_ url & \r -> req <- buildRequest ("POST " <> action)
r {NH.requestHeaders = [("User-Agent", _archiveUserAgent)]} pure $ req
{ NH.requestHeaders = ("Content-Type", "application/x-www-form-urlencoded") : NH.requestHeaders req
, NH.requestBody =
NH.RequestBodyLBS $
WH.urlEncodeAsForm
(([("submitid", submitId), ("url", href)]) :: [(String, String)])
, NH.redirectCount = 0
}
buildRequest :: String -> Handler Request
buildRequest url = do
ua <- _archiveUserAgent
pure $ NH.parseRequest_ url & \r ->
r { NH.requestHeaders =
[ ("Cache-Control", "max-age=0")
, ("Connection", "keep-alive")
, ("DNT", "1")
, ("Host", "archive.li")
, ("Upgrade-Insecure-Requests", "1")
, ("User-Agent", ua)
]
}
_archiveUserAgent :: Handler ByteString
_archiveUserAgent = do
mHost <- pure . requestHeaderHost . reqWaiRequest =<< getRequest
pure $ ("espial-" <>) (maybe "" (BS8.takeWhile (/= ':')) mHost)

View file

@ -11,7 +11,7 @@ import Import
deleteDeleteR :: Int64 -> Handler Html deleteDeleteR :: Int64 -> Handler Html
deleteDeleteR bid = do deleteDeleteR bid = do
userId <- requireAuthId userId <- requireAuthId
runDB $ do runDB do
let k_bid = BookmarkKey bid let k_bid = BookmarkKey bid
_ <- requireResource userId k_bid _ <- requireResource userId k_bid
deleteCascade k_bid deleteCascade k_bid
@ -20,7 +20,7 @@ deleteDeleteR bid = do
postReadR :: Int64 -> Handler Html postReadR :: Int64 -> Handler Html
postReadR bid = do postReadR bid = do
userId <- requireAuthId userId <- requireAuthId
runDB $ do runDB do
let k_bid = BookmarkKey bid let k_bid = BookmarkKey bid
_ <- requireResource userId k_bid _ <- requireResource userId k_bid
update k_bid [BookmarkToRead =. False] update k_bid [BookmarkToRead =. False]
@ -37,7 +37,7 @@ postUnstarR bid = _setSelected bid False
_setSelected :: Int64 -> Bool -> Handler Html _setSelected :: Int64 -> Bool -> Handler Html
_setSelected bid selected = do _setSelected bid selected = do
userId <- requireAuthId userId <- requireAuthId
runDB $ do runDB do
let k_bid = BookmarkKey bid let k_bid = BookmarkKey bid
bm <- requireResource userId k_bid bm <- requireResource userId k_bid
update k_bid [BookmarkSelected =. selected] update k_bid [BookmarkSelected =. selected]

View file

@ -18,7 +18,7 @@ getNotesR unamep@(UserNameP uname) = do
page = maybe 1 fromIntegral page' page = maybe 1 fromIntegral page'
mqueryp = fmap (\q -> (queryp, q)) mquery mqueryp = fmap (\q -> (queryp, q)) mquery
isowner = maybe False (== uname) mauthuname isowner = maybe False (== uname) mauthuname
(bcount, notes) <- runDB $ do (bcount, notes) <- runDB do
Entity userId user <- getBy404 (UniqueUserName uname) Entity userId user <- getBy404 (UniqueUserName uname)
let sharedp = if isowner then SharedAll else SharedPublic let sharedp = if isowner then SharedAll else SharedPublic
when (not isowner && userPrivacyLock user) when (not isowner && userPrivacyLock user)
@ -26,7 +26,7 @@ getNotesR unamep@(UserNameP uname) = do
getNoteList userId mquery sharedp limit page getNoteList userId mquery sharedp limit page
req <- getRequest req <- getRequest
mroute <- getCurrentRoute mroute <- getCurrentRoute
defaultLayout $ do defaultLayout do
rssLink (NotesFeedR unamep) "feed" rssLink (NotesFeedR unamep) "feed"
let pager = $(widgetFile "pager") let pager = $(widgetFile "pager")
search = $(widgetFile "search") search = $(widgetFile "search")
@ -54,7 +54,7 @@ getNoteR unamep@(UserNameP uname) slug = do
when (not isowner && (userPrivacyLock user || (not . noteShared . entityVal) note)) when (not isowner && (userPrivacyLock user || (not . noteShared . entityVal) note))
(redirect (AuthR LoginR)) (redirect (AuthR LoginR))
pure note pure note
defaultLayout $ do defaultLayout do
$(widgetFile "note") $(widgetFile "note")
toWidgetBody [julius| toWidgetBody [julius|
app.userR = "@{UserR unamep}"; app.userR = "@{UserR unamep}";
@ -70,7 +70,7 @@ getAddNoteViewR unamep@(UserNameP uname) = do
userId <- requireAuthId userId <- requireAuthId
let renderEl = "note" :: Text let renderEl = "note" :: Text
note <- liftIO $ Entity (NoteKey 0) <$> _toNote userId emptyNoteForm note <- liftIO $ Entity (NoteKey 0) <$> _toNote userId emptyNoteForm
defaultLayout $ do defaultLayout do
$(widgetFile "note") $(widgetFile "note")
toWidgetBody [julius| toWidgetBody [julius|
app.userR = "@{UserR unamep}"; app.userR = "@{UserR unamep}";
@ -84,7 +84,7 @@ getAddNoteViewR unamep@(UserNameP uname) = do
deleteDeleteNoteR :: Int64 -> Handler Html deleteDeleteNoteR :: Int64 -> Handler Html
deleteDeleteNoteR nid = do deleteDeleteNoteR nid = do
userId <- requireAuthId userId <- requireAuthId
runDB $ do runDB do
let k_nid = NoteKey nid let k_nid = NoteKey nid
_ <- requireResource userId k_nid _ <- requireResource userId k_nid
deleteCascade k_nid deleteCascade k_nid
@ -168,7 +168,7 @@ getNotesFeedR unamep@(UserNameP uname) = do
let limit = maybe 20 fromIntegral limit' let limit = maybe 20 fromIntegral limit'
page = maybe 1 fromIntegral page' page = maybe 1 fromIntegral page'
isowner = maybe False (== uname) mauthuname isowner = maybe False (== uname) mauthuname
(_, notes) <- runDB $ do (_, notes) <- runDB do
Entity userId user <- getBy404 (UniqueUserName uname) Entity userId user <- getBy404 (UniqueUserName uname)
when (not isowner && userPrivacyLock user) when (not isowner && userPrivacyLock user)
(redirect (AuthR LoginR)) (redirect (AuthR LoginR))

View file

@ -52,7 +52,7 @@ _getUser unamep@(UserNameP uname) sharedp' filterp' (TagsP pathtags) = do
mroute <- getCurrentRoute mroute <- getCurrentRoute
tagCloudMode <- getTagCloudMode isowner pathtags tagCloudMode <- getTagCloudMode isowner pathtags
req <- getRequest req <- getRequest
defaultLayout $ do defaultLayout do
let pager = $(widgetFile "pager") let pager = $(widgetFile "pager")
search = $(widgetFile "search") search = $(widgetFile "search")
renderEl = "bookmarks" :: Text renderEl = "bookmarks" :: Text

View file

@ -173,12 +173,12 @@ bookmarksQuery userId sharedp filterp tags mquery limit' page =
(,) -- total count (,) -- total count
<$> fmap (sum . fmap E.unValue) <$> fmap (sum . fmap E.unValue)
(select $ (select $
from $ \b -> do from \b -> do
_whereClause b _whereClause b
pure $ E.countRows) pure E.countRows)
-- paged data -- paged data
<*> (select $ <*> (select $
from $ \b -> do from \b -> do
_whereClause b _whereClause b
orderBy [desc (b ^. BookmarkTime)] orderBy [desc (b ^. BookmarkTime)]
limit limit' limit limit'
@ -189,7 +189,7 @@ bookmarksQuery userId sharedp filterp tags mquery limit' page =
where_ $ where_ $
foldl (\expr tag -> foldl (\expr tag ->
expr &&. (exists $ -- each tag becomes an exists constraint expr &&. (exists $ -- each tag becomes an exists constraint
from $ \t -> from \t ->
where_ (t ^. BookmarkTagBookmarkId E.==. b ^. BookmarkId &&. where_ (t ^. BookmarkTagBookmarkId E.==. b ^. BookmarkId &&.
(t ^. BookmarkTagTag `E.like` val tag)))) (t ^. BookmarkTagTag `E.like` val tag))))
(b ^. BookmarkUserId E.==. val userId) (b ^. BookmarkUserId E.==. val userId)
@ -261,7 +261,7 @@ parseTimeText t =
tagsQuery :: [Entity Bookmark] -> DB [Entity BookmarkTag] tagsQuery :: [Entity Bookmark] -> DB [Entity BookmarkTag]
tagsQuery bmarks = tagsQuery bmarks =
select $ select $
from $ \t -> do from \t -> do
where_ (t ^. BookmarkTagBookmarkId `in_` valList (fmap entityKey bmarks)) where_ (t ^. BookmarkTagBookmarkId `in_` valList (fmap entityKey bmarks))
orderBy [asc (t ^. BookmarkTagSeq)] orderBy [asc (t ^. BookmarkTagSeq)]
pure t pure t
@ -281,11 +281,11 @@ getNoteList key mquery sharedp limit' page =
(,) -- total count (,) -- total count
<$> fmap (sum . fmap E.unValue) <$> fmap (sum . fmap E.unValue)
(select $ (select $
from $ \b -> do from \b -> do
_whereClause b _whereClause b
pure $ E.countRows) pure $ E.countRows)
<*> (select $ <*> (select $
from $ \b -> do from \b -> do
_whereClause b _whereClause b
orderBy [desc (b ^. NoteCreated)] orderBy [desc (b ^. NoteCreated)]
limit limit' limit limit'
@ -471,7 +471,7 @@ allUserBookmarks user = do
bquery :: DB [Entity Bookmark] bquery :: DB [Entity Bookmark]
bquery = bquery =
select $ select $
from $ \b -> do from \b -> do
where_ (b ^. BookmarkUserId E.==. val user) where_ (b ^. BookmarkUserId E.==. val user)
orderBy [asc (b ^. BookmarkTime)] orderBy [asc (b ^. BookmarkTime)]
pure b pure b
@ -479,7 +479,7 @@ allUserBookmarks user = do
tquery = tquery =
fmap (\(tid, tags) -> (E.unValue tid, E.unValue tags)) <$> fmap (\(tid, tags) -> (E.unValue tid, E.unValue tags)) <$>
(select $ (select $
from $ \t -> do from \t -> do
where_ (t ^. BookmarkTagUserId E.==. val user) where_ (t ^. BookmarkTagUserId E.==. val user)
E.groupBy (t ^. BookmarkTagBookmarkId) E.groupBy (t ^. BookmarkTagBookmarkId)
let tags = sqlite_group_concat (t ^. BookmarkTagTag) (E.val " ") let tags = sqlite_group_concat (t ^. BookmarkTagTag) (E.val " ")
@ -539,7 +539,7 @@ tagCountTop user top =
sortOn (toLower . fst) . sortOn (toLower . fst) .
fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$> fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$>
( select $ ( select $
from $ \t -> do from \t -> do
where_ (t ^. BookmarkTagUserId E.==. val user) where_ (t ^. BookmarkTagUserId E.==. val user)
E.groupBy (E.lower_ $ t ^. BookmarkTagTag) E.groupBy (E.lower_ $ t ^. BookmarkTagTag)
let countRows' = E.countRows let countRows' = E.countRows
@ -552,7 +552,7 @@ tagCountLowerBound :: Key User -> Int -> DB [(Text, Int)]
tagCountLowerBound user lowerBound = tagCountLowerBound user lowerBound =
fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$> fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$>
( select $ ( select $
from $ \t -> do from \t -> do
where_ (t ^. BookmarkTagUserId E.==. val user) where_ (t ^. BookmarkTagUserId E.==. val user)
E.groupBy (E.lower_ $ t ^. BookmarkTagTag) E.groupBy (E.lower_ $ t ^. BookmarkTagTag)
let countRows' = E.countRows let countRows' = E.countRows
@ -565,11 +565,11 @@ tagCountRelated :: Key User -> [Tag] -> DB [(Text, Int)]
tagCountRelated user tags = tagCountRelated user tags =
fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$> fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$>
( select $ ( select $
from $ \t -> do from \t -> do
where_ $ where_ $
foldl (\expr tag -> foldl (\expr tag ->
expr &&. (exists $ expr &&. (exists $
from $ \u -> from \u ->
where_ (u ^. BookmarkTagBookmarkId E.==. t ^. BookmarkTagBookmarkId &&. where_ (u ^. BookmarkTagBookmarkId E.==. t ^. BookmarkTagBookmarkId &&.
(u ^. BookmarkTagTag `E.like` val tag)))) (u ^. BookmarkTagTag `E.like` val tag))))
(t ^. BookmarkTagUserId E.==. val user) (t ^. BookmarkTagUserId E.==. val user)
@ -704,7 +704,7 @@ _toBookmark userId BookmarkForm {..} = do
} }
fetchBookmarkByUrl :: Key User -> Maybe Text -> DB (Maybe (Entity Bookmark, [Entity BookmarkTag])) fetchBookmarkByUrl :: Key User -> Maybe Text -> DB (Maybe (Entity Bookmark, [Entity BookmarkTag]))
fetchBookmarkByUrl userId murl = runMaybeT $ do fetchBookmarkByUrl userId murl = runMaybeT do
bmark <- MaybeT . getBy . UniqueUserHref userId =<< (MaybeT $ pure murl) bmark <- MaybeT . getBy . UniqueUserHref userId =<< (MaybeT $ pure murl)
btags <- lift $ withTags (entityKey bmark) btags <- lift $ withTags (entityKey bmark)
pure (bmark, btags) pure (bmark, btags)

View file

@ -66,7 +66,7 @@ data AppSettings = AppSettings
} }
instance FromJSON AppSettings where instance FromJSON AppSettings where
parseJSON = withObject "AppSettings" $ \o -> do parseJSON = withObject "AppSettings" \o -> do
let defaultDev = let defaultDev =
#ifdef DEVELOPMENT #ifdef DEVELOPMENT
True True