162 lines
4.9 KiB
Haskell
162 lines
4.9 KiB
Haskell
{-# LANGUAGE CPP #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
module Hledger.Web.WebOptions where
|
|
|
|
import Data.ByteString (ByteString)
|
|
import qualified Data.ByteString.Char8 as BC
|
|
import Data.CaseInsensitive (CI, mk)
|
|
import Control.Monad (join)
|
|
import Data.Default (Default(def))
|
|
import Data.Maybe (fromMaybe)
|
|
import qualified Data.Text as T
|
|
import Data.Text (Text)
|
|
import System.Environment (getArgs)
|
|
|
|
import Hledger.Cli hiding (progname, version)
|
|
import Hledger.Web.Settings (defhost, defport, defbaseurl)
|
|
|
|
progname, version :: String
|
|
progname = "hledger-web"
|
|
#ifdef VERSION
|
|
version = VERSION
|
|
#else
|
|
version = ""
|
|
#endif
|
|
prognameandversion :: String
|
|
prognameandversion = progname ++ " " ++ version :: String
|
|
|
|
webflags :: [Flag [(String, String)]]
|
|
webflags =
|
|
[ flagNone
|
|
["serve", "server"]
|
|
(setboolopt "serve")
|
|
"serve and log requests, don't browse or auto-exit"
|
|
, flagReq
|
|
["host"]
|
|
(\s opts -> Right $ setopt "host" s opts)
|
|
"IPADDR"
|
|
("listen on this IP address (default: " ++ defhost ++ ")")
|
|
, flagReq
|
|
["port"]
|
|
(\s opts -> Right $ setopt "port" s opts)
|
|
"PORT"
|
|
("listen on this TCP port (default: " ++ show defport ++ ")")
|
|
, flagReq
|
|
["base-url"]
|
|
(\s opts -> Right $ setopt "base-url" s opts)
|
|
"BASEURL"
|
|
"set the base url (default: http://IPADDR:PORT)"
|
|
, flagReq
|
|
["file-url"]
|
|
(\s opts -> Right $ setopt "file-url" s opts)
|
|
"FILEURL"
|
|
"set the static files url (default: BASEURL/static)"
|
|
, flagReq
|
|
["capabilities"]
|
|
(\s opts -> Right $ setopt "capabilities" s opts)
|
|
"CAP,CAP2"
|
|
"enable these capabilities - comma-separated, possible values are: view, add, manage (default: view,add)"
|
|
, flagReq
|
|
["capabilities-header"]
|
|
(\s opts -> Right $ setopt "capabilities-header" s opts)
|
|
"HEADER"
|
|
"read enabled capabilities from a HTTP header (e.g. X-Sandstorm-Permissions, disabled by default)"
|
|
]
|
|
|
|
webmode :: Mode [(String, String)]
|
|
webmode =
|
|
(mode
|
|
"hledger-web"
|
|
[("command", "web")]
|
|
"start serving the hledger web interface"
|
|
(argsFlag "[PATTERNS]")
|
|
[])
|
|
{ modeGroupFlags =
|
|
Group
|
|
{ groupUnnamed = webflags
|
|
, groupHidden =
|
|
[ flagNone
|
|
["binary-filename"]
|
|
(setboolopt "binary-filename")
|
|
"show the download filename for this executable, and exit"
|
|
]
|
|
, groupNamed = [generalflagsgroup1]
|
|
}
|
|
, modeHelpSuffix = []
|
|
}
|
|
|
|
-- hledger-web options, used in hledger-web and above
|
|
data WebOpts = WebOpts
|
|
{ serve_ :: Bool
|
|
, host_ :: String
|
|
, port_ :: Int
|
|
, base_url_ :: String
|
|
, file_url_ :: Maybe String
|
|
, capabilities_ :: [Capability]
|
|
, capabilitiesHeader_ :: Maybe (CI ByteString)
|
|
, cliopts_ :: CliOpts
|
|
} deriving (Show)
|
|
|
|
defwebopts :: WebOpts
|
|
defwebopts = WebOpts def def def def def [CapView, CapAdd] Nothing def
|
|
|
|
instance Default WebOpts where def = defwebopts
|
|
|
|
rawOptsToWebOpts :: RawOpts -> IO WebOpts
|
|
rawOptsToWebOpts rawopts =
|
|
checkWebOpts <$> do
|
|
cliopts <- rawOptsToCliOpts rawopts
|
|
let h = fromMaybe defhost $ maybestringopt "host" rawopts
|
|
p = fromMaybe defport $ maybeintopt "port" rawopts
|
|
b =
|
|
maybe (defbaseurl h p) stripTrailingSlash $
|
|
maybestringopt "base-url" rawopts
|
|
caps' = join $ T.splitOn "," . T.pack <$> listofstringopt "capabilities" rawopts
|
|
caps = case traverse capabilityFromText caps' of
|
|
Left e -> error' ("Unknown capability: " ++ T.unpack e)
|
|
Right [] -> [CapView, CapAdd]
|
|
Right xs -> xs
|
|
return
|
|
defwebopts
|
|
{ serve_ = boolopt "serve" rawopts
|
|
, host_ = h
|
|
, port_ = p
|
|
, base_url_ = b
|
|
, file_url_ = stripTrailingSlash <$> maybestringopt "file-url" rawopts
|
|
, capabilities_ = caps
|
|
, capabilitiesHeader_ = mk . BC.pack <$> maybestringopt "capabilities-header" rawopts
|
|
, cliopts_ = cliopts
|
|
}
|
|
where
|
|
stripTrailingSlash = reverse . dropWhile (== '/') . reverse -- yesod don't like it
|
|
|
|
checkWebOpts :: WebOpts -> WebOpts
|
|
checkWebOpts wopts = do
|
|
let h = host_ wopts
|
|
if any (`notElem` (".0123456789" :: String)) h
|
|
then usageError $ "--host requires an IP address, not " ++ show h
|
|
else wopts
|
|
|
|
getHledgerWebOpts :: IO WebOpts
|
|
getHledgerWebOpts = do
|
|
args <- fmap replaceNumericFlags . expandArgsAt =<< getArgs
|
|
rawOptsToWebOpts . decodeRawOpts . either usageError id $ process webmode args
|
|
|
|
data Capability
|
|
= CapView
|
|
| CapAdd
|
|
| CapManage
|
|
deriving (Eq, Ord, Bounded, Enum, Show)
|
|
|
|
capabilityFromText :: Text -> Either Text Capability
|
|
capabilityFromText "view" = Right CapView
|
|
capabilityFromText "add" = Right CapAdd
|
|
capabilityFromText "manage" = Right CapManage
|
|
capabilityFromText x = Left x
|
|
|
|
capabilityFromBS :: ByteString -> Either ByteString Capability
|
|
capabilityFromBS "view" = Right CapView
|
|
capabilityFromBS "add" = Right CapAdd
|
|
capabilityFromBS "manage" = Right CapManage
|
|
capabilityFromBS x = Left x
|