htmx flags
This commit is contained in:
1 parent
87dda96e7e
commit
45277e02da
1 file changed
+51
-13
+51
-13
@@ -17,20 +17,22 @@ import Data.Maybe (fromMaybe)
|
|||||||
import Data.Map.Strict (Map)
|
import Data.Map.Strict (Map)
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import HomeAssistant.Runtime.Flags (Flags, allFlags, setEnabled, validNames)
|
import HomeAssistant.Runtime.Flags (Flags, allFlags, setEnabled, validNames, isEnabled)
|
||||||
import Control.Exception.Annotated (checkpoint, Annotation (Annotation))
|
import Control.Exception.Annotated (checkpoint, Annotation (Annotation))
|
||||||
import Servant.Server.Generic (genericServeT)
|
import Servant.Server.Generic (genericServeT)
|
||||||
import Network.Wai (Application)
|
import Network.Wai (Application)
|
||||||
import Network.HTTP.Media ((//))
|
import Network.HTTP.Media ((//))
|
||||||
import HomeAssistant.Runtime.Graphing (Graph (..), Range (..), lookupGraph, graphs, renderGraph)
|
import HomeAssistant.Runtime.Graphing (Graph (..), Range (..), lookupGraph, graphs, renderGraph)
|
||||||
import Katip (KatipContextT, Katip, runKatipContextT, LogEnv, KatipContext, ls, Severity (..), logFM)
|
import Katip (KatipContextT, Katip, runKatipContextT, LogEnv, KatipContext, ls, Severity (..), logFM, katipAddContext, sl)
|
||||||
import Data.Aeson (ToJSON, FromJSON)
|
import Data.Aeson (ToJSON (toJSON), FromJSON, object, (.=))
|
||||||
import Lucid (ToHtml, toHtml, renderBS, toHtmlRaw, HtmlT)
|
import Lucid (ToHtml, toHtml, renderBS, toHtmlRaw, HtmlT, Attributes)
|
||||||
import qualified Lucid.Html5 as H5
|
import qualified Lucid.Html5 as H5
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
import Data.Foldable (for_)
|
import Data.Foldable (for_)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Web.FormUrlEncoded (FromForm, fromForm, parseUnique)
|
import Web.FormUrlEncoded (FromForm, fromForm, parseUnique)
|
||||||
|
import Lucid.Base (makeAttributes)
|
||||||
|
import Data.Aeson.Key (fromText)
|
||||||
|
|
||||||
data ImagePng
|
data ImagePng
|
||||||
|
|
||||||
@@ -56,12 +58,13 @@ instance ToHtml StatsPage where
|
|||||||
toHtml (StatsPage inner) = H5.doctypehtml_ $ H5.body_ inner
|
toHtml (StatsPage inner) = H5.doctypehtml_ $ H5.body_ inner
|
||||||
toHtmlRaw = toHtml
|
toHtmlRaw = toHtml
|
||||||
|
|
||||||
|
|
||||||
data API mode = API
|
data API mode = API
|
||||||
{ getMetrics :: mode :- "metrics" :> Capture "graph" Graph :> QueryParam "range" Range :> Get '[ImagePng] GraphPng
|
{ getMetrics :: mode :- "metrics" :> Capture "graph" Graph :> QueryParam "range" Range :> Get '[ImagePng] GraphPng
|
||||||
, getStats :: mode :- "stats" :> QueryParam "range" Range :> Get '[HTML] StatsPage
|
, getStats :: mode :- "stats" :> QueryParam "range" Range :> Get '[HTML] StatsPage
|
||||||
, getFlags :: mode :- "flags" :> Get '[JSON, HTML] FlagsResponse
|
, getFlags :: mode :- "flags" :> Get '[JSON, HTML] FlagsResponse
|
||||||
, putFlag :: mode :- "flags" :> Capture "name" Text :> ReqBody '[JSON] FlagValue :> Put '[JSON] FlagsResponse
|
, putFlag :: mode :- "flags" :> Capture "name" Text :> ReqBody '[JSON, FormUrlEncoded] FlagValue :> Put '[HTML, JSON] FlagResponse
|
||||||
, postFlag :: mode :- "flags" :> Capture "name" Text :> ReqBody '[JSON, FormUrlEncoded] FlagValue :> Post '[JSON, HTML] FlagsResponse
|
, postFlag :: mode :- "flags" :> Capture "name" Text :> ReqBody '[JSON, FormUrlEncoded] FlagValue :> Post '[JSON, HTML] FlagResponse
|
||||||
}
|
}
|
||||||
deriving Generic
|
deriving Generic
|
||||||
|
|
||||||
@@ -76,17 +79,52 @@ instance FromForm FlagValue where
|
|||||||
newtype FlagsResponse = FlagsResponse (Map Text Bool)
|
newtype FlagsResponse = FlagsResponse (Map Text Bool)
|
||||||
deriving (ToJSON) via (Map Text Bool)
|
deriving (ToJSON) via (Map Text Bool)
|
||||||
|
|
||||||
|
data FlagResponse = FlagResponse Text Bool
|
||||||
|
|
||||||
|
instance ToHtml FlagResponse where
|
||||||
|
toHtml (FlagResponse name value) = flagRow name value
|
||||||
|
toHtmlRaw = toHtml
|
||||||
|
|
||||||
|
instance ToJSON FlagResponse where
|
||||||
|
toJSON (FlagResponse k v) = object [fromText k .= v]
|
||||||
|
|
||||||
flagsPage :: Monad m => FlagsResponse -> HtmlT m ()
|
flagsPage :: Monad m => FlagsResponse -> HtmlT m ()
|
||||||
flagsPage (FlagsResponse f) = H5.doctypehtml_ $
|
flagsPage (FlagsResponse f) = H5.doctypehtml_ $ do
|
||||||
H5.body_ $ H5.table_ $ for_ (M.toList f) $ \(k,v) -> H5.tr_ $
|
-- <script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.11/dist/htmx.min.js"
|
||||||
H5.td_ (toHtml k) <> H5.td_ (toHtml $ show v) <> H5.td_ (toggle k v)
|
-- integrity="sha384-2OatzQy1H+Zd/IIrjr1TcuDGqLXeHhbooAyJY1KdQMKnr4LZ22k31GBLdYKHmVjg"
|
||||||
|
-- crossorigin="anonymous"></script>
|
||||||
|
H5.head_ $ do
|
||||||
|
H5.script_
|
||||||
|
[ H5.src_ "https://cdn.jsdelivr.net/npm/htmx.org@2.0.11/dist/htmx.min.js"
|
||||||
|
, H5.integrity_ "sha384-2OatzQy1H+Zd/IIrjr1TcuDGqLXeHhbooAyJY1KdQMKnr4LZ22k31GBLdYKHmVjg"
|
||||||
|
, H5.crossorigin_ "anonymous"
|
||||||
|
]
|
||||||
|
("" :: Text)
|
||||||
|
H5.body_ $ H5.table_ $ for_ (M.toList f) $ \(k,v) -> H5.tr_ [H5.id_ ("status-" <> k)] $ flagRow k v
|
||||||
|
|
||||||
|
flagRow :: Monad m => Text -> Bool -> HtmlT m ()
|
||||||
|
flagRow name value =
|
||||||
|
H5.td_ (toHtml name) <> H5.td_ (toHtml $ show value) <> H5.td_ (toggle name value)
|
||||||
where
|
where
|
||||||
|
target k = "status-" <> k
|
||||||
toggle :: Monad m => Text -> Bool -> HtmlT m ()
|
toggle :: Monad m => Text -> Bool -> HtmlT m ()
|
||||||
toggle key current = H5.form_ [H5.method_ "post", H5.action_ $ postFlagsLink key] $ do
|
toggle key current = H5.form_ [H5.method_ "post", H5.action_ $ postFlagsLink key] $ do
|
||||||
H5.input_ [H5.name_ "flag-value", H5.type_ "hidden", H5.value_ (T.pack $ show (not current))]
|
H5.input_ [H5.name_ "flag-value", H5.type_ "hidden", H5.value_ (T.pack $ show (not current))]
|
||||||
H5.input_ [H5.type_ "submit", H5.value_ "Toggle"]
|
H5.input_ [H5.type_ "submit", H5.value_ "Toggle", hxTrigger "click", hxTarget ("#" <> target key), hxPut (postFlagsLink key), hxSwap "outerHtml"]
|
||||||
postFlagsLink k = "/" <> toUrlPiece (fieldLink putFlag k)
|
postFlagsLink k = "/" <> toUrlPiece (fieldLink putFlag k)
|
||||||
|
|
||||||
|
hxTrigger :: Text -> Attributes
|
||||||
|
hxTrigger = makeAttributes "hx-trigger"
|
||||||
|
|
||||||
|
hxTarget :: Text -> Attributes
|
||||||
|
hxTarget = makeAttributes "hx-target"
|
||||||
|
|
||||||
|
hxPut :: Text -> Attributes
|
||||||
|
hxPut = makeAttributes "hx-put"
|
||||||
|
|
||||||
|
hxSwap :: Text -> Attributes
|
||||||
|
hxSwap = makeAttributes "hx-swap"
|
||||||
|
|
||||||
instance ToHtml FlagsResponse where
|
instance ToHtml FlagsResponse where
|
||||||
toHtml = flagsPage
|
toHtml = flagsPage
|
||||||
toHtmlRaw = toHtml
|
toHtmlRaw = toHtml
|
||||||
@@ -94,14 +132,14 @@ instance ToHtml FlagsResponse where
|
|||||||
getFlagsHandler :: Flags -> LoggingHandler FlagsResponse
|
getFlagsHandler :: Flags -> LoggingHandler FlagsResponse
|
||||||
getFlagsHandler = fmap FlagsResponse . allFlags
|
getFlagsHandler = fmap FlagsResponse . allFlags
|
||||||
|
|
||||||
putFlagHandler :: Flags -> Text -> FlagValue -> LoggingHandler FlagsResponse
|
putFlagHandler :: Flags -> Text -> FlagValue -> LoggingHandler FlagResponse
|
||||||
putFlagHandler flags name (FlagValue val)
|
putFlagHandler flags name (FlagValue val)
|
||||||
| name `S.notMember` validNames flags = throwM err404
|
| name `S.notMember` validNames flags = throwM err404
|
||||||
| otherwise = do
|
| otherwise = checkpoint (Annotation (name, val)) $ katipAddContext (sl "flag" (name, val)) $ do
|
||||||
setEnabled flags name val `catch` \e -> do
|
setEnabled flags name val `catch` \e -> do
|
||||||
logFM ErrorS (ls ("failed to persist handler flag: " <> show (e :: SomeException)))
|
logFM ErrorS (ls ("failed to persist handler flag: " <> show (e :: SomeException)))
|
||||||
throwM err500
|
throwM err500
|
||||||
FlagsResponse <$> allFlags flags
|
FlagResponse name <$> isEnabled flags name
|
||||||
|
|
||||||
|
|
||||||
getMetricsHandler :: FilePath -> FilePath -> Graph -> Maybe Range -> LoggingHandler GraphPng
|
getMetricsHandler :: FilePath -> FilePath -> Graph -> Maybe Range -> LoggingHandler GraphPng
|
||||||
|
|||||||
Reference in new issue
Block a user