forked from thoughtbot/carnival
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Settings.hs
201 lines (174 loc) · 7.76 KB
/
Settings.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
-- | Settings are centralized, as much as possible, into this file. This
-- includes database connection settings, static file locations, etc.
-- In addition, you can configure a number of different aspects of Yesod
-- by overriding methods in the Yesod typeclass. That instance is
-- declared in the Foundation.hs file.
module Settings where
import ClassyPrelude.Yesod
import Control.Exception (throw)
import Data.Aeson (Result (..), fromJSON, withObject, (.!=),
(.:?))
import Data.FileEmbed (embedFile)
import Data.Yaml (decodeEither')
import Database.Persist.Postgresql (PostgresConf)
import Language.Haskell.TH.Syntax (Exp, Name, Q)
import Network.Wai.Handler.Warp (HostPreference)
import Yesod.Default.Config2 (applyEnvValue, configSettingsYml)
import Yesod.Default.Util (WidgetFileSettings, widgetFileNoReload,
widgetFileReload,
TemplateLanguage(..),
defaultTemplateLanguages, globFile,
wfsHamletSettings, wfsLanguages)
import Text.Coffee
import Text.Hamlet
import Text.Lucius
import Text.Shakespeare.Text
-- | Actions which only require access to the database connection can be given
-- type @DB a@ (as opposed to @YesodDB App a@). This allows them to also be
-- called in tests.
type DB a = forall (m :: * -> *).
(MonadIO m, Functor m) => ReaderT SqlBackend m a
data OAuthKeys = OAuthKeys
{ oauthKeysClientId :: Text
, oauthKeysClientSecret :: Text
}
data StripeKeys = StripeKeys
{ stripeKeysSecretKey :: Text
, stripeKeysPublishableKey :: Text
}
-- | Runtime settings to configure this application. These settings can be
-- loaded from various sources: defaults, environment variables, config files,
-- theoretically even a database.
data AppSettings = AppSettings
{ appStaticDir :: String
-- ^ Directory from which to serve static files.
, appDatabaseConf :: PostgresConf
-- ^ Configuration settings for accessing the database.
, appRoot :: Text
-- ^ Base for all generated URLs.
, appHost :: HostPreference
-- ^ Host/interface the server should bind to.
, appPort :: Int
-- ^ Port to listen on
, appIpFromHeader :: Bool
-- ^ Get the IP address from the header when logging. Useful when sitting
-- behind a reverse proxy.
, appDetailedRequestLogging :: Bool
-- ^ Use detailed request logging system
, appShouldLogAll :: Bool
-- ^ Should all log messages be displayed?
, appReloadTemplates :: Bool
-- ^ Use the reload version of templates
, appMutableStatic :: Bool
-- ^ Assume that files in the static dir may change after compilation
, appSkipCombining :: Bool
-- ^ Perform no stylesheet/script combining
-- Example app-specific configuration values.
, appCopyright :: Text
-- ^ Copyright text to appear in the footer of the page
, appAnalytics :: Maybe Text
-- ^ Segment Analytics code
, appIntercomSecret :: Maybe Text
-- ^ Intercom secure secret
, appSendMail :: Bool
-- ^ Actually send e-mail (via SendGrid)?
, appDatabaseUrl :: Bool
-- ^ Parse database info from DATABASE_URL?
, appAllowDummyAuth :: Bool
-- ^ Add the Dummy Auth plugin (for authenticating in tests)?
, appForceSSL :: Bool
-- ^ Force all traffic to use SSL via sslOnlyMiddleware?
}
instance FromJSON AppSettings where
parseJSON = withObject "AppSettings" $ \o -> do
let defaultDev =
#if DEVELOPMENT
True
#else
False
#endif
appStaticDir <- o .: "static-dir"
appDatabaseConf <- o .: "database"
appRoot <- o .: "approot"
appHost <- fromString <$> o .: "host"
appPort <- o .: "port"
appIpFromHeader <- o .: "ip-from-header"
appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev
appShouldLogAll <- o .:? "should-log-all" .!= defaultDev
appReloadTemplates <- o .:? "reload-templates" .!= defaultDev
appMutableStatic <- o .:? "mutable-static" .!= defaultDev
appSkipCombining <- o .:? "skip-combining" .!= defaultDev
appCopyright <- o .: "copyright"
appAnalytics <- o .:? "analytics"
appIntercomSecret <- o .:? "intercom-secret"
appSendMail <- o .:? "send-mail" .!= (not defaultDev)
appDatabaseUrl <- o .:? "database-url" .!= (not defaultDev)
appAllowDummyAuth <- o .:? "allow-dummy-auth" .!= defaultDev
appForceSSL <- o .:? "force-ssl" .!= (not defaultDev)
return AppSettings {..}
-- | Settings for 'widgetFile', such as which template languages to support and
-- default Hamlet settings.
--
-- For more information on modifying behavior, see:
--
-- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile
widgetFileSettings :: WidgetFileSettings
widgetFileSettings = def
{ wfsLanguages = \hset -> defaultTemplateLanguages hset ++ [coffeeLang]
, wfsHamletSettings = defaultHamletSettings
{ hamletNewlines = AlwaysNewlines
}
}
where
coffeeLang = TemplateLanguage True "coffee"
Text.Coffee.coffeeFile Text.Coffee.coffeeFileReload
-- | How static files should be combined.
combineSettings :: CombineSettings
combineSettings = def
-- The rest of this file contains settings which rarely need changing by a
-- user.
widgetFile :: String -> Q Exp
widgetFile = (if appReloadTemplates compileTimeAppSettings
then widgetFileReload
else widgetFileNoReload)
widgetFileSettings
coffeeFile :: String -> Q Exp
coffeeFile f =
if appReloadTemplates compileTimeAppSettings
then Text.Coffee.coffeeFileReload $ globFile "coffee" f
else Text.Coffee.coffeeFile $ globFile "coffee" f
luciusFile :: String -> Q Exp
luciusFile f =
if appReloadTemplates compileTimeAppSettings
then Text.Lucius.luciusFileReload $ globFile "lucius" f
else Text.Lucius.luciusFile $ globFile "lucius" f
textFile :: String -> Q Exp
textFile f =
if appReloadTemplates compileTimeAppSettings
then Text.Shakespeare.Text.textFileReload $ globFile "text" f
else Text.Shakespeare.Text.textFile $ globFile "text" f
-- | Raw bytes at compile time of @config/settings.yml@
configSettingsYmlBS :: ByteString
configSettingsYmlBS = $(embedFile configSettingsYml)
-- | @config/settings.yml@, parsed to a @Value@.
configSettingsYmlValue :: Value
configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS
-- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.
compileTimeAppSettings :: AppSettings
compileTimeAppSettings =
case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of
Error e -> error e
Success settings -> settings
-- The following two functions can be used to combine multiple CSS or JS files
-- at compile time to decrease the number of http requests.
-- Sample usage (inside a Widget):
--
-- > $(combineStylesheets 'StaticR [style1_css, style2_css])
combineStylesheets :: Name -> [Route Static] -> Q Exp
combineStylesheets = combineStylesheets'
(appSkipCombining compileTimeAppSettings)
combineSettings
combineScripts :: Name -> [Route Static] -> Q Exp
combineScripts = combineScripts'
(appSkipCombining compileTimeAppSettings)
combineSettings