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
|
||||
- Remove default JWT secret - @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
|
||||
|
||||
|
||||
+4
-13
@@ -1,11 +1,6 @@
|
||||
FROM debian:jessie
|
||||
|
||||
ENV POSTGREST_VERSION 0.3.2.0
|
||||
ENV POSTGREST_SCHEMA public
|
||||
ENV POSTGREST_ANONYMOUS postgres
|
||||
ENV POSTGREST_JWT_SECRET thisisnotarealsecret
|
||||
ENV POSTGREST_MAX_ROWS 1000000
|
||||
ENV POSTGREST_POOL 200
|
||||
ENV POSTGREST_VERSION 0.4.0.0
|
||||
|
||||
RUN apt-get update && \
|
||||
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 && \
|
||||
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} \
|
||||
--port 3000 \
|
||||
--schema ${POSTGREST_SCHEMA} \
|
||||
--anonymous ${POSTGREST_ANONYMOUS} \
|
||||
--pool ${POSTGREST_POOL} \
|
||||
--jwt-secret ${POSTGREST_JWT_SECRET} \
|
||||
--max-rows ${POSTGREST_MAX_ROWS}
|
||||
# PostgREST reads /etc/postgrest.conf so map the configuration
|
||||
# file in when you run this container
|
||||
CMD exec postgrest
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
@@ -8,12 +8,14 @@ import PostgREST.Config (AppConfig (..),
|
||||
minimumPgVersion,
|
||||
prettyVersion,
|
||||
readOptions)
|
||||
import PostgREST.Error (prettyUsageError)
|
||||
import PostgREST.OpenAPI (isMalformedProxyUri)
|
||||
import PostgREST.DbStructure
|
||||
|
||||
import Control.AutoUpdate
|
||||
import Data.String (IsString (..))
|
||||
import Data.Text (stripPrefix)
|
||||
import Data.Text.IO (hPutStrLn)
|
||||
import Data.Function (id)
|
||||
import Data.Time.Clock.POSIX (getPOSIXTime)
|
||||
import qualified Hasql.Query as H
|
||||
@@ -68,6 +70,10 @@ main = do
|
||||
<> show minimumPgVersion)
|
||||
getDbStructure (toS $ configSchema conf)
|
||||
|
||||
forM_ (lefts [result]) $ \e -> do
|
||||
hPutStrLn stderr (prettyUsageError e)
|
||||
exitFailure
|
||||
|
||||
refDbStructure <- newIORef $ either (panic . show) id result
|
||||
|
||||
#ifndef mingw32_HOST_OS
|
||||
|
||||
@@ -51,11 +51,13 @@ library
|
||||
, bytestring
|
||||
, case-insensitive
|
||||
, cassava
|
||||
, configurator
|
||||
, containers
|
||||
, contravariant
|
||||
, hasql
|
||||
, hasql-pool == 0.4.1
|
||||
, hasql-transaction == 0.4.5.1
|
||||
, heredoc
|
||||
, HTTP
|
||||
, http-types
|
||||
, insert-ordered-containers
|
||||
|
||||
+102
-32
@@ -2,12 +2,13 @@
|
||||
Module : PostgREST.Config
|
||||
Description : Manages PostgREST configuration options.
|
||||
|
||||
This module provides a helper function to read the command line arguments using the optparse-applicative
|
||||
and the AppConfig type to store them.
|
||||
It also can be used to define other middleware configuration that may be delegated to some sort of
|
||||
external configuration.
|
||||
This module provides a helper function to read the command line
|
||||
arguments using the optparse-applicative and the AppConfig type to store
|
||||
them. It also can be used to define other middleware configuration that
|
||||
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.
|
||||
-}
|
||||
@@ -19,20 +20,26 @@ module PostgREST.Config ( prettyVersion
|
||||
)
|
||||
where
|
||||
|
||||
import System.IO.Error (IOError)
|
||||
import Control.Applicative
|
||||
import qualified Data.ByteString.Char8 as BS
|
||||
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.Text (strip, intercalate)
|
||||
import Data.Text.IO (hPutStrLn)
|
||||
import Data.Version (versionBranch)
|
||||
import Network.Wai
|
||||
import Network.Wai.Middleware.Cors (CorsResourcePolicy (..))
|
||||
import Options.Applicative
|
||||
import Options.Applicative hiding (str)
|
||||
import Paths_postgrest (version)
|
||||
import Text.Heredoc
|
||||
|
||||
import Protolude hiding (intercalate
|
||||
, (<>))
|
||||
import Safe (readMay)
|
||||
|
||||
-- | Data type to store all command line options
|
||||
-- | Config file settings for the server
|
||||
data AppConfig = AppConfig {
|
||||
configDatabase :: Text
|
||||
, configAnonRole :: Text
|
||||
@@ -47,20 +54,6 @@ data AppConfig = AppConfig {
|
||||
, 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 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
|
||||
readOptions :: IO AppConfig
|
||||
readOptions = customExecParser parserPrefs opts
|
||||
where
|
||||
opts = info (helper <*> argParser) $
|
||||
fullDesc
|
||||
<> progDesc (
|
||||
"PostgREST "
|
||||
<> toS prettyVersion
|
||||
<> " / create a REST API to an existing Postgres database"
|
||||
)
|
||||
parserPrefs = prefs showHelpOnError
|
||||
readOptions = do
|
||||
args <- customExecParser parserPrefs opts
|
||||
|
||||
when (caExample args) $ do
|
||||
putStrLn (
|
||||
[str|db-uri = "postgres://user:pass@localhost:5432/dbname"
|
||||
|db-schema = "public"
|
||||
|db-anon-role = "postgres"
|
||||
|db-pool = 10
|
||||
|
|
||||
|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
|
||||
minimumPgVersion :: Integer
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE TypeSynonymInstances #-}
|
||||
|
||||
module PostgREST.Error (pgErrResponse, errResponse) where
|
||||
module PostgREST.Error (pgErrResponse, errResponse, prettyUsageError) where
|
||||
|
||||
import Protolude
|
||||
import Data.Aeson ((.=))
|
||||
@@ -29,6 +29,11 @@ pgErrResponse authed e =
|
||||
else [jsonType] in
|
||||
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
|
||||
toJSON (P.ConnectionError e) = JSON.object [
|
||||
"code" .= ("" :: Text),
|
||||
|
||||
Reference in New Issue
Block a user