-- | Helper routines for startup. Find startup sequence itself in module 'Hagia'
module Startup
( verifyExistingRepos
, HagiaOperation (..)
, ServerConfig (..)
, AdminConfig (..)
, getConfig
) where
import ClassyPrelude hiding ((</>))
import qualified Log
import qualified Options.Applicative as Options
import qualified System.OsPath as OsPath
import Database (Repository (Repository, repositoryName), User (User, userUsername))
import Database.Persist (entityKey, entityVal, selectList, (==.))
import Database.Persist.Sql (SqlBackend)
import Network.Wai.Handler.Warp (HostPreference)
import Options.Applicative (strOption)
import System.Directory.OsPath (doesDirectoryExist)
import System.OsPath (OsPath, (</>))
verifyExistingRepos
:: MonadIO m
=> OsPath
-> ReaderT SqlBackend m [OsPath]
-- ^ Paths to repos that don't exist on disk, relative to root
verifyExistingRepos root = do
Log.info "Starting check for missing repositories"
users <- selectList [] []
dirs' <- forM users $ \user -> do
let User{userUsername} = entityVal user
Log.trace $ "Checking user " <> tshow userUsername
repos <- selectList [#user ==. entityKey user] []
forM repos $ \repo -> do
let Repository{repositoryName} = entityVal repo
Log.trace $ "Checking repo " <> tshow repositoryName
let toPath = liftIO . OsPath.encodeUtf . unpack
path <- liftA2 (</>) (toPath userUsername) (toPath repositoryName)
exists <- liftIO $ doesDirectoryExist $ root </> path
pure $
if exists
then Nothing
else Just path
pure . catMaybes . concat $ dirs'
data ServerConfig = ServerConfig
{ database :: !OsPath
, projectsRoot :: !OsPath
, host :: !HostPreference
, port :: !Int
, baseUrl :: !Text
, gitExecutable :: !OsPath
}
deriving (Eq, Show)
serverConfigParser :: Options.Parser ServerConfig
serverConfigParser = do
let toPath path = case OsPath.encodeUtf $ unpack path of
Left e -> error $ show e
Right x -> x
toPath :: Text -> OsPath
database <-
toPath
<$> strOption
( Options.long "database"
<> Options.metavar "PATH"
<> Options.help "Path to sqlite database (will be created)"
)
projectsRoot <-
toPath
<$> strOption
( Options.long "projects-path"
<> Options.metavar "PATH"
<> Options.help "Path to directory where git repositories will be stored"
)
host <-
strOption
( Options.long "host"
<> Options.metavar "ADDR"
<> Options.help "Address to bind to. Use '*' for any"
<> Options.value "localhost"
<> Options.showDefault
)
port <-
Options.option
Options.auto
( Options.long "port"
<> Options.metavar "NUMBER"
<> Options.help "TCP port to bind to"
<> Options.value 3000
<> Options.showDefault
)
baseUrl <-
strOption
( Options.long "base-url"
<> Options.metavar "HOST"
<> Options.help "Base address of server, for generating access links"
<> Options.value "http://localhost:3000"
<> Options.showDefault
)
gitExecutable <-
toPath
<$> strOption
( Options.long "git-executable"
<> Options.metavar "PATH"
<> Options.help "Git command to use. Will use 'git' in path by default"
<> Options.value "git"
<> Options.showDefault
)
pure
ServerConfig
{ database
, projectsRoot
, host
, port
, baseUrl
, gitExecutable
}
data AdminConfig = CreateUser
{ username :: !Text
, password :: !Text
, database :: !Text
}
createUserParser :: Options.Parser AdminConfig
createUserParser =
CreateUser
<$> strOption
( Options.long "username"
<> Options.metavar "TEXT"
<> Options.help "Username to create"
)
<*> strOption
( Options.long "password"
<> Options.metavar "TEXT"
<> Options.help "Plaintext password for user"
)
<*> strOption
( Options.long "database"
<> Options.metavar "PATH"
<> Options.help "Path to sqlite database (will be created)"
)
adminConfigParser :: Options.Parser AdminConfig
adminConfigParser =
Options.hsubparser $
Options.command
"add-user"
(Options.info createUserParser (Options.progDesc "Admin: create user"))
data HagiaOperation = Server ServerConfig | Adminka AdminConfig
configOptions :: Options.ParserInfo HagiaOperation
configOptions =
Options.info
( fmap Adminka adminConfigParser
<|> fmap Server serverConfigParser <**> Options.helper
)
$ Options.fullDesc
<> Options.progDesc "Hagia web server"
-- | Parse hagia config from command line options
getConfig :: IO HagiaOperation
getConfig = Options.execParser configOptions