{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleContexts, AllowAmbiguousTypes, FlexibleInstances, IncoherentInstances, UndecidableInstances, PolyKinds, BlockArguments, DataKinds #-}
module IHP.Controller.FileUpload where
import IHP.Prelude
import Network.Wai.Parse (FileInfo, fileContent)
import Network.Wai (Request)
import qualified IHP.ModelSupport as ModelSupport
import qualified Data.ByteString.Lazy as LBS
import Wai.Request.Params.Middleware (RequestBody (..))
import IHP.Controller.Context ()
import qualified System.Process as Process
fileOrNothing :: (?request :: Request) => ByteString -> Maybe (FileInfo LBS.ByteString)
fileOrNothing :: (?request::Request) => ByteString -> Maybe (FileInfo ByteString)
fileOrNothing !ByteString
name =
case ?request::Request
Request
?request.parsedBody of
FormBody { [File ByteString]
files :: [File ByteString]
files :: RequestBody -> [File ByteString]
files } ->
case ByteString -> [File ByteString] -> Maybe (FileInfo ByteString)
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
name [File ByteString]
files of
Just FileInfo ByteString
fileInfo | Bool -> Bool
not (ByteString -> Bool
LBS.null (FileInfo ByteString
fileInfo.fileContent)) -> FileInfo ByteString -> Maybe (FileInfo ByteString)
forall a. a -> Maybe a
Just FileInfo ByteString
fileInfo
Maybe (FileInfo ByteString)
_ -> Maybe (FileInfo ByteString)
forall a. Maybe a
Nothing
RequestBody
_ -> Maybe (FileInfo ByteString)
forall a. Maybe a
Nothing
filesByName :: (?request :: Request) => ByteString -> [FileInfo LBS.ByteString]
filesByName :: (?request::Request) => ByteString -> [FileInfo ByteString]
filesByName !ByteString
name =
case ?request::Request
Request
?request.parsedBody of
FormBody { [File ByteString]
files :: RequestBody -> [File ByteString]
files :: [File ByteString]
files } -> [File ByteString]
files
[File ByteString]
-> ([File ByteString] -> [File ByteString]) -> [File ByteString]
forall a b. a -> (a -> b) -> b
|> (File ByteString -> Bool) -> [File ByteString] -> [File ByteString]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(ByteString
filename, FileInfo ByteString
_) -> ByteString
filename ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
name)
[File ByteString]
-> ([File ByteString] -> [FileInfo ByteString])
-> [FileInfo ByteString]
forall a b. a -> (a -> b) -> b
|> (File ByteString -> FileInfo ByteString)
-> [File ByteString] -> [FileInfo ByteString]
forall a b. (a -> b) -> [a] -> [b]
map File ByteString -> FileInfo ByteString
forall a b. (a, b) -> b
snd
RequestBody
_ -> []
data ImageUploadOptions = ImageUploadOptions {
ImageUploadOptions -> Text
convertTo :: Text
, ImageUploadOptions -> Text
imageMagickOptions :: Text
}
uploadImageWithOptions :: forall (fieldName :: Symbol) record (tableName :: Symbol). (
?request :: Request
, SetField fieldName record (Maybe Text)
, KnownSymbol fieldName
, HasField "id" record (ModelSupport.Id (ModelSupport.NormalizeModel record))
, Show (ModelSupport.PrimaryKey (ModelSupport.GetTableName (ModelSupport.NormalizeModel record)))
, tableName ~ ModelSupport.GetTableName record
, KnownSymbol tableName
) => ImageUploadOptions -> Proxy fieldName -> record -> IO record
uploadImageWithOptions :: forall (fieldName :: Symbol) record (tableName :: Symbol).
(?request::Request, SetField fieldName record (Maybe Text),
KnownSymbol fieldName,
HasField "id" record (Id (NormalizeModel record)),
Show (PrimaryKey (GetTableName (NormalizeModel record))),
tableName ~ GetTableName record, KnownSymbol tableName) =>
ImageUploadOptions -> Proxy fieldName -> record -> IO record
uploadImageWithOptions ImageUploadOptions
options Proxy fieldName
_ record
user =
let
ByteString
fieldName :: ByteString = String -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs (Proxy fieldName -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
forall (t :: Symbol). Proxy t
Proxy @fieldName))
Text
tableName :: Text = String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (Proxy tableName -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
forall (t :: Symbol). Proxy t
Proxy @tableName))
Text
uploadDir :: Text = Text
"static"
Text
baseImagePath :: Text = Text
"/uploads/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Id' (GetTableName (GetModelByTableName tableName)) -> Text
forall a. Show a => a -> Text
tshow record
user.id Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/picture."
Text
imagePath :: Text = Text
baseImagePath Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"jpg"
uploadFilePath :: Text
uploadFilePath = Text
baseImagePath Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"upload"
in case (?request::Request) => ByteString -> Maybe (FileInfo ByteString)
ByteString -> Maybe (FileInfo ByteString)
fileOrNothing ByteString
fieldName of
Just FileInfo ByteString
file -> IO record -> IO record
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO do
_ <- String -> IO ExitCode
Process.system (String
"mkdir -p `dirname " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a b. ConvertibleStrings a b => a -> b
cs (Text
uploadDir Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
uploadFilePath) String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"`")
let fullImagePath = Text
uploadDir Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
imagePath
fileContent file |> LBS.writeFile (cs (uploadDir <> uploadFilePath))
Process.runCommand (cs ("convert " <> cs uploadDir <> uploadFilePath <> " " <> (getField @"imageMagickOptions" options) <> " " <> cs fullImagePath))
user
|> setField @fieldName (Just (cs imagePath :: Text))
|> return
Maybe (FileInfo ByteString)
_ -> record -> IO record
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure record
user
uploadImageFile :: forall (fieldName :: Symbol) record (tableName :: Symbol). (
?request :: Request
, SetField fieldName record (Maybe Text)
, KnownSymbol fieldName
, HasField "id" record (ModelSupport.Id (ModelSupport.NormalizeModel record))
, Show (ModelSupport.PrimaryKey (ModelSupport.GetTableName (ModelSupport.NormalizeModel record)))
, tableName ~ ModelSupport.GetTableName record
, KnownSymbol tableName
) => Text -> Proxy fieldName -> record -> IO record
uploadImageFile :: forall (fieldName :: Symbol) record (tableName :: Symbol).
(?request::Request, SetField fieldName record (Maybe Text),
KnownSymbol fieldName,
HasField "id" record (Id (NormalizeModel record)),
Show (PrimaryKey (GetTableName (NormalizeModel record))),
tableName ~ GetTableName record, KnownSymbol tableName) =>
Text -> Proxy fieldName -> record -> IO record
uploadImageFile Text
ext Proxy fieldName
_ record
user =
let
ByteString
fieldName :: ByteString = String -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs (Proxy fieldName -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
forall (t :: Symbol). Proxy t
Proxy @fieldName))
Text
tableName :: Text = String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (Proxy tableName -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
forall (t :: Symbol). Proxy t
Proxy @tableName))
Text
uploadDir :: Text = Text
"static"
Text
imagePath :: Text = Text
"/uploads/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Id' (GetTableName (GetModelByTableName tableName)) -> Text
forall a. Show a => a -> Text
tshow record
user.id Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/picture." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
ext
in case (?request::Request) => ByteString -> Maybe (FileInfo ByteString)
ByteString -> Maybe (FileInfo ByteString)
fileOrNothing ByteString
fieldName of
Just FileInfo ByteString
file -> IO record -> IO record
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO do
_ <- String -> IO ExitCode
Process.system (String
"mkdir -p `dirname " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a b. ConvertibleStrings a b => a -> b
cs (Text
uploadDir Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
imagePath) String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"`")
fileContent file |> LBS.writeFile (cs $ uploadDir <> imagePath)
user
|> setField @fieldName (Just (cs imagePath :: Text))
|> pure
Maybe (FileInfo ByteString)
_ -> record -> IO record
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure record
user
uploadPng ::
( ?request :: Request
, SetField fieldName record (Maybe Text)
, HasField "id" record (ModelSupport.Id' (GetTableName (ModelSupport.GetModelByTableName (GetTableName record))))
, Show (ModelSupport.PrimaryKey (GetTableName (ModelSupport.GetModelByTableName (GetTableName record))))
, KnownSymbol fieldName
, KnownSymbol (GetTableName record)
) => Proxy fieldName -> record -> IO record
uploadPng :: forall (fieldName :: Symbol) record.
(?request::Request, SetField fieldName record (Maybe Text),
HasField
"id"
record
(Id' (GetTableName (GetModelByTableName (GetTableName record)))),
Show
(PrimaryKey
(GetTableName (GetModelByTableName (GetTableName record)))),
KnownSymbol fieldName, KnownSymbol (GetTableName record)) =>
Proxy fieldName -> record -> IO record
uploadPng Proxy fieldName
field record
record = Text -> Proxy fieldName -> record -> IO record
forall (fieldName :: Symbol) record (tableName :: Symbol).
(?request::Request, SetField fieldName record (Maybe Text),
KnownSymbol fieldName,
HasField "id" record (Id (NormalizeModel record)),
Show (PrimaryKey (GetTableName (NormalizeModel record))),
tableName ~ GetTableName record, KnownSymbol tableName) =>
Text -> Proxy fieldName -> record -> IO record
uploadImageFile Text
"png" Proxy fieldName
field record
record
uploadSVG ::
( ?request :: Request
, SetField fieldName record (Maybe Text)
, HasField "id" record (ModelSupport.Id' (GetTableName (ModelSupport.GetModelByTableName (GetTableName record))))
, Show (ModelSupport.PrimaryKey (GetTableName (ModelSupport.GetModelByTableName (GetTableName record))))
, KnownSymbol fieldName
, KnownSymbol (GetTableName record)
) => Proxy fieldName -> record -> IO record
uploadSVG :: forall (fieldName :: Symbol) record.
(?request::Request, SetField fieldName record (Maybe Text),
HasField
"id"
record
(Id' (GetTableName (GetModelByTableName (GetTableName record)))),
Show
(PrimaryKey
(GetTableName (GetModelByTableName (GetTableName record)))),
KnownSymbol fieldName, KnownSymbol (GetTableName record)) =>
Proxy fieldName -> record -> IO record
uploadSVG = Text -> Proxy fieldName -> record -> IO record
forall (fieldName :: Symbol) record (tableName :: Symbol).
(?request::Request, SetField fieldName record (Maybe Text),
KnownSymbol fieldName,
HasField "id" record (Id (NormalizeModel record)),
Show (PrimaryKey (GetTableName (NormalizeModel record))),
tableName ~ GetTableName record, KnownSymbol tableName) =>
Text -> Proxy fieldName -> record -> IO record
uploadImageFile Text
"svg"