initial commit

This commit is contained in:
outfoxxed 2025-02-10 20:03:25 -08:00
commit a60daddd0c
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
11 changed files with 333 additions and 0 deletions

79
src/PlausibleQuery.hs Normal file
View file

@ -0,0 +1,79 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module PlausibleQuery (queryPageviewsLoop) where
import GHC.Generics (Generic)
import Data.Map (Map)
import qualified Data.ByteString.UTF8 as BS.UTF8
import qualified Data.Map as Map
import qualified Data.Aeson as JSON
import Data.Aeson (FromJSON, (.=))
import qualified Network.HTTP.Simple as H
import qualified Network.HTTP.Types.Header as H
import Control.Concurrent (MVar, threadDelay, putMVar)
import Control.Concurrent.MVar (takeMVar)
import Control.Exception (Exception (displayException), catch, SomeException)
import TemplatePage (PageContext (ctxPageviews))
import qualified Config as C
newtype MetricsResponse = MetricsResponse { results :: [MRResult] } deriving (Generic, Show)
data MRResult = MRResult { metrics :: [Int], dimensions :: [String] } deriving (Generic, Show)
instance FromJSON MetricsResponse where
instance FromJSON MRResult where
parseMetricsResponse :: MetricsResponse -> Either String (Map String Int)
parseMetricsResponse MetricsResponse { results = r } = Map.fromList <$> mapM parseResult r
where parseResult :: MRResult -> Either String (String, Int)
parseResult MRResult { metrics = [count], dimensions = [path] } = Right (removeTrailingSlash path, count)
parseResult result = Left $ "Could not parse result: " ++ show result
removeTrailingSlash "" = ""
removeTrailingSlash "/" = ""
removeTrailingSlash (x:xs) = x : removeTrailingSlash xs
queryPageviews :: C.Config -> IO (Either String (Map String Int))
queryPageviews config = do
request' <- H.parseRequest $ "POST " ++ C.queryUrl (C.plausible config)
let request
= H.addRequestHeader H.hAuthorization (BS.UTF8.fromString $ "Bearer " ++ C.token (C.plausible config))
$ H.setRequestBodyJSON
(JSON.object [
"site_id" .= C.siteId (C.plausible config),
"metrics" .= ["visitors" :: JSON.Value],
"dimensions" .= ["event:page" :: JSON.Value],
"filters" .= [[
"matches" :: JSON.Value,
"event:page" :: JSON.Value,
JSON.toJSON ['^' : C.baseUrl (C.serve config) ++ "/.*$"]
]],
"date_range" .= ("all" :: JSON.Value)
])
$ request'
response <- H.httpJSON request
return $ parseMetricsResponse $ H.getResponseBody response
queryPageviewsLoop :: C.Config -> MVar PageContext -> MVar () -> IO ()
queryPageviewsLoop config context updateNotify = loop
where loop = do
putStrLn "Querying pageviews from analytics..."
pageviewResult <- queryPageviews config `catch` \e -> return $ Left $ displayException (e :: SomeException)
case pageviewResult of
Left e -> do
putStrLn $ "Failed to query pageviews: " ++ e ++ "\nRetrying in 10 seconds."
threadDelay $ 10 * 1000000
loop
Right pageviews -> do
context' <- takeMVar context
let context'' = context' { ctxPageviews = pageviews }
putMVar context context''
putStrLn $ "Updated pageview cache: " ++ show (Map.toList pageviews)
threadDelay $ 20 * 1000000
-- Don't make another query until a page is queried, as the stats shouldn't change without a pageview.
takeMVar updateNotify
loop