From b9e4ac31ebc75423bc3c6bb5a33ccb8e8dcd7c25 Mon Sep 17 00:00:00 2001 From: dylan <41266749+downdawn@users.noreply.github.com> Date: Fri, 21 Apr 2023 20:10:07 +0800 Subject: [PATCH] Add environment variable management and fix Pydantic validation error (#15) * Use env to manage important configurations and distinguish environments * fix Pydantic validation error caused by None value in links field of Page class * Production environment no longer exposes API docs * Extract TOKEN_URL to conf --- README.md | 2 +- backend/app/.env.example | 21 ++++++++++++++++ backend/app/api/jwt.py | 2 +- backend/app/common/pagination.py | 4 +-- backend/app/core/conf.py | 43 ++++++++++++++++++++------------ 5 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 backend/app/.env.example diff --git a/README.md b/README.md index e121f6c1..46dc88d6 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ git clone https://github.com/wu-clan/fastapi_best_architecture.git 2. Create a database `fba`, choose utf8mb4 encode 3. Install and start Redis -4. View `backend/app/core/conf.py`, update database configuration information +4. Copy .env.example to .env and view `backend/app/core/conf.py`, update database configuration information 5. Perform a database migration [alembic](https://alembic.sqlalchemy.org/en/latest/tutorial.html) ```shell cd backend/app/ diff --git a/backend/app/.env.example b/backend/app/.env.example new file mode 100644 index 00000000..004816d5 --- /dev/null +++ b/backend/app/.env.example @@ -0,0 +1,21 @@ +# env: test、dev、pro +ENVIRONMENT='pro' +# mysql +DB_HOST='127.0.0.1' +DB_PORT=3306 +DB_USER='root' +DB_PASSWORD='' +# redis +REDIS_HOST='127.0.0.1' +REDIS_PORT=6379 +REDIS_PASSWORD='' +REDIS_DATABASE=0 +# APScheduler +APS_REDIS_HOST='127.0.0.1' +APS_REDIS_PORT=6379 +APS_REDIS_PASSWORD='' +# token +TOKEN_SECRET_KEY='1VkVF75nsNABBjK_7-qz7GtzNy3AMvktc9TCPwKczCk' +# email +EMAIL_USER='xxxx-nav@qq.com' +EMAIL_PASSWORD='' diff --git a/backend/app/api/jwt.py b/backend/app/api/jwt.py index 6d823f1e..3c975bf7 100644 --- a/backend/app/api/jwt.py +++ b/backend/app/api/jwt.py @@ -18,7 +18,7 @@ from backend.app.models import User pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto') -oauth2_schema = OAuth2PasswordBearer(tokenUrl='/v1/users/login') +oauth2_schema = OAuth2PasswordBearer(tokenUrl=settings.TOKEN_URL) def get_hash_password(password: str) -> str: diff --git a/backend/app/common/pagination.py b/backend/app/common/pagination.py index a7764f41..d251bddd 100644 --- a/backend/app/common/pagination.py +++ b/backend/app/common/pagination.py @@ -3,7 +3,7 @@ from __future__ import annotations import math -from typing import TypeVar, Generic, Sequence, Dict +from typing import TypeVar, Generic, Sequence, Dict, Union from fastapi import Query from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams @@ -35,7 +35,7 @@ class Page(AbstractPage[T], Generic[T]): page: int # 第n页 size: int # 每页数量 total_pages: int # 总页数 - links: Dict[str, str] # 跳转链接 + links: Dict[str, Union[str, None]] # 跳转链接 __params_type__ = Params # 使用自定义的Params diff --git a/backend/app/core/conf.py b/backend/app/core/conf.py index 74d92384..93cde31a 100644 --- a/backend/app/core/conf.py +++ b/backend/app/core/conf.py @@ -3,18 +3,25 @@ from functools import lru_cache from typing import Optional -from pydantic import BaseSettings +from pydantic import BaseSettings, root_validator class Settings(BaseSettings): + ENVIRONMENT: str # FastAPI TITLE: str = 'FastAPI' VERSION: str = 'v0.0.1' DESCRIPTION: str = "FastAPI Best Architecture" DOCS_URL: Optional[str] = '/v1/docs' - REDOCS_URL: Optional[str] = None + REDOCS_URL: Optional[str] = '/v1/redocs' OPENAPI_URL: Optional[str] = '/v1/openapi' + @root_validator + def validator_api_url(cls, values): + if values['ENVIRONMENT'] == 'pro': + values['OPENAPI_URL'] = None + return values + # Uvicorn UVICORN_HOST: str = '127.0.0.1' UVICORN_PORT: int = 8000 @@ -25,24 +32,24 @@ class Settings(BaseSettings): # MySQL DB_ECHO: bool = False - DB_HOST: str = '127.0.0.1' - DB_PORT: int = 3306 - DB_USER: str = 'root' - DB_PASSWORD: str = '123456' + DB_HOST: str + DB_PORT: int + DB_USER: str + DB_PASSWORD: str DB_DATABASE: str = 'fba' DB_CHARSET: str = 'utf8mb4' # Redis - REDIS_HOST: str = '127.0.0.1' - REDIS_PORT: int = 6379 - REDIS_PASSWORD: str = '' - REDIS_DATABASE: int = 0 + REDIS_HOST: str + REDIS_PORT: int + REDIS_PASSWORD: str + REDIS_DATABASE: int REDIS_TIMEOUT: int = 5 # APScheduler DB - APS_REDIS_HOST: str = '127.0.0.1' - APS_REDIS_PORT: int = 6379 - APS_REDIS_PASSWORD: str = '' + APS_REDIS_HOST: str + APS_REDIS_PORT: int + APS_REDIS_PASSWORD: str APS_REDIS_DATABASE: int = 1 APS_REDIS_TIMEOUT: int = 10 @@ -53,15 +60,16 @@ class Settings(BaseSettings): # Token TOKEN_ALGORITHM: str = 'HS256' # 算法 - TOKEN_SECRET_KEY: str = '1VkVF75nsNABBjK_7-qz7GtzNy3AMvktc9TCPwKczCk' # 密钥 secrets.token_urlsafe(32)) + TOKEN_SECRET_KEY: str # 密钥 secrets.token_urlsafe(32)) TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 1 # token 时效 60 * 24 * 1 = 1 天 + TOKEN_URL: str = '/v1/users/login' # Email EMAIL_DESCRIPTION: str = 'fastapi_sqlalchemy_mysql' # 默认发件说明 EMAIL_SERVER: str = 'smtp.qq.com' EMAIL_PORT: int = 465 - EMAIL_USER: str = 'xxxx-nav@qq.com' - EMAIL_PASSWORD: str = '' # 授权密码,非邮箱密码 + EMAIL_USER: str + EMAIL_PASSWORD: str # 授权密码,非邮箱密码 EMAIL_SSL: bool = True # 是否使用ssl # 邮箱登录验证码过期时间 @@ -75,6 +83,9 @@ class Settings(BaseSettings): MIDDLEWARE_GZIP: bool = True MIDDLEWARE_ACCESS: bool = False + class Config: + env_file = '.env' + @lru_cache def get_settings():