Skip to content

Commit 53d7a83

Browse files
authored
Merge pull request #228 from rragundez/add-hosts
Add hosts settings and some validation logic
2 parents 421676c + b1fdc52 commit 53d7a83

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

scripts/local_with_uvicorn/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
APP_NAME="My Project"
1717
APP_DESCRIPTION="My Project Description"
1818
APP_VERSION="0.1"
19+
APP_BACKEND_HOST="http://localhost:8000"
20+
APP_FRONTEND_HOST="http://localhost:3000"
1921
CONTACT_NAME="Me"
2022
CONTACT_EMAIL="[email protected]"
2123
LICENSE_NAME="MIT"

src/app/core/config.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
import os
2+
import warnings
23
from enum import Enum
4+
from typing import Self
35

4-
from pydantic import SecretStr, computed_field
6+
from pydantic import SecretStr, computed_field, field_validator, model_validator
57
from pydantic_settings import BaseSettings, SettingsConfigDict
68

79

810
class AppSettings(BaseSettings):
911
APP_NAME: str = "FastAPI app"
1012
APP_DESCRIPTION: str | None = None
1113
APP_VERSION: str | None = None
14+
APP_BACKEND_HOST: str = "http://localhost:8000"
15+
APP_FRONTEND_HOST: str | None = None
1216
LICENSE_NAME: str | None = None
1317
CONTACT_NAME: str | None = None
1418
CONTACT_EMAIL: str | None = None
1519

20+
@field_validator("APP_BACKEND_HOST", "APP_FRONTEND_HOST", mode="after")
21+
@classmethod
22+
def validate_hosts(cls, host: str) -> str:
23+
if host is not None and not (host.startswith("http://") or host.startswith("https://")):
24+
raise ValueError(
25+
f"HOSTS must define their protocol and start with http:// or https://. Received the host '{host}'."
26+
)
27+
return host
28+
1629

1730
class CryptSettings(BaseSettings):
1831
SECRET_KEY: SecretStr = SecretStr("secret-key")
@@ -150,5 +163,30 @@ class Settings(
150163
extra="ignore",
151164
)
152165

166+
@model_validator(mode="after")
167+
def validate_environment_settings(self) -> Self:
168+
"The validation should not modify any of the settings. It should provide"
169+
"feedback to the user if any misconfiguration is detected."
170+
if self.ENVIRONMENT == EnvironmentOption.LOCAL:
171+
pass
172+
elif self.ENVIRONMENT == EnvironmentOption.STAGING:
173+
if "*" in self.CORS_ORIGINS:
174+
warnings.warn(
175+
"For security, in a staging environment CORS_ORIGINS should not include '*'. "
176+
"It's recommended to specify explicit origins (e.g., ['https://staging.example.com'])."
177+
)
178+
elif self.ENVIRONMENT == EnvironmentOption.PRODUCTION:
179+
if "*" in self.CORS_ORIGINS:
180+
raise ValueError(
181+
"For security, in a production environment CORS_ORIGINS cannot include '*'. "
182+
"You must specify explicit allowed origins (e.g., ['https://example.com', 'https://www.example.com'])."
183+
)
184+
if self.APP_FRONTEND_HOST and not self.APP_FRONTEND_HOST.startswith("https://"):
185+
raise ValueError(
186+
"In production, APP_FRONTEND_HOST must start with the https:// protocol. "
187+
f"Received the host '{self.APP_FRONTEND_HOST}'."
188+
)
189+
return self
190+
153191

154192
settings = Settings()

0 commit comments

Comments
 (0)