diff --git a/Makefile b/Makefile index 9de29e1..3dfab37 100644 --- a/Makefile +++ b/Makefile @@ -2,4 +2,10 @@ dev: docker-compose up -d dev-down: - docker-compose down \ No newline at end of file + docker-compose down + +start-server: + uvicorn app.main:app --reload + +install-modules: + pip install fastapi[all] fastapi-mail fastapi-jwt-auth[asymmetric] passlib[bcrypt] pymongo \ No newline at end of file diff --git a/app/database.py b/app/database.py index ed87d09..fad8ae7 100644 --- a/app/database.py +++ b/app/database.py @@ -2,8 +2,14 @@ import pymongo from app.config import settings -client = mongo_client.MongoClient(settings.DATABASE_URL) -print('Connected to MongoDB...') +client = mongo_client.MongoClient( + settings.DATABASE_URL, serverSelectionTimeoutMS=5000) + +try: + conn = client.server_info() + print(f'Connected to MongoDB {conn.get("version")}') +except Exception: + print("Unable to connect to the MongoDB server.") db = client[settings.MONGO_INITDB_DATABASE] User = db.users diff --git a/app/email.py b/app/email.py index 28545d0..939c58f 100644 --- a/app/email.py +++ b/app/email.py @@ -31,8 +31,8 @@ async def sendMail(self, subject, template): MAIL_FROM=settings.EMAIL_FROM, MAIL_PORT=settings.EMAIL_PORT, MAIL_SERVER=settings.EMAIL_HOST, - MAIL_TLS=True, - MAIL_SSL=False, + MAIL_STARTTLS=False, + MAIL_SSL_TLS=False, USE_CREDENTIALS=True, VALIDATE_CERTS=True ) diff --git a/app/oauth2.py b/app/oauth2.py index 517960f..11146cd 100644 --- a/app/oauth2.py +++ b/app/oauth2.py @@ -5,7 +5,7 @@ from pydantic import BaseModel from bson.objectid import ObjectId -from app.serializers import userEntity +from app.serializers.userSerializers import userEntity from .database import User from .config import settings diff --git a/app/routers/auth.py b/app/routers/auth.py index a4472fb..cf28605 100644 --- a/app/routers/auth.py +++ b/app/routers/auth.py @@ -8,7 +8,7 @@ from app import oauth2 from app.database import User from app.email import Email -from app.serializers import userEntity +from app.serializers.userSerializers import userEntity from .. import schemas, utils from app.oauth2 import AuthJWT from ..config import settings @@ -62,10 +62,11 @@ async def create_user(payload: schemas.CreateUserSchema, request: Request): @router.post('/login') def login(payload: schemas.LoginUserSchema, response: Response, Authorize: AuthJWT = Depends()): # Check if the user exist - user = userEntity(User.find_one({'email': payload.email.lower()})) - if not user: + db_user = User.find_one({'email': payload.email.lower()}) + if not db_user: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='Incorrect Email or Password') + user = userEntity(db_user) # Check if user verified his email if not user['verified']: diff --git a/app/routers/user.py b/app/routers/user.py index ae1c722..46a5917 100644 --- a/app/routers/user.py +++ b/app/routers/user.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, Depends from bson.objectid import ObjectId -from app.serializers import userResponseEntity +from app.serializers.userSerializers import userResponseEntity from app.database import User from .. import schemas, oauth2 diff --git a/app/serializers.py b/app/serializers/userSerializers.py similarity index 80% rename from app/serializers.py rename to app/serializers/userSerializers.py index 9a82e3f..fc25f84 100644 --- a/app/serializers.py +++ b/app/serializers/userSerializers.py @@ -24,5 +24,14 @@ def userResponseEntity(user) -> dict: } +def embeddedUserResponse(user) -> dict: + return { + "id": str(user["_id"]), + "name": user["name"], + "email": user["email"], + "photo": user["photo"] + } + + def userListEntity(users) -> list: return [userEntity(user) for user in users] diff --git a/example.env b/example.env new file mode 100644 index 0000000..e4d3db5 --- /dev/null +++ b/example.env @@ -0,0 +1,20 @@ +MONGO_INITDB_ROOT_USERNAME=admin +MONGO_INITDB_ROOT_PASSWORD=password123 +MONGO_INITDB_DATABASE=fastapi + +DATABASE_URL=mongodb://admin:password123@localhost:6000/fastapi?authSource=admin + +ACCESS_TOKEN_EXPIRES_IN=15 +REFRESH_TOKEN_EXPIRES_IN=60 +JWT_ALGORITHM=RS256 + +CLIENT_ORIGIN=http://localhost:3000 + +EMAIL_HOST=smtp.mailtrap.io +EMAIL_PORT=587 +EMAIL_USERNAME=90cf952fb44469 +EMAIL_PASSWORD=0524531956c552 +EMAIL_FROM=admin@admin.com + +JWT_PRIVATE_KEY=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCT2dJQkFBSkJBSSs3QnZUS0FWdHVQYzEzbEFkVk94TlVmcWxzMm1SVmlQWlJyVFpjd3l4RVhVRGpNaFZuCi9KVHRsd3h2a281T0pBQ1k3dVE0T09wODdiM3NOU3ZNd2xNQ0F3RUFBUUpBYm5LaENOQ0dOSFZGaHJPQ0RCU0IKdmZ2ckRWUzVpZXAwd2h2SGlBUEdjeWV6bjd0U2RweUZ0NEU0QTNXT3VQOXhqenNjTFZyb1pzRmVMUWlqT1JhUwp3UUloQU84MWl2b21iVGhjRkltTFZPbU16Vk52TGxWTW02WE5iS3B4bGh4TlpUTmhBaUVBbWRISlpGM3haWFE0Cm15QnNCeEhLQ3JqOTF6bVFxU0E4bHUvT1ZNTDNSak1DSVFEbDJxOUdtN0lMbS85b0EyaCtXdnZabGxZUlJPR3oKT21lV2lEclR5MUxaUVFJZ2ZGYUlaUWxMU0tkWjJvdXF4MHdwOWVEejBEWklLVzVWaSt6czdMZHRDdUVDSUVGYwo3d21VZ3pPblpzbnU1clBsTDJjZldLTGhFbWwrUVFzOCtkMFBGdXlnCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0t +JWT_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZ3d0RRWUpLb1pJaHZjTkFRRUJCUUFEU3dBd1NBSkJBSSs3QnZUS0FWdHVQYzEzbEFkVk94TlVmcWxzMm1SVgppUFpSclRaY3d5eEVYVURqTWhWbi9KVHRsd3h2a281T0pBQ1k3dVE0T09wODdiM3NOU3ZNd2xNQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ== \ No newline at end of file diff --git a/readMe.md b/readMe.md new file mode 100644 index 0000000..8a971b5 --- /dev/null +++ b/readMe.md @@ -0,0 +1,33 @@ +# Build API with Python & FastAPI: SignUp User and Verify Email + +This article will teach you how to send HTML Emails with Python, FastAPI, PyMongo, MongoDB, Jinja2, and Docker. Also, you will learn how to use Jinja2 to generate different HTML templates. + +![Build API with Python & FastAPI: SignUp User and Verify Email](https://codevoweb.com/wp-content/uploads/2022/07/Build-API-with-Python-FastAPI-SignUp-User-and-Verify-Email.webp) + +## Topics Covered + +- Send HTML Emails with Jinja2 & FastAPI Example +- Creating the SMTP Provider Account +- Edit the Environment Variables File +- Validating the Environment Variables with Pydantic +- Creating the HTML Email Templates in FastAPI +- Creating the SMTP Email Sender +- Sending the HTML Emails in FastAPI +- Update the SignUp Controller +- Create a Handler to Validate the Verification Code + +Read the entire article here: [https://codevoweb.com/api-with-python-fastapi-signup-user-and-verify-email](https://codevoweb.com/api-with-python-fastapi-signup-user-and-verify-email) + +Articles in this series: + +### 1. API with Python, FastAPI, and MongoDB: JWT Authentication + +[API with Python, FastAPI, and MongoDB: JWT Authentication](https://codevoweb.com/api-with-python-fastapi-and-mongodb-jwt-authentication) + +### 2. Build API with Python & FastAPI: SignUp User and Verify Email + +[Build API with Python & FastAPI: SignUp User and Verify Email](https://codevoweb.com/api-with-python-fastapi-signup-user-and-verify-email) + +### 3. CRUD RESTful API Server with Python, FastAPI, and MongoDB + +[CRUD RESTful API Server with Python, FastAPI, and MongoDB](https://codevoweb.com/crud-restful-api-server-with-python-fastapi-and-mongodb) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c1009e6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,44 @@ +aiosmtplib==2.0.0 +anyio==3.6.2 +bcrypt==4.0.1 +black==22.12.0 +blinker==1.5 +certifi==2022.12.7 +cffi==1.15.1 +click==8.1.3 +colorama==0.4.6 +cryptography==3.4.8 +dnspython==2.2.1 +email-validator==1.3.0 +fastapi==0.88.0 +fastapi-jwt-auth==0.5.0 +fastapi-mail==1.2.4 +h11==0.14.0 +httpcore==0.16.3 +httptools==0.5.0 +httpx==0.23.1 +idna==3.4 +itsdangerous==2.1.2 +Jinja2==3.1.2 +MarkupSafe==2.1.1 +mypy-extensions==0.4.3 +orjson==3.8.3 +passlib==1.7.4 +pathspec==0.10.3 +platformdirs==2.6.2 +pycparser==2.21 +pydantic==1.10.3 +PyJWT==1.7.1 +pymongo==4.3.3 +python-dotenv==0.21.0 +python-multipart==0.0.5 +PyYAML==6.0 +rfc3986==1.5.0 +six==1.16.0 +sniffio==1.3.0 +starlette==0.22.0 +typing_extensions==4.4.0 +ujson==5.6.0 +uvicorn==0.20.0 +watchfiles==0.18.1 +websockets==10.4