Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.

Commit bc0ac46

Browse files
authored
Refactoring + Error Redirects (#12)
* use some monads * refactoring, wip add redirect to preview * add error redirects to download/preview * fix http code * fix problem with staus code (could be improved) * fix message in redirect and always redirect on error for now
1 parent e29d69b commit bc0ac46

File tree

1 file changed

+67
-45
lines changed

1 file changed

+67
-45
lines changed

app/Main.hs

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{-# LANGUAGE DeriveGeneric #-}
22
{-# LANGUAGE DuplicateRecordFields #-}
33
{-# LANGUAGE OverloadedStrings #-}
4-
{-# LANGUAGE TemplateHaskell #-}
54

65
module Main where
76

@@ -19,6 +18,7 @@ import Data.Functor.Identity
1918
import Data.Maybe (fromMaybe)
2019
import qualified Data.Text as DataText
2120
import GHC.Generics
21+
import GHC.IO.Encoding (setLocaleEncoding)
2222
import GHC.Int
2323
import Lib
2424
import Network.HTTP.Req
@@ -33,18 +33,14 @@ import System.Environment
3333
import System.FilePath
3434
import System.IO
3535
import System.IO.Temp
36-
import GHC.IO.Encoding (setLocaleEncoding)
3736

3837
-- | Entrypoint to our application
3938
main :: IO ()
4039
main = do
4140
-- For ease of setup, we want to have a "sanity" command line
42-
-- argument. We'll see how this is used in the Dockerfile
43-
-- later. Desired behavior:
41+
-- argument.
4442
--
4543
-- If we have the argument "sanity", immediately exit
46-
-- If we have no arguments, run the server
47-
-- Otherwise, error out
4844
setLocaleEncoding utf8
4945
args <- getArgs
5046
case args of
@@ -54,7 +50,7 @@ main = do
5450
-- Run our application (defined below) on port 5000 with cors enabled
5551
run 5000 $ cors (const devCorsPolicy) app
5652
[restUrl, "stage"] -> do
57-
logStdOut "Launching DataHandler with dev profile"
53+
logStdOut "Launching DataHandler with stage profile"
5854
-- Run our application (defined below) on port 5000 with cors enabled
5955
run 5000 $ cors (const devCorsPolicy) app
6056
[restUrl, "prod"] -> do
@@ -72,8 +68,8 @@ app req send =
7268
["data", "upload", id] -> upload req send
7369
["data", "download"] -> download req send
7470
["data", "delete", id] -> delete req send
75-
["data","preview",id] -> preview req send
76-
["data","preview",id,_] -> preview req send
71+
["data", "preview", id] -> preview req send
72+
["data", "preview", id, _] -> preview req send
7773
["data", "health"] -> health req send
7874
-- anything else: 404
7975
missingEndpoint ->
@@ -164,6 +160,7 @@ download :: Application
164160
download req send = do
165161
let headers = requestHeaders req
166162
queryParam = getDownloadQuery $ queryString req
163+
redirectOnError = True --todo: make this a query param or something
167164
case queryParam of
168165
Nothing ->
169166
send $
@@ -175,8 +172,8 @@ download req send = do
175172
restUrl <- getRestUrl
176173
logStdOut "download"
177174
(responseBody, responseStatusCode, responseStatusMessage, fileNameHeader) <- getApi headers param restUrl
178-
case responseStatusCode of
179-
200 -> do
175+
case (responseStatusCode, redirectOnError) of
176+
(200, _) -> do
180177
let d = (eitherDecode $ L.fromStrict responseBody) :: (Either String [RestResponseFile])
181178
case d of
182179
Left err ->
@@ -200,18 +197,18 @@ download req send = do
200197
]
201198
path
202199
Nothing
203-
xs ->
200+
files ->
204201
withSystemTempFile "FileFighterFileHandler.zip" $
205202
\tmpFileName handle ->
206203
do
207204
let nameOfTheFolder = fromMaybe "Files" fileNameHeader
208205
let ss =
209206
mapM
210207
( \file -> do
211-
inZipPath <- mkEntrySelector $ fromMaybe (name file) (path file)
212-
loadEntry Store inZipPath (getPathFromFileId (show $ fileSystemId file))
208+
inZipPath <- mkEntrySelector $ fromMaybe (name file) (path file) -- either take the filename or path
209+
loadEntry Deflate inZipPath (getPathFromFileId (show $ fileSystemId file))
213210
)
214-
xs
211+
files
215212
createArchive tmpFileName ss
216213
send $
217214
responseFile
@@ -221,7 +218,24 @@ download req send = do
221218
]
222219
tmpFileName
223220
Nothing
224-
_ ->
221+
(_, True) -> do
222+
let decoded = (eitherDecode $ L.fromStrict responseBody) :: (Either String RestApiStatus)
223+
case decoded of
224+
Left err ->
225+
send $
226+
responseLBS
227+
HttpTypes.status500
228+
[("Content-Type", "application/json; charset=utf-8")]
229+
(encode $ RestApiStatus err "Internal Server Error")
230+
Right status ->
231+
let location =
232+
"/error?dest="
233+
<> HttpTypes.urlEncode True (rawPathInfo req)
234+
<> HttpTypes.urlEncode True (rawQueryString req)
235+
<> "&message="
236+
<> HttpTypes.urlEncode True (S8.pack $ message status)
237+
in send $ responseLBS HttpTypes.status303 [("Location", location)] ""
238+
(_, False) ->
225239
send $
226240
responseLBS
227241
(HttpTypes.mkStatus responseStatusCode responseStatusMessage)
@@ -242,59 +256,69 @@ getApi allHeaders param restUrl = runReq (defaultHttpConfig {httpConfigCheckResp
242256
liftIO $ logStdOut $ show (getOneHeader allHeaders "Cookie")
243257
return (responseBody r, responseStatusCode r, responseStatusMessage r, responseHeader r "X-FF-NAME")
244258

245-
246-
247-
preview :: Application
259+
preview :: Application
248260
preview req send = do
249261
let headers = requestHeaders req
250262
id = pathInfo req !! 2
263+
redirectOnError = True --todo: make this a query param or something
251264
restUrl <- getRestUrl
252265
(responseBody, responseStatusCode, responseStatusMessage) <- previewApi headers id restUrl
253-
case responseStatusCode of
254-
200 -> do
266+
logStdOut $ S8.unpack responseStatusMessage
267+
case (responseStatusCode, redirectOnError) of
268+
(200, _) -> do
255269
let decoded = (eitherDecode $ L.fromStrict responseBody) :: (Either String RestResponseFile)
256270
case decoded of
257271
Left err ->
258-
send $
259-
responseLBS
272+
send $
273+
responseLBS
260274
HttpTypes.status500
261275
[("Content-Type", "application/json; charset=utf-8")]
262276
(encode $ RestApiStatus err "Internal Server Error")
263-
Right file -> do
264-
let fileID = fileSystemId file
265-
fileMimeType = fromMaybe "application/octet-stream" (mimeType file)
266-
path = getPathFromFileId $ show fileID
267-
send $
268-
responseFile
269-
HttpTypes.status200
270-
[ ("Content-Type", S8.pack fileMimeType)
271-
]
272-
path
273-
Nothing
274-
_ ->
277+
Right file ->
278+
let fileID = fileSystemId file
279+
fileMimeType = fromMaybe "application/octet-stream" (mimeType file)
280+
path = getPathFromFileId $ show fileID
281+
in send $
282+
responseFile
283+
HttpTypes.status200
284+
[("Content-Type", S8.pack fileMimeType)]
285+
path
286+
Nothing
287+
(_, True) -> do
288+
let decoded = (eitherDecode $ L.fromStrict responseBody) :: (Either String RestApiStatus)
289+
case decoded of
290+
Left err ->
291+
send $
292+
responseLBS
293+
HttpTypes.status500
294+
[("Content-Type", "application/json; charset=utf-8")]
295+
(encode $ RestApiStatus err "Internal Server Error")
296+
Right status ->
297+
let location =
298+
"/error?dest=" <> HttpTypes.urlEncode True (rawPathInfo req)
299+
<> "&message="
300+
<> HttpTypes.urlEncode True (S8.pack $ message status)
301+
in send $ responseLBS HttpTypes.status303 [("Location", location)] ""
302+
(_, False) ->
275303
send $
276304
responseLBS
277305
(HttpTypes.mkStatus responseStatusCode responseStatusMessage)
278306
[("Content-Type", "application/json; charset=utf-8")]
279307
(L.fromStrict responseBody)
280-
281-
282-
283308

284309
previewApi :: [HttpTypes.Header] -> DataText.Text -> String -> IO (S8.ByteString, Int, S8.ByteString)
285310
previewApi allHeaders id restUrl = runReq (defaultHttpConfig {httpConfigCheckResponse = httpConfigDontCheckResponse}) $ do
286311
r <-
287312
req
288313
GET -- method
289-
(http (DataText.pack restUrl) /: "api" /: "v1" /: "filesystem" /: id /: "info" ) -- safe by construction URL
314+
(http (DataText.pack restUrl) /: "api" /: "v1" /: "filesystem" /: id /: "info") -- safe by construction URL
290315
--(http (DataText.pack restUrl) /: "v1" /: "filesystem" /: id /: "info" ) -- safe by construction URL
291316
NoReqBody -- use built-in options or add your own
292317
bsResponse -- specify how to interpret response
293318
(header "Cookie" (getOneHeader allHeaders "Cookie") <> port 8080) --PORT !!
294319
-- mempty -- query params, headers, explicit port number, etc.
295-
liftIO $ logStdOut $ show (getOneHeader allHeaders "Cookie")
320+
liftIO $ logStdOut "Requested fileinfo"
296321
return (responseBody r, responseStatusCode r, responseStatusMessage r)
297-
298322

299323
delete :: Application
300324
delete req send = do
@@ -340,14 +364,12 @@ deleteApi allHeaders restUrl fileId = runReq (defaultHttpConfig {httpConfigCheck
340364
health :: Application
341365
health req send = do
342366
deploymentType <- getDeploymentType
343-
foldersIO <- fmap (filterM doesDirectoryExist) (listDirectory ".")
344-
folders <- foldersIO
345-
files <- concat <$> mapM listDirectoryRelative folders
367+
files <- concat <$> (mapM listDirectoryRelative =<< (filterM doesDirectoryExist =<< listDirectory "."))
346368
actualFilesSize <- sum <$> mapM getFileSize files
347369

348370
let response =
349371
object
350-
[ "version" .= ("0.2.0" :: String),
372+
[ "version" .= ("0.2.1" :: String),
351373
"deploymentType" .= deploymentType,
352374
"actualFilesSize" .= actualFilesSize,
353375
"fileCount" .= length files

0 commit comments

Comments
 (0)