Zakladni mailing

This commit is contained in:
Michal Kváček
2024-02-01 07:22:14 +01:00
parent cc95197682
commit 77b9552a9d
11 changed files with 124 additions and 46 deletions
+5
View File
@@ -7,3 +7,8 @@ MYSQL_DATABASE: "poletani_cz"
MYSQL_USER: "mysql_user"
MYSQL_PASSWORD: "****"
APP_ENV: "production"
SMTP_HOST: ""
SMTP_PORT: ""
SMTP_USERNAME: ""
SMTP_PASSWORD: ""
+2 -1
View File
@@ -8,4 +8,5 @@ __pycache__/
# app specific files and dirs
uploads/*
!uploads/.gitkeep
src/emails/templates/html/*
src/emails/templates/html/*
.env
+2
View File
@@ -17,6 +17,8 @@ services:
- 8000:8000
volumes:
- .:/app
env_file:
- .env
environment:
TZ: "Europe/Prague"
API_URL: "http://localhost:8000"
+1
View File
@@ -1,5 +1,6 @@
fastapi==0.109.0
fastapi-jwt==0.2.0
fastapi-mail==1.4.1
strawberry-graphql[fastapi]==0.217.1
uvicorn==0.26.0
sqlalchemy[asyncio] == 2.0.25
+7 -2
View File
@@ -1,2 +1,7 @@
async def send_email(email):
pass
from emails.emails import Email
from mailer import Mailer
async def send_email(email: Email):
mailer = Mailer()
return await mailer.send(email)
+5
View File
@@ -11,5 +11,10 @@ SENTRY_DSN = os.environ.get("SENTRY_DSN")
APP_SECRET_KEY = os.environ.get("APP_SECRET_KEY") or "test"
SMTP_HOST = os.environ.get("SMTP_HOST")
SMTP_PORT = int(os.environ.get("SMTP_PORT") or 0)
SMTP_USERNAME = os.environ.get("SMTP_USERNAME")
SMTP_PASSWORD = os.environ.get("SMTP_PASSWORD")
if not APP_SECRET_KEY:
raise ValueError("Missing APP_SECRET_KEY!")
+49
View File
@@ -0,0 +1,49 @@
from typing import Any
from fastapi_mail import MessageSchema, MessageType
from pydantic import EmailStr
class Email(MessageSchema):
template: str
def __init__(self, to: list[EmailStr | str], subject: str, template: str, template_variables: dict, **data: Any) -> None:
data['subtype'] = MessageType.html
data['recipients'] = to
data['template'] = template
data['template_body'] = template_variables
data['subject'] = subject + " | Polétání.cz"
super().__init__(**data)
class ContactFormEmail(Email):
def __init__(self, email_from: str, content: str):
super().__init__(
to=["michal@kvacek.cz"],
reply_to=[email_from],
subject="Zpráva z kontaktního formuláře",
template="contact_form.html",
template_variables={
"content": content,
"email_from": email_from
}
)
class WelcomeEmail(Email):
def __init__(self, to: list[EmailStr | str]):
super().__init__(
to=to,
subject="Vítej",
template="welcome.html",
template_variables={}
)
class ResetPasswordEmail(Email):
def __init__(self, to: list[EmailStr | str], template_variables: dict):
super().__init__(
to=to,
subject="Reset hesla",
template="reset_password.html",
template_variables=template_variables
)
-39
View File
@@ -1,39 +0,0 @@
class Email:
def __init__(self, subject: str, to: str, template: str, template_variables: dict):
self.subject = subject + " | Polétání.cz"
self.to = to
self.template = template
self.template_variables = template_variables
def get_template_content(self) -> str:
return ""
class ContactFormEmail(Email):
def __init__(self, template_variables: dict):
super().__init__(
subject="Zpráva z kontaktního formuláře",
to="michal@kvacek.cz",
template="contact_form.html",
template_variables=template_variables
)
class WelcomeEmail(Email):
def __init__(self, to: str, template_variables: dict):
super().__init__(
subject="Vítej",
to=to,
template="welcome.html",
template_variables=template_variables
)
class ResetPasswordEmail(Email):
def __init__(self, to: str, template_variables: dict):
super().__init__(
subject="Reset hesla",
to=to,
template="reset_password.html",
template_variables=template_variables
)
+20
View File
@@ -0,0 +1,20 @@
from fastapi import BackgroundTasks
from pydantic import BaseModel
from starlette.responses import Response
from background_jobs.send_email import send_email
from emails.emails import WelcomeEmail, ContactFormEmail
class ContactInput(BaseModel):
email_from: str
content: str
class ContactEndpoint():
async def on_post(self, input: ContactInput, resp: Response, backgorund_tasks: BackgroundTasks):
backgorund_tasks.add_task(send_email, ContactFormEmail(
email_from=input.email_from,
content=input.content
))
# todo vratit nejakou smysluplnou
+22
View File
@@ -0,0 +1,22 @@
from fastapi_mail import ConnectionConfig, FastMail
from config import SMTP_USERNAME, SMTP_PASSWORD, SMTP_PORT, SMTP_HOST
from emails.emails import Email
class Mailer:
def __init__(self):
self.conf = ConnectionConfig(
MAIL_USERNAME=SMTP_USERNAME,
MAIL_PASSWORD=SMTP_PASSWORD,
MAIL_FROM_NAME="Polétání",
MAIL_PORT=SMTP_PORT,
MAIL_SERVER=SMTP_HOST,
MAIL_STARTTLS=False,
MAIL_SSL_TLS=True,
USE_CREDENTIALS=True,
TEMPLATE_FOLDER='/app/src/emails/templates/html'
)
async def send(self, mail: Email):
fm = FastMail(self.conf)
await fm.send_message(mail, mail.template)
+11 -4
View File
@@ -12,11 +12,14 @@ from starlette.staticfiles import StaticFiles
from strawberry.fastapi import GraphQLRouter
from config import APP_SECRET_KEY, GRAPHIQL, APP_DEBUG, ALLOW_CORS_ORIGINS, SENTRY_DSN, REFRESH_TOKEN_VALIDITY_DAYS
from database import models, async_session
from emails.emails import WelcomeEmail
from endpoints.contact import ContactEndpoint, ContactInput
from endpoints.forgotten_password import ForgottenPasswordEndpoint
from endpoints.login import LoginEndpoint, LoginInput, RefreshEndpoint, LogoutEndpoint
from endpoints.photo_editor_preview import PhotoEditorEndpoint
from endpoints.registration import RegistrationInput, RegistrationEndpoint
from graphql_schema.schema import schema, GraphQLContext
from mailer import Mailer
class App:
@@ -125,6 +128,14 @@ class App:
refresh_token=self.refresh_security
).on_post(user, resp)
@self.api_router.post("/contact")
async def contact(resp: Response, input: ContactInput, background_tasks: BackgroundTasks):
return await ContactEndpoint().on_post(
input,
resp,
background_tasks
)
@self.api_router.post("/logout")
async def logout(resp: Response):
return await LogoutEndpoint(
@@ -132,10 +143,6 @@ class App:
refresh_token=self.refresh_security
).on_post(resp)
# @self.api_router.get("/forgotten-password")
# async def logout(resp: Response):
# return await ForgottenPasswordEndpoint().on_get(resp)
@self.api_router.get("/photo/editor-preview/{photo_id}")
async def photo_editor_preview(
photo_id: int,