{-# LANGUAGE OverloadedStrings #-} {- | Entry point for the Roux recipe server. Starts a Warp HTTP server and serves recipe content. -} module Main (main) where import Data.String (fromString) import qualified Network.Wai.Handler.Warp as Warp import qualified Options.Applicative as Opts import System.IO (BufferMode (NoBuffering), hSetBuffering, stderr, stdout) import qualified Roux import qualified Roux.Config as Config -- --------------------------------------------------------------------------- -- CLI options -- --------------------------------------------------------------------------- data Options = Options { optHost :: String , optPort :: Int , optRecipeDir :: FilePath , optConfigFile :: FilePath } optionsParser :: Opts.Parser Options optionsParser = Options <$> Opts.strOption ( Opts.long "host" <> Opts.metavar "HOST" <> Opts.help "Listen host" <> Opts.value "0.0.0.0" <> Opts.showDefault ) <*> Opts.option Opts.auto ( Opts.long "port" <> Opts.short 'p' <> Opts.metavar "PORT" <> Opts.help "Listen port" <> Opts.value 8080 <> Opts.showDefault ) <*> Opts.strOption ( Opts.long "recipe-dir" <> Opts.short 'r' <> Opts.metavar "DIR" <> Opts.help "Path to directory containing Cooklang recipe files" <> Opts.value "recipes" <> Opts.showDefault ) <*> Opts.strOption ( Opts.long "config-file" <> Opts.metavar "FILE" <> Opts.help "Path to YAML configuration file" <> Opts.value "roux-config.yaml" <> Opts.showDefault ) main :: IO () main = do -- Disable buffering so Docker sees log output immediately. hSetBuffering stdout NoBuffering hSetBuffering stderr NoBuffering opts <- Opts.execParser $ Opts.info (optionsParser Opts.<**> Opts.helper) ( Opts.fullDesc <> Opts.progDesc "Roux recipe management server" <> Opts.header "roux-server — personal/family recipe manager" ) let settings = Warp.setPort (optPort opts) $ Warp.setHost (fromString (optHost opts)) Warp.defaultSettings putStrLn $ "[roux] scanning recipes from " <> optRecipeDir opts putStrLn $ "[roux] listening on " <> optHost opts <> ":" <> show (optPort opts) putStrLn $ "[roux] config file: " <> optConfigFile opts -- Load config (file may be missing; LLM features disabled gracefully) configResult <- Config.loadConfig (optConfigFile opts) let config = case configResult of Right cfg -> cfg Left _ -> Config.RouxConfig Nothing case configResult of Left err -> putStrLn $ "[roux] warning: " <> err <> " — LLM features disabled" Right _ -> pure () waiApp <- Roux.app (optRecipeDir opts) config Warp.runSettings settings waiApp