Read settings from config file (#714)
This commit is contained in:
@@ -32,6 +32,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- HTTP 401 rather than 400 for expired JWT - @begriffs
|
- HTTP 401 rather than 400 for expired JWT - @begriffs
|
||||||
- Remove default JWT secret - @begriffs
|
- Remove default JWT secret - @begriffs
|
||||||
- Use GUC request.jwt.claim.foo rather than postgrest.claims.foo - @begriffs
|
- Use GUC request.jwt.claim.foo rather than postgrest.claims.foo - @begriffs
|
||||||
|
- Use config file rather than command line arguments - @begriffs
|
||||||
|
|
||||||
## [0.3.2.0] - 2016-06-10
|
## [0.3.2.0] - 2016-06-10
|
||||||
|
|
||||||
|
|||||||
+4
-13
@@ -1,11 +1,6 @@
|
|||||||
FROM debian:jessie
|
FROM debian:jessie
|
||||||
|
|
||||||
ENV POSTGREST_VERSION 0.3.2.0
|
ENV POSTGREST_VERSION 0.4.0.0
|
||||||
ENV POSTGREST_SCHEMA public
|
|
||||||
ENV POSTGREST_ANONYMOUS postgres
|
|
||||||
ENV POSTGREST_JWT_SECRET thisisnotarealsecret
|
|
||||||
ENV POSTGREST_MAX_ROWS 1000000
|
|
||||||
ENV POSTGREST_POOL 200
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y tar xz-utils wget libpq-dev && \
|
apt-get install -y tar xz-utils wget libpq-dev && \
|
||||||
@@ -16,12 +11,8 @@ RUN wget http://github.com/begriffs/postgrest/releases/download/v${POSTGREST_VER
|
|||||||
mv postgrest /usr/local/bin/postgrest && \
|
mv postgrest /usr/local/bin/postgrest && \
|
||||||
rm postgrest-${POSTGREST_VERSION}-ubuntu.tar.xz
|
rm postgrest-${POSTGREST_VERSION}-ubuntu.tar.xz
|
||||||
|
|
||||||
CMD exec postgrest postgres://${PG_ENV_POSTGRES_USER}:${PG_ENV_POSTGRES_PASSWORD}@${PG_PORT_5432_TCP_ADDR}:${PG_PORT_5432_TCP_PORT}/${PG_ENV_POSTGRES_DB} \
|
# PostgREST reads /etc/postgrest.conf so map the configuration
|
||||||
--port 3000 \
|
# file in when you run this container
|
||||||
--schema ${POSTGREST_SCHEMA} \
|
CMD exec postgrest
|
||||||
--anonymous ${POSTGREST_ANONYMOUS} \
|
|
||||||
--pool ${POSTGREST_POOL} \
|
|
||||||
--jwt-secret ${POSTGREST_JWT_SECRET} \
|
|
||||||
--max-rows ${POSTGREST_MAX_ROWS}
|
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ import PostgREST.Config (AppConfig (..),
|
|||||||
minimumPgVersion,
|
minimumPgVersion,
|
||||||
prettyVersion,
|
prettyVersion,
|
||||||
readOptions)
|
readOptions)
|
||||||
|
import PostgREST.Error (prettyUsageError)
|
||||||
import PostgREST.OpenAPI (isMalformedProxyUri)
|
import PostgREST.OpenAPI (isMalformedProxyUri)
|
||||||
import PostgREST.DbStructure
|
import PostgREST.DbStructure
|
||||||
|
|
||||||
import Control.AutoUpdate
|
import Control.AutoUpdate
|
||||||
import Data.String (IsString (..))
|
import Data.String (IsString (..))
|
||||||
import Data.Text (stripPrefix)
|
import Data.Text (stripPrefix)
|
||||||
|
import Data.Text.IO (hPutStrLn)
|
||||||
import Data.Function (id)
|
import Data.Function (id)
|
||||||
import Data.Time.Clock.POSIX (getPOSIXTime)
|
import Data.Time.Clock.POSIX (getPOSIXTime)
|
||||||
import qualified Hasql.Query as H
|
import qualified Hasql.Query as H
|
||||||
@@ -68,6 +70,10 @@ main = do
|
|||||||
<> show minimumPgVersion)
|
<> show minimumPgVersion)
|
||||||
getDbStructure (toS $ configSchema conf)
|
getDbStructure (toS $ configSchema conf)
|
||||||
|
|
||||||
|
forM_ (lefts [result]) $ \e -> do
|
||||||
|
hPutStrLn stderr (prettyUsageError e)
|
||||||
|
exitFailure
|
||||||
|
|
||||||
refDbStructure <- newIORef $ either (panic . show) id result
|
refDbStructure <- newIORef $ either (panic . show) id result
|
||||||
|
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
|
|||||||
@@ -51,11 +51,13 @@ library
|
|||||||
, bytestring
|
, bytestring
|
||||||
, case-insensitive
|
, case-insensitive
|
||||||
, cassava
|
, cassava
|
||||||
|
, configurator
|
||||||
, containers
|
, containers
|
||||||
, contravariant
|
, contravariant
|
||||||
, hasql
|
, hasql
|
||||||
, hasql-pool == 0.4.1
|
, hasql-pool == 0.4.1
|
||||||
, hasql-transaction == 0.4.5.1
|
, hasql-transaction == 0.4.5.1
|
||||||
|
, heredoc
|
||||||
, HTTP
|
, HTTP
|
||||||
, http-types
|
, http-types
|
||||||
, insert-ordered-containers
|
, insert-ordered-containers
|
||||||
|
|||||||
+102
-32
@@ -2,12 +2,13 @@
|
|||||||
Module : PostgREST.Config
|
Module : PostgREST.Config
|
||||||
Description : Manages PostgREST configuration options.
|
Description : Manages PostgREST configuration options.
|
||||||
|
|
||||||
This module provides a helper function to read the command line arguments using the optparse-applicative
|
This module provides a helper function to read the command line
|
||||||
and the AppConfig type to store them.
|
arguments using the optparse-applicative and the AppConfig type to store
|
||||||
It also can be used to define other middleware configuration that may be delegated to some sort of
|
them. It also can be used to define other middleware configuration that
|
||||||
external configuration.
|
may be delegated to some sort of external configuration.
|
||||||
|
|
||||||
It currently includes a hardcoded CORS policy but this could easly be turned in configurable behaviour if needed.
|
It currently includes a hardcoded CORS policy but this could easly be
|
||||||
|
turned in configurable behaviour if needed.
|
||||||
|
|
||||||
Other hardcoded options such as the minimum version number also belong here.
|
Other hardcoded options such as the minimum version number also belong here.
|
||||||
-}
|
-}
|
||||||
@@ -19,20 +20,26 @@ module PostgREST.Config ( prettyVersion
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import System.IO.Error (IOError)
|
||||||
|
import Control.Applicative
|
||||||
import qualified Data.ByteString.Char8 as BS
|
import qualified Data.ByteString.Char8 as BS
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
import qualified Data.Configurator as C
|
||||||
|
import qualified Data.Configurator.Types as C
|
||||||
import Data.List (lookup)
|
import Data.List (lookup)
|
||||||
import Data.Text (strip, intercalate)
|
import Data.Text (strip, intercalate)
|
||||||
|
import Data.Text.IO (hPutStrLn)
|
||||||
import Data.Version (versionBranch)
|
import Data.Version (versionBranch)
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Middleware.Cors (CorsResourcePolicy (..))
|
import Network.Wai.Middleware.Cors (CorsResourcePolicy (..))
|
||||||
import Options.Applicative
|
import Options.Applicative hiding (str)
|
||||||
import Paths_postgrest (version)
|
import Paths_postgrest (version)
|
||||||
|
import Text.Heredoc
|
||||||
|
|
||||||
import Protolude hiding (intercalate
|
import Protolude hiding (intercalate
|
||||||
, (<>))
|
, (<>))
|
||||||
import Safe (readMay)
|
|
||||||
|
|
||||||
-- | Data type to store all command line options
|
-- | Config file settings for the server
|
||||||
data AppConfig = AppConfig {
|
data AppConfig = AppConfig {
|
||||||
configDatabase :: Text
|
configDatabase :: Text
|
||||||
, configAnonRole :: Text
|
, configAnonRole :: Text
|
||||||
@@ -47,20 +54,6 @@ data AppConfig = AppConfig {
|
|||||||
, configQuiet :: Bool
|
, configQuiet :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
argParser :: Parser AppConfig
|
|
||||||
argParser = AppConfig
|
|
||||||
<$> (toS <$> argument str (help "(REQUIRED) database connection string, e.g. postgres://user:pass@host:port/db" <> metavar "DB_URL"))
|
|
||||||
<*> (toS <$> strOption (long "anonymous" <> short 'a' <> help "(REQUIRED) postgres role to use for non-authenticated requests" <> metavar "ROLE"))
|
|
||||||
<*> (optional . map toS . strOption) (long "proxy-uri" <> short 'x' <> help "proxy uri of the HTTP server" <> metavar "PROXY")
|
|
||||||
<*> (toS <$> strOption (long "schema" <> short 's' <> help "schema to use for API routes" <> metavar "NAME" <> value "public" <> showDefault))
|
|
||||||
<*> (toS <$> strOption (long "host" <> short 'l' <> help "hostname or ip on which to run HTTP server" <> metavar "HOST" <> value "*4" <> showDefault))
|
|
||||||
<*> option auto (long "port" <> short 'p' <> help "port number on which to run HTTP server" <> metavar "PORT" <> value 3000 <> showDefault)
|
|
||||||
<*> (optional . map toS <$> strOption) (long "jwt-secret" <> short 'j' <> help "secret used to encrypt and decrypt JWT tokens" <> metavar "SECRET")
|
|
||||||
<*> option auto (long "pool" <> short 'o' <> help "max connections in database pool" <> metavar "COUNT" <> value 10 <> showDefault)
|
|
||||||
<*> (readMay <$> strOption (long "max-rows" <> short 'm' <> help "max rows in response" <> metavar "COUNT" <> value "infinity" <> showDefault))
|
|
||||||
<*> (optional . map toS . strOption) (long "pre-request" <> help "schema-qualified name of proc to call to validate requests" <> metavar "FUNCTION")
|
|
||||||
<*> pure False
|
|
||||||
|
|
||||||
defaultCorsPolicy :: CorsResourcePolicy
|
defaultCorsPolicy :: CorsResourcePolicy
|
||||||
defaultCorsPolicy = CorsResourcePolicy Nothing
|
defaultCorsPolicy = CorsResourcePolicy Nothing
|
||||||
["GET", "POST", "PATCH", "DELETE", "OPTIONS"] ["Authorization"] Nothing
|
["GET", "POST", "PATCH", "DELETE", "OPTIONS"] ["Authorization"] Nothing
|
||||||
@@ -90,16 +83,93 @@ prettyVersion = intercalate "." $ map show $ versionBranch version
|
|||||||
|
|
||||||
-- | Function to read and parse options from the command line
|
-- | Function to read and parse options from the command line
|
||||||
readOptions :: IO AppConfig
|
readOptions :: IO AppConfig
|
||||||
readOptions = customExecParser parserPrefs opts
|
readOptions = do
|
||||||
where
|
args <- customExecParser parserPrefs opts
|
||||||
opts = info (helper <*> argParser) $
|
|
||||||
fullDesc
|
when (caExample args) $ do
|
||||||
<> progDesc (
|
putStrLn (
|
||||||
"PostgREST "
|
[str|db-uri = "postgres://user:pass@localhost:5432/dbname"
|
||||||
<> toS prettyVersion
|
|db-schema = "public"
|
||||||
<> " / create a REST API to an existing Postgres database"
|
|db-anon-role = "postgres"
|
||||||
)
|
|db-pool = 10
|
||||||
parserPrefs = prefs showHelpOnError
|
|
|
||||||
|
|server-host = "*4"
|
||||||
|
|server-port = 3000
|
||||||
|
|
|
||||||
|
|## base url for swagger output
|
||||||
|
|# server-proxy-uri = ""
|
||||||
|
|
|
||||||
|
|## choose a secret to enable JWT auth
|
||||||
|
|## (use "@filename" to load from separate file)
|
||||||
|
|# jwt-secret = "foo"
|
||||||
|
|
|
||||||
|
|## limit rows in response
|
||||||
|
|# max-rows = 1000
|
||||||
|
|
|
||||||
|
|## stored proc to exec immediately after auth
|
||||||
|
|# pre-request = "stored_proc_name"
|
||||||
|
|]::Text)
|
||||||
|
exitSuccess
|
||||||
|
|
||||||
|
conf <- catch
|
||||||
|
(C.load [C.Required $ caConfig args])
|
||||||
|
configNotfoundHint
|
||||||
|
|
||||||
|
handle missingKeyHint $ do
|
||||||
|
-- db ----------------
|
||||||
|
cDbUri <- C.require conf "db-uri"
|
||||||
|
cDbSchema <- C.require conf "db-schema"
|
||||||
|
cDbAnon <- C.require conf "db-anon-role"
|
||||||
|
cPool <- C.lookupDefault 10 conf "db-pool"
|
||||||
|
-- server ------------
|
||||||
|
cHost <- C.lookupDefault "*4" conf "server-host"
|
||||||
|
cPort <- C.lookupDefault 3000 conf "server-port"
|
||||||
|
cProxy <- C.lookup conf "server-proxy-uri"
|
||||||
|
-- jwt ---------------
|
||||||
|
cJwtSec <- C.lookup conf "jwt-secret"
|
||||||
|
-- safety ------------
|
||||||
|
cMaxRows <- C.lookup conf "max-rows"
|
||||||
|
cReqCheck <- C.lookup conf "pre-request"
|
||||||
|
|
||||||
|
return $ AppConfig cDbUri cDbAnon cProxy cDbSchema cHost cPort
|
||||||
|
cJwtSec cPool cMaxRows cReqCheck False
|
||||||
|
|
||||||
|
where
|
||||||
|
opts = info (helper <*> argParser) $
|
||||||
|
fullDesc
|
||||||
|
<> progDesc (
|
||||||
|
"PostgREST "
|
||||||
|
<> toS prettyVersion
|
||||||
|
<> " / create a REST API to an existing Postgres database"
|
||||||
|
)
|
||||||
|
parserPrefs = prefs showHelpOnError
|
||||||
|
|
||||||
|
configNotfoundHint :: IOError -> IO a
|
||||||
|
configNotfoundHint e = do
|
||||||
|
hPutStrLn stderr $ intercalate "\n" [
|
||||||
|
"Cannot open config file:",
|
||||||
|
"\t" <> show e,
|
||||||
|
"\nUse the --help flag to learn how to fix this."]
|
||||||
|
exitFailure
|
||||||
|
|
||||||
|
missingKeyHint :: C.KeyError -> IO a
|
||||||
|
missingKeyHint (C.KeyError n) = do
|
||||||
|
hPutStrLn stderr $
|
||||||
|
"Required config parameter \"" <> n <> "\" is missing or of wrong type.\n" <>
|
||||||
|
"Try the --example-config option to see how to configure PostgREST."
|
||||||
|
exitFailure
|
||||||
|
|
||||||
|
data CmdArgs = CmdArgs {
|
||||||
|
caConfig :: FilePath
|
||||||
|
, caExample :: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
argParser :: Parser CmdArgs
|
||||||
|
argParser = CmdArgs <$>
|
||||||
|
(toS <$> strOption
|
||||||
|
(short 'c' <> metavar "filename" <>
|
||||||
|
help "Path to configuration file")) <*>
|
||||||
|
switch (long "example-config" <> help "output an example config file")
|
||||||
|
|
||||||
-- | Tells the minimum PostgreSQL version required by this version of PostgREST
|
-- | Tells the minimum PostgreSQL version required by this version of PostgREST
|
||||||
minimumPgVersion :: Integer
|
minimumPgVersion :: Integer
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE TypeSynonymInstances #-}
|
{-# LANGUAGE TypeSynonymInstances #-}
|
||||||
|
|
||||||
module PostgREST.Error (pgErrResponse, errResponse) where
|
module PostgREST.Error (pgErrResponse, errResponse, prettyUsageError) where
|
||||||
|
|
||||||
import Protolude
|
import Protolude
|
||||||
import Data.Aeson ((.=))
|
import Data.Aeson ((.=))
|
||||||
@@ -29,6 +29,11 @@ pgErrResponse authed e =
|
|||||||
else [jsonType] in
|
else [jsonType] in
|
||||||
responseLBS status hdrs (JSON.encode e)
|
responseLBS status hdrs (JSON.encode e)
|
||||||
|
|
||||||
|
prettyUsageError :: P.UsageError -> Text
|
||||||
|
prettyUsageError (P.ConnectionError e) =
|
||||||
|
"Database connection error:\n" <> toS (fromMaybe "" e)
|
||||||
|
prettyUsageError e = show $ JSON.encode e
|
||||||
|
|
||||||
instance JSON.ToJSON P.UsageError where
|
instance JSON.ToJSON P.UsageError where
|
||||||
toJSON (P.ConnectionError e) = JSON.object [
|
toJSON (P.ConnectionError e) = JSON.object [
|
||||||
"code" .= ("" :: Text),
|
"code" .= ("" :: Text),
|
||||||
|
|||||||
Reference in New Issue
Block a user