hbs2-dashboard and hbs2-fixer removed

This commit is contained in:
voidlizard 2025-02-11 13:09:46 +03:00
parent 57b480a454
commit 96b5b051b3
34 changed files with 0 additions and 6450 deletions

View File

@ -34,10 +34,7 @@ outputs = { self, nixpkgs, flake-utils, ... }@inputs:
"hbs2-core" "hbs2-core"
"hbs2-storage-simple" "hbs2-storage-simple"
"hbs2-git" "hbs2-git"
"hbs2-git-dashboard"
"hbs2-git3" "hbs2-git3"
"hbs2-qblf"
"hbs2-fixer"
"hbs2-cli" "hbs2-cli"
"hbs2-sync" "hbs2-sync"
"fixme-new" "fixme-new"

View File

View File

@ -1,724 +0,0 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE UndecidableInstances #-}
module Main where
import HBS2.Prelude.Plated
import HBS2.Actors.Peer
import HBS2.Base58
import HBS2.OrDie
import HBS2.Hash
import HBS2.Data.Types.Refs
import HBS2.Net.Auth.Credentials
import HBS2.Polling
import HBS2.Misc.PrettyStuff
import HBS2.System.Dir
import HBS2.System.Logger.Simple.ANSI hiding (info)
import HBS2.Net.Messaging.Unix
import HBS2.Git.Data.LWWBlock
import HBS2.Net.Proto.Notify
import HBS2.Net.Proto.Service
import HBS2.Peer.Notify
import HBS2.Peer.RPC.API.Peer
import HBS2.Peer.RPC.API.RefLog
import HBS2.Peer.RPC.API.LWWRef
import HBS2.Peer.RPC.API.Storage
import HBS2.Peer.RPC.Client.StorageClient
import HBS2.Peer.CLI.Detect
import HBS2.Peer.Proto.RefLog
import Data.Config.Suckless
import Data.Time.Clock
import Data.Coerce
import Control.Monad.Reader
import Lens.Micro.Platform
import System.Directory
import Options.Applicative
import Data.Maybe
import Data.Either
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HM
import Control.Monad.Trans.Cont
import Control.Monad.Trans.Maybe
import Data.ByteString.Lazy.Char8 qualified as LBS
import Data.Text qualified as Text
import Data.Hashable
import Control.Exception qualified as E
import System.Process.Typed
import System.Environment qualified as Env
import System.Exit qualified as Exit
import Data.Cache qualified as Cache
import Data.Cache (Cache)
import System.Exit
{- HLINT ignore "Functor law" -}
type Config = [Syntax C]
type RLWW = LWWRefKey 'HBS2Basic
type RRefLog = RefLogKey 'HBS2Basic
newtype Watcher =
Watcher [Syntax C]
deriving newtype (Semigroup,Monoid)
data Ref =
RefRefLog RRefLog
| RefLWW RLWW
deriving stock (Eq,Generic)
instance Pretty Ref where
pretty (RefRefLog r) = parens $ "reflog" <+> dquotes (pretty r)
pretty (RefLWW r) = parens $ "lwwref" <+> dquotes (pretty r)
newtype AnyPolledRef =
AnyPolledRef (PubKey 'Sign 'HBS2Basic)
deriving (Eq,Generic)
instance Hashable AnyPolledRef
-- FIXME: move-to-suckless-conf
deriving newtype instance Hashable Id
instance Pretty AnyPolledRef where
pretty (AnyPolledRef r) = pretty (AsBase58 r)
-- deriving newtype instance Pretty (PubKey 'Sign 'HBS2Basic) => Pretty AnyPolledRef
instance FromStringMaybe AnyPolledRef where
fromStringMay = fmap AnyPolledRef . fromStringMay
newtype PolledRef =
PolledRef (Ref, NominalDiffTime)
deriving stock (Eq,Generic)
deriving newtype (Pretty)
instance Hashable Ref
instance Hashable PolledRef where
hashWithSalt salt (PolledRef (r,_)) = hashWithSalt salt r
data FixerEnv = FixerEnv
{ _configFile :: Maybe FilePath
, _lwwAPI :: ServiceCaller LWWRefAPI UNIX
, _refLogAPI :: ServiceCaller RefLogAPI UNIX
, _refLogSink :: NotifySink (RefLogEvents L4Proto) UNIX
, _peerAPI :: ServiceCaller PeerAPI UNIX
, _sto :: AnyStorage
, _config :: TVar Config
, _configPoll :: TVar Int
, _watchers :: TVar (HashMap PolledRef Watcher)
, _listeners :: TVar (HashMap RRefLog (Async ()))
, _result :: TVar (HashMap Ref (Maybe HashRef, Maybe HashRef))
, _runNum :: TVar Int
, _locals :: TVar (HashMap Id (Syntax C))
, _pipeline :: TQueue (IO ())
}
makeLenses ''FixerEnv
newtype FixerM m a = FixerM { runFixerM :: ReaderT FixerEnv m a }
deriving newtype (Applicative, Functor, Monad, MonadIO, MonadReader FixerEnv, MonadUnliftIO)
instance MonadIO m => HasConf (FixerM m) where
getConf = asks _config >>= readTVarIO
debugPrefix = toStdout . logPrefix "[debug] "
readConf :: MonadIO m => FilePath -> m [Syntax C]
readConf fn = liftIO (readFile fn) <&> parseTop <&> fromRight mempty
withConfig :: MonadUnliftIO m => Maybe FilePath -> FixerM m () -> FixerM m ()
withConfig cfgPath m = do
defConfDir <- liftIO $ getXdgDirectory XdgConfig "hbs2-fixer"
let configPath = fromMaybe (defConfDir </> "config") cfgPath
unless (isJust cfgPath) do
debug $ pretty configPath
touch configPath
syn <- readConf configPath
tsyn <- newTVarIO syn
local (set config tsyn . set configFile (Just configPath)) (void m)
withApp :: Maybe FilePath -> FixerM IO () -> IO ()
withApp cfgPath action = do
setLogging @DEBUG debugPrefix
setLogging @INFO defLog
setLogging @ERROR errorPrefix
setLogging @WARN warnPrefix
setLogging @NOTICE noticePrefix
fix \next -> do
flip runContT pure do
soname' <- lift detectRPC
soname <- ContT $ maybe1 soname' (pure ())
client <- lift $ race (pause @'Seconds 1) (newMessagingUnix False 1.0 soname)
>>= orThrowUser ("can't connect to" <+> pretty soname)
mess <- ContT $ withAsync $ runMessagingUnix client
peerAPI <- makeServiceCaller @PeerAPI (fromString soname)
refLogAPI <- makeServiceCaller @RefLogAPI (fromString soname)
storageAPI <- makeServiceCaller @StorageAPI (fromString soname)
lwwAPI <- makeServiceCaller @LWWRefAPI (fromString soname)
let endpoints = [ Endpoint @UNIX peerAPI
, Endpoint @UNIX refLogAPI
, Endpoint @UNIX lwwAPI
, Endpoint @UNIX storageAPI
]
mn <- ContT $ withAsync $ liftIO $ runReaderT (runServiceClientMulti endpoints) client
let o = [MUDontRetry]
clientN <- newMessagingUnixOpts o False 1.0 soname
notif <- ContT $ withAsync (runMessagingUnix clientN)
sink <- newNotifySink
void $ ContT $ withAsync $ flip runReaderT clientN $ do
debug $ red "notify restarted!"
runNotifyWorkerClient sink
p1 <- ContT $ withAsync $ flip runReaderT clientN $ do
runProto @UNIX
[ makeResponse (makeNotifyClient @(RefLogEvents L4Proto) sink)
]
env <- FixerEnv Nothing
lwwAPI
refLogAPI
sink
peerAPI
(AnyStorage (StorageClient storageAPI))
<$> newTVarIO mempty
<*> newTVarIO 30
<*> newTVarIO mempty
<*> newTVarIO mempty
<*> newTVarIO mempty
<*> newTVarIO 0
<*> newTVarIO mempty
<*> newTQueueIO
void $ ContT $ bracket (pure ()) $ \_ -> do
readTVarIO (_listeners env) <&> HM.elems >>= mapM_ cancel
p3 <- ContT $ withAsync $ runReaderT (runFixerM $ withConfig cfgPath action) env
void $ waitAnyCatchCancel [mess,mn,notif,p1,p3]
debug $ red "respawning..."
pause @'Seconds 5
next
setLoggingOff @DEBUG
setLoggingOff @INFO
setLoggingOff @ERROR
setLoggingOff @WARN
setLoggingOff @NOTICE
where
errorPrefix = toStdout . logPrefix "[error] "
warnPrefix = toStdout . logPrefix "[warn] "
noticePrefix = toStdout
data ConfWatch =
ConfWatch
| ConfRead
| ConfUpdate [Syntax C]
mainLoop :: FixerM IO ()
mainLoop = do
debug "hbs2-fixer. do stuff since 2024"
conf <- getConf
-- debug $ line <> vcat (fmap pretty conf)
flip runContT pure do
debug $ red "Reloading..."
lift $ updateFromConfig conf
p1 <- ContT $ withAsync $ do
cfg <- asks _configFile `orDie` "config file not specified"
flip fix ConfRead $ \next -> \case
ConfRead -> do
debug $ yellow "read config" <+> pretty cfg
newConf <- readConf cfg
oldConf <- getConf
let a = hashObject @HbSync (LBS.pack $ show $ pretty newConf)
let b = hashObject @HbSync (LBS.pack $ show $ pretty oldConf)
let changed = a /= b
if not changed then
next ConfWatch
else
next (ConfUpdate newConf)
ConfUpdate new -> do
debug $ yellow "read config / update state"
updateFromConfig new
next ConfWatch
ConfWatch{} -> do
w <- asks _configPoll >>= readTVarIO
pause (TimeoutSec (realToFrac w))
next ConfRead
-- poll reflogs
p2 <- ContT $ withAsync do
let w = asks _watchers
>>= readTVarIO
<&> HM.toList
<&> \wtf -> [ (ByFirst r wa, t) | (PolledRef (r,t), wa) <- wtf ]
polling (Polling 1 1) w $ \case
ByFirst ref wa -> do
new <- getRefRpc ref
re <- asks _result
old <- readTVarIO re
<&> (snd <=< HM.lookup ref)
when (new /= old) do
atomically $ modifyTVar re (HM.insert ref (old, new))
-- bindId
forM_ new (runWatcher wa ref)
pure ()
jobs <- asks _pipeline
p3 <- ContT $ withAsync $ fix \next -> do
r <- liftIO $ E.try @SomeException (join $ atomically $ readTQueue jobs)
case r of
Left e -> do
err (viaShow e)
let ee = fromException @AsyncCancelled e
unless (isJust ee) do
next
_ -> next
void $ waitAnyCatchCancel [p1,p2,p3]
oneSec :: MonadUnliftIO m => m b -> m (Either () b)
oneSec = race (pause @'Seconds 1)
fromStrLitMay :: forall s c . FromStringMaybe s => Syntax c -> Maybe s
fromStrLitMay = \case
LitStrVal s -> fromStringMay (Text.unpack s)
_ -> Nothing
pattern PTop :: forall {c}. Id -> [Syntax c] -> Syntax c
pattern PTop ctor rest <- ListVal (SymbolVal ctor : rest)
pattern PPolledRef :: forall {c}. Id -> AnyPolledRef -> Syntax c
pattern PPolledRef t r <- ListVal [ SymbolVal t, fromStrLitMay @AnyPolledRef -> Just r ]
pattern PWatchRef :: forall {c}. Integer -> Id -> AnyPolledRef -> [Syntax c] -> [Syntax c]
pattern PWatchRef n t r w <- (LitIntVal n : PPolledRef t r : w)
pattern PListenRef :: forall {c}. Id -> AnyPolledRef -> [Syntax c] -> [Syntax c]
pattern PListenRef t r w <- (PPolledRef t r : w)
-- pattern PDisplay :: Syntax c
pattern PDisplay :: forall {c}. Syntax c -> Syntax c
pattern PDisplay w <- ListVal [ SymbolVal "display", w ]
pattern PApply :: Id -> [Syntax C] -> Syntax C
pattern PApply f a <- ListVal ( SymbolVal f : a )
fetchRef :: forall m . MonadIO m => Ref -> FixerM m ()
fetchRef r = do
case r of
RefRefLog ref -> do
api <- asks _refLogAPI
void $ liftIO $ oneSec $ void $ callService @RpcRefLogFetch api (fromRefLogKey ref)
RefLWW ref -> do
api <- asks _lwwAPI
void $ liftIO $ oneSec $ void $ callService @RpcLWWRefFetch api ref
getRefRpc :: forall m . MonadIO m => Ref -> FixerM m (Maybe HashRef)
getRefRpc r = do
case r of
RefRefLog ref -> do
api <- asks _refLogAPI
liftIO (oneSec $ callService @RpcRefLogGet api (fromRefLogKey ref))
>>= \case
Right (Right x) -> pure x
_ -> pure Nothing
RefLWW ref -> do
api <- asks _lwwAPI
liftIO (oneSec $ callService @RpcLWWRefGet api ref) >>= \case
Right (Right x) -> pure (lwwValue <$> x)
_ -> pure Nothing
subscribeRef :: forall m . MonadIO m => Integer -> Ref -> FixerM m ()
subscribeRef n r = do
debug $ "subscribeRef" <+> pretty n <+> pretty r
let (puk,t) = case r of
RefRefLog k -> (coerce k, "reflog")
RefLWW k -> (coerce k, "lwwref")
let minutes = fromIntegral $ max 1 (n `div` 60)
api <- asks _peerAPI
void $ liftIO $ oneSec $ callService @RpcPollAdd api (puk, t, minutes)
asRef :: Id -> AnyPolledRef -> Maybe Ref
asRef t r = case t of
"lwwref" -> Just $ RefLWW (coerce r)
"reflog" -> Just $ RefRefLog (coerce r)
_ -> Nothing
runWatcher :: forall m . MonadUnliftIO m => Watcher -> Ref -> HashRef -> FixerM m ()
runWatcher (Watcher code) ref new = do
debug $ yellow "CHANGED" <+> pretty ref <+> pretty new
sto <- asks _sto
newCode <- flip transformBiM code $ \case
PApply "lwwref:get-hbs2-git-reflog" _ -> do
v <- case ref of
RefLWW k -> readLWWBlock sto k
_ -> pure Nothing
-- FIXME: wrappers-for-syntax-ctors
let vv = maybe1 v (List (noContext @C) mempty) $
\(_, LWWBlockData{..}) ->
List (noContext @C) [ Symbol (noContext @C) "reflog"
, Literal (noContext @C)
(mkLit @Text (fromString $ show $ pretty (AsBase58 lwwRefLogPubKey)))
]
pure vv
w -> pure w
debug (pretty newCode)
runConfig newCode
display :: forall m . MonadUnliftIO m => Syntax C -> FixerM m ()
display what = do
case what of
LitStrVal s -> notice (pretty s)
ast -> notice (pretty ast)
list_ :: [Syntax C] -> Syntax C
list_ = List (noContext @C)
symbol_ :: Id -> Syntax C
symbol_ = Symbol (noContext @C)
str_ :: Text -> Syntax C
str_ s = Literal (noContext @C) (LitStr s)
int_ :: Integer -> Syntax C
int_ s = Literal (noContext @C) (LitInt s)
bool_ :: Bool -> Syntax C
bool_ s = Literal (noContext @C) (LitBool s)
-- FIXME: to-suckless-conf
class AsString s where
asString :: s -> String
instance AsString Literal where
asString (LitStr s) = Text.unpack s
asString other = show $ pretty other
instance AsString (Syntax c) where
asString (Literal _ x) = asString x
asString x = show $ pretty x
data RunOpts =
RunCWD FilePath
instance Pretty RunOpts where
pretty = \case
RunCWD f -> parens ("cwd" <+> pretty f)
eval :: forall m . MonadUnliftIO m => Syntax C -> FixerM m (Syntax C)
eval = eval'
-- debug $ "EVAL" <+> pretty syn <+> pretty r
-- pure r
eval' :: forall m . MonadUnliftIO m => Syntax C -> FixerM m (Syntax C)
eval' syn = do
case syn of
x@(Literal{}) -> pure x
(SymbolVal n) -> lookupLocal n
w@(PApply "list" code') -> do
code <- mapM unquote code'
pure $ list_ (symbol_ "list" : code)
PApply "local" [SymbolVal n, what] -> do
bindLocal n =<< eval what
pure nil
PApply "eval" [e] -> do
eval e >>= \case
(ListVal ( SymbolVal "list" : es ) ) -> do
lastDef nil <$> mapM eval es
_ -> pure nil
PApply "listen" (what' : code) -> do
what <- eval what'
case what of
PPolledRef "reflog" ref -> do
setReflogListener (coerce ref) =<< mapM unquote code
PPolledRef tp r -> do
warn $ yellow "not supported listener type" <+> pretty tp
_ -> pure ()
pure nil
PApply "watch" (p' : what' : watcher') -> do
p <- eval p'
what <- eval what'
watcher <- mapM unquote watcher'
case (p, what) of
(LitIntVal n, PPolledRef tp ref) -> do
let re = asRef tp ref
forM_ re (subscribeRef n)
void $ async (pause @'Seconds 5 >> forM_ re fetchRef)
void $ runMaybeT do
-- FIXME: more-diagnostics
pref <- toMPlus $ case tp of
"lwwref" -> Just $ PolledRef (RefLWW (coerce ref), fromIntegral n)
"reflog" -> Just $ PolledRef (RefRefLog (coerce ref), fromIntegral n)
_ -> Nothing
debug $ blue "watch" <+> pretty n <+> pretty tp <+> pretty ref
w <- asks _watchers
atomically $ modifyTVar w (HM.insert pref (Watcher watcher))
_ -> pure ()
pure nil
PApply "on-start" wtf -> do
rn <- asks _runNum
rnn <- atomically do
x <- readTVar rn
modifyTVar rn succ
pure x
when (rnn == 0) do
mapM_ eval wtf
pure nil
PApply fn args' -> do
args <- mapM eval args'
case fn of
"reflog" -> do
pure $ list_ (symbol_ "reflog" : args)
"lwwref" -> do
pure $ list_ (symbol_ "lwwref" : args)
"watch-config" -> do
case headDef (int_ 30) args of
LitIntVal n -> do
debug $ "watch-config" <+> pretty n
asks _configPoll >>= atomically . flip writeTVar (fromIntegral n)
_ -> do
pure ()
pure nil
"debug" -> do
let onOff = headDef (bool_ False) args
case onOff of
LitBoolVal True -> do
setLogging @DEBUG debugPrefix
_ -> do
setLoggingOff @DEBUG
pure nil
"string-append" -> do
pieces <- for args $ \case
LitStrVal s -> pure s
other -> pure (Text.pack $ show $ pretty other)
pure $ str_ $ mconcat pieces
"display" -> do
first <- headDef nil <$> mapM eval args
case first of
LitStrVal s -> notice (pretty s)
ast -> notice (pretty ast)
pure nil
"getenv" -> do
let name = asString $ headDef nil args
liftIO $ Env.lookupEnv name
>>= \case
Nothing -> pure nil
Just s -> pure $ str_ (fromString s)
"mkdir" -> do
debug $ "mkdir" <+> pretty args
mapM_ mkdir [ Text.unpack s | (LitStrVal s) <- args ]
pure nil
"exit" -> do
case headDef (int_ 0) args of
LitIntVal 0 -> liftIO Exit.exitSuccess
LitIntVal w -> liftIO $ Exit.exitWith (ExitFailure $ fromIntegral w)
_ -> liftIO Exit.exitFailure
pure nil
"run" -> do
debug $ red "RUN-ARGS" <+> pretty args
(o,cargs) <- case args of
(ListVal (SymbolVal "list" : SymbolVal "opts" : opts) : rest) -> do
let pairs = [ (opt, e) | ListVal [SymbolVal opt, e] <- opts ]
oo <- for pairs $ \(o, e) -> (o,) <$> eval e
let cwd = lastMay [ RunCWD (Text.unpack f )
| ("cwd", LitStrVal f) <- oo
]
pure (maybeToList cwd, rest)
rest -> do
pure (mempty, rest)
let what = unwords $ [Text.unpack s | LitStrVal s <- cargs]
let cwd = case headMay [ p | c@(RunCWD p) <- o ] of
Just c -> setWorkingDir c
_ -> id
debug $ red "RUN" <+> pretty what <+> pretty o
let job = void $ runProcess_ (shell what & cwd)
pip <- asks _pipeline
atomically $ writeTQueue pip job
pure nil
_ -> pure nil
_ -> pure nil
unquote :: forall code m . (code ~ Syntax C, MonadUnliftIO m) => code -> FixerM m code
unquote code = flip transformBiM code $ \case
x@(ListVal [SymbolVal "unquoted", rest] :: Syntax C) -> do
eval rest
x -> pure x
setReflogListener :: forall m . MonadUnliftIO m => RRefLog -> [Syntax C] -> FixerM m ()
setReflogListener reflog code = do
debug $ green "setReflogListener" <+> pretty reflog <> line <> pretty code
dudes <- asks _listeners
a <- atomically do
x <- readTVar dudes <&> HM.lookup reflog
modifyTVar dudes (HM.delete reflog)
pure x
maybe1 a none cancel
sink <- asks _refLogSink
debug $ "subscribe to" <+> pretty reflog
new <- async do
cache <- liftIO $ Cache.newCache (Just (toTimeSpec (TimeoutSec 10)))
runNotifySink sink (RefLogNotifyKey reflog) $ \(RefLogUpdateNotifyData _ h) -> do
debug $ "Got notification" <+> pretty reflog <+> pretty h
here <- liftIO (Cache.lookup cache (reflog, h)) <&> isJust
unless here do
liftIO $ Cache.insert cache (reflog,h) ()
runConfig code
atomically $ modifyTVar dudes (HM.insert reflog new)
bindLocal :: forall m . MonadUnliftIO m => Id -> Syntax C -> FixerM m ()
bindLocal l e = do
-- debug $ "bindLocal" <+> pretty l
asks _locals >>= atomically . flip modifyTVar (HM.insert l e)
lookupLocal :: forall m . MonadUnliftIO m => Id ->FixerM m (Syntax C)
lookupLocal name = do
-- debug $ "lookupLocal" <+> pretty name
asks _locals >>= readTVarIO <&> fromMaybe nil . HM.lookup name
runConfig :: forall m . MonadUnliftIO m => Config -> FixerM m ()
runConfig conf = do
debug $ green "runConfig"
bindLocal "off" (bool_ False)
bindLocal "on" (bool_ True)
mapM_ eval conf
updateFromConfig :: MonadUnliftIO m => Config -> FixerM m ()
updateFromConfig conf = do
asks _config >>= atomically . flip writeTVar conf
runConfig conf
main :: IO ()
main = do
runMe =<< customExecParser (prefs showHelpOnError)
( info (helper <*> opts)
( fullDesc
<> header "hbs2-fixer"
<> progDesc "Intermediary between hbs2-peer and external applications. Listen events / do stuff"
))
where
opts = optional $ strOption (short 'c' <> long "config" <> metavar "FILE" <> help "Specify configuration file")
runMe opt = withApp opt mainLoop

View File

@ -1,72 +0,0 @@
;; hbs2-fixer config example
(local home (getenv "HOME"))
(local root (string-append home "/.local/share/hbs2-git-repos/0.24.1"))
(local hbs2-repo "BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP" )
(local hbs2-repo-path (string-append root "/" hbs2-repo))
(watch-config 30)
(debug off)
(display (string-append "repo1" " " hbs2-repo-path))
(eval (list (display "OKAY11 FROM EVAL")))
(on-start
(display (string-append "on-start" " " "mkdir" " " hbs2-repo-path))
(mkdir hbs2-repo-path)
(run (string-append "git init --bare " hbs2-repo-path))
(display update-hbs2-repo)
(run (list opts (cwd hbs2-repo-path))
(string-append "git hbs2 import" " " hbs2-repo))
(run (list opts (cwd hbs2-repo-path))
(string-append "git gc" ) )
)
(watch 60 (lwwref "BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP")
(run-config
(watch 300 (lwwref:get-hbs2-git-reflog)
(display "GIT REFLOG CHANGED BY WATCH")
(run (list opts (cwd hbs2-repo-path))
(string-append "git hbs2 import" " " hbs2-repo ))
(display (string-append "Updated " hbs2-repo " OK"))
)
(listen (lwwref:get-hbs2-git-reflog)
(display "GIT REFLOG CHANGED BY LISTENER")
(run (list opts (cwd hbs2-repo-path))
(string-append "git hbs2 import" " " hbs2-repo ))
(display (string-append "Updated " hbs2-repo " OK"))
)
)
(display (string-append "Updated " hbs2-repo))
)
; (watch 30 (lwwref "Byc3XUeSbJBXVFueumkNkVJMPHbGoUdxYEJBgzJPf8io")
; (run "./on-my-ref4.sh")
; )
; (watch 30 (lwwref "DTmSb3Au7apDTMctQn6yqs9GJ8mFW7YQXzgVqZpmkTtf")
; (run "./on-my-ref4.sh")
; )
; (watch 30 (reflog "BKtvRLispCM9UuQqHaNxu4SEUzpQNQ3PeRNknecKGPZ6")
; (run "./on-my-ref4.sh")
; )
; (display "JOPAKITA 111")

View File

@ -1,4 +0,0 @@
(display (getenv 1234))
(display (getenv "HOME"))

View File

@ -1,10 +0,0 @@
(local home (getenv "HOME"))
(local root (string-append home "/.local/share/hbs2-git-repos/0.24.1"))
(local hbs2-repo "BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP" )
(local hbs2-repo-path (string-append root "/" hbs2-repo))
(display root)

View File

@ -1,25 +0,0 @@
;; hbs2-fixer config example
; (debug off)
(watch-config 30)
(local home (getenv "HOME"))
(local root (string-append home "/.local/share/hbs2-git-repos/0.24.1"))
(local hbs2-repo "BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP" )
(local hbs2-repo-path (string-append root "/" hbs2-repo))
(local myref "BKtvRLispCM9UuQqHaNxu4SEUzpQNQ3PeRNknecKGPZ6" )
(listen (reflog myref)
(display (string-append "HELLO FROM REFLOG " (unquoted myref)))
)
(listen (lwwref myref)
(display "WON'T HAPPEN")
)
(display "FUUBAR!")

View File

@ -1,5 +0,0 @@
(local code (list (display "HELLO")))
(eval code)

View File

@ -1,4 +0,0 @@
(watch 30 (lwwref "BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP")
(display "PREVED")
)

View File

@ -1,129 +0,0 @@
cabal-version: 3.0
name: hbs2-fixer
version: 0.25.0.1
-- synopsis:
-- description:
license: BSD-3-Clause
license-file: LICENSE
author: Dmitry Zuikov
maintainer: dzuikov@gmail.com
-- copyright:
category: Development
build-type: Simple
extra-doc-files: CHANGELOG.md
-- extra-source-files:
common shared-properties
ghc-options:
-Wall
-Wno-type-defaults
-fprint-potential-instances
-- -fno-warn-unused-matches
-- -fno-warn-unused-do-bind
-- -Werror=missing-methods
-- -Werror=incomplete-patterns
-- -fno-warn-unused-binds
default-language: Haskell2010
default-extensions:
ApplicativeDo
, BangPatterns
, BlockArguments
, ConstraintKinds
, DataKinds
, DeriveDataTypeable
, DeriveGeneric
, DerivingStrategies
, DerivingVia
, ExtendedDefaultRules
, FlexibleContexts
, FlexibleInstances
, GADTs
, GeneralizedNewtypeDeriving
, ImportQualifiedPost
, LambdaCase
, MultiParamTypeClasses
, OverloadedStrings
, QuasiQuotes
, RecordWildCards
, ScopedTypeVariables
, StandaloneDeriving
, TupleSections
, TypeApplications
, TypeOperators
, TypeFamilies
, TemplateHaskell
build-depends: hbs2-core, hbs2-peer, hbs2-git
, attoparsec
, aeson
, async
, base16-bytestring
, bytestring
, cache
, containers
, streaming
, streaming-bytestring
, streaming-commons
, crypton
, directory
, exceptions
, filelock
, filepath
, filepattern
, generic-lens
, hashable
, http-conduit
, interpolatedstring-perl6
, memory
, microlens-platform
, mtl
, prettyprinter
, prettyprinter-ansi-terminal
, random
, resourcet
, safe
, saltine
, serialise
, split
, sqlite-simple
, stm
, suckless-conf
, temporary
, text
, time
, timeit
, transformers
, typed-process
, uniplate
, unliftio
, unliftio-core
, unordered-containers
, wai-app-file-cgi
, wai-extra
executable hbs2-fixer
import: shared-properties
main-is: Main.hs
ghc-options:
-threaded
-rtsopts
"-with-rtsopts=-N4 -A64m -AL256m -I0"
other-modules:
-- other-extensions:
build-depends:
base, hbs2-core, hbs2-peer
, optparse-applicative
, unliftio
hs-source-dirs: app
default-language: GHC2021

View File

@ -1,199 +0,0 @@
; vim: set filetype=scheme :
;; hbs2-fixer config example
(local root "/var/www/git")
(local hbs2-ref (lwwref "BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP"))
(local hbs2-repo "BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP" )
(local hbs2-repo-path (string-append root "/" hbs2-repo))
(local suckless-ref (lwwref "JAuk1UJzZfbDGKVazSQU5yYQ3NGfk4gVeZzBCduf5TgQ"))
(local suckless-repo "JAuk1UJzZfbDGKVazSQU5yYQ3NGfk4gVeZzBCduf5TgQ" )
(local suckless-repo-path (string-append root "/" suckless-repo))
(local fixme-ref (lwwref "Fujv1Uy4W5d9Z7REEArMxbXSJ8nLLn4dYuvaAs8b86hr"))
(local fixme-repo "Fujv1Uy4W5d9Z7REEArMxbXSJ8nLLn4dYuvaAs8b86hr" )
(local fixme-repo-path (string-append root "/" fixme-repo))
(local dbpipe-ref (lwwref "5xrwbTzzweS9yeJQnrrUY9gQJfhJf84pbyHhF2MMmSft"))
(local dbpipe-repo "5xrwbTzzweS9yeJQnrrUY9gQJfhJf84pbyHhF2MMmSft" )
(local dbpipe-repo-path (string-append root "/" dbpipe-repo))
(watch-config 30)
(debug on)
(display (string-append "repo1" " " hbs2-repo-path))
(on-start
(display (string-append "on-start" " " "mkdir" " " hbs2-repo-path))
(mkdir hbs2-repo-path)
(run (string-append "git init --bare " hbs2-repo-path))
(run (list opts (cwd hbs2-repo-path))
(string-append "git hbs2 import" " " hbs2-repo))
(run (list opts (cwd hbs2-repo-path))
(string-append "git gc" ) )
(mkdir suckless-repo-path)
(run (string-append "git init --bare " suckless-repo-path))
(run (list opts (cwd suckless-repo-path))
(string-append "git hbs2 import" " " suckless-repo))
(run (list opts (cwd suckless-repo-path))
(string-append "git gc" ) )
(mkdir fixme-repo-path)
(run (string-append "git init --bare " fixme-repo-path))
(run (list opts (cwd fixme-repo-path))
(string-append "git hbs2 import" " " fixme-repo))
(run (list opts (cwd fixme-repo-path))
(string-append "git gc" ) )
(mkdir dbpipe-repo-path)
(run (string-append "git init --bare " dbpipe-repo-path))
(run (list opts (cwd dbpipe-repo-path))
(string-append "git hbs2 import" " " dbpipe-repo))
(run (list opts (cwd dbpipe-repo-path))
(string-append "git gc" ) )
)
(watch 60 hbs2-ref
(display (string-append "hbs2-repo" " " hbs2-ref))
(display (string-append "hbs2-repo" " " hbs2-ref))
(run-config
(display (string-append "REF" (lwwref:get-hbs2-git-reflog)))
(watch 300 (lwwref:get-hbs2-git-reflog)
(run (list opts (cwd hbs2-repo-path))
(string-append "git hbs2 import" " " hbs2-repo ))
(display (string-append "Updated " hbs2-repo " OK"))
)
(listen (lwwref:get-hbs2-git-reflog)
(display "subscribed hbs2")
(run (list opts (cwd hbs2-repo-path))
(string-append "git hbs2 import" " " hbs2-repo ))
(display (string-append "Updated " hbs2-repo " OK")))
)
)
;; fixme
(watch 60 fixme-ref
(display (string-append "fixme-repo" " " fixme-ref))
(display (string-append "fixme-repo" " " fixme-ref))
(run-config
(display (string-append "REF" (lwwref:get-hbs2-git-reflog)))
(watch 300 (lwwref:get-hbs2-git-reflog)
(run (list opts (cwd fixme-repo-path))
(string-append "git hbs2 import" " " fixme-repo ))
(display (string-append "Updated " fixme-repo " OK"))
)
(listen (lwwref:get-hbs2-git-reflog)
(display "subscribed fixme")
(run (list opts (cwd fixme-repo-path))
(string-append "git hbs2 import" " " fixme-repo ))
(display (string-append "Updated " fixme-repo " OK")))
)
)
;; suckless
(watch 60 suckless-ref
(display (string-append "suckless-repo" " " fixme-ref))
(display (string-append "suckless-repo" " " fixme-ref))
(run-config
(display (string-append "REF" (lwwref:get-hbs2-git-reflog)))
(watch 300 (lwwref:get-hbs2-git-reflog)
(run (list opts (cwd suckless-repo-path))
(string-append "git hbs2 import" " " suckless-repo ))
(display (string-append "Updated " suckless-repo " OK"))
)
(listen (lwwref:get-hbs2-git-reflog)
(display "subscribed suckless")
(run (list opts (cwd suckless-repo-path))
(string-append "git hbs2 import" " " suckless-repo ))
(display (string-append "Updated " suckless-repo " OK")))
)
)
;; dbpipe
(watch 60 dbpipe-ref
(display (string-append "dbpipe-repo" " " fixme-ref))
(display (string-append "dbpipe-repo" " " fixme-ref))
(run-config
(display (string-append "REF" (lwwref:get-hbs2-git-reflog)))
(watch 300 (lwwref:get-hbs2-git-reflog)
(run (list opts (cwd dbpipe-repo-path))
(string-append "git hbs2 import" " " dbpipe-repo ))
(display (string-append "Updated " dbpipe-repo " OK"))
)
(listen (lwwref:get-hbs2-git-reflog)
(display "subscribed dbpipe")
(run (list opts (cwd dbpipe-repo-path))
(string-append "git hbs2 import" " " dbpipe-repo ))
(display (string-append "Updated " dbpipe-repo " OK")))
)
)

View File

@ -1,794 +0,0 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# Language UndecidableInstances #-}
{-# Language AllowAmbiguousTypes #-}
module Main where
import HBS2.Git.DashBoard.Prelude
import HBS2.Net.Messaging.Unix
import HBS2.Net.Proto
import HBS2.Net.Proto.Service
import HBS2.System.Dir
import HBS2.OrDie
import HBS2.Polling
import HBS2.Actors.Peer
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.State.Commits
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.Fixme
import HBS2.Git.DashBoard.Manifest
import HBS2.Git.Web.Html.Root
import HBS2.Git.Web.Html.Issue
import HBS2.Git.Web.Html.Repo
import HBS2.Git.Web.Html.Fixme
import HBS2.Peer.CLI.Detect
import DBPipe.SQLite
import Data.Config.Suckless.Script
import Lucid (renderTextT,HtmlT(..),toHtml)
import Data.Either
import Data.Text qualified as Text
import Data.Text.Lazy qualified as LT
import Data.ByteString.Lazy qualified as LBS
import Data.ByteString.Lazy (ByteString)
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 as Scotty
import Control.Monad.Except
import System.Random
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HM
import Control.Concurrent.STM (flushTQueue)
import System.FilePath
import System.Process.Typed
import System.Directory (XdgDirectory(..),getXdgDirectory)
import Data.ByteString.Lazy.Char8 qualified as LBS8
import System.Environment
import System.Exit
import System.IO.Temp
{- HLINT ignore "Eta reduce" -}
{- HLINT ignore "Functor law" -}
getRPC :: Monad m => HasConf m => m (Maybe FilePath)
getRPC = pure Nothing
data CallRPC
data PingRPC
data IndexNowRPC
type MyRPC = '[ PingRPC, IndexNowRPC, CallRPC ]
instance HasProtocol UNIX (ServiceProto MyRPC UNIX) where
type instance ProtocolId (ServiceProto MyRPC UNIX) = 0xFAFABEBE
type instance Encoded UNIX = ByteString
decode = either (const Nothing) Just . deserialiseOrFail
encode = serialise
type instance Input CallRPC = String
type instance Output CallRPC = String
type instance Input PingRPC = ()
type instance Output PingRPC = String
type instance Input IndexNowRPC = ()
type instance Output IndexNowRPC = ()
class HasDashBoardEnv m where
getDashBoardEnv :: m DashBoardEnv
instance (MonadIO m) => HandleMethod m CallRPC where
handleMethod n = do
debug $ "RPC CALL" <+> pretty n
pure ""
instance (MonadIO m, HasDashBoardEnv m) => HandleMethod m PingRPC where
handleMethod _ = do
debug $ "RPC PING"
pure "pong"
instance (DashBoardPerks m, HasDashBoardEnv m) => HandleMethod m IndexNowRPC where
handleMethod _ = do
e <- getDashBoardEnv
debug $ "rpc: index:now"
withDashBoardEnv e $ addJob (liftIO $ withDashBoardEnv e updateIndex)
instance HasLimit (FromParams 'FixmeDomain [Param]) where
-- TODO: optimal-page-size
limit (FromParams p) = Just limits
where
pageSize = fromIntegral fixmePageSize
page = fromMaybe 0 $ headMay [ readDef 0 (Text.unpack n) | ("$page", n) <- p ]
offset = page
limits = (fromIntegral offset, fromIntegral pageSize)
instance HasPredicate (FromParams 'FixmeDomain [Param]) where
predicate (FromParams args) = do
flip fix seed $ \next -> \case
[] -> All
( clause : rest ) -> And clause (next rest)
where
seed = [ AttrLike a b | (a,b) <- args, a /= "$page" ]
readConfig :: DashBoardPerks m => m [Syntax C]
readConfig = do
xdgConf <- liftIO $ getXdgDirectory XdgConfig hbs2_git_dashboard
let confPath = xdgConf
let confFile = confPath </> "config"
touch confFile
runExceptT (liftIO $ readFile confFile)
<&> fromRight mempty
<&> parseTop
<&> fromRight mempty
runDashBoardM :: DashBoardPerks m => DashBoardM m a -> m a
runDashBoardM m = do
xdgData <- liftIO $ getXdgDirectory XdgData hbs2_git_dashboard
let dataDir = xdgData
-- 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
dataDir
peerAPI
refLogAPI
refChanAPI
lwwAPI
sto
lift $ withDashBoardEnv env do
mkdir dataDir
notice "evolving db"
withState evolveDB
void $ ContT $ withAsync do
fix \next -> do
dbe' <- readTVarIO (_db env)
case dbe' of
Just dbe -> do
notice $ green "Aquired database!"
runPipe dbe
forever do
pause @'Seconds 30
Nothing -> do
pause @'Seconds 5
next
replicateM_ 2 do
ContT $ withAsync do
q <- withDashBoardEnv env $ asks _pipeline
forever do
liftIO (atomically $ readTQueue q) & liftIO . join
lift $ withDashBoardEnv env 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
renderHtml :: forall m a . MonadIO m => HtmlT (ActionT m) a -> ActionT m ()
renderHtml m = renderTextT m >>= html
runDashboardWeb :: WebOptions -> ScottyT (DashBoardM IO) ()
runDashboardWeb WebOptions{..} = do
middleware logStdout
case _assetsOverride of
Nothing -> do
middleware (E.static assetsDir)
Just f -> do
middleware $ staticPolicy (noDots >-> addBase f)
get (routePattern RepoListPage) do
renderHtml dashboardRootPage
get "/:lww" do
lww <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
>>= orThrow (itemNotFound "repository key")
asksBaseUrl $ withBaseUrl $
redirect (LT.fromStrict $ toBaseURL (RepoPage (CommitsTab Nothing) lww))
get (routePattern (RepoPage "tab" "lww")) do
lww <- captureParam @String "lww" <&> fromStringMay
>>= orThrow (itemNotFound "repository key")
tab <- captureParam @String "tab"
<&> fromStringMay
<&> fromMaybe (CommitsTab Nothing)
qp <- queryParams
renderHtml (repoPage tab lww qp)
get (routePattern (RepoManifest "lww")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
flip runContT pure do
lww <- lwws' & orFall (status status404)
TopInfoBlock{..} <- lift $ getTopInfoBlock lww
lift $ html (LT.fromStrict manifest)
get (routePattern (RepoRefs "lww")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
-- setHeader "HX-Push-Url" [qc|/{show $ pretty lwws'}|]
flip runContT pure do
lww <- lwws' & orFall (status status404)
lift $ renderHtml (repoRefs lww)
get (routePattern (RepoTree "lww" "co" "hash")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
hash' <- captureParam @String "hash" <&> fromStringMay @GitHash
co' <- captureParam @String "co" <&> fromStringMay @GitHash
flip runContT pure do
lww <- lwws' & orFall (status status404)
hash <- hash' & orFall (status status404)
co <- co' & orFall (status status404)
lift $ renderHtml (repoTree lww co hash)
get (routePattern (RepoBlob "lww" "co" "hash" "blob")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
hash' <- captureParam @String "hash" <&> fromStringMay @GitHash
co' <- captureParam @String "co" <&> fromStringMay @GitHash
blob' <- captureParam @String "blob" <&> fromStringMay @GitHash
flip runContT pure do
lww <- lwws' & orFall (status status404)
hash <- hash' & orFall (status status404)
co <- co' & orFall (status status404)
blobHash <- blob' & orFall (status status404)
blobInfo <- lift (selectBlobInfo (BlobHash blobHash))
>>= orFall (status status404)
lift $ renderHtml (repoBlob lww (TreeCommit co) (TreeTree hash) blobInfo)
get (routePattern (RepoSomeBlob "lww" "syntax" "blob")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
syn <- captureParamMaybe @Text "syntax" <&> fromMaybe "default"
blob' <- captureParam @String "blob" <&> fromStringMay @GitHash
flip runContT pure do
lww <- lwws' & orFall (status status404)
blob <- blob' & orFall (status status404)
lift $ renderHtml (repoSomeBlob lww syn blob)
get (routePattern (RepoCommitDefault "lww" "hash")) (commitRoute RepoCommitSummary)
get (routePattern (RepoCommitSummaryQ "lww" "hash")) (commitRoute RepoCommitSummary)
get (routePattern (RepoCommitPatchQ "lww" "hash")) (commitRoute RepoCommitPatch)
get (routePattern (RepoForksHtmx "lww")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
flip runContT pure do
lww <- lwws' & orFall (status status404)
lift $ renderHtml (repoForks lww)
-- lift $ renderHtml (toHtml $ show $ pretty lww)
get (routePattern (IssuePage "lww" "fixme")) do
r <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
f <- captureParam @String "fixme" <&> fromStringMay @FixmeKey
debug $ blue "AAAA" <+> pretty r <+> pretty f
flip runContT pure do
lww <- r & orFall (status status404)
fme <- f & orFall (status status404)
lift $ renderHtml (issuePage (RepoLww lww) fme)
get (routePattern (RepoFixmeHtmx mempty "lww")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
p <- queryParams
debug $ "FIXME: GET QUERY" <+> pretty p
flip runContT pure do
lww <- lwws' & orFall (status status404)
lift $ renderHtml (repoFixme (FromParams @'FixmeDomain p) lww)
get (routePattern (RepoCommits "lww")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
let pred = mempty & set commitPredOffset 0
& set commitPredLimit 100
flip runContT pure do
lww <- lwws' & orFall (status status404)
lift $ renderHtml (repoCommits lww (Right pred))
get (routePattern (RepoCommitsQ "lww" "off" "lim")) do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey 'HBS2Basic)
off <- captureParam @Int "off"
lim <- captureParam @Int "lim"
let pred = mempty & set commitPredOffset off
& set commitPredLimit lim
flip runContT pure do
lww <- lwws' & orFall (status status404)
-- FIXME: this
referrer <- asksBaseUrl $ withBaseUrl $ lift (Scotty.header "Referer")
>>= orFall (redirect $ LT.fromStrict $ toBaseURL (RepoPage (CommitsTab Nothing) lww))
lift $ renderHtml (repoCommits lww (Left pred))
-- "pages"
where
commitRoute style = do
lwws' <- captureParam @String "lww" <&> fromStringMay @(LWWRefKey HBS2Basic)
co <- captureParam @String "hash" <&> fromStringMay @GitHash
referrer <- Scotty.header "Referer"
debug $ yellow "COMMIT-REFERRER" <+> pretty referrer
flip runContT pure do
lww <- lwws' & orFall (status status404)
hash <- co & orFall (status status404)
lift $ renderHtml (repoCommit style lww hash)
runScotty :: DashBoardPerks m => DashBoardM m ()
runScotty = do
env <- ask
notice "running config"
conf <- readConfig
run theDict conf
pno <- getHttpPortNumber
wo <- WebOptions <$> getDevAssets
flip runContT pure do
void $ ContT $ withAsync updateIndexPeriodially
void $ ContT $ withAsync runRPC
scottyT pno (withDashBoardEnv env) (runDashboardWeb wo)
data RPCEnv = RPCEnv
{ rpcMessaging :: MessagingUnix
, dashBoardEnv :: DashBoardEnv
}
newtype RunRPCM m a = RunRPCM { fromRunRPC :: ReaderT RPCEnv m a }
deriving newtype ( Applicative
, Functor
, Monad
, MonadIO
, MonadUnliftIO
, MonadTrans
, MonadReader RPCEnv
)
runRPCMonad :: DashBoardEnv -> MessagingUnix -> RunRPCM m a -> m a
runRPCMonad env s m = runReaderT (fromRunRPC m) (RPCEnv s env)
instance HasFabriq UNIX (RunRPCM IO) where
getFabriq = asks (Fabriq . rpcMessaging)
instance HasOwnPeer UNIX (RunRPCM IO) where
ownPeer = asks ( msgUnixSelf . rpcMessaging)
instance HasDashBoardEnv (ResponseM UNIX (RunRPCM IO)) where
getDashBoardEnv = lift $ asks dashBoardEnv
runRPC :: DashBoardPerks m => DashBoardM m ()
runRPC = do
debug $ green "runRPC loop"
env <- ask
liftIO $ flip runContT pure do
soname <- ContT $ bracket (liftIO $ emptySystemTempFile "hbs2-git-dashboard-socket") rm
liftIO $ withDashBoardEnv env do
setRPCSocket soname
void $ ContT $ bracket (pure soname) (\_ -> withDashBoardEnv env $ delRPCSocket)
notice $ green "rpc-socket" <+> pretty soname
server <- newMessagingUnix True 1.0 soname
m1 <- ContT $ withAsync (runMessagingUnix server)
p1 <- ContT $ withAsync $ runRPCMonad env server do
runProto @UNIX
[ makeResponse (makeServer @MyRPC)
]
void $ waitAnyCatchCancel [m1,p1]
-- pure ()
updateIndexPeriodially :: DashBoardPerks m => DashBoardM m ()
updateIndexPeriodially = do
api <- asks _refLogAPI
env <- ask
changes <- newTQueueIO
-- queues <- newTVarIO ( mempty :: HashMap RepoLww (TQueue (IO ()) ) )
flip runContT pure do
lift $ addJob (withDashBoardEnv env updateIndex)
p1 <- ContT $ withAsync $ do
pause @'Seconds 30
forever do
rs <- atomically $ peekTQueue changes >> flushTQueue changes
addJob (withDashBoardEnv env updateIndex)
-- pause @'Seconds 1
p2 <- pollRepos changes
p3 <- pollFixmies
p4 <- pollRepoIndex
void $ waitAnyCatchCancel [p1,p2,p3,p4]
where
pollFixmies = do
env <- ask
api <- asks _refChanAPI
cached <- newTVarIO ( mempty :: HashMap MyRefChan HashRef )
let chans = selectRepoFixme
<&> fmap (,60)
ContT $ withAsync $ do
polling (Polling 10 30) chans $ \(l,r) -> do
debug $ yellow "POLL FIXME CHAN" <+> pretty (AsBase58 r)
void $ runMaybeT do
new <- lift (callRpcWaitMay @RpcRefChanGet (TimeoutSec 1) api (coerce r))
<&> join
>>= toMPlus
old <- readTVarIO cached <&> HM.lookup r
atomically $ modifyTVar cached (HM.insert r new)
when (Just new /= old) $ lift do
debug $ yellow "fixme refchan changed" <+> "run update" <+> pretty new
addJob do
-- TODO: this-is-not-100-percent-reliable
-- $workflow: backlog
-- откуда нам вообще знать, что там всё получилось?
void $ try @_ @SomeException (withDashBoardEnv env $ updateFixmeFor l r)
pollRepos changes = do
cached <- newTVarIO ( mempty :: HashMap MyRefLogKey HashRef )
api <- asks _refLogAPI
let rlogs = selectRefLogs <&> fmap (over _1 (coerce @_ @MyRefLogKey)) . fmap (, 60)
ContT $ withAsync $ do
polling (Polling 10 30) rlogs $ \r -> do
debug $ yellow "POLL REFLOG" <+> pretty r
rv <- callRpcWaitMay @RpcRefLogGet (TimeoutSec 1) api (coerce r)
<&> join
old <- readTVarIO cached <&> HM.lookup r
for_ rv $ \x -> do
atomically $ modifyTVar cached (HM.insert r x)
when (rv /= old) do
debug $ yellow "REFLOG UPDATED" <+> pretty r <+> pretty x
atomically $ modifyTVar cached (HM.insert r x)
atomically $ writeTQueue changes r
flip runContT pure $ callCC $ \exit -> do
lww <- lift (selectLwwByRefLog (RepoRefLog r))
>>= maybe (exit ()) pure
dir <- lift $ repoDataPath (coerce lww)
here <- doesDirectoryExist dir
unless here do
debug $ red "INIT DATA DIR" <+> pretty dir
mkdir dir
void $ runProcess $ shell [qc|git --git-dir {dir} init --bare|]
let cmd = [qc|git --git-dir {dir} hbs2 import {show $ pretty lww}|]
debug $ red "SYNC" <+> pretty cmd
void $ runProcess $ shell cmd
pollRepoIndex = do
api <- asks _refLogAPI
let rlogs = selectRefLogs <&> fmap (over _1 (coerce @_ @MyRefLogKey)) . fmap (, 600)
ContT $ withAsync $ do
polling (Polling 1 30) rlogs $ \r -> do
lww' <- selectLwwByRefLog (RepoRefLog r)
for_ lww' $ addRepoIndexJob . coerce
quit :: DashBoardPerks m => m ()
quit = liftIO exitSuccess
withMyRPCClient :: ( MonadUnliftIO m )
-- , HasTimeLimits UNIX (ServiceProto MyRPC UNIX) m)
=> FilePath -> (ServiceCaller MyRPC UNIX -> IO b) -> m b
withMyRPCClient soname m = do
liftIO do
client <- newMessagingUnix False 1.0 soname
flip runContT pure do
mess <- ContT $ withAsync $ runMessagingUnix client
caller <- makeServiceCaller @MyRPC @UNIX (msgUnixSelf client)
p2 <- ContT $ withAsync $ runReaderT (runServiceClient caller) client
void $ ContT $ bracket none (const $ cancel mess)
void $ ContT $ bracket none (const $ cancel p2)
liftIO $ m caller
theDict :: forall m . ( DashBoardPerks m
-- , HasTimeLimits UNIX (ServiceProto MyRPC UNIX) m
) => Dict C (DashBoardM m)
theDict = do
makeDict @C do
-- TODO: write-man-entries
myHelpEntry
fixmeAllowEntry
fixmeAllowDropEntry
webEntry
portEntry
developAssetsEntry
baseUrlEntry
getRpcSocketEntry
rpcPingEntry
rpcIndexEntry
debugEntries
where
myHelpEntry = do
entry $ bindMatch "--help" $ nil_ $ \case
HelpEntryBound what -> do
helpEntry what
quit
[StringLike s] -> helpList False (Just s) >> quit
_ -> helpList False Nothing >> quit
fixmeAllowEntry = do
brief "allows fixme for given reflog" $
args [arg "public-key" "reflog"] $
examples [qc|
fixme-allow BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP
|]
$ entry $ bindMatch "fixme-allow" $ nil_ \case
[SignPubKeyLike what] -> do
lift $ insertFixmeAllowed (RepoRefLog (RefLogKey what))
_ -> throwIO $ BadFormException @C nil
fixmeAllowDropEntry = do
brief "drop all allowed fixme records" $
examples [qc|
fixme-allow:drop
|]
$ entry $ bindMatch "fixme-allow:drop" $ nil_ \case
[] -> do
lift $ deleteFixmeAllowed
_ -> throwIO $ BadFormException @C nil
webEntry = do
brief "run web interface" $
entry $ bindMatch "web" $ nil_ $ const do
lift runScotty
portEntry = do
brief "set http port for web interface" $
entry $ bindMatch "port" $ nil_ \case
[LitIntVal n] -> do
tp <- lift $ asks _dashBoardHttpPort
atomically $ writeTVar tp (Just (fromIntegral n))
_ -> throwIO $ BadFormException @C nil
developAssetsEntry = do
entry $ bindMatch "develop-assets" $ nil_ \case
[StringLike s] -> do
devAssTVar <- lift $ asks _dashBoardDevAssets
atomically $ writeTVar devAssTVar (Just s)
_ -> none
baseUrlEntry = do
entry $ bindMatch "base-url" $ nil_ \case
[StringLike s] -> do
urlTV <- lift $ asks _dashBoardBaseUrl
atomically $ writeTVar urlTV (Just (Text.pack s))
_ -> none
getRpcSocketEntry = do
entry $ bindMatch "rpc:socket" $ nil_ $ const do
lift getRPCSocket >>= liftIO . maybe exitFailure putStr
rpcPingEntry = do
entry $ bindMatch "ping" $ nil_ $ const $ lift do
so <- getRPCSocket >>= orThrowUser "rpc socket down"
withMyRPCClient so $ \caller -> do
what <- callService @PingRPC caller ()
print what
rpcIndexEntry = do
entry $ bindMatch "index:now" $ nil_ $ const $ lift do
so <- getRPCSocket >>= orThrowUser "rpc socket down"
withMyRPCClient so $ \caller -> do
void $ callService @IndexNowRPC caller ()
-- TODO: ASAP-hide-debug-functions-from-help
debugEntries = do
entry $ bindMatch "debug:cache:ignore:on" $ nil_ $ const $ lift do
t <- asks _dashBoardIndexIgnoreCaches
atomically $ writeTVar t True
entry $ bindMatch "debug:cache:ignore:off" $ nil_ $ const $ lift do
t <- asks _dashBoardIndexIgnoreCaches
atomically $ writeTVar t False
entry $ bindMatch "debug:build-commit-index" $ nil_ $ \case
[SignPubKeyLike lw] -> lift do
buildCommitTreeIndex (LWWRefKey lw)
_ -> throwIO $ BadFormException @C nil
entry $ bindMatch "debug:build-single-commit-index" $ nil_ $ \case
[SignPubKeyLike lw, StringLike h'] -> lift do
h <- fromStringMay @GitHash h'
& orThrowUser ("invalid git object hash" <+> pretty h')
buildSingleCommitTreeIndex (LWWRefKey lw) h
_ -> throwIO $ BadFormException @C nil
-- rs <- selectRepoFixme
-- for_ rs $ \(r,f) -> do
-- liftIO $ print $ pretty r <+> pretty (AsBase58 f)
entry $ bindMatch "debug:select-repo-fixme" $ nil_ $ const $ lift do
rs <- selectRepoFixme
for_ rs $ \(r,f) -> do
liftIO $ print $ pretty r <+> pretty (AsBase58 f)
entry $ bindMatch "debug:check-fixme-allowed" $ nil_ $ \case
[SignPubKeyLike s] -> do
what <- lift $ checkFixmeAllowed (RepoLww (LWWRefKey s))
liftIO $ print $ pretty what
_ -> throwIO $ BadFormException @C nil
entry $ bindMatch "debug:test-with-fixme" $ nil_ $ \case
[SignPubKeyLike s] -> lift do
r <- listFixme (RepoLww (LWWRefKey s)) ()
for_ r $ \f -> do
liftIO $ print $ pretty f
_ -> throwIO $ BadFormException @C nil
entry $ bindMatch "debug:count-fixme" $ nil_ $ \case
[SignPubKeyLike s] -> lift do
r <- countFixme (RepoLww (LWWRefKey s))
liftIO $ print $ pretty r
_ -> throwIO $ BadFormException @C nil
main :: IO ()
main = do
argz <- getArgs
cli <- parseTop (unlines $ unwords <$> splitForms argz)
& either (error.show) pure
let dict = theDict
void $ runDashBoardM $ do
run dict cli

View File

@ -1,201 +0,0 @@
{-# Language TemplateHaskell #-}
module HBS2.Git.Web.Assets where
import Data.FileEmbed
import Data.ByteString
import Data.Text (Text)
import Text.InterpolatedString.Perl6 (qc)
import Lucid.Base
version :: Int
version = 8
assetsDir :: [(FilePath, ByteString)]
assetsDir = $(embedDir "hbs2-git-dashboard-assets/assets")
data IconType
= IconCopy
| IconCopyDone
| IconLockClosed
| IconGitCommit
| IconGitFork
| IconGitBranch
| IconTag
| IconFolderFilled
| IconHaskell
| IconMarkdown
| IconNix
| IconBash
| IconPython
| IconJavaScript
| IconSql
| IconSettingsFilled
| IconFileFilled
| IconRefresh
| IconArrowUturnLeft
| IconLicense
| IconPinned
| IconFixme
svgIcon :: Monad m => IconType -> HtmlT m ()
svgIcon = toHtmlRaw . svgIconText
svgIconText :: IconType -> Text
svgIconText IconCopy = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 7m0 2.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667z" />
<path d="M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1" />
</svg>|]
svgIconText IconCopyDone = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy-check" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 7m0 2.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667z" />
<path d="M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1" />
<path d="M11 14l2 2l4 -4" />
</svg>|]
svgIconText IconLockClosed = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-lock" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6z" />
<path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0" />
<path d="M8 11v-4a4 4 0 1 1 8 0v4" />
</svg>|]
svgIconText IconGitCommit = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-git-commit" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 12m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0" />
<path d="M12 3l0 6" />
<path d="M12 15l0 6" />
</svg>|]
svgIconText IconGitFork = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-git-fork" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M7 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M17 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M7 8v2a2 2 0 0 0 2 2h6a2 2 0 0 0 2 -2v-2" />
<path d="M12 12l0 4" />
</svg>|]
svgIconText IconGitBranch = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-git-branch" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M7 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M17 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M7 8l0 8" />
<path d="M9 18h6a2 2 0 0 0 2 -2v-5" />
<path d="M14 14l3 -3l3 3" />
</svg>|]
svgIconText IconTag = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-tag" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7.5 7.5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M3 6v5.172a2 2 0 0 0 .586 1.414l7.71 7.71a2.41 2.41 0 0 0 3.408 0l5.592 -5.592a2.41 2.41 0 0 0 0 -3.408l-7.71 -7.71a2 2 0 0 0 -1.414 -.586h-5.172a3 3 0 0 0 -3 3z" />
</svg>|]
svgIconText IconFolderFilled = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-folder-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="#currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 3a1 1 0 0 1 .608 .206l.1 .087l2.706 2.707h6.586a3 3 0 0 1 2.995 2.824l.005 .176v8a3 3 0 0 1 -2.824 2.995l-.176 .005h-14a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-11a3 3 0 0 1 2.824 -2.995l.176 -.005h4z" stroke-width="0" fill="currentColor" />
</svg>|]
svgIconText IconHaskell = [qc|<svg role="img" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Haskell</title>
<path d="M0 3.535L5.647 12 0 20.465h4.235L9.883 12 4.235 3.535zm5.647 0L11.294 12l-5.647 8.465h4.235l3.53-5.29 3.53 5.29h4.234L9.883 3.535zm8.941 4.938l1.883 2.822H24V8.473zm2.824 4.232l1.882 2.822H24v-2.822z"/>
</svg>|]
svgIconText IconMarkdown = [qc|<svg role="img" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Markdown</title>
<path d="M22.27 19.385H1.73A1.73 1.73 0 010 17.655V6.345a1.73 1.73 0 011.73-1.73h20.54A1.73 1.73 0 0124 6.345v11.308a1.73 1.73 0 01-1.73 1.731zM5.769 15.923v-4.5l2.308 2.885 2.307-2.885v4.5h2.308V8.078h-2.308l-2.307 2.885-2.308-2.885H3.46v7.847zM21.232 12h-2.309V8.077h-2.307V12h-2.308l3.461 4.039z"/>
</svg>|]
svgIconText IconNix = [qc|<svg role="img" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Nix</title>
<path d="M7.352 1.592l-1.364.002L5.32 2.75l1.557 2.713-3.137-.008-1.32 2.34H14.11l-1.353-2.332-3.192-.006-2.214-3.865zm6.175 0l-2.687.025 5.846 10.127 1.341-2.34-1.59-2.765 2.24-3.85-.683-1.182h-1.336l-1.57 2.705-1.56-2.72zm6.887 4.195l-5.846 10.125 2.696-.008 1.601-2.76 4.453.016.682-1.183-.666-1.157-3.13-.008L21.778 8.1l-1.365-2.313zM9.432 8.086l-2.696.008-1.601 2.76-4.453-.016L0 12.02l.666 1.157 3.13.008-1.575 2.71 1.365 2.315L9.432 8.086zM7.33 12.25l-.006.01-.002-.004-1.342 2.34 1.59 2.765-2.24 3.85.684 1.182H7.35l.004-.006h.001l1.567-2.698 1.558 2.72 2.688-.026-.004-.006h.01L7.33 12.25zm2.55 3.93l1.354 2.332 3.192.006 2.215 3.865 1.363-.002.668-1.156-1.557-2.713 3.137.008 1.32-2.34H9.881Z"/>
</svg>|]
svgIconText IconBash = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-terminal-2" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M8 9l3 3l-3 3" />
<path d="M13 15l3 0" />
<path d="M3 4m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" />
</svg>|]
svgIconText IconPython = [qc|<svg role="img" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Python</title>
<path d="M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z"/>
</svg>|]
svgIconText IconJavaScript = [qc|<svg role="img" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>JavaScript</title>
<path d="M0 0h24v24H0V0zm22.034 18.276c-.175-1.095-.888-2.015-3.003-2.873-.736-.345-1.554-.585-1.797-1.14-.091-.33-.105-.51-.046-.705.15-.646.915-.84 1.515-.66.39.12.75.42.976.9 1.034-.676 1.034-.676 1.755-1.125-.27-.42-.404-.601-.586-.78-.63-.705-1.469-1.065-2.834-1.034l-.705.089c-.676.165-1.32.525-1.71 1.005-1.14 1.291-.811 3.541.569 4.471 1.365 1.02 3.361 1.244 3.616 2.205.24 1.17-.87 1.545-1.966 1.41-.811-.18-1.26-.586-1.755-1.336l-1.83 1.051c.21.48.45.689.81 1.109 1.74 1.756 6.09 1.666 6.871-1.004.029-.09.24-.705.074-1.65l.046.067zm-8.983-7.245h-2.248c0 1.938-.009 3.864-.009 5.805 0 1.232.063 2.363-.138 2.711-.33.689-1.18.601-1.566.48-.396-.196-.597-.466-.83-.855-.063-.105-.11-.196-.127-.196l-1.825 1.125c.305.63.75 1.172 1.324 1.517.855.51 2.004.675 3.207.405.783-.226 1.458-.691 1.811-1.411.51-.93.402-2.07.397-3.346.012-2.054 0-4.109 0-6.179l.004-.056z"/>
</svg>|]
svgIconText IconSql = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-type-sql" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M14 3v4a1 1 0 0 0 1 1h4" />
<path d="M14 3v4a1 1 0 0 0 1 1h4" />
<path d="M5 20.25c0 .414 .336 .75 .75 .75h1.25a1 1 0 0 0 1 -1v-1a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-1a1 1 0 0 1 1 -1h1.25a.75 .75 0 0 1 .75 .75" />
<path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v4" />
<path d="M18 15v6h2" />
<path d="M13 15a2 2 0 0 1 2 2v2a2 2 0 1 1 -4 0v-2a2 2 0 0 1 2 -2z" />
<path d="M14 20l1.5 1.5" />
</svg>|]
svgIconText IconSettingsFilled = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-settings-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M14.647 4.081a.724 .724 0 0 0 1.08 .448c2.439 -1.485 5.23 1.305 3.745 3.744a.724 .724 0 0 0 .447 1.08c2.775 .673 2.775 4.62 0 5.294a.724 .724 0 0 0 -.448 1.08c1.485 2.439 -1.305 5.23 -3.744 3.745a.724 .724 0 0 0 -1.08 .447c-.673 2.775 -4.62 2.775 -5.294 0a.724 .724 0 0 0 -1.08 -.448c-2.439 1.485 -5.23 -1.305 -3.745 -3.744a.724 .724 0 0 0 -.447 -1.08c-2.775 -.673 -2.775 -4.62 0 -5.294a.724 .724 0 0 0 .448 -1.08c-1.485 -2.439 1.305 -5.23 3.744 -3.745a.722 .722 0 0 0 1.08 -.447c.673 -2.775 4.62 -2.775 5.294 0zm-2.647 4.919a3 3 0 1 0 0 6a3 3 0 0 0 0 -6z" stroke-width="0" fill="currentColor" />
</svg>|]
svgIconText IconFileFilled = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2l.117 .007a1 1 0 0 1 .876 .876l.007 .117v4l.005 .15a2 2 0 0 0 1.838 1.844l.157 .006h4l.117 .007a1 1 0 0 1 .876 .876l.007 .117v9a3 3 0 0 1 -2.824 2.995l-.176 .005h-10a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-14a3 3 0 0 1 2.824 -2.995l.176 -.005h5z" stroke-width="0" fill="currentColor" />
<path d="M19 7h-4l-.001 -4.001z" stroke-width="0" fill="currentColor" />
</svg>|]
svgIconText IconRefresh = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4" />
<path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4" />
</svg>|]
svgIconText IconArrowUturnLeft = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-uturn-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 14l-4 -4l4 -4" />
<path d="M5 10h11a4 4 0 1 1 0 8h-1" />
</svg>|]
svgIconText IconLicense = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-license" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M15 21h-9a3 3 0 0 1 -3 -3v-1h10v2a2 2 0 0 0 4 0v-14a2 2 0 1 1 2 2h-2m2 -4h-11a3 3 0 0 0 -3 3v11" />
<path d="M9 7l4 0" />
<path d="M9 11l4 0" />
</svg>|]
svgIconText IconPinned = [qc|<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-pinned" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 4v6l-2 4v2h10v-2l-2 -4v-6" />
<path d="M12 16l0 5" />
<path d="M8 4l8 0" />
</svg>|]
svgIconText IconFixme = [qc|
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="icon icon-tabler icons-tabler-outline icon-tabler-stack-3">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2l-8 4l8 4l8 -4l-8 -4" />
<path d="M4 10l8 4l8 -4" />
<path d="M4 18l8 4l8 -4" />
<path d="M4 14l8 4l8 -4" />
</svg>|]

View File

@ -1,370 +0,0 @@
/* fastpok CSS start */
:root {
--pico-form-element-spacing-vertical: .5rem;
--pico-form-element-spacing-horizontal: .625rem;
}
[type=search] {
--pico-border-radius: inherit;
}
[role=search] {
--pico-border-radius: inherit;
}
[role=search]>:first-child {
border-top-left-radius: var(--pico-border-radius);
border-bottom-left-radius: var(--pico-border-radius);
}
[role=search]>:last-child {
border-top-right-radius: var(--pico-border-radius);
border-bottom-right-radius: var(--pico-border-radius);
}
body>footer, body>header, body>main {
padding-block: 0;
}
header>nav {
border-bottom: var(--pico-border-width) solid var(--pico-muted-border-color);
}
.wrapper {
display: flex;
}
.hidden{
display: none;
}
.sidebar {
width: 20rem;
flex-shrink: 0;
padding-top: var(--pico-block-spacing-vertical);
padding-right: var(--pico-block-spacing-horizontal);
padding-bottom: var(--pico-block-spacing-vertical);
border-right: var(--pico-border-width) solid var(--pico-muted-border-color);
display: flex;
flex-direction: column;
}
.content {
padding-top: var(--pico-block-spacing-vertical);
padding-bottom: var(--pico-block-spacing-vertical);
padding-left: var(--pico-block-spacing-horizontal);
overflow: auto;
}
article {
border: var(--pico-border-width) solid var(--pico-card-border-color);
box-shadow: none;
}
.repo-list-item {
display: flex;
justify-content: space-between;
gap: var(--pico-block-spacing-horizontal);
}
.repo-list-item-link-wrapper {
display: flex;
align-items: center;
margin-bottom: var(--pico-typography-spacing-vertical);
}
.copy-button {
margin-left: calc(var(--pico-spacing) * .5);
background-color: transparent;
border: none;
padding: 0;
border-radius: 0;
box-shadow: none;
color: var(--pico-secondary);
transition: color var(--pico-transition);
}
.copy-button:hover {
color: var(--pico-secondary-hover);
}
.copy-button .icon {
width: 1.125rem;
height: 1.125rem;
}
.inline-icon-wrapper {
display: inline-block;
}
.inline-icon-wrapper .icon {
margin-right: calc(var(--pico-spacing) * .25);
vertical-align: middle;
}
.info-block {
margin-bottom: var(--pico-block-spacing-vertical);
}
.repo-menu {
--pico-nav-breadcrumb-divider: '|';
}
.repo-menu li.active {
color: var(--pico-primary);
}
aside li {
padding: 0;
}
aside ul {
padding: 0;
}
aside li :where(a,[role=link]):not(:hover) {
text-decoration: none;
}
.sidebar-title {
margin-bottom: calc(var(--pico-typography-spacing-vertical) * .25);
}
.issue-info-card {
padding: 0;
}
.issue-info-card>header {
margin: 0;
}
.issue-info-card>header h5 {
color: inherit;
margin-bottom: 0;
}
.issue-info-table tr:hover {
background-color: var(--pico-background-color);
}
.issue-info-table td,
.issue-info-table th {
border-bottom-color: var(--pico-card-border-color);
}
.issue-info-table tr:last-child>td,
.issue-info-table tr:last-child>th {
border-bottom: none;
}
.issue-info-table tr:last-child>td:first-child,
.issue-info-table tr:last-child>th:first-child {
border-bottom-left-radius: var(--pico-border-radius);
}
.issue-info-table tr:last-child>td:last-child,
.issue-info-table tr:last-child>th:last-child {
border-bottom-right-radius: var(--pico-border-radius);
}
.issue-info-card .issue-id {
cursor: pointer;
border-bottom: none;
color: var(--pico-secondary);
}
.issue-info-card .issue-id:hover {
text-decoration: underline;
color: var(--pico-secondary-hover);
}
/* Tailwind-style classes */
.mb-0 {
margin-bottom: 0;
}
.mb-1 {
margin-bottom: var(--pico-spacing);
}
.p-0 {
padding: 0;
}
.py-0 {
padding-top: 0;
padding-bottom: 0;
}
.w-full {
width: 100%;
}
.whitespace-nowrap {
white-space: nowrap;
}
.font-normal {
font-weight: 400;
}
.text-secondary {
color: var(--pico-secondary);
}
.overflow-x-auto {
overflow-x: auto;
}
/* fastpok CSS end */
ul.misc-menu {
margin: 0 0 0 0;
padding: 0 0 0 0;
}
ul.misc-menu li {
padding: 0 0 0 0;
margin-right: 1em;
display: inline;
}
.mono {
font-family: 'Courier New', Courier, monospace;
}
.tree {
font-weight: 600;
}
td.tree-locator {
border-bottom: none;
}
td.tree-locator span {
margin-right: .5rem;
}
tr.commit-brief-title td,
tr.commit-brief-title th {
border-bottom: none;
vertical-align: top;
}
tr.commit-brief-details td,
tr.commit-brief-details th {
border-top: none;
}
td.commit-brief-title {
text-align: left;
}
tr.commit-brief-last td {
border: none;
}
tr.commit-brief-last th {
border: none;
}
td.commit-icon {
width: 4rem;
/* width: px; */
}
td.commit-hash {
width: 10rem;
text-align: left;
}
table.minimal {
}
table.minimal tr td {
border: none;
padding: 0.15em;
}
table.minimal tr {
border: none;
}
table tr:hover {
background-color: #f1f1f1;
}
.lim-text {
max-width: 80ch;
word-wrap: break-word;
}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: auto; }
code.sourceCode > span { color: inherit; text-decoration: inherit; overflow: auto; }
div.sourceCode { margin: 1em 0; overflow: auto; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; max-width: 120rem; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ef2929; } /* Alert */
code span.an { color: #8f5902; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #204a87; } /* Attribute */
code span.bn { color: #0000cf; } /* BaseN */
code span.cf { color: #204a87; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4e9a06; } /* Char */
code span.cn { color: #8f5902; } /* Constant */
code span.co { color: #8f5902; font-style: italic; } /* Comment */
code span.cv { color: #8f5902; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #8f5902; font-weight: bold; font-style: italic; } /* Documentation */
code span.dt { color: #204a87; } /* DataType */
code span.dv { color: #0000cf; } /* DecVal */
code span.er { color: #a40000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #0000cf; } /* Float */
code span.fu { color: #204a87; font-weight: bold; } /* Function */
code span.im { } /* Import */
code span.in { color: #8f5902; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #204a87; font-weight: bold; } /* Keyword */
code span.op { color: #ce5c00; font-weight: bold; } /* Operator */
code span.ot { color: #8f5902; } /* Other */
code span.pp { color: #8f5902; font-style: italic; } /* Preprocessor */
code span.sc { color: #ce5c00; font-weight: bold; } /* SpecialChar */
code span.ss { color: #4e9a06; } /* SpecialString */
code span.st { color: #4e9a06; } /* String */
code span.va { color: #000000; } /* Variable */
code span.vs { color: #4e9a06; } /* VerbatimString */
code span.wa { color: #8f5902; font-weight: bold; font-style: italic; } /* Warning */

File diff suppressed because one or more lines are too long

View File

@ -1,142 +0,0 @@
module HBS2.Git.DashBoard.Fixme
( F.HasPredicate(..)
, F.HasLimit(..)
, HasItemOrder(..)
, ItemOrder(..)
, Reversed(..)
, F.SelectPredicate(..)
, WithLimit(..)
, QueryOffset
, QueryLimit
, runInFixme
, countFixme
, countFixmeByAttribute
, listFixme
, getFixme
, RunInFixmeError(..)
, Fixme(..)
, FixmeKey(..)
, FixmeTitle(..)
, FixmeTag(..)
, FixmePlainLine(..)
, FixmeAttrName(..)
, FixmeAttrVal(..)
, FixmeOpts(..)
, fixmePageSize
, fixmeGet
) where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import HBS2.OrDie
import Fixme.State qualified as F
import Fixme.State ( HasPredicate(..)
, HasLimit(..)
, HasItemOrder(..)
, WithLimit(..)
, QueryOffset
, QueryLimit
, ItemOrder
, Reversed
)
import Fixme.Types
import Fixme.Config
import DBPipe.SQLite (shutdown)
import Data.Either
import Data.Generics.Product.Fields (field)
data RunInFixmeError =
FixmeRefChanNotFound RepoLww
deriving stock (Generic, Typeable, Show)
instance Exception RunInFixmeError
fixmePageSize :: QueryLimit
fixmePageSize = 100
-- TODO: less-hacky-approach
-- этот код подразумевает, что мы знаем довольно много деталей
-- реализации про fixme-new
--
-- Хорошо бы как-то абстрагировать, изолировать и т.п.
--
runInFixme :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> RepoLww
-> FixmeM m a
-> m a
runInFixme repo m = do
denv <- ask
fixmeRChan <- withDashBoardEnv denv $ selectRepoFixmeRefChan repo
>>= orThrow (FixmeRefChanNotFound repo)
p <- fixmeDataPath fixmeRChan
-- TODO: check-if-database-exists
fenv <- fixmeEnvBare
fo <- newTVarIO (FixmeOpts True)
twd <- newTVarIO p
let fenvNew = fenv & set (field @"fixmeEnvWorkDir") twd
& set (field @"fixmeEnvOpts") fo
flip runContT pure do
dbe <- lift $ withFixmeEnv fenvNew $ F.withState ask
void $ ContT $ bracket none (const $ shutdown False dbe)
lift $ withFixmeEnv fenvNew do
dbp <- localDBPath
wd <- fixmeWorkDir
cfg <- localConfig
trace $ "fixme:dir" <+> pretty wd
trace $ "fixme:config" <+> pretty cfg
trace $ "fixme:db" <+> pretty dbp
m
listFixme :: ( DashBoardPerks m
, MonadReader DashBoardEnv m
, HasPredicate q
, HasLimit q
, HasItemOrder q
) => RepoLww -> q -> m [Fixme]
listFixme repo q = do
runInFixme repo $ F.listFixme q
-- FIXME: error-handling
-- at least print log entry
& try @_ @SomeException
<&> fromRight mempty
getFixme :: ( DashBoardPerks m
, MonadReader DashBoardEnv m
) => RepoLww -> FixmeKey -> m (Maybe Fixme)
getFixme repo fk = do
-- FIXME: error-handling
-- at least print log entry
try @_ @SomeException (runInFixme repo $ runMaybeT do
k <- lift (F.selectFixmeKey (coerce fk)) >>= toMPlus
lift (F.getFixme k) >>= toMPlus ) <&> fromRight Nothing
countFixme :: (DashBoardPerks m, MonadReader DashBoardEnv m) => RepoLww -> m (Maybe Int)
countFixme repo = do
runInFixme repo $ F.countFixme
& try @_ @SomeException
<&> either (const Nothing) Just
countFixmeByAttribute :: (DashBoardPerks m, MonadReader DashBoardEnv m) => RepoLww -> String -> m [(FixmeAttrVal, Int)]
countFixmeByAttribute repo name = do
runInFixme repo $ F.countByAttribute (fromString name)
& try @_ @SomeException
<&> fromRight mempty

View File

@ -1,55 +0,0 @@
{-# Language PatternSynonyms #-}
{-# Language ViewPatterns #-}
module HBS2.Git.DashBoard.Manifest where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.Data.RepoHead
import Data.Text qualified as Text
import Data.Either
import Streaming.Prelude qualified as S
pattern FixmeRefChanP :: forall {c} . PubKey Sign HBS2Basic -> Syntax c
pattern FixmeRefChanP x <- ListVal [ SymbolVal "fixme:"
, ListVal [ SymbolVal "refchan", SignPubKeyLike x
]]
pattern PinnedRefBlob :: forall {c}. Text -> Text -> GitHash -> Syntax c
pattern PinnedRefBlob syn name hash <- ListVal [ SymbolVal "blob"
, SymbolVal (Id syn)
, LitStrVal name
, asGitHash -> Just hash
]
{-# COMPLETE PinnedRefBlob #-}
asGitHash :: forall c . Syntax c -> Maybe GitHash
asGitHash = \case
LitStrVal s -> fromStringMay (Text.unpack s)
_ -> Nothing
parseManifest :: Monad m => RepoHead -> m ([Syntax C], Text)
parseManifest mhead = do
let rawManifest = maybe mempty Text.lines (_repoManifest mhead)
w <- S.toList_ do
flip fix rawManifest $ \next ss -> do
case ss of
( "" : rest ) -> S.yield (Right (Text.stripStart (Text.unlines rest)))
( a : rest ) -> S.yield (Left a ) >> next rest
[] -> pure ()
let meta = Text.unlines (lefts w)
& Text.unpack
& parseTop
& fromRight mempty
let manifest = mconcat $ rights w
pure (meta, manifest)

View File

@ -1,65 +0,0 @@
{-# LANGUAGE PatternSynonyms #-}
module HBS2.Git.DashBoard.Prelude
( module HBS2.Git.DashBoard.Prelude
, module HBS2.Prelude.Plated
, module HBS2.Data.Types.Refs
, module HBS2.Base58
, module HBS2.Merkle
, module HBS2.Net.Proto.Service
, module HBS2.Storage
, module API
, module Config
, module Logger
, module Maybe
, module Reader
, module Coerce
, module TransCont
, module TransMaybe
, module Lens.Micro.Platform
, module UnliftIO
, module Codec.Serialise
, GitRef(..), GitHash(..), GitObjectType(..)
, pattern SignPubKeyLike
, qc, q
) where
import HBS2.Data.Types.Refs
import HBS2.Base58
import HBS2.Net.Proto.Service hiding (encode,decode)
import HBS2.Prelude.Plated
import HBS2.Storage
import HBS2.Merkle
import HBS2.System.Logger.Simple.ANSI as Logger
import HBS2.Misc.PrettyStuff as Logger
import HBS2.Net.Auth.Credentials
import HBS2.Peer.RPC.API.RefChan as API
import HBS2.Peer.RPC.API.RefLog as API
import HBS2.Peer.RPC.API.Peer as API
import HBS2.Peer.RPC.API.LWWRef as API
import HBS2.Peer.Proto.RefLog as API
import HBS2.Peer.Proto.LWWRef as API
import HBS2.Peer.Proto.RefChan.Types as API
import HBS2.Peer.Proto.RefChan.RefChanUpdate as API
import HBS2.Git.Local
import Data.Config.Suckless as Config
import Text.InterpolatedString.Perl6 (qc,q)
import Data.Maybe as Maybe
import Control.Monad.Reader as Reader
import Data.Coerce as Coerce
import Control.Monad.Trans.Cont as TransCont
import Control.Monad.Trans.Maybe as TransMaybe
import Lens.Micro.Platform hiding (at)
import UnliftIO
import Codec.Serialise

View File

@ -1,162 +0,0 @@
{-# LANGUAGE TemplateHaskell #-}
module HBS2.Git.DashBoard.State.Commits where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.Local
import HBS2.Git.Local.CLI
import Data.ByteString.Lazy.Char8 qualified as LBS8
import Data.Text.Encoding qualified as Text
import Data.Text qualified as Text
import Data.Time (UTCTime,LocalTime)
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
import Data.Either
import Streaming.Prelude qualified as S
{- HLINT ignore "Functor law" -}
class Monoid a => FromQueryParams a where
fromQueryParams :: [(Text,Text)] -> a
data CommitListStyle = CommitListBrief
data SelectCommitsPred =
SelectCommitsPred
{ _commitListStyle :: CommitListStyle
, _commitPredOffset :: Int
, _commitPredLimit :: Int
, _commitRef :: Maybe GitRef
}
makeLenses ''SelectCommitsPred
instance Semigroup SelectCommitsPred where
(<>) _ b = mempty & set commitListStyle (view commitListStyle b)
& set commitPredOffset (view commitPredOffset b)
& set commitPredLimit (view commitPredLimit b)
& set commitRef (view commitRef b)
instance Monoid SelectCommitsPred where
mempty = SelectCommitsPred CommitListBrief 0 100 Nothing
briefCommits :: SelectCommitsPred
briefCommits = mempty
instance FromQueryParams SelectCommitsPred where
fromQueryParams args = do
let val = headMay [ GitRef (fromString (Text.unpack v)) | ("ref", v) <- args ]
mempty & set commitRef val
newtype Author = Author Text
deriving stock (Generic,Data)
deriving newtype (Show)
newtype CommitListItemHash = CommitListItemHash GitHash
deriving stock (Generic,Data)
deriving newtype (Show,Pretty)
newtype CommitListItemTime = CommitListItemTime Integer
deriving stock (Generic,Data)
deriving newtype (Show)
newtype CommitListItemTitle = CommitListItemTitle Text
deriving stock (Generic,Data)
deriving newtype (Show)
newtype CommitListItemAuthor = CommitListItemAuthor Author
deriving stock (Generic,Data)
deriving newtype (Show)
data CommitListItem =
CommitListItemBrief
{ commitListHash :: CommitListItemHash
, commitListTime :: CommitListItemTime
, commitListTitle :: CommitListItemTitle
, commitListAuthor :: CommitListItemAuthor
}
deriving stock (Generic,Data)
selectCommits :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> SelectCommitsPred
-> m [CommitListItem]
selectCommits lww SelectCommitsPred{..} = do
let lim = _commitPredLimit
let off = _commitPredOffset
let delim = "|||" :: Text
dir <- repoDataPath lww
let what = maybe "--all" (show . pretty) _commitRef
let cmd = case _commitListStyle of
CommitListBrief -> do
let fmt = [qc|--pretty=format:"%H{delim}%at{delim}%an{delim}%s"|] :: String
[qc|git --git-dir={dir} log {what} --max-count {lim} --skip {off} {fmt}|]
debug $ red "selectCommits" <+> pretty cmd
ls <- gitRunCommand cmd
<&> fromRight mempty
<&> LBS8.lines
<&> fmap (Text.decodeUtf8 . LBS8.toStrict)
S.toList_ do
for_ ls $ \l -> do
case Text.splitOn "|||" l of
z@[cohash,ts,au,msg] -> do
let utc = readMay @Integer (Text.unpack ts)
<&> CommitListItemTime
let hash = fromStringMay @GitHash (Text.unpack cohash)
<&> CommitListItemHash
let co = CommitListItemBrief
<$> hash
<*> utc
<*> pure (CommitListItemTitle msg)
<*> pure (CommitListItemAuthor (Author au))
maybe1 co none S.yield
_ -> none
getCommitRawBrief :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> GitHash
-> m Text
getCommitRawBrief lww hash = do
dir <- repoDataPath lww
let cmd = [qc|git --git-dir={dir} show --stat {pretty hash}|]
debug $ red "getCommitRawBrief" <+> viaShow cmd
gitRunCommand cmd
<&> fromRight mempty
<&> Text.decodeUtf8 . LBS8.toStrict
getCommitRawPatch :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> GitHash
-> m Text
getCommitRawPatch lww hash = do
dir <- repoDataPath lww
let cmd = [qc|git --git-dir={dir} show {pretty hash}|]
debug $ red "getCommitRawPatch" <+> viaShow cmd
gitRunCommand cmd
<&> fromRight mempty
<&> Text.decodeUtf8 . LBS8.toStrict

View File

@ -1,20 +0,0 @@
module HBS2.Git.DashBoard.State.Index
( module HBS2.Git.DashBoard.State.Index
, module HBS2.Git.DashBoard.State.Index.Channels
, module HBS2.Git.DashBoard.State.Index.Peer
) where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State.Index.Channels
import HBS2.Git.DashBoard.State.Index.Peer
updateIndex :: (DashBoardPerks m, MonadReader DashBoardEnv m) => m ()
updateIndex = do
debug "updateIndex"
updateIndexFromPeer
updateIndexFromChannels

View File

@ -1,75 +0,0 @@
module HBS2.Git.DashBoard.State.Index.Channels where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import DBPipe.SQLite hiding (insert)
import DBPipe.SQLite.Generic as G
import Streaming.Prelude qualified as S
updateIndexFromChannels :: (DashBoardPerks m, MonadReader DashBoardEnv m) => m ()
updateIndexFromChannels = do
debug "updateIndexChannels"
rchanAPI <- asks _refChanAPI
sto <- asks _sto
flip runContT pure do
es <- lift getIndexEntries
for_ es $ \rc -> do
callCC \next -> do
debug $ red (pretty (AsBase58 rc))
h <- lift (callRpcWaitMay @RpcRefChanGet (1 :: Timeout 'Seconds) rchanAPI rc)
<&> join
>>= maybe (next ()) pure
debug $ "rechan val" <+> red (pretty h)
txs <- S.toList_ do
walkMerkle @[HashRef] (coerce h) (getBlock sto) $ \case
Left{} -> pure ()
Right hs -> mapM_ S.yield hs
for_ txs $ \txh -> void $ runMaybeT do
done <- lift $ lift $ withState do
select @(Only Int)
[qc|select 1 from processed where hash = ? limit 1|]
(Only (TxHash txh)) <&> isJust . listToMaybe
guard (not done)
tx@GitIndexTx{..} <- getBlock sto (coerce txh)
>>= toMPlus
>>= readProposeTranMay @(GitIndexTx 'HBS2Basic) @L4Proto
>>= toMPlus
lift $ lift $ withState $ transactional do
let nm = [ RepoName n | GitIndexRepoName n <- universeBi gitIndexTxPayload ] & headMay
let bri = [ RepoBrief n | GitIndexRepoBrief n <- universeBi gitIndexTxPayload ] & headMay
insert @RepoTable $ onConflictIgnore @RepoTable (Only (RepoLww gitIndexTxRef))
insert @RepoChannelTable $
onConflictIgnore @RepoChannelTable (RepoLww gitIndexTxRef, RepoChannel rc)
-- FIXME: on-conflict-update!
for_ nm $ \n -> do
insert @RepoNameTable $
onConflictIgnore @RepoNameTable (RepoLww gitIndexTxRef, n)
for_ bri $ \n -> do
insert @RepoBriefTable $
onConflictIgnore @RepoBriefTable (RepoLww gitIndexTxRef, n)
lift $ withState $ transactional do
for_ txs $ \t -> do
insert @TxProcessedTable $ onConflictIgnore @TxProcessedTable (Only (TxHash t))

View File

@ -1,146 +0,0 @@
module HBS2.Git.DashBoard.State.Index.Peer where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import HBS2.Git.DashBoard.Manifest
import HBS2.Git.Data.LWWBlock
import HBS2.Git.Data.Tx.Git
import HBS2.Hash
import HBS2.System.Dir
import Streaming.Prelude qualified as S
import Data.HashMap.Strict qualified as HM
import System.Process.Typed
{- HLINT ignore "Functor law" -}
seconds = TimeoutSec
addRepoIndexJob :: (DashBoardPerks m, MonadReader DashBoardEnv m) => LWWRefKey 'HBS2Basic -> m ()
addRepoIndexJob lww = do
e <- ask
let wip = _repoCommitIndexWIP e
n <- atomically do
modifyTVar wip (HM.insertWith (+) (coerce lww) 1)
readTVar wip <&> HM.lookup (coerce lww) <&> fromMaybe 0
when ( n < 2 ) do
addJob $ withDashBoardEnv e do
buildCommitTreeIndex (coerce lww)
`finally` do
atomically do
modifyTVar wip (HM.adjust pred (coerce lww))
updateFixmeFor :: ( MonadUnliftIO m
, MonadReader DashBoardEnv m
)
=> RepoLww
-> MyRefChan
-> m ()
updateFixmeFor (RepoLww lw) f = do
p <- fixmeDataPath f
debug $ red "UPDATE-FIXME-FOR" <+> pretty (AsBase58 lw) <+> pretty (AsBase58 f) <+> pretty p
let rcp = show $ pretty (AsBase58 f)
mkdir p
let cmdStr = [qc|fixme-new refchan {rcp} and fixme:refchan:import|]
let cmd = shell cmdStr & setWorkingDir p
debug $ "run fixme for:" <+> pretty rcp <+> pretty cmdStr
void $ runProcess cmd
updateIndexFromPeer :: (DashBoardPerks m, MonadReader DashBoardEnv m) => m ()
updateIndexFromPeer = do
debug "updateIndexFromPeer"
peer <- asks _peerAPI
reflog <- asks _refLogAPI
lwwAPI <- asks _lwwRefAPI
sto <- asks _sto
polls <- callRpcWaitMay @RpcPollList2 (TimeoutSec 1) peer (Just "lwwref", Nothing)
<&> join . maybeToList
<&> fmap (LWWRefKey @HBS2Basic . view _1)
repos <- S.toList_ $ forM_ polls $ \r -> void $ runMaybeT do
lwval <- liftIO (callRpcWaitMay @RpcLWWRefGet (seconds 1) lwwAPI r)
>>= toMPlus >>= toMPlus
(lw,blk) <- readLWWBlock sto r >>= toMPlus
let rk = lwwRefLogPubKey blk
lift $ S.yield (r,lwval,RefLogKey @'HBS2Basic rk,blk)
for_ repos $ \(lw,wv,rk,LWWBlockData{..}) -> do
mhead <- callRpcWaitMay @RpcRefLogGet (TimeoutSec 1) reflog (coerce rk)
<&> join
for_ mhead $ \mh -> do
txs <- S.toList_ $ do
walkMerkle @[HashRef] (fromHashRef mh) (getBlock sto) $ \case
Left{} -> do
pure ()
Right hxs -> do
for_ hxs $ \htx -> void $ runMaybeT do
done <- lift $ withState $ isProcessed (HashRef $ hashObject @HbSync (serialise (lw,htx)))
guard (not done)
getBlock sto (fromHashRef htx) >>= toMPlus
<&> deserialiseOrFail @(RefLogUpdate L4Proto)
>>= toMPlus
>>= unpackTx
>>= \(n,h,blk) -> lift (S.yield (n,htx,blk))
headz <- S.toList_ do
for_ txs $ \(n,tx,blk) -> void $ runMaybeT do
(rhh, rhead) <- readRepoHeadFromTx sto tx >>= toMPlus
debug $ yellow "found repo head" <+> pretty rhh <+> pretty "for" <+> pretty lw
(man, _) <- parseManifest rhead
let fme = headMay [ x | FixmeRefChanP x <- man ]
lift $ S.yield (lw, RepoHeadTx tx, RepoHeadRef rhh, rhead, fme)
withState $ transactional do
-- withState do
for_ headz $ \(l, tx, rh, rhead, fme) -> do
let rlwwseq = RepoLwwSeq (fromIntegral $ lwwSeq wv)
insertRepoHead l rlwwseq (RepoRefLog rk) tx rh rhead
insertProcessed (HashRef $ hashObject @HbSync (serialise (l,coerce @_ @HashRef tx)))
for_ fme $ \f -> do
insertRepoFixme l rlwwseq f
-- WTF?
env <- ask
buildCommitTreeIndex (coerce lw)
fxe <- selectRepoFixme
for_ fxe $ \(r,f) -> do
allowed <- checkFixmeAllowed r
when allowed do
env <-ask
addJob (withDashBoardEnv env $ updateFixmeFor r f)

View File

@ -1,174 +0,0 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# Language UndecidableInstances #-}
{-# Language AllowAmbiguousTypes #-}
{-# Language TemplateHaskell #-}
module HBS2.Git.DashBoard.Types
( module HBS2.Git.DashBoard.Types
, module HBS2.Git.Data.Tx.Index
) where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.Data.Tx.Index
import HBS2.Net.Messaging.Unix
import DBPipe.SQLite
import HBS2.System.Dir
import System.FilePath
import Data.Word
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HM
import Data.Text qualified as Text
type MyRefChan = RefChanId L4Proto
type MyRefLogKey = RefLogKey 'HBS2Basic
data HttpPortOpt
data DevelopAssetsOpt
instance HasCfgKey HttpPortOpt a where
key = "port"
instance HasCfgKey DevelopAssetsOpt a where
key = "develop-assets"
data RunDashBoardOpts = RunDashBoardOpts
{ configPath :: Maybe FilePath }
instance Monoid RunDashBoardOpts where
mempty = RunDashBoardOpts Nothing
instance Semigroup RunDashBoardOpts where
(<>) _ b = RunDashBoardOpts { configPath = configPath b }
data DashBoardEnv =
DashBoardEnv
{ _peerAPI :: ServiceCaller PeerAPI UNIX
, _refLogAPI :: ServiceCaller RefLogAPI UNIX
, _refChanAPI :: ServiceCaller RefChanAPI UNIX
, _lwwRefAPI :: ServiceCaller LWWRefAPI UNIX
, _sto :: AnyStorage
, _dataDir :: FilePath
, _db :: TVar (Maybe DBPipeEnv)
, _pipeline :: TQueue (IO ())
, _dashBoardHttpPort :: TVar (Maybe Word16)
, _dashBoardDevAssets :: TVar (Maybe FilePath)
, _dashBoardBaseUrl :: TVar (Maybe Text)
, _dashBoardIndexIgnoreCaches :: TVar Bool
, _repoCommitIndexWIP :: TVar (HashMap (LWWRefKey 'HBS2Basic) Int)
}
makeLenses 'DashBoardEnv
repoDataPath :: (DashBoardPerks m, MonadReader DashBoardEnv m) => LWWRefKey 'HBS2Basic -> m FilePath
repoDataPath lw = asks _dataDir <&> (</> (show $ pretty lw)) >>= canonicalizePath
fixmeDataPath :: (DashBoardPerks m, MonadReader DashBoardEnv m) => MyRefChan -> m FilePath
fixmeDataPath rchan = asks _dataDir <&> (</> (show $ "fixme-" <> pretty (AsBase58 rchan))) >>= canonicalizePath
type DashBoardPerks m = MonadUnliftIO m
newtype DashBoardM m a = DashBoardM { fromDashBoardM :: ReaderT DashBoardEnv m a }
deriving newtype
( Applicative
, Functor
, Monad
, MonadIO
, MonadUnliftIO
, MonadTrans
, MonadReader DashBoardEnv
)
newDashBoardEnv :: MonadIO m
=> FilePath
-> ServiceCaller PeerAPI UNIX
-> ServiceCaller RefLogAPI UNIX
-> ServiceCaller RefChanAPI UNIX
-> ServiceCaller LWWRefAPI UNIX
-> AnyStorage
-> m DashBoardEnv
newDashBoardEnv ddir peer rlog rchan lww sto = do
DashBoardEnv peer rlog rchan lww sto ddir
<$> newTVarIO mzero
<*> newTQueueIO
<*> newTVarIO (Just 8911)
<*> newTVarIO Nothing
<*> newTVarIO Nothing
<*> newTVarIO False
<*> newTVarIO mempty
getHttpPortNumber :: (MonadIO m, MonadReader DashBoardEnv m, Integral a) => m a
getHttpPortNumber = do
asks _dashBoardHttpPort
>>= readTVarIO
<&> fromIntegral . fromMaybe 8911
getDevAssets :: (MonadIO m, MonadReader DashBoardEnv m, Integral a) => m (Maybe FilePath)
getDevAssets = do
asks _dashBoardDevAssets
>>= readTVarIO
getIgnoreCaches :: (MonadIO m, MonadReader DashBoardEnv m, Integral a) => m Bool
getIgnoreCaches = do
asks _dashBoardIndexIgnoreCaches
>>= readTVarIO
asksBaseUrl :: (MonadIO m, MonadReader DashBoardEnv m) => (Text -> m a) -> m a
asksBaseUrl thingInside = do
mUrl <- readTVarIO =<< asks _dashBoardBaseUrl
thingInside (fromMaybe (Text.pack "") mUrl)
withDashBoardEnv :: Monad m => DashBoardEnv -> DashBoardM m a -> m a
withDashBoardEnv env m = runReaderT (fromDashBoardM m) env
data StateFSM m a =
S0
| SConnect
withState :: forall m a . (MonadIO m, MonadReader DashBoardEnv m) => DBPipeM m a -> m a
withState f = do
dbFile <- asks _dataDir <&> (</> "state.db")
tdb <- asks _db
flip fix S0 $ \next -> \case
SConnect -> do
notice $ yellow "connecting to db"
dbe <- liftIO $ try @_ @SomeException (newDBPipeEnv (dbPipeOptsDef {dbPipeBatchTime = 1}) dbFile)
case dbe of
Right e -> do
atomically $ writeTVar tdb (Just e)
next S0
Left what -> do
err $ viaShow what
pause @Seconds 1
next SConnect
S0 -> do
dbe <- readTVarIO tdb
case dbe of
Just d -> withDB d f
Nothing -> next SConnect
addJob :: (DashBoardPerks m, MonadReader DashBoardEnv m) => IO () -> m ()
addJob f = do
q <- asks _pipeline
atomically $ writeTQueue q f
hbs2_git_dashboard :: FilePath
hbs2_git_dashboard = "hbs2-git-dashboard"

View File

@ -1,102 +0,0 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE CPP #-}
module HBS2.Git.Web.Html.Fixme where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import HBS2.Git.DashBoard.Fixme as Fixme
import HBS2.Git.Web.Html.Types
import Data.Map qualified as Map
import Lucid.Base
import Lucid.Html5 hiding (for_)
import Lucid.Htmx
import Data.Word
import Data.List qualified as List
import Web.Scotty.Trans as Scotty
#if __GLASGOW_HASKELL__ < 906
import Control.Applicative -- add liftA2 into scope
#endif
instance ToHtml (H FixmeKey) where
toHtmlRaw (H k) = toHtmlRaw $ take 10 $ show $ pretty k
toHtml (H k) = toHtml $ take 10 $ show $ pretty k
instance ToHtml (H FixmeTag) where
toHtmlRaw (H k) = toHtmlRaw $ coerce @_ @Text k
toHtml (H k) = toHtml $ coerce @_ @Text k
instance ToHtml (H FixmeTitle) where
toHtmlRaw (H k) = toHtmlRaw $ coerce @_ @Text k
toHtml (H k) = toHtml $ coerce @_ @Text k
repoFixme :: ( MonadReader DashBoardEnv m
, DashBoardPerks m
, HasLimit q
, HasPredicate q
, q ~ FromParams 'FixmeDomain [Param]
)
=> q
-> LWWRefKey HBS2Basic
-> HtmlT m ()
repoFixme q@(FromParams p') lww = asksBaseUrl $ withBaseUrl do
let p = Map.fromList p'
now <- liftIO $ getPOSIXTime <&> round
debug $ blue "repoFixme" <+> "LIMITS" <+> viaShow (limit q)
let offset = maybe 0 fst (limit q)
fme <- lift $ listFixme (RepoLww lww) (Reversed q)
for_ fme $ \fixme -> do
tr_ [class_ "commit-brief-title"] $ do
td_ [class_ "mono", width_ "10"] do
a_ [ href_ (toBaseURL (IssuePage (RepoLww lww) (fixmeKey fixme)))
] $ toHtml (H $ fixmeKey fixme)
td_ [width_ "10"] do
strong_ [] $ toHtml (H $ fixmeTag fixme)
td_ [] do
toHtml (H $ fixmeTitle fixme)
tr_ [class_ "commit-brief-details"] $ do
td_ [colspan_ "3"] do
let mco = fixmeGet "commit-time" fixme & pretty & show & readMay @Word64
let mw = fixmeGet "workflow" fixme <&> coerce @_ @Text
let cla = fixmeGet "class" fixme <&> coerce @_ @Text
let mn = liftA2 (-) (fixmeEnd fixme) (fixmeStart fixme)
small_ do
for_ mw $ \w -> do
span_ [] (toHtml $ show $ brackets $ pretty w)
" "
for_ mco $ \co ->
span_ [] $ toHtml $ show $ brackets ("commited" <+> pretty (agePure co now))
for_ cla $ \c ->
span_ [] $ toHtml $ show $ brackets (pretty c)
for_ mn $ \n -> do
when (n > 0) do
span_ [] $ toHtml $ show $ brackets ("text:" <+> pretty n)
unless (List.null fme) do
tr_ [ class_ "commit-brief-last"
, hxGet_ (toBaseURL (Paged (offset + fromIntegral fixmePageSize) (RepoFixmeHtmx p (RepoLww lww))))
, hxTrigger_ "revealed"
, hxSwap_ "afterend"
] do
td_ [colspan_ "3"] mempty

View File

@ -1,156 +0,0 @@
{-# LANGUAGE CPP #-}
module HBS2.Git.Web.Html.Issue (issuePage) where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import HBS2.Git.DashBoard.Fixme as Fixme
import HBS2.OrDie
import HBS2.Git.Web.Assets
import HBS2.Git.Web.Html.Types
import HBS2.Git.Web.Html.Root
import HBS2.Git.Web.Html.Markdown
import HBS2.Git.Web.Html.Fixme()
import HBS2.Git.Web.Html.Parts.Blob
import Data.Text qualified as Text
import Lucid.Base
import Lucid.Html5 hiding (for_)
#if __GLASGOW_HASKELL__ < 906
import Control.Applicative -- add liftA2 into scope
#endif
data IssueOptionalArg w t = IssueOptionalArg w t
issueOptionalArg :: Fixme -> FixmeAttrName -> IssueOptionalArg Fixme FixmeAttrName
issueOptionalArg = IssueOptionalArg
instance ToHtml (IssueOptionalArg Fixme FixmeAttrName) where
toHtml (IssueOptionalArg fxm n) = do
for_ (fixmeGet n fxm) $ \t -> do
tr_ do
td_ [class_ "whitespace-nowrap"] $ strong_ (toHtml $ show $ pretty n)
td_ [class_ "w-full"] (toHtml $ show $ pretty t)
toHtmlRaw = toHtml
issuePage :: (MonadIO m, DashBoardPerks m, MonadReader DashBoardEnv m)
=> RepoLww
-> FixmeKey
-> HtmlT m ()
issuePage repo@(RepoLww lww) f = asksBaseUrl $ withBaseUrl $ rootPage do
ti@TopInfoBlock{} <- lift $ getTopInfoBlock (coerce repo)
fxm <- lift (getFixme repo f)
>>= orThrow (itemNotFound f)
let txt = fixmePlain fxm & fmap coerce & Text.intercalate "\n"
let mbFile = fixmeGet "file" fxm
mbBlob <- runMaybeT do
blobHashText <- fixmeGet "blob" fxm & toMPlus
debug $ red "BLOB HASH TEXT" <+> pretty blobHashText
hash <- coerce blobHashText
& Text.unpack
& fromStringMay @GitHash
& toMPlus
debug $ red "BLOB" <+> pretty hash
lift (lift $ selectBlobInfo (BlobHash hash))
>>= toMPlus
debug $ "BLOB INFO" <> line <> pretty (fmap blobHash mbBlob)
main_ [class_ "container-fluid"] do
div_ [class_ "wrapper"] do
aside_ [class_ "sidebar"] do
-- issuesSidebar (coerce repo) ti mempty
repoTopInfoBlock (coerce repo) ti
div_ [class_ "content"] $ do
nav_ [class_ "mb-1"] do
div_ do
small_ do
a_ [ href_ (toBaseURL (RepoPage IssuesTab lww))
] do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconArrowUturnLeft
span_ [] "back to issues"
article_ [class_ "issue-info-card"] do
header_ do
h5_ do
toHtml (coerce @_ @Text $ fixmeTag fxm)
" "
span_ [class_ "font-normal"] do
let fkKey = coerce @_ @Text $ fixmeKey fxm
span_ [ class_ "issue-id secondary"
, data_ "tooltip" "Copy"
, onClickCopyText $ Text.take 10 fkKey
] $ toHtml (H $ fixmeKey fxm)
" "
toHtml (coerce @_ @Text $ fixmeTitle fxm)
div_ [class_ "overflow-x-auto"] $ table_ [class_ "issue-info-table mb-0"] do
toHtml (issueOptionalArg fxm "workflow")
toHtml (issueOptionalArg fxm "class")
toHtml (issueOptionalArg fxm "assigned")
toHtml (issueOptionalArg fxm "scope")
toHtml (issueOptionalArg fxm "committer-name")
toHtml (issueOptionalArg fxm "commit")
maybe1 mbFile none $ \file -> do
tr_ do
th_ $ strong_ [] $ "file"
case mbBlob of
Nothing -> do
td_ do
toHtml $ show $ pretty file
Just (BlobInfo{}) -> do
td_ do
a_ [ href_ "#"
, hyper_ "on click toggle .hidden on #issue-blob"
] do
toHtml $ show $ pretty file
-- toHtml (issueOptionalArg fxm "file")
section_ [class_ "lim-text"] do
toHtmlRaw $ renderMarkdown txt
let s0 = fixmeStart fxm
let e0 = fixmeEnd fxm
let n = liftA2 (-) e0 s0 & fromMaybe 0
let hide = if n > 3 then "hidden" else ""
section_ [id_ "issue-blob", class_ hide ] $ void $ runMaybeT do
blob <- toMPlus mbBlob
s <- s0 & toMPlus <&> fromIntegral
e <- e0 & toMPlus <&> fromIntegral
let before = max 0 (s - 2)
let seize = max 1 (e - s + 100)
debug $ "PREPROCESS BLOB" <+> pretty before <+> pretty seize
lift $ doRenderBlob' (pure mempty) (trim before seize) lww blob
where
trim before seize txt =
Text.lines txt & drop before & take seize & Text.unlines

View File

@ -1,24 +0,0 @@
module HBS2.Git.Web.Html.Markdown where
import HBS2.Git.DashBoard.Prelude
import Data.Text qualified as Text
import Lucid.Base
import Lucid.Html5 hiding (for_)
import Text.Pandoc hiding (getPOSIXTime)
markdownToHtml :: Text -> Either PandocError String
markdownToHtml markdown = runPure $ do
doc <- readMarkdown def {readerExtensions = pandocExtensions} markdown
html <- writeHtml5String def {writerExtensions = pandocExtensions} doc
return $ Text.unpack html
renderMarkdown' :: Text -> Text
renderMarkdown' markdown = case markdownToHtml markdown of
Left{} -> markdown
Right html -> Text.pack html
renderMarkdown :: Text -> Html ()
renderMarkdown markdown = case markdownToHtml markdown of
Left{} -> blockquote_ (toHtml markdown)
Right html -> toHtmlRaw $ Text.pack html

View File

@ -1,79 +0,0 @@
module HBS2.Git.Web.Html.Parts.Blob where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.State
import HBS2.Git.DashBoard.Types
import HBS2.Git.Web.Html.Markdown
import Data.ByteString.Lazy qualified as LBS
import Data.Text.Encoding qualified as Text
import Lucid.Base
import Lucid.Html5 hiding (for_)
import Skylighting qualified as Sky
import Skylighting.Tokenizer
import Skylighting.Format.HTML.Lucid as Lucid
import Control.Applicative
{-HLINT ignore "Functor law"-}
doRenderBlob :: (MonadReader DashBoardEnv m, MonadUnliftIO m)
=> (Text -> HtmlT m ())
-> LWWRefKey HBS2Basic
-> BlobInfo
-> HtmlT m ()
doRenderBlob fallback = doRenderBlob' fallback id
doRenderBlob' :: (MonadReader DashBoardEnv m, MonadUnliftIO m)
=> (Text -> HtmlT m ())
-> (Text -> Text)
-> LWWRefKey HBS2Basic
-> BlobInfo
-> HtmlT m ()
doRenderBlob' fallback preprocess lww BlobInfo{..} = do
fromMaybe mempty <$> runMaybeT do
guard (blobSize < 10485760)
let fn = blobName & coerce
let syntaxMap = Sky.defaultSyntaxMap
syn <- ( Sky.syntaxesByFilename syntaxMap fn
& headMay
) <|> Sky.syntaxByName syntaxMap "default"
& toMPlus
lift do
txt <- lift (readBlob lww blobHash)
<&> LBS.toStrict
<&> Text.decodeUtf8
case blobSyn of
BlobSyn (Just "markdown") -> do
div_ [class_ "lim-text"] do
toHtmlRaw (renderMarkdown' txt)
_ -> do
txt <- lift (readBlob lww blobHash)
<&> LBS.toStrict
<&> Text.decodeUtf8
<&> preprocess
let config = TokenizerConfig { traceOutput = False, syntaxMap = syntaxMap }
case tokenize config syn txt of
Left _ -> fallback txt
Right tokens -> do
let fo = Sky.defaultFormatOpts { Sky.numberLines = False, Sky.ansiColorLevel = Sky.ANSI256Color }
let code = renderText (Lucid.formatHtmlBlock fo tokens)
toHtmlRaw code

View File

@ -1,105 +0,0 @@
module HBS2.Git.Web.Html.Parts.Issues.Sidebar where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import HBS2.Git.DashBoard.Fixme as Fixme
import HBS2.Git.Web.Html.Types
import HBS2.Git.Web.Html.Parts.TopInfoBlock
import Data.Map qualified as Map
import Lucid.Base
import Lucid.Html5 hiding (for_)
import Lucid.Htmx
issuesSidebar :: (MonadIO m, DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> TopInfoBlock
-> [(Text,Text)]
-> HtmlT m ()
issuesSidebar lww topInfoBlock p' = asksBaseUrl $ withBaseUrl do
let p = Map.fromList p'
tot <- lift $ countFixme (RepoLww lww)
fmw <- lift $ countFixmeByAttribute (RepoLww lww) "workflow"
fmt <- lift $ countFixmeByAttribute (RepoLww lww) "fixme-tag"
ass <- lift $ countFixmeByAttribute (RepoLww lww) "assigned"
cla <- lift $ countFixmeByAttribute (RepoLww lww) "class"
repoTopInfoBlock lww topInfoBlock
div_ [class_ "info-block" ] do
summary_ [class_ "sidebar-title"] $ small_ $ strong_ "Tag"
-- TODO: make-this-block-properly
ul_ do
for_ fmt $ \(s,n) -> do
li_ [] $ small_ [] do
a_ [ class_ "secondary"
, hxGet_ (toBaseURL (Paged 0 (RepoFixmeHtmx (Map.insert "fixme-tag" (coerce s) p) (RepoLww lww))))
, hxTarget_ "#fixme-tab-data"
] do
span_ [style_ "display: inline-block; width: 4ch; text-align: right; padding-right: 0.5em;"] $
toHtml $ show $ pretty n
span_ [] $ toHtml $ show $ pretty s
summary_ [class_ "sidebar-title"] $ small_ $ strong_ "Status"
ul_ do
li_ [] $ small_ [] do
a_ [ class_ "secondary"
, hxGet_ (toBaseURL (Paged 0 (RepoFixmeHtmx (Map.delete "workflow" p) (RepoLww lww))))
, hxTarget_ "#fixme-tab-data"
] do
span_ [style_ "display: inline-block; width: 4ch; text-align: right; padding-right: 0.5em;"] $
toHtml $ show $ pretty (fromMaybe 0 tot)
span_ [] $ toHtml $ show $ pretty "[all]"
for_ fmw $ \(s,n) -> do
li_ [] $ small_ [] do
a_ [ class_ "secondary"
, hxGet_ (toBaseURL (Paged 0 (RepoFixmeHtmx (Map.insert "workflow" (coerce s) p) (RepoLww lww))))
, hxTarget_ "#fixme-tab-data"
] do
span_ [style_ "display: inline-block; width: 4ch; text-align: right; padding-right: 0.5em;"] $
toHtml $ show $ pretty n
span_ [] $ toHtml $ show $ pretty s
summary_ [class_ "sidebar-title"] $ small_ $ strong_ "Assigned"
for_ ass $ \(s,n) -> do
li_ [] $ small_ [] do
a_ [ class_ "secondary"
, hxGet_ (toBaseURL (Paged 0 (RepoFixmeHtmx (Map.insert "assigned" (coerce s) p) (RepoLww lww))))
, hxTarget_ "#fixme-tab-data"
] do
span_ [style_ "display: inline-block; width: 4ch; text-align: right; padding-right: 0.5em;"] $
toHtml $ show $ pretty n
span_ [] $ toHtml $ show $ pretty s
summary_ [class_ "sidebar-title"] $ small_ $ strong_ "Class"
for_ cla $ \(s,n) -> do
li_ [] $ small_ [] do
a_ [ class_ "secondary"
, hxGet_ (toBaseURL (Paged 0 (RepoFixmeHtmx (Map.insert "class" (coerce s) p) (RepoLww lww))))
, hxTarget_ "#fixme-tab-data"
] do
span_ [style_ "display: inline-block; width: 4ch; text-align: right; padding-right: 0.5em;"] $
toHtml $ show $ pretty n
span_ [] $ toHtml $ show $ pretty s
pure ()

View File

@ -1,152 +0,0 @@
module HBS2.Git.Web.Html.Parts.TopInfoBlock where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import HBS2.Git.DashBoard.Manifest
import HBS2.Git.DashBoard.Fixme as Fixme
import HBS2.OrDie
import HBS2.Git.Data.Tx.Git
import HBS2.Git.Web.Assets
import HBS2.Git.Web.Html.Types
import Data.Text qualified as Text
import Lucid.Base
import Lucid.Html5 hiding (for_)
data TopInfoBlock =
TopInfoBlock
{ author :: Maybe Text
, public :: Maybe Text
, forksNum :: RepoForks
, commitsNum :: RepoCommitsNum
, manifest :: Text
, fixme :: Maybe MyRefChan
, fixmeCnt :: Int
, pinned :: [(Text, Syntax C)]
, repoHeadRef :: RepoHeadRef
, repoHead :: Maybe RepoHead
, repoName :: RepoName
}
repoTopInfoBlock :: (MonadIO m, DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> TopInfoBlock
-> HtmlT m ()
repoTopInfoBlock lww TopInfoBlock{..} = asksBaseUrl $ withBaseUrl do
div_ [class_ "info-block" ] do
let url = toBaseURL (RepoPage (CommitsTab Nothing) lww)
let txt = toHtml (ShortRef lww)
a_ [href_ url, class_ "secondary"] txt
div_ [class_ "info-block" ] do
summary_ [class_ "sidebar-title"] $ small_ $ strong_ "About"
ul_ [class_ "mb-0"] do
for_ author $ \a -> do
li_ $ small_ do
"Author: "
toHtml a
for_ public $ \p -> do
li_ $ small_ do
"Public: "
toHtml p
when (Text.length manifest > 100) do
li_ $ small_ do
a_ [class_ "secondary", href_ (toBaseURL (RepoPage ManifestTab lww))] do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconLicense
"Manifest"
for_ fixme $ \_ -> do
li_ $ small_ do
a_ [ class_ "secondary"
, href_ (toBaseURL (RepoPage IssuesTab lww)) ] do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconFixme
toHtml $ show fixmeCnt
" Issues"
when (forksNum > 0) do
li_ $ small_ do
a_ [class_ "secondary"
, href_ (toBaseURL (RepoPage ForksTab lww))
] do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconGitFork
toHtml $ show forksNum
" forks"
li_ $ small_ do
a_ [class_ "secondary"
, href_ (toBaseURL (RepoPage (CommitsTab Nothing) lww))
] do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconGitCommit
toHtml $ show commitsNum
" commits"
for_ pinned $ \(_,ref) -> do
case ref of
PinnedRefBlob s n hash -> small_ do
li_ $ a_ [class_ "secondary"
, href_ (toBaseURL (RepoPage (PinnedTab (Just (s,n,hash))) lww))
] do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconPinned
toHtml (Text.take 12 n)
" "
toHtml $ ShortRef hash
parsedManifest :: (DashBoardPerks m, MonadReader DashBoardEnv m) => RepoListItem -> m ([Syntax C], Text)
parsedManifest RepoListItem{..} = do
sto <- asks _sto
mhead <- readRepoHeadFromTx sto (coerce rlRepoTx)
case mhead of
Just x -> parseManifest (snd x)
Nothing -> pure (mempty, coerce rlRepoBrief)
getTopInfoBlock :: ( MonadUnliftIO m, MonadIO m
, MonadReader DashBoardEnv m
)
=> LWWRefKey HBS2Basic -> m TopInfoBlock
getTopInfoBlock lww = do
debug $ red "getTopInfoBlock"
it@RepoListItem{..} <- (selectRepoList ( mempty
& set repoListByLww (Just lww)
& set repoListLimit (Just 1))
<&> listToMaybe
) >>= orThrow (itemNotFound lww)
sto <- asks _sto
mhead <- readRepoHeadFromTx sto (coerce rlRepoTx)
let repoHead = snd <$> mhead
(meta, manifest) <- parsedManifest it
let author = headMay [ s | ListVal [ SymbolVal "author:", LitStrVal s ] <- meta ]
let public = headMay [ s | ListVal [ SymbolVal "public:", SymbolVal (Id s) ] <- meta ]
let pinned = [ (name,r) | ListVal [ SymbolVal "pinned:", r@(PinnedRefBlob _ name _) ] <- meta ] & take 5
allowed <- checkFixmeAllowed (RepoLww lww)
let fixme = headMay [ x | allowed, FixmeRefChanP x <- meta ]
fixmeCnt <- if allowed then
Fixme.countFixme (RepoLww lww) <&> fromMaybe 0
else
pure 0
let forksNum = rlRepoForks
let commitsNum = rlRepoCommits
let repoHeadRef = rlRepoHead
let repoName = rlRepoName
pure $ TopInfoBlock{..}

View File

@ -1,593 +0,0 @@
{-# Language MultiWayIf #-}
module HBS2.Git.Web.Html.Repo where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import HBS2.Git.DashBoard.State.Commits
import HBS2.Git.DashBoard.Manifest
import HBS2.OrDie
import HBS2.Git.Data.Tx.Git
import HBS2.Git.Data.RepoHead
import HBS2.Git.Web.Assets
import HBS2.Git.Web.Html.Types
import HBS2.Git.Web.Html.Root
import HBS2.Git.Web.Html.Markdown
import HBS2.Git.Web.Html.Parts.Issues.Sidebar
import HBS2.Git.Web.Html.Parts.Blob
import Data.Map qualified as Map
import Data.Text qualified as Text
import Lucid.Base
import Lucid.Html5 hiding (for_)
import Lucid.Htmx
import Skylighting qualified as Sky
import Skylighting.Tokenizer
import Skylighting.Format.HTML.Lucid as Lucid
import Data.Either
import Data.List qualified as List
import Data.List (sortOn)
import Streaming.Prelude qualified as S
isActiveTab :: RepoPageTabs -> RepoPageTabs -> Bool
isActiveTab a b = case (a,b) of
(CommitsTab{},CommitsTab{}) -> True
(ManifestTab{},ManifestTab{}) -> True
(TreeTab{},TreeTab{}) -> True
_ -> False
repoPage :: (MonadIO m, DashBoardPerks m, MonadReader DashBoardEnv m)
=> RepoPageTabs
-> LWWRefKey 'HBS2Basic
-> [(Text,Text)]
-> HtmlT m ()
repoPage IssuesTab lww p' = asksBaseUrl $ withBaseUrl $ rootPage do
ti@TopInfoBlock{..} <- lift $ getTopInfoBlock lww
main_ [class_ "container-fluid"] do
div_ [class_ "wrapper"] do
aside_ [class_ "sidebar"] do
issuesSidebar lww ti p'
div_ [class_ "content"] $ do
section_ do
h5_ $ toHtml (show $ "Issues ::" <+> pretty repoName)
form_ [role_ "search"] do
input_ [name_ "search", type_ "search"]
input_ [type_ "submit", value_ "Search"]
table_ [] do
tbody_ [id_ "fixme-tab-data"] mempty
div_ [ id_ "repo-tab-data"
, hxTrigger_ "load"
, hxTarget_ "#fixme-tab-data"
, hxGet_ (toBaseURL (RepoFixmeHtmx mempty (RepoLww lww)))
] mempty
div_ [id_ "repo-tab-data-embedded"] mempty
repoPage tab lww params = asksBaseUrl $ withBaseUrl $ rootPage do
sto <- asks _sto
topInfoBlock@TopInfoBlock{..} <- lift $ getTopInfoBlock lww
main_ [class_ "container-fluid"] do
div_ [class_ "wrapper"] do
aside_ [class_ "sidebar"] do
repoTopInfoBlock lww topInfoBlock
for_ repoHead $ \rh -> do
let theHead = headMay [ v | (GitRef "HEAD", v) <- view repoHeadRefs rh ]
let checkHead v what | v == theHead = strong_ what
| otherwise = what
div_ [class_ "info-block" ] do
summary_ [class_ "sidebar-title"] $ small_ $ strong_ "Heads"
ul_ [class_ "mb-0"] $ do
for_ (view repoHeadHeads rh) $ \(branch,v) -> do
li_ $ small_ do
a_ [class_ "secondary", href_ (toBaseURL (RepoPage (CommitsTab (Just v)) lww ))] do
checkHead (Just v) $ toHtml branch
div_ [class_ "info-block" ] do
summary_ [class_ "sidebar-title"] $ small_ $ strong_ "Tags"
ul_ [class_ "mb-0"] $ do
for_ (view repoHeadTags rh) $ \(tag,v) -> do
li_ $ small_ do
a_ [class_ "secondary", href_ (toBaseURL (RepoPage (CommitsTab (Just v)) lww ))] do
checkHead (Just v) $ toHtml tag
div_ [class_ "content"] $ do
article_ [class_ "py-0"] $ nav_ [ariaLabel_ "breadcrumb", class_ "repo-menu"] $ ul_ do
let menuTabClasses isActive = if isActive then "tab contrast" else "tab"
menuTab t misc name = li_ do
a_ ([class_ $ menuTabClasses $ isActiveTab tab t] <> misc <> [tabClick]) do
name
menuTab (CommitsTab Nothing)
[ href_ "#"
, hxGet_ (toBaseURL (RepoCommits lww))
, hxTarget_ "#repo-tab-data"
] "commits"
menuTab (TreeTab Nothing)
[ href_ "#"
, hxGet_ (toBaseURL (RepoRefs lww))
, hxTarget_ "#repo-tab-data"
] "tree"
section_ do
strong_ $ toHtml repoName
div_ [id_ "repo-tab-data"] do
case tab of
TreeTab{} -> do
let tree = [ fromStringMay @GitHash (Text.unpack v)
| ("tree", v) <- params
] & catMaybes & headMay
maybe (repoRefs lww) (\t -> repoTree lww t t) tree
ManifestTab -> do
for_ repoHead $ thisRepoManifest
CommitsTab{} -> do
let predicate = Right (fromQueryParams params)
repoCommits lww predicate
ForksTab -> do
repoForks lww
PinnedTab w -> do
pinned' <- S.toList_ $ for_ pinned $ \(_,ref) -> case ref of
PinnedRefBlob s n hash -> do
S.yield (hash, (s,n))
let pinned = Map.fromList pinned'
void $ runMaybeT do
ref <- [ fromStringMay @GitHash (Text.unpack v)
| ("ref", v) <- params
] & catMaybes
& headMay
& toMPlus
(s,n) <- Map.lookup ref pinned & toMPlus
lift $ repoSomeBlob lww s ref
mempty
div_ [id_ "repo-tab-data-embedded"] mempty
thisRepoManifest :: (DashBoardPerks m, MonadReader DashBoardEnv m) => RepoHead -> HtmlT m ()
thisRepoManifest rh = do
(_, man) <- lift $ parseManifest rh
div_ [class_ "lim-text"] $ toHtmlRaw (renderMarkdown' man)
repoRefs :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> HtmlT m ()
repoRefs lww = asksBaseUrl $ withBaseUrl do
refs <- lift $ gitShowRefs lww
table_ [] do
for_ refs $ \(r,h) -> do
let r_ = Text.pack $ show $ pretty r
let co = show $ pretty h
let uri = toBaseURL (RepoTree lww h h)
let showRef = Text.isPrefixOf "refs" r_
when showRef do
tr_ do
td_ do
if | Text.isPrefixOf "refs/heads" r_ -> do
svgIcon IconGitBranch
| Text.isPrefixOf "refs/tags" r_ -> do
svgIcon IconTag
| otherwise -> mempty
td_ (toHtml r_)
td_ [class_ "mono"] $ do
a_ [ href_ "#"
, hxGet_ uri
, hxTarget_ "#repo-tab-data"
] (toHtml $ show $ pretty h)
treeLocator :: (WithBaseUrl, DashBoardPerks m)
=> LWWRefKey 'HBS2Basic
-> GitHash
-> TreeLocator
-> HtmlT m ()
-> HtmlT m ()
treeLocator lww co locator next = do
let repo = show $ pretty $ lww
let co_ = show $ pretty co
let prefixSlash x = if fromIntegral x > 1 then span_ "/" else ""
let showRoot =
[ hxGet_ (toBaseURL (RepoTree lww co co))
, hxTarget_ "#repo-tab-data"
, href_ "#"
]
span_ [] $ a_ [ hxGet_ (toBaseURL (RepoRefs lww))
, hxTarget_ "#repo-tab-data"
, href_ "#"
] $ toHtml (take 10 repo <> "..")
span_ [] "/"
span_ [] $ a_ showRoot $ toHtml (take 10 co_ <> "..")
unless (List.null locator) do
span_ [] "/"
for_ locator $ \(_,this,level,name) -> do
prefixSlash level
let uri = toBaseURL (RepoTree lww co (coerce @_ @GitHash this))
span_ [] do
a_ [ href_ "#"
, hxGet_ uri
, hxTarget_ "#repo-tab-data"
] (toHtml (show $ pretty name))
next
repoTreeEmbedded :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> GitHash -- ^ this
-> GitHash -- ^ this
-> HtmlT m ()
repoTreeEmbedded = repoTree_ True
repoTree :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> GitHash -- ^ this
-> GitHash -- ^ this
-> HtmlT m ()
repoTree = repoTree_ False
repoTree_ :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> Bool
-> LWWRefKey 'HBS2Basic
-> GitHash -- ^ this
-> GitHash -- ^ this
-> HtmlT m ()
repoTree_ embed lww co root = asksBaseUrl $ withBaseUrl $ do
tree <- lift $ gitShowTree lww root
back' <- lift $ selectParentTree (TreeCommit co) (TreeTree root)
let syntaxMap = Sky.defaultSyntaxMap
let sorted = sortOn (\(tp, _, name) -> (tpOrder tp, name)) tree
where
tpOrder Tree = (0 :: Int)
tpOrder Blob = 1
tpOrder _ = 2
locator <- lift $ selectTreeLocator (TreeCommit co) (TreeTree root)
let target = if embed then "#repo-tab-data-embedded" else "#repo-tab-data"
table_ [] do
unless embed do
tr_ do
td_ [class_ "tree-locator", colspan_ "3"] do
treeLocator lww co locator none
tr_ mempty do
for_ back' $ \r -> do
let rootLink = toBaseURL (RepoTree lww co (coerce @_ @GitHash r))
td_ $ svgIcon IconArrowUturnLeft
td_ ".."
td_ do a_ [ href_ "#"
, hxGet_ rootLink
, hxTarget_ target
] (toHtml $ show $ pretty r)
for_ sorted $ \(tp,h,name) -> do
let itemClass = pretty tp & show & Text.pack
let hash_ = show $ pretty h
let uri = toBaseURL $ RepoTree lww co h
tr_ mempty do
td_ $ case tp of
Commit -> mempty
Tree -> svgIcon IconFolderFilled
Blob -> do
let syn = Sky.syntaxesByFilename syntaxMap (Text.unpack name)
& headMay
<&> Text.toLower . Sky.sName
let icon = case syn of
Just "haskell" -> IconHaskell
Just "markdown" -> IconMarkdown
Just "nix" -> IconNix
Just "bash" -> IconBash
Just "python" -> IconPython
Just "javascript" -> IconJavaScript
Just "sql" -> IconSql
Just s | s `elem` ["cabal","makefile","toml","ini","yaml"]
-> IconSettingsFilled
_ -> IconFileFilled
svgIcon icon
-- debug $ red "PUSH URL" <+> pretty (path ["back", wtf])
td_ [class_ itemClass] (toHtml $ show $ pretty name)
td_ [class_ "mono"] do
case tp of
Blob -> do
let blobUri = toBaseURL $ RepoBlob lww co root h
a_ [ href_ "#"
, hxGet_ blobUri
, hxTarget_ target
] (toHtml hash_)
Tree -> do
a_ [ href_ "#"
, hxGet_ uri
, hxTarget_ target
] (toHtml hash_)
_ -> mempty
{- HLINT ignore "Functor law" -}
data RepoCommitStyle = RepoCommitSummary | RepoCommitPatch
deriving (Eq,Ord,Show)
repoCommit :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> RepoCommitStyle
-> LWWRefKey 'HBS2Basic
-> GitHash
-> HtmlT m ()
repoCommit style lww hash = asksBaseUrl $ withBaseUrl do
let syntaxMap = Sky.defaultSyntaxMap
txt <- lift $ getCommitRawBrief lww hash
let header = Text.lines txt & takeWhile (not . Text.null)
& fmap Text.words
let au = [ Text.takeWhile (/= '<') (Text.unwords a)
| ("Author:" : a) <- header
] & headMay
table_ [class_ "item-attr"] do
tr_ do
th_ [width_ "16rem"] $ strong_ "back"
td_ $ a_ [ href_ (toBaseURL (RepoPage (CommitsTab (Just hash)) lww))
] $ toHtml $ show $ pretty hash
for_ au $ \author -> do
tr_ do
th_ $ strong_ "author"
td_ $ toHtml author
tr_ $ do
th_ $ strong_ "view"
td_ do
ul_ [class_ "misc-menu"]do
li_ $ a_ [ href_ "#"
, hxGet_ (toBaseURL (RepoCommitSummaryQ lww hash))
, hxTarget_ "#repo-tab-data"
] "summary"
li_ $ a_ [ href_ "#"
, hxGet_ (toBaseURL (RepoCommitPatchQ lww hash))
, hxTarget_ "#repo-tab-data"
] "patch"
li_ $ a_ [ href_ (toBaseURL (RepoPage (TreeTab (Just hash)) lww))
] "tree"
case style of
RepoCommitSummary -> do
let msyn = Sky.syntaxByName syntaxMap "default"
for_ msyn $ \syn -> do
let config = TokenizerConfig { traceOutput = False, syntaxMap = syntaxMap }
case tokenize config syn txt of
Left _ -> mempty
Right tokens -> do
let fo = Sky.defaultFormatOpts { Sky.numberLines = False, Sky.ansiColorLevel = Sky.ANSI256Color }
let code = renderText (Lucid.formatHtmlBlock fo tokens)
toHtmlRaw code
RepoCommitPatch -> do
let msyn = Sky.syntaxByName syntaxMap "diff"
for_ msyn $ \syn -> do
txt <- lift $ getCommitRawPatch lww hash
let config = TokenizerConfig { traceOutput = False, syntaxMap = syntaxMap }
case tokenize config syn txt of
Left _ -> mempty
Right tokens -> do
let fo = Sky.defaultFormatOpts { Sky.numberLines = False, Sky.ansiColorLevel = Sky.ANSI256Color }
let code = renderText (Lucid.formatHtmlBlock fo tokens)
toHtmlRaw code
repoForks :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> HtmlT m ()
repoForks lww = asksBaseUrl $ withBaseUrl do
forks <- lift $ selectRepoForks lww
now <- getEpoch
unless (List.null forks) do
table_ $ do
tr_ $ th_ [colspan_ "3"] mempty
for_ forks $ \it@RepoListItem{..} -> do
let lwwTo = coerce @_ @(LWWRefKey 'HBS2Basic) rlRepoLww
tr_ [class_ "commit-brief-title"] do
td_ $ svgIcon IconGitFork
td_ [class_ "mono"] $
a_ [ href_ (toBaseURL (RepoPage (CommitsTab Nothing) lwwTo))
] do
toHtmlRaw $ view rlRepoLwwAsText it
td_ $ small_ $ toHtml (agePure rlRepoSeq now)
repoCommits :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> Either SelectCommitsPred SelectCommitsPred
-> HtmlT m ()
repoCommits lww predicate' = asksBaseUrl $ withBaseUrl do
now <- getEpoch
debug $ red "repoCommits"
let predicate = either id id predicate'
co <- lift $ selectCommits lww predicate
let off = view commitPredOffset predicate
let lim = view commitPredLimit predicate
let noff = off + lim
let query = RepoCommitsQ lww noff lim --) path ["repo", repo, "commits", show noff, show lim]
let normalizeText s = l $ (Text.take 60 . Text.unwords . Text.words) s
where l x | Text.length x < 60 = x
| otherwise = x <> "..."
let rows = do
tr_ $ th_ [colspan_ "5"] mempty
for_ co $ \case
CommitListItemBrief{..} -> do
tr_ [class_ "commit-brief-title"] do
td_ [class_ "commit-icon"] $ svgIcon IconGitCommit
td_ [class_ "commit-hash mono"] do
let hash = coerce @_ @GitHash commitListHash
a_ [ href_ "#"
, hxGet_ (toBaseURL (RepoCommitDefault lww hash))
, hxTarget_ "#repo-tab-data"
, hxPushUrl_ (toBaseURL query)
] $ toHtml (ShortRef hash)
td_ [class_ "commit-brief-title"] do
toHtml $ normalizeText $ coerce @_ @Text commitListTitle
tr_ [class_ "commit-brief-details"] do
td_ [colspan_ "3"] do
small_ do
toHtml (agePure (coerce @_ @Integer commitListTime) now)
toHtml " by "
toHtml $ coerce @_ @Text commitListAuthor
unless (List.null co) do
tr_ [ class_ "commit-brief-last"
, hxGet_ (toBaseURL query)
, hxTrigger_ "revealed"
, hxSwap_ "afterend"
] do
td_ [colspan_ "4"] do
mempty
if isRight predicate' then do
table_ rows
else do
rows
repoSomeBlob :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> Text
-> GitHash
-> HtmlT m ()
repoSomeBlob lww syn hash = do
bi <- lift (selectBlobInfo (BlobHash hash))
>>= orThrow (itemNotFound hash)
doRenderBlob (pure mempty) lww bi
repoBlob :: (DashBoardPerks m, MonadReader DashBoardEnv m)
=> LWWRefKey 'HBS2Basic
-> TreeCommit
-> TreeTree
-> BlobInfo
-> HtmlT m ()
repoBlob lww co tree bi@BlobInfo{..} = asksBaseUrl $ withBaseUrl do
locator <- lift $ selectTreeLocator co tree
table_ [] do
tr_ do
td_ [class_ "tree-locator", colspan_ "3"] do
treeLocator lww (coerce co) locator do
span_ "/"
span_ $ toHtml (show $ pretty blobName)
table_ [class_ "item-attr"] do
tr_ do
th_ $ strong_ "hash"
td_ [colspan_ "7"] do
span_ [class_ "mono"] $ toHtml $ show $ pretty blobHash
tr_ do
th_ $ strong_ "syntax"
td_ $ toHtml $ show $ pretty blobSyn
th_ $ strong_ "size"
td_ $ toHtml $ show $ pretty blobSize
td_ [colspan_ "3"] mempty
doRenderBlob (pure mempty) lww bi

View File

@ -1,160 +0,0 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# Language PatternSynonyms #-}
{-# Language ViewPatterns #-}
{-# Language MultiWayIf #-}
module HBS2.Git.Web.Html.Root
( module HBS2.Git.Web.Html.Root
, module HBS2.Git.Web.Html.Types
, module HBS2.Git.Web.Html.Parts.TopInfoBlock
) where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.Types
import HBS2.Git.DashBoard.State
import HBS2.Git.Web.Assets
import HBS2.Git.Web.Html.Types
import HBS2.Git.Web.Html.Markdown
import HBS2.Git.Web.Html.Parts.TopInfoBlock
import Lucid.Base
import Lucid.Html5 hiding (for_)
import Data.Word
myCss :: (WithBaseUrl, Monad m) => HtmlT m ()
myCss = do
link_ [rel_ "stylesheet", href_ (toBaseURL "css/custom.css")]
hyper_ :: Text -> Attribute
hyper_ = makeAttribute "_"
ariaLabel_ :: Text -> Attribute
ariaLabel_ = makeAttribute "aria-label"
onClickCopy :: Text -> Attribute
onClickCopy s =
hyper_ [qc|on click writeText('{s}') into the navigator's clipboard
set my innerHTML to '{svgIconText IconCopyDone}'
set @data-tooltip to 'Copied!'
wait 2s
set my innerHTML to '{svgIconText IconCopy}'
set @data-tooltip to 'Copy'
|]
onClickCopyText :: Text -> Attribute
onClickCopyText s =
hyper_ [qc|on click writeText('{s}') into the navigator's clipboard
set @data-tooltip to 'Copied!'
wait 2s
set @data-tooltip to 'Copy'
|]
instance ToHtml RepoBrief where
toHtml (RepoBrief txt) = toHtmlRaw (renderMarkdown' txt)
toHtmlRaw (RepoBrief txt) = toHtmlRaw (renderMarkdown' txt)
data WithTime a = WithTime Integer a
instance ToHtml GitRef where
toHtml (GitRef s)= toHtml s
toHtmlRaw (GitRef s)= toHtmlRaw s
rootPage :: (WithBaseUrl, Monad m) => HtmlT m () -> HtmlT m ()
rootPage content = do
doctypehtml_ do
head_ do
meta_ [charset_ "UTF-8"]
meta_ [name_ "viewport", content_ "width=device-width, initial-scale=1.0"]
-- FIXME: static-local-loading
link_ [rel_ "stylesheet", href_ "https://cdn.jsdelivr.net/npm/@picocss/pico@2.0.6/css/pico.min.css"]
script_ [src_ "https://unpkg.com/hyperscript.org@0.9.12"] ""
script_ [src_ "https://unpkg.com/htmx.org@1.9.11"] ""
myCss
body_ do
header_ [class_ "container-fluid"] do
nav_ do
ul_ $ li_ $ a_ [href_ (toBaseURL RepoListPage)] $ strong_ "hbs2-git dashboard"
content
dashboardRootPage :: (DashBoardPerks m, MonadReader DashBoardEnv m) => HtmlT m ()
dashboardRootPage = asksBaseUrl $ withBaseUrl $ rootPage do
items <- lift $ selectRepoList mempty
now <- liftIO getPOSIXTime <&> fromIntegral . round
main_ [class_ "container-fluid"] $ do
div_ [class_ "wrapper"] $ do
aside_ [class_ "sidebar"] $ do
div_ [class_ "info-block"] $ small_ "Всякая разная рандомная информация хрен знает, что тут пока выводить"
div_ [class_ "info-block"] $ small_ "Всякая разная рандомная информация хрен знает, что тут пока выводить"
div_ [class_ "content"] do
section_ do
h2_ "Git repositories"
form_ [role_ "search"] do
input_ [name_ "search", type_ "search"]
input_ [type_ "submit", value_ "Search"]
section_ do
for_ items $ \it@RepoListItem{..} -> do
let locked = isJust $ coerce @_ @(Maybe HashRef) rlRepoGK0
let url = toBaseURL (RepoPage (CommitsTab Nothing) (coerce @_ @(LWWRefKey 'HBS2Basic) rlRepoLww))
-- path ["repo", Text.unpack $ view rlRepoLwwAsText it]
let t = fromIntegral $ coerce @_ @Word64 rlRepoSeq
let updated = agePure t now
article_ [class_ "repo-list-item"] do
div_ do
h5_ do
toHtml rlRepoName
div_ [class_ "repo-list-item-link-wrapper"] $ do
a_ [href_ url] (toHtml $ view rlRepoLwwAsText it)
button_ [class_ "copy-button", onClickCopy (view rlRepoLwwAsText it), data_ "tooltip" "Copy"] do
svgIcon IconCopy
toHtml rlRepoBrief
div_ do
div_ [class_ "whitespace-nowrap"] do
small_ $ "Updated " <> toHtml updated
when locked do
div_ do
small_ do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconLockClosed
"Encrypted"
div_ do
small_ do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconGitCommit
strong_ $ toHtml $ show rlRepoCommits
" commits"
div_ do
small_ do
span_ [class_ "inline-icon-wrapper"] $ svgIcon IconGitFork
strong_ $ toHtml $ show rlRepoForks
" forks"
tabClick :: Attribute
tabClick =
hyper_ "on click take .contrast from .tab for event's target"

View File

@ -1,322 +0,0 @@
{-# Language MultiWayIf #-}
{-# Language ImplicitParams #-}
module HBS2.Git.Web.Html.Types where
import HBS2.Git.DashBoard.Prelude
import HBS2.Git.DashBoard.State
import HBS2.Git.DashBoard.Fixme as Fixme
import Data.Kind
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Text qualified as Text
import Data.Word
import Lucid.Base
import Network.URI.Encode
import System.FilePath
import Web.Scotty.Trans as Scotty
import Network.HTTP.Types.Status
newtype H a = H a
raiseStatus :: forall m . MonadIO m => Status -> Text -> m ()
raiseStatus s t = throwIO (StatusError s t)
itemNotFound s = StatusError status404 (Text.pack $ show $ pretty s)
rootPath :: [String] -> [String]
rootPath = ("/":)
data Domain = FixmeDomain
newtype FromParams (e :: Domain) a = FromParams a
class Path a where
path :: [a] -> Text
instance Path String where
path = Text.pack . joinPath . rootPath
class ToRoutePattern a where
routePattern :: a -> RoutePattern
type WithBaseUrl = ?dashBoardBaseUrl :: Text
getBaseUrl :: WithBaseUrl => Text
getBaseUrl = ?dashBoardBaseUrl
withBaseUrl :: (WithBaseUrl => r) -> Text -> r
withBaseUrl thingInside baseUrl =
let ?dashBoardBaseUrl = baseUrl in thingInside
toBaseURL :: (WithBaseUrl, ToURL a) => a -> Text
toBaseURL x = getBaseUrl <> toURL x
class ToURL a where
toURL :: a -> Text
data family Tabs a :: Type
data RepoListPage = RepoListPage
data RepoPageTabs = CommitsTab (Maybe GitHash)
| ManifestTab
| TreeTab (Maybe GitHash)
| IssuesTab
| ForksTab
| PinnedTab (Maybe (Text, Text, GitHash))
deriving stock (Eq,Ord,Show)
data RepoPage s a = RepoPage s a
data RepoRefs repo = RepoRefs repo
data RepoTree repo commit tree = RepoTree repo commit tree
data RepoTreeEmbedded repo commit tree = RepoTreeEmbedded repo commit tree
data RepoBlob repo commit tree blob = RepoBlob repo commit tree blob
data RepoSomeBlob repo blob tp = RepoSomeBlob repo blob tp
data RepoForksHtmx repo = RepoForksHtmx repo
newtype RepoManifest repo = RepoManifest repo
newtype RepoCommits repo = RepoCommits repo
data Paged q = Paged QueryOffset q
data RepoFixmeHtmx repo = RepoFixmeHtmx (Map Text Text) repo
data RepoCommitsQ repo off lim = RepoCommitsQ repo off lim
data RepoCommitDefault repo commit = RepoCommitDefault repo commit
data RepoCommitSummaryQ repo commit = RepoCommitSummaryQ repo commit
data RepoCommitPatchQ repo commit = RepoCommitPatchQ repo commit
data IssuePage repo issue = IssuePage repo issue
newtype ShortRef a = ShortRef a
shortRef :: Int -> Int -> String -> String
shortRef n k a = if k > 0 then [qc|{b}..{r}|] else [qc|{b}|]
where
b = take n a
r = reverse $ take k (reverse a)
instance ToHtml (ShortRef GitHash) where
toHtml (ShortRef a) = toHtml (shortRef 10 0 (show $ pretty a))
toHtmlRaw (ShortRef a) = toHtml (shortRef 10 0 (show $ pretty a))
instance ToHtml (ShortRef (LWWRefKey 'HBS2Basic)) where
toHtml (ShortRef a) = toHtml (shortRef 14 3 (show $ pretty a))
toHtmlRaw (ShortRef a) = toHtml (shortRef 14 3 (show $ pretty a))
toArg :: (Semigroup a, IsString a) => a -> a
toArg s = ":" <> s
toPattern :: Text -> RoutePattern
toPattern = fromString . Text.unpack
instance Pretty RepoPageTabs where
pretty = \case
CommitsTab{} -> "commits"
ManifestTab{} -> "manifest"
TreeTab{} -> "tree"
ForksTab{} -> "forks"
IssuesTab{} -> "issues"
PinnedTab{} -> "pinned"
instance FromStringMaybe RepoPageTabs where
fromStringMay = \case
"commits" -> pure (CommitsTab Nothing)
"manifest" -> pure ManifestTab
"tree" -> pure (TreeTab Nothing)
"forks" -> pure ForksTab
"issues" -> pure IssuesTab
"pinned" -> pure $ PinnedTab Nothing
_ -> pure (CommitsTab Nothing)
instance ToRoutePattern RepoListPage where
routePattern = \case
RepoListPage -> "/"
instance ToURL String where
toURL str = path [str]
instance ToURL (RepoPage RepoPageTabs (LWWRefKey 'HBS2Basic)) where
toURL (RepoPage s w) = path @String [ "/", show (pretty s), show (pretty w)]
<> pred_
where
-- FIXME: use-uri-encode
pred_ = case s of
CommitsTab (Just p) -> Text.pack $ "?ref=" <> show (pretty p)
TreeTab (Just p) -> Text.pack $ "?tree=" <> show (pretty p)
PinnedTab (Just (s,n,h)) -> Text.pack $ "?ref=" <> show (pretty h)
_ -> mempty
instance ToRoutePattern (RepoPage String String) where
routePattern (RepoPage s w) = path ["/", toArg s, toArg w] & toPattern
instance ToURL RepoListPage where
toURL _ = "/"
instance ToURL (RepoRefs (LWWRefKey 'HBS2Basic)) where
toURL (RepoRefs repo') = path ["/", "htmx", "refs", repo]
where
repo = show $ pretty repo'
instance ToRoutePattern (RepoRefs String) where
routePattern (RepoRefs s) = path ["/", "htmx", "refs", toArg s] & toPattern
instance ToURL (RepoTree (LWWRefKey 'HBS2Basic) GitHash GitHash) where
toURL (RepoTree k co tree') = path ["/", "htmx", "tree", repo, commit, tree]
where
repo = show $ pretty k
commit = show $ pretty co
tree = show $ pretty tree'
instance ToRoutePattern (RepoTree String String String) where
routePattern (RepoTree r co tree) =
path ["/", "htmx", "tree", toArg r, toArg co, toArg tree] & toPattern
instance ToURL (RepoBlob (LWWRefKey 'HBS2Basic) GitHash GitHash GitHash) where
toURL (RepoBlob k co t bo) = path ["/", "htmx", "blob", repo, commit, tree, blob]
where
repo = show $ pretty k
commit = show $ pretty co
tree = show $ pretty t
blob = show $ pretty bo
instance ToRoutePattern (RepoBlob String String String String) where
routePattern (RepoBlob r c t b) =
path ["/", "htmx", "blob", toArg r, toArg c, toArg t, toArg b] & toPattern
instance ToURL (RepoSomeBlob (LWWRefKey 'HBS2Basic) Text GitHash) where
toURL (RepoSomeBlob k tp' blo) = path ["/", "htmx", "some-blob", repo, tp, blob]
where
repo = show $ pretty k
tp = Text.unpack tp'
blob = show $ pretty blo
instance ToRoutePattern (RepoSomeBlob String String String) where
routePattern (RepoSomeBlob r t b) =
path ["/", "htmx", "some-blob", toArg r, toArg t, toArg b] & toPattern
instance ToURL (RepoManifest (LWWRefKey 'HBS2Basic)) where
toURL (RepoManifest repo') = path ["/", "htmx", "manifest", repo]
where
repo = show $ pretty repo'
instance ToRoutePattern (RepoManifest String) where
routePattern (RepoManifest s) = path ["/", "htmx", "manifest", toArg s] & toPattern
instance ToURL (RepoCommits (LWWRefKey 'HBS2Basic)) where
toURL (RepoCommits repo') = path ["/", "htmx", "commits", repo]
where
repo = show $ pretty repo'
instance ToRoutePattern (RepoCommits String) where
routePattern (RepoCommits s) = path ["/", "htmx", "commits", toArg s] & toPattern
instance ToURL (RepoCommitsQ (LWWRefKey 'HBS2Basic) Int Int) where
toURL (RepoCommitsQ repo' off lim) = path ["/", "htmx", "commits", repo, show off, show lim]
where
repo = show $ pretty repo'
instance ToRoutePattern (RepoCommitsQ String String String) where
routePattern (RepoCommitsQ r o l) =
path ["/", "htmx", "commits", toArg r, toArg o, toArg l] & toPattern
instance ToURL (RepoCommitDefault (LWWRefKey 'HBS2Basic) GitHash) where
toURL (RepoCommitDefault repo' h) = toURL (RepoCommitSummaryQ repo' h)
instance ToRoutePattern (RepoCommitDefault String String) where
routePattern (RepoCommitDefault r h) = routePattern (RepoCommitSummaryQ r h)
instance ToURL (RepoCommitSummaryQ (LWWRefKey 'HBS2Basic) GitHash) where
toURL (RepoCommitSummaryQ repo' h) = path ["/", "htmx", "commit", "summary", repo, ha]
where
repo = show $ pretty repo'
ha = show $ pretty h
instance ToRoutePattern (RepoCommitSummaryQ String String) where
routePattern (RepoCommitSummaryQ r h) =
path ["/", "htmx", "commit", "summary", toArg r, toArg h] & toPattern
instance ToURL (RepoCommitPatchQ (LWWRefKey 'HBS2Basic) GitHash) where
toURL (RepoCommitPatchQ repo' h) = path ["/", "htmx", "commit", "patch", repo, ha]
where
repo = show $ pretty repo'
ha = show $ pretty h
instance ToRoutePattern (RepoCommitPatchQ String String) where
routePattern (RepoCommitPatchQ r h) =
path ["/", "htmx", "commit", "patch", toArg r, toArg h] & toPattern
instance ToURL (RepoTreeEmbedded (LWWRefKey 'HBS2Basic) GitHash GitHash) where
toURL (RepoTreeEmbedded k co tree') = path ["/", "htmx", "tree", "embedded", repo, commit, tree]
where
repo = show $ pretty k
commit = show $ pretty co
tree = show $ pretty tree'
instance ToRoutePattern (RepoTreeEmbedded String String String) where
routePattern (RepoTreeEmbedded r co tree) =
path ["/", "htmx", "tree", "embedded", toArg r, toArg co, toArg tree] & toPattern
instance ToURL (RepoForksHtmx (LWWRefKey 'HBS2Basic)) where
toURL (RepoForksHtmx k) = path ["/", "htmx", "forks", repo]
where
repo = show $ pretty k
instance ToRoutePattern (RepoFixmeHtmx String) where
routePattern (RepoFixmeHtmx _ r) =
path ["/", "htmx", "fixme", toArg r] & toPattern
instance ToURL (RepoFixmeHtmx RepoLww) where
toURL (RepoFixmeHtmx argz' k) = path ["/", "htmx", "fixme", repo] <> "?" <> filtPart
where
repo = show $ pretty k
filtPart = Text.intercalate "&" [ [qc|{encodeText k}={encodeText v}|] | (k,v) <- argz ]
argz = Map.toList argz'
instance ToURL (Paged (RepoFixmeHtmx RepoLww)) where
toURL (Paged p (RepoFixmeHtmx a k)) = toURL (RepoFixmeHtmx paged k)
where paged = Map.insert "$page" (Text.pack (show p)) a
instance ToRoutePattern (RepoForksHtmx String) where
routePattern (RepoForksHtmx r) =
path ["/", "htmx", "forks", toArg r] & toPattern
instance ToRoutePattern (IssuePage String String) where
routePattern (IssuePage s w) = path ["/", "issues", toArg s, toArg w] & toPattern
instance ToURL (IssuePage RepoLww FixmeKey) where
toURL (IssuePage r i) = path ["/", "issues", repo, issue]
where
repo = show $ pretty r
issue = show $ pretty i
agePure :: forall a b . (Integral a,Integral b) => a -> b -> Text
agePure t0 t = do
let sec = fromIntegral @_ @Word64 t - fromIntegral t0
fromString $ show $
if | sec > 86400 -> pretty (sec `div` 86400) <+> "days ago"
| sec > 3600 -> pretty (sec `div` 3600) <+> "hours ago"
| otherwise -> pretty (sec `div` 60) <+> "minutes ago"

View File

@ -1,221 +0,0 @@
cabal-version: 3.0
name: hbs2-git-dashboard
version: 0.25.0.1
license: BSD-3-Clause
author: Dmitry Zuikov
category: System
build-type: Simple
common shared-properties
ghc-options:
-Wall
-fno-warn-type-defaults
-fno-warn-unused-matches
-fno-warn-name-shadowing
-O2
default-language: GHC2021
default-extensions:
ApplicativeDo
, BangPatterns
, BlockArguments
, ConstraintKinds
, DataKinds
, DeriveDataTypeable
, DeriveGeneric
, DerivingStrategies
, DerivingVia
, ExtendedDefaultRules
, FlexibleContexts
, FlexibleInstances
, GADTs
, GeneralizedNewtypeDeriving
, ImportQualifiedPost
, LambdaCase
, MultiParamTypeClasses
, OverloadedStrings
, QuasiQuotes
, RecordWildCards
, ScopedTypeVariables
, StandaloneDeriving
, TupleSections
, TypeApplications
, TypeFamilies
library hbs2-git-dashboard-assets
import: shared-properties
build-depends:
base
, bytestring
, interpolatedstring-perl6
, file-embed
, lucid
, text
exposed-modules:
HBS2.Git.Web.Assets
hs-source-dirs: hbs2-git-dashboard-assets
default-language: GHC2021
library hbs2-git-dashboard-core
import: shared-properties
build-depends:
, base
, hbs2-git-dashboard-assets
, hbs2-core
, hbs2-peer
, hbs2-storage-simple
, hbs2-git
, hbs2-keyman-direct-lib
, db-pipe
, suckless-conf
, fixme-new
, aeson
, atomic-write
, attoparsec
, binary
, bytestring
, containers
, deriving-compat
, directory
, exceptions
, filepath
, filepattern
, generic-data
, generic-deriving
, generic-lens
, http-types
, interpolatedstring-perl6
, lucid
, lucid-htmx
, memory
, microlens-platform
, mtl
, network-uri
, optparse-applicative
, pandoc
, prettyprinter
, prettyprinter-ansi-terminal
, random
, safe
, scotty >= 0.21
, serialise
, skylighting
, skylighting-core
, skylighting-lucid
, stm
, streaming
, split
, temporary
, text
, time
, timeit
, transformers
, typed-process
, unix
, unliftio
, unliftio-core
, unordered-containers
, uri-encode
, vector
, wai
, wai-extra
, wai-middleware-static
, wai-middleware-static-embedded
, zlib
exposed-modules:
HBS2.Git.DashBoard.Prelude
HBS2.Git.DashBoard.Types
HBS2.Git.DashBoard.State
HBS2.Git.DashBoard.State.Commits
HBS2.Git.DashBoard.State.Index
HBS2.Git.DashBoard.State.Index.Channels
HBS2.Git.DashBoard.State.Index.Peer
HBS2.Git.DashBoard.Manifest
HBS2.Git.DashBoard.Fixme
HBS2.Git.Web.Html.Types
HBS2.Git.Web.Html.Parts.TopInfoBlock
HBS2.Git.Web.Html.Parts.Issues.Sidebar
HBS2.Git.Web.Html.Parts.Blob
HBS2.Git.Web.Html.Markdown
HBS2.Git.Web.Html.Root
HBS2.Git.Web.Html.Issue
HBS2.Git.Web.Html.Repo
HBS2.Git.Web.Html.Fixme
hs-source-dirs: hbs2-git-dashboard-core
default-language: GHC2021
executable hbs2-git-dashboard
import: shared-properties
main-is: GitDashBoard.hs
ghc-options:
-threaded
-rtsopts
-O2
"-with-rtsopts=-N4 -A64m -AL256m -I0"
other-modules:
-- other-extensions:
build-depends:
base
, hbs2-core
, hbs2-git
, hbs2-git-dashboard-assets
, hbs2-git-dashboard-core
, hbs2-peer
, suckless-conf
, db-pipe
, binary
, bytestring
, deriving-compat
, directory
, filepath
, generic-data
, generic-deriving
, http-types
, lucid
, lucid-htmx
, mtl
, network-uri
, optparse-applicative
, pandoc
, random
, scotty >= 0.21
, skylighting
, skylighting-core
, skylighting-lucid
, stm
, temporary
, text
, transformers
, typed-process
, unordered-containers
, vector
, wai
, wai-extra
, wai-middleware-static
, wai-middleware-static-embedded
hs-source-dirs:
app
default-language: GHC2021