mirror of
https://github.com/aykhans/portfolio-blog.git
synced 2025-09-08 15:10:45 +00:00
Added send-email feature
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
from pydantic import PostgresDsn
|
||||
from pydantic_settings import BaseSettings
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic_settings import BaseSettings
|
||||
from pydantic import (
|
||||
EmailStr,
|
||||
PostgresDsn
|
||||
)
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
PROJECT_NAME: str = 'FastAPI Portfolio & Blog'
|
||||
@@ -36,5 +41,14 @@ class Settings(BaseSettings):
|
||||
path=self.POSTGRES_DB
|
||||
)
|
||||
|
||||
SMTP_SSL_TLS: bool = True
|
||||
SMTP_PORT: int = 587
|
||||
SMTP_HOST: str = "smtp.gmail.com"
|
||||
SMTP_USER: EmailStr
|
||||
SMTP_PASSWORD: str
|
||||
|
||||
EMAILS_FROM_NAME: str = PROJECT_NAME
|
||||
EMAIL_RECIPIENTS: list[EmailStr] = []
|
||||
|
||||
|
||||
settings = Settings()
|
@@ -40,30 +40,6 @@ app = FastAPI(
|
||||
|
||||
app.include_router(main_router)
|
||||
|
||||
# templates = Jinja2Templates(directory=main_path / 'templates')
|
||||
|
||||
|
||||
|
||||
# @app.get("/", response_class=HTMLResponse)
|
||||
# async def read_item(request: Request):
|
||||
# return templates.TemplateResponse("test.html", {"request": request})
|
||||
|
||||
# from pydantic import BaseModel
|
||||
|
||||
|
||||
# class User(BaseModel):
|
||||
# username: str
|
||||
# password: str
|
||||
|
||||
# @app.post("/", response_class=HTMLResponse)
|
||||
# async def read_item(request: Request, user: User):
|
||||
# print(user)
|
||||
# return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
# @app.get("/t", response_class=HTMLResponse)
|
||||
# async def read_item(request: Request):
|
||||
# return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
|
||||
@app.exception_handler(ValidationError)
|
||||
async def validation_exception_handler(request, exc):
|
||||
|
14
src/app/schemas/main.py
Normal file
14
src/app/schemas/main.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Form
|
||||
|
||||
from pydantic import EmailStr
|
||||
|
||||
|
||||
@dataclass
|
||||
class SendEmail:
|
||||
name: str = Form(..., max_length=50)
|
||||
email: EmailStr = Form(...)
|
||||
phone: Optional[str] = Form(None, max_length=20)
|
||||
message: str = Form(..., max_length=1000)
|
@@ -56,18 +56,18 @@
|
||||
<div class="col-md-10 col-lg-8 mx-auto">
|
||||
<h1>Contact me</h1>
|
||||
<p>Want to get in touch? Fill out the form below to send me a message and I will get back to you as soon as possible!</p>
|
||||
<form id="contactForm" name="sentMessage">
|
||||
<form action="/send-email" method="POST" id="contactForm" name="sentMessage">
|
||||
<div class="control-group">
|
||||
<div class="form-floating controls mb-3"><input class="form-control" type="text" id="name" required="" placeholder="Name"><label class="form-label" for="name">Name</label><small class="form-text text-danger help-block"></small></div>
|
||||
<div class="form-floating controls mb-3"><input name="name" class="form-control" type="text" id="name" required maxlength="50" placeholder="Name"><label class="form-label" for="name">Name</label><small class="form-text text-danger help-block"></small></div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-floating controls mb-3"><input class="form-control" type="email" id="email" required="" placeholder="Email Address"><label class="form-label">Email Address</label><small class="form-text text-danger help-block"></small></div>
|
||||
<div class="form-floating controls mb-3"><input class="form-control" name="email" type="email" id="email" required placeholder="Email Address"><label class="form-label">Email Address</label><small class="form-text text-danger help-block"></small></div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-floating controls mb-3"><input class="form-control" type="tel" id="phone" required="" placeholder="Phone Number"><label class="form-label">Phone Number</label><small class="form-text text-danger help-block"></small></div>
|
||||
<div class="form-floating controls mb-3"><input class="form-control" name="phone" type="tel" id="phone" maxlength="20" placeholder="Phone Number"><label class="form-label">Phone Number</label><small class="form-text text-danger help-block"></small></div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-floating controls mb-3"><textarea class="form-control" id="message" data-validation-required-message="Please enter a message." required="" placeholder="Message" style="height: 150px;"></textarea><label class="form-label">Message</label><small class="form-text text-danger help-block"></small></div>
|
||||
<div class="form-floating controls mb-3"><textarea class="form-control" name="message" id="message" maxlength="1000" data-validation-required-message="Please enter a message." required="" placeholder="Message" style="height: 150px;"></textarea><label class="form-label">Message</label><small class="form-text text-danger help-block"></small></div>
|
||||
</div>
|
||||
<div id="success"></div>
|
||||
<div class="mb-3"><button class="btn btn-primary" id="sendMessageButton" type="submit">Send</button></div>
|
||||
|
37
src/app/utils/email.py
Normal file
37
src/app/utils/email.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from functools import partial
|
||||
|
||||
from fastapi_mail import (
|
||||
FastMail,
|
||||
MessageSchema,
|
||||
ConnectionConfig,
|
||||
MessageType
|
||||
)
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
def send_email_notification(
|
||||
subject: str,
|
||||
body: str
|
||||
) -> partial:
|
||||
|
||||
if settings.EMAIL_RECIPIENTS:
|
||||
conf = ConnectionConfig(
|
||||
MAIL_USERNAME = settings.SMTP_USER,
|
||||
MAIL_PASSWORD = settings.SMTP_PASSWORD,
|
||||
MAIL_FROM = settings.SMTP_USER,
|
||||
MAIL_PORT = settings.SMTP_PORT,
|
||||
MAIL_SERVER = settings.SMTP_HOST,
|
||||
MAIL_SSL_TLS = settings.SMTP_SSL_TLS,
|
||||
MAIL_STARTTLS = True
|
||||
)
|
||||
|
||||
message = MessageSchema(
|
||||
subject = subject,
|
||||
recipients = settings.EMAIL_RECIPIENTS,
|
||||
body = body,
|
||||
subtype=MessageType.plain
|
||||
)
|
||||
|
||||
fast_mail = FastMail(conf)
|
||||
return partial(fast_mail.send_message, message)
|
@@ -1,12 +1,13 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Query,
|
||||
Request,
|
||||
Depends
|
||||
Depends,
|
||||
BackgroundTasks
|
||||
)
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -14,6 +15,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app import crud
|
||||
from app.core.config import settings
|
||||
from app.schemas import ListPostInTemplate
|
||||
from app.schemas.main import SendEmail
|
||||
from app.utils.email import send_email_notification
|
||||
from app.views.depends import get_async_db
|
||||
|
||||
|
||||
@@ -30,6 +33,31 @@ async def home(request: Request):
|
||||
)
|
||||
|
||||
|
||||
@router.post('/send-email')
|
||||
async def send_email(
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks,
|
||||
form_data: SendEmail = Depends()
|
||||
):
|
||||
|
||||
body = f"name: {form_data.name}\n"\
|
||||
f"email: {form_data.email}\n"\
|
||||
f"phone: {form_data.phone}\n"\
|
||||
f"message: {form_data.message}"
|
||||
|
||||
background_tasks.add_task(
|
||||
send_email_notification(
|
||||
subject = f"Portfolio Blog (by {form_data.email})",
|
||||
body = body
|
||||
)
|
||||
)
|
||||
|
||||
return RedirectResponse(
|
||||
str(request.url_for('home')) + '#contact',
|
||||
status_code=303
|
||||
)
|
||||
|
||||
|
||||
@router.get('/blog', response_class=HTMLResponse)
|
||||
async def blog(
|
||||
request: Request,
|
||||
|
Reference in New Issue
Block a user