{-# OPTIONS_GHC -fno-warn-orphans #-} {-# Language UndecidableInstances #-} {-# Language AllowAmbiguousTypes #-} module Main where import HBS2.Git.DashBoard.Prelude import HBS2.System.Dir import HBS2.Net.Messaging.Unix import HBS2.OrDie import HBS2.Peer.RPC.API.Storage import HBS2.Peer.RPC.Client.StorageClient import HBS2.Git.Web.Assets import HBS2.Git.DashBoard.State import HBS2.Git.DashBoard.State.Index import HBS2.Git.DashBoard.Types import HBS2.Git.Web.Html.Root import HBS2.Peer.CLI.Detect import Lucid import Options.Applicative as O import Data.Either import Data.ByteString.Lazy qualified as LBS import Network.HTTP.Types.Status import Network.Wai.Middleware.Static hiding ((<|>)) import Network.Wai.Middleware.StaticEmbedded as E import Network.Wai.Middleware.RequestLogger import Web.Scotty.Trans import System.Directory import Control.Monad.Except configParser :: DashBoardPerks m => Parser (m ()) configParser = do opts <- RunDashBoardOpts <$> optional (strOption ( long "config" <> short 'c' <> metavar "FILEPATH" <> help "Path to the configuration file" <> completer (bashCompleter "file") )) cmd <- subparser ( command "web" (O.info pRunWeb (progDesc "Run the web interface")) <> command "index" (O.info pRunIndex (progDesc "update index")) ) pure $ cmd opts pRunWeb :: DashBoardPerks m => Parser (RunDashBoardOpts -> m ()) pRunWeb = pure $ \x -> runDashBoardM x runScotty pRunIndex :: DashBoardPerks m => Parser (RunDashBoardOpts -> m ()) pRunIndex = pure $ \x -> runDashBoardM x do updateIndex {- HLINT ignore "Eta reduce" -} {- HLINT ignore "Functor law" -} getRPC :: Monad m => HasConf m => m (Maybe FilePath) getRPC = pure Nothing runDashBoardM :: DashBoardPerks m => RunDashBoardOpts -> DashBoardM m a -> m a runDashBoardM cli m = do let hbs2_git_dashboard = "hbs2-git-dashboard" xdgConf <- liftIO $ getXdgDirectory XdgConfig hbs2_git_dashboard xdgData <- liftIO $ getXdgDirectory XdgData hbs2_git_dashboard let cliConfPath = cli & configPath let confPath = fromMaybe xdgConf cliConfPath let confFile = confPath "config" let dbFile = xdgData "state.db" when (isNothing cliConfPath) do touch confFile conf <- runExceptT (liftIO $ readFile confFile) <&> fromRight mempty <&> parseTop <&> fromRight mempty liftIO $ print (pretty conf) -- FIXME: unix-socket-from-config soname <- detectRPC `orDie` "hbs2-peer rpc not found" let errorPrefix = toStderr . logPrefix "[error] " let warnPrefix = toStderr . logPrefix "[warn] " let noticePrefix = toStderr . logPrefix "" let debugPrefix = toStderr . logPrefix "[debug] " setLogging @INFO defLog setLogging @ERROR errorPrefix setLogging @DEBUG debugPrefix setLogging @WARN warnPrefix setLogging @NOTICE noticePrefix flip runContT pure do client <- liftIO $ race (pause @'Seconds 1) (newMessagingUnix False 1.0 soname) >>= orThrowUser ("can't connect to" <+> pretty soname) void $ ContT $ withAsync $ runMessagingUnix client peerAPI <- makeServiceCaller @PeerAPI (fromString soname) refLogAPI <- makeServiceCaller @RefLogAPI (fromString soname) refChanAPI <- makeServiceCaller @RefChanAPI (fromString soname) storageAPI <- makeServiceCaller @StorageAPI (fromString soname) lwwAPI <- makeServiceCaller @LWWRefAPI (fromString soname) let sto = AnyStorage (StorageClient storageAPI) let endpoints = [ Endpoint @UNIX peerAPI , Endpoint @UNIX refLogAPI , Endpoint @UNIX refChanAPI , Endpoint @UNIX lwwAPI , Endpoint @UNIX storageAPI ] void $ ContT $ withAsync $ liftIO $ runReaderT (runServiceClientMulti endpoints) client env <- newDashBoardEnv conf dbFile peerAPI refLogAPI refChanAPI lwwAPI sto void $ ContT $ withAsync do q <- withDashBoardEnv env $ asks _pipeline forever do liftIO (atomically $ readTQueue q) & liftIO . join lift $ withDashBoardEnv env (withState evolveDB >> m) `finally` do setLoggingOff @DEBUG setLoggingOff @INFO setLoggingOff @ERROR setLoggingOff @WARN setLoggingOff @NOTICE data WebOptions = WebOptions { _assetsOverride :: Maybe FilePath } orFall :: m r -> Maybe a -> ContT r m a orFall a mb = ContT $ maybe1 mb a runDashboardWeb :: WebOptions -> ScottyT (DashBoardM IO) () runDashboardWeb wo = do middleware logStdout let assets = _assetsOverride wo case assets of Nothing -> do middleware (E.static assetsDir) Just f -> do middleware $ staticPolicy (noDots >-> addBase f) get "/" do html =<< lift (renderTextT dashboardRootPage) get "/repo/:lww" do lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey HBS2Basic) env <- lift ask flip runContT pure do lww <- lwws' & orFall (status status404) item <- lift (selectRepoList ( mempty & set repoListByLww (Just lww) & set repoListLimit (Just 1)) ) <&> listToMaybe >>= orFall (status status404) lift $ html =<< renderTextT (repoPage item) runScotty :: DashBoardPerks m => DashBoardM m () runScotty = do pno <- cfgValue @HttpPortOpt @(Maybe Int) <&> fromMaybe 8090 wo <- cfgValue @DevelopAssetsOpt @(Maybe FilePath) <&> WebOptions env <- ask addJob (withDashBoardEnv env updateIndex) scottyT pno (withDashBoardEnv env) (runDashboardWeb wo) main :: IO () main = do execParser opts & join where opts = O.info (configParser <**> helper) ( fullDesc <> progDesc "hbs2-git-dashboard" <> O.header "hbs2-git-dashboard" )