{-|
Module: IHP.FileStorage.Config
Description: Helpers for Config.hs to set up the File Storage
Copyright: (c) digitally induced GmbH, 2021
-}
module IHP.FileStorage.Config
( initS3Storage
, initStaticDirStorage
, initMinioStorage
, initFilebaseStorage
) where

import IHP.Prelude
import IHP.FileStorage.Types
import IHP.FrameworkConfig

import Network.Minio

import qualified Control.Monad.Trans.State.Strict as State
import qualified Data.TMap as TMap
import qualified System.Environment as Env
import qualified Data.Text as Text
import Control.Monad.Trans.Maybe

-- | The AWS access key and secret key have to be provided using the @AWS_ACCESS_KEY_ID@ and @AWS_SECRET_ACCESS_KEY@ env vars.
--
-- __Example:__ Set up a s3 storage in @Config.hs@
--
-- > module Config where
-- >
-- > import IHP.Prelude
-- > import IHP.Environment
-- > import IHP.FrameworkConfig
-- > import IHP.FileStorage.Config
-- >
-- > config :: ConfigBuilder
-- > config = do
-- >     option Development
-- >     option (AppHostname "localhost")
-- >     initS3Storage "eu-central-1" "my-bucket-name"
--
initS3Storage :: Text -> Text -> State.StateT TMap.TMap IO ()
initS3Storage :: Text -> Text -> StateT TMap IO ()
initS3Storage Text
region Text
bucket = do
    ConnectInfo
connectInfo <- ConnectInfo
awsCI
        ConnectInfo -> (ConnectInfo -> ConnectInfo) -> ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> Text -> ConnectInfo -> ConnectInfo
setRegion Text
region
        ConnectInfo -> (ConnectInfo -> IO ConnectInfo) -> IO ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> [Provider] -> ConnectInfo -> IO ConnectInfo
setCredsFrom [Provider
fromAWSEnv]
        IO ConnectInfo
-> (IO ConnectInfo -> StateT TMap IO ConnectInfo)
-> StateT TMap IO ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> IO ConnectInfo -> StateT TMap IO ConnectInfo
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO

    let baseUrl :: Text
baseUrl = Text
"https://" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
bucket Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
".s3." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
region Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
".amazonaws.com/"
    FileStorage -> StateT TMap IO ()
forall option. Typeable option => option -> StateT TMap IO ()
option S3Storage :: ConnectInfo -> Text -> Text -> FileStorage
S3Storage { ConnectInfo
$sel:connectInfo:StaticDirStorage :: ConnectInfo
connectInfo :: ConnectInfo
connectInfo, Text
$sel:bucket:StaticDirStorage :: Text
bucket :: Text
bucket, Text
$sel:baseUrl:StaticDirStorage :: Text
baseUrl :: Text
baseUrl }

-- | The Minio access key and secret key have to be provided using the @MINIO_ACCESS_KEY@ and @MINIO_SECRET_KEY@ env vars.
--
-- __Example:__ Set up a minio storage in @Config.hs@
--
-- > module Config where
-- >
-- > import IHP.Prelude
-- > import IHP.Environment
-- > import IHP.FrameworkConfig
-- > import IHP.FileStorage.Config
-- >
-- > config :: ConfigBuilder
-- > config = do
-- >     option Development
-- >     option (AppHostname "localhost")
-- >     initMinioStorage "https://minio.example.com" "my-bucket-name"
--
initMinioStorage :: Text -> Text -> State.StateT TMap.TMap IO ()
initMinioStorage :: Text -> Text -> StateT TMap IO ()
initMinioStorage Text
server Text
bucket = do
    ConnectInfo
connectInfo <- Text
server
        Text -> (Text -> String) -> String
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> Text -> String
forall a b. ConvertibleStrings a b => a -> b
cs
        String -> (String -> ConnectInfo) -> ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> String -> ConnectInfo
forall a. IsString a => String -> a
fromString
        ConnectInfo -> (ConnectInfo -> IO ConnectInfo) -> IO ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> [Provider] -> ConnectInfo -> IO ConnectInfo
setCredsFrom [Provider
fromMinioEnv]
        IO ConnectInfo
-> (IO ConnectInfo -> StateT TMap IO ConnectInfo)
-> StateT TMap IO ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> IO ConnectInfo -> StateT TMap IO ConnectInfo
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO

    let baseUrl :: Text
baseUrl = Text
server Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
bucket Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/"
    FileStorage -> StateT TMap IO ()
forall option. Typeable option => option -> StateT TMap IO ()
option S3Storage :: ConnectInfo -> Text -> Text -> FileStorage
S3Storage { ConnectInfo
connectInfo :: ConnectInfo
$sel:connectInfo:StaticDirStorage :: ConnectInfo
connectInfo, Text
bucket :: Text
$sel:bucket:StaticDirStorage :: Text
bucket, Text
baseUrl :: Text
$sel:baseUrl:StaticDirStorage :: Text
baseUrl }

-- | Stores files publicly visible inside the @static@ directory
--
-- __Example:__ Store uploaded files in the @static/@ directory
--
-- > module Config where
-- >
-- > import IHP.Prelude
-- > import IHP.Environment
-- > import IHP.FrameworkConfig
-- > import IHP.FileStorage.Config
-- >
-- > config :: ConfigBuilder
-- > config = do
-- >     option Development
-- >     option (AppHostname "localhost")
-- >     initStaticDirStorage
--
initStaticDirStorage :: State.StateT TMap.TMap IO ()
initStaticDirStorage :: StateT TMap IO ()
initStaticDirStorage = FileStorage -> StateT TMap IO ()
forall option. Typeable option => option -> StateT TMap IO ()
option FileStorage
StaticDirStorage

-- | The Filebase access key and secret key have to be provided using the @FILEBASE_KEY@ and @FILEBASE_SECRET@ env vars.
--
-- __Example:__ Set up a Filebase storage in @Config.hs@
--
-- > module Config where
-- >
-- > import IHP.Prelude
-- > import IHP.Environment
-- > import IHP.FrameworkConfig
-- > import IHP.FileStorage.Config
-- >
-- > config :: ConfigBuilder
-- > config = do
-- >     option Development
-- >     option (AppHostname "localhost")
-- >     initFilebaseStorage "my-bucket-name"
--
initFilebaseStorage :: Text -> State.StateT TMap.TMap IO ()
initFilebaseStorage :: Text -> StateT TMap IO ()
initFilebaseStorage Text
bucket = do
    ConnectInfo
connectInfo <- ConnectInfo
filebaseCI
        ConnectInfo -> (ConnectInfo -> IO ConnectInfo) -> IO ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> [Provider] -> ConnectInfo -> IO ConnectInfo
setCredsFrom [Provider
fromFilebaseEnv]
        IO ConnectInfo
-> (IO ConnectInfo -> StateT TMap IO ConnectInfo)
-> StateT TMap IO ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> IO ConnectInfo -> StateT TMap IO ConnectInfo
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO

    let baseUrl :: Text
baseUrl = Text
"https://" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
bucket Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
".s3.filebase.com/"
    FileStorage -> StateT TMap IO ()
forall option. Typeable option => option -> StateT TMap IO ()
option S3Storage :: ConnectInfo -> Text -> Text -> FileStorage
S3Storage { ConnectInfo
connectInfo :: ConnectInfo
$sel:connectInfo:StaticDirStorage :: ConnectInfo
connectInfo, Text
bucket :: Text
$sel:bucket:StaticDirStorage :: Text
bucket, Text
baseUrl :: Text
$sel:baseUrl:StaticDirStorage :: Text
baseUrl }

filebaseCI :: ConnectInfo
filebaseCI :: ConnectInfo
filebaseCI = ConnectInfo
"https://s3.filebase.com" ConnectInfo -> (ConnectInfo -> ConnectInfo) -> ConnectInfo
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> Text -> ConnectInfo -> ConnectInfo
setRegion Text
"us-east-1"

fromFilebaseEnv :: Provider
fromFilebaseEnv :: Provider
fromFilebaseEnv = MaybeT IO Credentials -> Provider
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT IO Credentials -> Provider)
-> MaybeT IO Credentials -> Provider
forall a b. (a -> b) -> a -> b
$ do
    String
filebaseKey <- IO (Maybe String) -> MaybeT IO String
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (IO (Maybe String) -> MaybeT IO String)
-> IO (Maybe String) -> MaybeT IO String
forall a b. (a -> b) -> a -> b
$ String -> IO (Maybe String)
Env.lookupEnv String
"FILEBASE_KEY"
    String
filebaseSecret <- IO (Maybe String) -> MaybeT IO String
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (IO (Maybe String) -> MaybeT IO String)
-> IO (Maybe String) -> MaybeT IO String
forall a b. (a -> b) -> a -> b
$ String -> IO (Maybe String)
Env.lookupEnv String
"FILEBASE_SECRET"
    Credentials -> MaybeT IO Credentials
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Credentials -> MaybeT IO Credentials)
-> Credentials -> MaybeT IO Credentials
forall a b. (a -> b) -> a -> b
$ Text -> Text -> Credentials
Credentials (String -> Text
Text.pack String
filebaseKey) (String -> Text
Text.pack String
filebaseSecret)