diff --git a/src/Roux/Server.hs b/src/Roux/Server.hs index 8ba1412..7d20e83 100644 --- a/src/Roux/Server.hs +++ b/src/Roux/Server.hs @@ -50,7 +50,8 @@ import qualified Roux.Html as Html import qualified Roux.RecipeIndex as Idx import Roux.SchemaOrg (SchemaOrgRecipe (..), parseSchemaOrgRecipe, schemaOrgToCooklang) import System.Exit (ExitCode (..)) -import System.Process (readProcessWithExitCode) +import System.IO (hSetBinaryMode) +import System.Process (CreateProcess (..), StdStream (CreatePipe), createProcess, proc, waitForProcess) -- | Read the full request body as a lazy ByteString. readRequestBody :: Wai.Request -> IO LB.ByteString @@ -234,6 +235,22 @@ parseFormBody body = in toEnum hexVal : go rest go (c : rest) = c : go rest +-- | Run a subprocess and capture stdout/stderr as raw bytes (no locale decoding). +readProcessBytes :: FilePath -> [String] -> IO (ExitCode, LB.ByteString, LB.ByteString) +readProcessBytes cmd args = do + let process = + (proc cmd args) + { std_out = CreatePipe + , std_err = CreatePipe + } + (Just outH, Just errH, _, ph) <- createProcess process + hSetBinaryMode outH True + hSetBinaryMode errH True + out <- LB.hGetContents outH + err <- LB.hGetContents errH + ec <- waitForProcess ph + return (ec, out, err) + -- | Locate the scrape-recipe script: check PATH first, then relative to project. findScrapeScript :: IO FilePath findScrapeScript = do @@ -250,23 +267,19 @@ runImportPipeline recipeDir url = do -- Step 1: locate and run the scraper script <- findScrapeScript putStrLn $ "[roux] importing from " <> T.unpack url <> " using script " <> script - (exitCode, stdout, stderr) <- - readProcessWithExitCode - script - ["--pretty", T.unpack url] - "" + (exitCode, stdoutBytes, stderrBytes) <- + readProcessBytes script ["--pretty", T.unpack url] case exitCode of ExitFailure _ -> do putStrLn $ "[roux] scrape-recipe exited with code " <> show exitCode <> " for " <> T.unpack url - putStrLn $ "[roux] scrape-recipe stderr: " <> stderr + putStrLn $ "[roux] scrape-recipe stderr: " <> T.unpack (TE.decodeUtf8 (LB.toStrict stderrBytes)) return $ Left $ Html.ImportError $ - "Could not scrape URL (" <> url <> "). The scraper exited with: " <> T.pack stderr + "Could not scrape URL (" <> url <> "). The scraper exited with: " <> TE.decodeUtf8 (LB.toStrict stderrBytes) ExitSuccess -> do - -- Step 2: decode stdout (String) as JSON Value - let jsonBytes = LB.fromStrict (TE.encodeUtf8 (T.pack stdout)) - jsonVal = A.decode jsonBytes :: Maybe A.Value + -- Step 2: decode stdout (raw bytes) as JSON Value + let jsonVal = A.decode stdoutBytes :: Maybe A.Value case jsonVal of Nothing -> do putStrLn $ "[roux] failed to decode JSON from scrape-recipe stdout for " <> T.unpack url