Group configuration by component ownership

This commit is contained in:
ms2k 2026-07-20 02:21:33 +09:00
commit 67cc0cb802

View file

@ -1,5 +1,13 @@
module Nuthatch.Config
( Config (..)
, RuntimeConfig (..)
, HttpConfig (..)
, OidcConfig (..)
, PostgresConfig (..)
, RedisConfig (..)
, S3Config (..)
, ProductConfig (..)
, RecoveryConfig (..)
, Secret
, secret
, revealSecret
@ -17,7 +25,7 @@ import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Env
import Nuthatch.Streaming (maximumMultipartBytes)
import Nuthatch.Product.Limits (maximumObjectBytes)
import qualified Text.URI as URI
newtype Secret value = Secret value
@ -29,6 +37,22 @@ revealSecret :: Secret value -> value
revealSecret (Secret value) = value
data Config = Config
{ cfgRuntime :: RuntimeConfig
, cfgHttp :: HttpConfig
, cfgOidc :: OidcConfig
, cfgPostgres :: PostgresConfig
, cfgRedis :: RedisConfig
, cfgS3 :: S3Config
, cfgProduct :: ProductConfig
, cfgRecovery :: RecoveryConfig
}
data RuntimeConfig = RuntimeConfig
{ cfgStartupAttempts :: Int
, cfgStartupDelayMs :: Int
}
data HttpConfig = HttpConfig
{ cfgListenHost :: String
, cfgListenPort :: Int
, cfgTlsCertPath :: FilePath
@ -39,41 +63,59 @@ data Config = Config
, cfgCookieName :: Text
, cfgCsrfCookieName :: Text
, cfgOidcTransactionCookieName :: Text
, cfgOidcIssuer :: Text
}
data OidcConfig = OidcConfig
{ cfgOidcIssuer :: Text
, cfgOidcClientId :: Text
, cfgOidcAdditionalAudiences :: [Text]
, cfgOidcClientSecret :: Secret Text
, cfgOidcRedirectUri :: Text
, cfgOidcTransactionMinutes :: Integer
, cfgOidcMaxIdTokenSeconds :: Integer
, cfgPgConn :: Secret String
, cfgRedisHost :: String
, cfgOidcTransactionSecret :: Secret Text
}
data PostgresConfig = PostgresConfig
{ cfgPgConn :: Secret String
}
data RedisConfig = RedisConfig
{ cfgRedisHost :: String
, cfgRedisPort :: Int
, cfgRedisDb :: Integer
, cfgS3Endpoint :: Text
, cfgThrottleBytesPerSec :: Integer
, cfgThrottleBurstBytes :: Integer
}
data S3Config = S3Config
{ cfgS3Endpoint :: Text
, cfgS3Region :: Text
, cfgS3Bucket :: Text
, cfgS3AccessKey :: Secret Text
, cfgS3SecretKey :: Secret Text
, cfgS3PathStyle :: Bool
, cfgPersonalQuotaBytes :: Integer
, cfgS3RequestTimeoutSec :: Integer
}
data ProductConfig = ProductConfig
{ cfgPersonalQuotaBytes :: Integer
, cfgSharedQuotaBytes :: Integer
, cfgMaxObjectBytes :: Integer
, cfgThrottleBytesPerSec :: Integer
, cfgThrottleBurstBytes :: Integer
, cfgListCacheTtlSec :: Integer
, cfgBrowsePageSize :: Int
, cfgSearchMaxKeys :: Int
, cfgSearchTimeoutMs :: Integer
, cfgDeletePageSize :: Int
, cfgRecoveryGraceSec :: Integer
, cfgIdempotencyWindowSec :: Integer
, cfgObjectRequestTimeoutSec :: Integer
, cfgStartupAttempts :: Int
, cfgStartupDelayMs :: Int
, cfgAuditObjectKeys :: Bool
}
data RecoveryConfig = RecoveryConfig
{ cfgRecoveryGraceSec :: Integer
, cfgObjectRequestTimeoutSec :: Integer
}
loadConfig :: IO Config
loadConfig = do
listen <- envString "NUTHATCH_LISTEN" "127.0.0.1:8080"
@ -131,7 +173,7 @@ loadConfig = do
maxObject <- envInteger "NUTHATCH_MAX_OBJECT_BYTES" (100 * 1024 * 1024)
throttleRate <- envInteger "NUTHATCH_THROTTLE_BYTES_PER_SEC" (10 * 1024 * 1024)
throttleBurst <- envInteger "NUTHATCH_THROTTLE_BURST_BYTES" (20 * 1024 * 1024)
when (maxObject <= 0 || maxObject > maximumMultipartBytes) $
when (maxObject <= 0 || maxObject > maximumObjectBytes) $
fail "NUTHATCH_MAX_OBJECT_BYTES exceeds the supported streaming range"
when (throttleRate <= 0 || throttleRate > luaExactIntegerMax) $
fail "NUTHATCH_THROTTLE_BYTES_PER_SEC exceeds the exact throttle range"
@ -167,49 +209,67 @@ loadConfig = do
when (searchTimeoutMs <= 0 || searchTimeoutMs > 60000) $
fail "NUTHATCH_SEARCH_TIMEOUT_MS must be between 1 and 60000"
pure Config
{ cfgListenHost = host
, cfgListenPort = port
, cfgTlsCertPath = tlsCertPath
, cfgTlsKeyPath = tlsKeyPath
, cfgPublicBaseUrl = publicBase
, cfgJwtSecret = jwtSecret
, cfgSessionHours = sessionHours
, cfgCookieName = cookieName
, cfgCsrfCookieName = csrfCookie
, cfgOidcTransactionCookieName = transactionCookie
, cfgOidcIssuer = oidcIssuer
, cfgOidcClientId = oidcClientId
, cfgOidcAdditionalAudiences = oidcAdditionalAudiences
, cfgOidcClientSecret = oidcClientSecret
, cfgOidcRedirectUri = oidcRedirectUri
, cfgOidcTransactionMinutes = oidcTransactionMinutes
, cfgOidcMaxIdTokenSeconds = oidcMaxIdTokenSeconds
, cfgPgConn = pgConn
, cfgRedisHost = redisHost
, cfgRedisPort = redisPort
, cfgRedisDb = redisDb
, cfgS3Endpoint = s3Endpoint
, cfgS3Region = s3Region
, cfgS3Bucket = s3Bucket
, cfgS3AccessKey = s3Access
, cfgS3SecretKey = s3Secret
, cfgS3PathStyle = pathStyle
, cfgPersonalQuotaBytes = personalQuota
, cfgSharedQuotaBytes = sharedQuota
, cfgMaxObjectBytes = maxObject
, cfgThrottleBytesPerSec = throttleRate
, cfgThrottleBurstBytes = throttleBurst
, cfgListCacheTtlSec = listTtl
, cfgBrowsePageSize = browsePageSize
, cfgSearchMaxKeys = searchMax
, cfgSearchTimeoutMs = searchTimeoutMs
, cfgDeletePageSize = deletePageSize
, cfgRecoveryGraceSec = recoveryGrace
, cfgIdempotencyWindowSec = idempotencyWindow
, cfgObjectRequestTimeoutSec = objectRequestTimeout
, cfgStartupAttempts = startupAttempts
, cfgStartupDelayMs = startupDelayMs
, cfgAuditObjectKeys = auditObjectKeys
{ cfgRuntime = RuntimeConfig
{ cfgStartupAttempts = startupAttempts
, cfgStartupDelayMs = startupDelayMs
}
, cfgHttp = HttpConfig
{ cfgListenHost = host
, cfgListenPort = port
, cfgTlsCertPath = tlsCertPath
, cfgTlsKeyPath = tlsKeyPath
, cfgPublicBaseUrl = publicBase
, cfgJwtSecret = jwtSecret
, cfgSessionHours = sessionHours
, cfgCookieName = cookieName
, cfgCsrfCookieName = csrfCookie
, cfgOidcTransactionCookieName = transactionCookie
}
, cfgOidc = OidcConfig
{ cfgOidcIssuer = oidcIssuer
, cfgOidcClientId = oidcClientId
, cfgOidcAdditionalAudiences = oidcAdditionalAudiences
, cfgOidcClientSecret = oidcClientSecret
, cfgOidcRedirectUri = oidcRedirectUri
, cfgOidcTransactionMinutes = oidcTransactionMinutes
, cfgOidcMaxIdTokenSeconds = oidcMaxIdTokenSeconds
, cfgOidcTransactionSecret = jwtSecret
}
, cfgPostgres = PostgresConfig
{ cfgPgConn = pgConn
}
, cfgRedis = RedisConfig
{ cfgRedisHost = redisHost
, cfgRedisPort = redisPort
, cfgRedisDb = redisDb
, cfgThrottleBytesPerSec = throttleRate
, cfgThrottleBurstBytes = throttleBurst
}
, cfgS3 = S3Config
{ cfgS3Endpoint = s3Endpoint
, cfgS3Region = s3Region
, cfgS3Bucket = s3Bucket
, cfgS3AccessKey = s3Access
, cfgS3SecretKey = s3Secret
, cfgS3PathStyle = pathStyle
, cfgS3RequestTimeoutSec = objectRequestTimeout
}
, cfgProduct = ProductConfig
{ cfgPersonalQuotaBytes = personalQuota
, cfgSharedQuotaBytes = sharedQuota
, cfgMaxObjectBytes = maxObject
, cfgListCacheTtlSec = listTtl
, cfgBrowsePageSize = browsePageSize
, cfgSearchMaxKeys = searchMax
, cfgSearchTimeoutMs = searchTimeoutMs
, cfgDeletePageSize = deletePageSize
, cfgIdempotencyWindowSec = idempotencyWindow
, cfgAuditObjectKeys = auditObjectKeys
}
, cfgRecovery = RecoveryConfig
{ cfgRecoveryGraceSec = recoveryGrace
, cfgObjectRequestTimeoutSec = objectRequestTimeout
}
}
luaExactIntegerMax :: Integer