2 Module : Gargantext.API.Settings
3 Description : Settings of the API (Server and Client)
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
12 {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
14 {-# LANGUAGE NoImplicitPrelude #-}
15 {-# LANGUAGE DataKinds #-}
16 {-# LANGUAGE DeriveGeneric #-}
17 {-# LANGUAGE ScopedTypeVariables #-}
18 {-# LANGUAGE TemplateHaskell #-}
19 {-# LANGUAGE OverloadedStrings #-}
20 {-# LANGUAGE FlexibleInstances #-}
22 module Gargantext.API.Settings
25 import System.Directory
26 import System.Log.FastLogger
28 import GHC.Generics (Generic)
29 import Prelude (Bounded(), fail)
30 import System.Environment (lookupEnv)
31 import System.IO (FilePath)
32 import Database.PostgreSQL.Simple (Connection, connect)
33 import Network.HTTP.Client (Manager)
34 import Network.HTTP.Client.TLS (newTlsManager)
37 import Data.Maybe (fromMaybe)
38 import Data.Either (either)
40 import Data.Text.Encoding (encodeUtf8)
41 import Data.ByteString.Lazy.Internal
44 import Servant.Client (BaseUrl, parseBaseUrl)
45 import Servant.Job.Async (newJobEnv, defaultSettings)
46 import Web.HttpApiData (parseUrlPiece)
47 import qualified Jose.Jwk as Jose
48 import qualified Jose.Jwa as Jose
50 import Control.Concurrent
51 import Control.Monad.Logger
53 import Gargantext.Prelude
54 import Gargantext.Database.Utils (databaseParameters, HasConnection(..))
55 import Gargantext.API.Ngrams (NgramsRepo, HasRepoVar(..), initMockRepo, r_version)
56 import Gargantext.API.Orchestrator.Types
60 data SendEmailType = SendEmailViaAws
63 deriving (Show, Read, Enum, Bounded, Generic)
66 data Settings = Settings
67 { _allowedOrigin :: ByteString -- ^ allowed origin for CORS
68 , _allowedHost :: ByteString -- ^ allowed host for CORS
69 , _appPort :: PortNumber
70 , _logLevelLimit :: LogLevel -- ^ log level from the monad-logger package
71 -- , _dbServer :: Text
72 -- ^ this is not used yet
73 , _jwtSecret :: Jose.Jwk -- ^ key from the jose-jwt package
74 , _sendLoginEmails :: SendEmailType
75 , _scrapydUrl :: BaseUrl
81 parseJwk :: Text -> Jose.Jwk
82 parseJwk secretStr = jwk
84 secretBs = encodeUtf8 secretStr
85 jwk = Jose.SymmetricJwk secretBs
88 (Just $ Jose.Signed Jose.HS256)
90 devSettings :: Settings
91 devSettings = Settings
92 { _allowedOrigin = "http://localhost:8008"
93 , _allowedHost = "localhost:3000"
95 , _logLevelLimit = LevelDebug
96 -- , _dbServer = "localhost"
97 -- generate with dd if=/dev/urandom bs=1 count=32 | base64
98 -- make sure jwtSecret differs between development and production, because you do not want
99 -- your production key inside source control.
100 , _jwtSecret = parseJwk "MVg0YAPVSPiYQc/qIs/rV/X32EFR0zOJWfHFgMbszMw="
101 , _sendLoginEmails = LogEmailToConsole
102 , _scrapydUrl = fromMaybe (panic "Invalid scrapy URL") $ parseBaseUrl "http://localhost:6800"
107 reqSetting :: FromHttpApiData a => Text -> IO a
109 e <- fromMaybe (panic $ "Missing " <> name) <$> lookupEnv (unpack name)
110 pure $ either (panic $ "Unable to parse " <> name) identity $ parseUrlPiece $ pack e
112 optSetting :: FromHttpApiData a => Text -> a -> IO a
113 optSetting name d = do
114 me <- lookupEnv (unpack name)
117 Just e -> pure $ either (panic $ "Unable to parse " <> name) identity $ parseUrlPiece $ pack e
119 --settingsFromEnvironment :: IO Settings
120 --settingsFromEnvironment =
121 -- Settings <$> (encodeUtf8 <$> reqSetting "ALLOWED_ORIGIN")
122 -- <*> (encodeUtf8 <$> reqSetting "ALLOWED_HOST")
123 -- <*> optSetting "PORT" 3000
124 -- <*> (parseLogLevel <$> optSetting "LOG_LEVEL" "warn")
125 -- <*> reqSetting "DB_SERVER"
126 -- <*> (parseJwk <$> reqSetting "JWT_SECRET")
127 -- <*> optSetting "SEND_EMAIL" SendEmailViaAws
129 data FireWall = FireWall { unFireWall :: Bool }
132 { _env_settings :: !Settings
133 , _env_logger :: !LoggerSet
134 , _env_conn :: !Connection
135 , _env_repo_var :: !(MVar NgramsRepo)
136 , _env_manager :: !Manager
137 , _env_self_url :: !BaseUrl
138 , _env_scrapers :: !ScrapersEnv
144 instance HasConnection Env where
145 connection = env_conn
147 instance HasRepoVar Env where
148 repoVar = env_repo_var
150 data MockEnv = MockEnv
151 { _menv_firewall :: !FireWall
157 repoSnapshot :: FilePath
158 repoSnapshot = "repo.json"
160 readRepo :: IO (MVar NgramsRepo)
162 repoExists <- doesFileExist repoSnapshot
166 e_repo <- eitherDecodeFileStrict repoSnapshot
167 repo <- either fail pure e_repo
168 let archive = repoSnapshot <> ".v" <> show (repo ^. r_version)
169 renameFile repoSnapshot archive
174 cleanEnv :: HasRepoVar env => env -> IO ()
175 cleanEnv env = encodeFile repoSnapshot =<< readMVar (env ^. repoVar)
177 newEnv :: PortNumber -> FilePath -> IO Env
178 newEnv port file = do
179 manager <- newTlsManager
180 settings <- pure (devSettings & appPort .~ port) -- TODO read from 'file'
181 when (port /= settings ^. appPort) $
182 panic "TODO: conflicting settings of port"
183 self_url <- parseBaseUrl $ "http://0.0.0.0:" <> show port
184 param <- databaseParameters file
185 conn <- connect param
187 scrapers_env <- newJobEnv defaultSettings manager
188 logger <- newStderrLoggerSet defaultBufSize
190 { _env_settings = settings
191 , _env_logger = logger
193 , _env_repo_var = repo_var
194 , _env_manager = manager
195 , _env_scrapers = scrapers_env
196 , _env_self_url = self_url
200 { _dev_env_conn :: !Connection
201 , _dev_env_repo_var :: !(MVar NgramsRepo)
206 instance HasConnection DevEnv where
207 connection = dev_env_conn
209 instance HasRepoVar DevEnv where
210 repoVar = dev_env_repo_var
212 newDevEnvWith :: FilePath -> IO DevEnv
213 newDevEnvWith file = do
214 param <- databaseParameters file
215 conn <- connect param
216 repo_var <- newMVar initMockRepo
218 { _dev_env_conn = conn
219 , _dev_env_repo_var = repo_var
222 newDevEnv :: IO DevEnv
223 newDevEnv = newDevEnvWith "gargantext.ini"