Zakladni mailing
This commit is contained in:
@@ -7,3 +7,8 @@ MYSQL_DATABASE: "poletani_cz"
|
|||||||
MYSQL_USER: "mysql_user"
|
MYSQL_USER: "mysql_user"
|
||||||
MYSQL_PASSWORD: "****"
|
MYSQL_PASSWORD: "****"
|
||||||
APP_ENV: "production"
|
APP_ENV: "production"
|
||||||
|
|
||||||
|
SMTP_HOST: ""
|
||||||
|
SMTP_PORT: ""
|
||||||
|
SMTP_USERNAME: ""
|
||||||
|
SMTP_PASSWORD: ""
|
||||||
@@ -9,3 +9,4 @@ __pycache__/
|
|||||||
uploads/*
|
uploads/*
|
||||||
!uploads/.gitkeep
|
!uploads/.gitkeep
|
||||||
src/emails/templates/html/*
|
src/emails/templates/html/*
|
||||||
|
.env
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ services:
|
|||||||
- 8000:8000
|
- 8000:8000
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
environment:
|
environment:
|
||||||
TZ: "Europe/Prague"
|
TZ: "Europe/Prague"
|
||||||
API_URL: "http://localhost:8000"
|
API_URL: "http://localhost:8000"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
fastapi==0.109.0
|
fastapi==0.109.0
|
||||||
fastapi-jwt==0.2.0
|
fastapi-jwt==0.2.0
|
||||||
|
fastapi-mail==1.4.1
|
||||||
strawberry-graphql[fastapi]==0.217.1
|
strawberry-graphql[fastapi]==0.217.1
|
||||||
uvicorn==0.26.0
|
uvicorn==0.26.0
|
||||||
sqlalchemy[asyncio] == 2.0.25
|
sqlalchemy[asyncio] == 2.0.25
|
||||||
|
|||||||
@@ -1,2 +1,7 @@
|
|||||||
async def send_email(email):
|
from emails.emails import Email
|
||||||
pass
|
from mailer import Mailer
|
||||||
|
|
||||||
|
|
||||||
|
async def send_email(email: Email):
|
||||||
|
mailer = Mailer()
|
||||||
|
return await mailer.send(email)
|
||||||
@@ -11,5 +11,10 @@ SENTRY_DSN = os.environ.get("SENTRY_DSN")
|
|||||||
|
|
||||||
APP_SECRET_KEY = os.environ.get("APP_SECRET_KEY") or "test"
|
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:
|
if not APP_SECRET_KEY:
|
||||||
raise ValueError("Missing APP_SECRET_KEY!")
|
raise ValueError("Missing APP_SECRET_KEY!")
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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
|
|
||||||
)
|
|
||||||
@@ -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
|
||||||
@@ -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
@@ -12,11 +12,14 @@ from starlette.staticfiles import StaticFiles
|
|||||||
from strawberry.fastapi import GraphQLRouter
|
from strawberry.fastapi import GraphQLRouter
|
||||||
from config import APP_SECRET_KEY, GRAPHIQL, APP_DEBUG, ALLOW_CORS_ORIGINS, SENTRY_DSN, REFRESH_TOKEN_VALIDITY_DAYS
|
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 database import models, async_session
|
||||||
|
from emails.emails import WelcomeEmail
|
||||||
|
from endpoints.contact import ContactEndpoint, ContactInput
|
||||||
from endpoints.forgotten_password import ForgottenPasswordEndpoint
|
from endpoints.forgotten_password import ForgottenPasswordEndpoint
|
||||||
from endpoints.login import LoginEndpoint, LoginInput, RefreshEndpoint, LogoutEndpoint
|
from endpoints.login import LoginEndpoint, LoginInput, RefreshEndpoint, LogoutEndpoint
|
||||||
from endpoints.photo_editor_preview import PhotoEditorEndpoint
|
from endpoints.photo_editor_preview import PhotoEditorEndpoint
|
||||||
from endpoints.registration import RegistrationInput, RegistrationEndpoint
|
from endpoints.registration import RegistrationInput, RegistrationEndpoint
|
||||||
from graphql_schema.schema import schema, GraphQLContext
|
from graphql_schema.schema import schema, GraphQLContext
|
||||||
|
from mailer import Mailer
|
||||||
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
@@ -125,6 +128,14 @@ class App:
|
|||||||
refresh_token=self.refresh_security
|
refresh_token=self.refresh_security
|
||||||
).on_post(user, resp)
|
).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")
|
@self.api_router.post("/logout")
|
||||||
async def logout(resp: Response):
|
async def logout(resp: Response):
|
||||||
return await LogoutEndpoint(
|
return await LogoutEndpoint(
|
||||||
@@ -132,10 +143,6 @@ class App:
|
|||||||
refresh_token=self.refresh_security
|
refresh_token=self.refresh_security
|
||||||
).on_post(resp)
|
).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}")
|
@self.api_router.get("/photo/editor-preview/{photo_id}")
|
||||||
async def photo_editor_preview(
|
async def photo_editor_preview(
|
||||||
photo_id: int,
|
photo_id: int,
|
||||||
|
|||||||
Reference in New Issue
Block a user