Skip to content

Commit

Permalink
✨ Mail Framework Working
Browse files Browse the repository at this point in the history
  • Loading branch information
pheeef committed Dec 18, 2020
1 parent 2742ba8 commit ceb8267
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 21 deletions.
47 changes: 33 additions & 14 deletions assets/mail.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
import smtplib
from socket import gaierror
import ssl
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText


def sendMail(to: str, subject: str, message: str):
def sendMail(to: str, subject: str, message: str, html: bool = False):
import env as e
try:
with smtplib.SMTP(e.mail_host, e.mail_port) as server:
server.login(e.mail_username, e.mail_password)
server.sendmail(from_addr=e.mail_sender, to_addrs=to, msg=message)
except (gaierror, ConnectionRefusedError):
return "failed to connect to the server"
except smtplib.SMTPServerDisconnected:
return "Failed to connect to the server with the given credentials"
except smtplib.SMTPException as e:
return "SMTP error occured: " + str(e)
server.close()
return True

context = ssl.create_default_context()

mail = MIMEMultipart("alternative")
mail["Subject"] = subject
mail["From"] = e.mail_sender
mail["To"] = to

if html:
msg = MIMEText(message, "html")
else:
msg = MIMEText(message, "plain")

mail.attach(msg)

with smtplib.SMTP_SSL(e.mail_host, e.mail_port, context=context) as server:

server.login(
user=e.mail_sender,
password=e.mail_password
)

server.sendmail(
from_addr=e.mail_sender,
to_addrs=to,
msg=mail.as_string()
)

return True
20 changes: 20 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM python:3.8.1-slim-buster

ENV WORKDIR=/usr/src/app
ENV USER=app
ENV APP_HOME=/home/app/web
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1

WORKDIR $WORKDIR

RUN pip install --upgrade pip
COPY ./requirements.txt $WORKDIR/requirements.txt
RUN pip install -r requirements.txt

RUN adduser --system --group $USER
RUN mkdir $APP_HOME
WORKDIR $APP_HOME

WORKDIR $APP_HOME
RUN chown -R $USER:$USER $APP_HOME
USER $USER
12 changes: 12 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: '3.8'

services:
web:
build:
context: ./services/api
command: gunicorn main:app --bind 0.0.0.0:5000 -k uvicorn.workers.UvicornWorker
expose:
- 5000
labels:
- "traefik.enable=true"
- "traefik.http.routers.fastapi.rule=Host(`fastapi.localhost`)"
5 changes: 3 additions & 2 deletions functions/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ def get_user(ID=None, EMAIL=None):

if ID is not None:
cursor.execute("SELECT * FROM USERDATA WHERE ID = %s", (ID,))
return fetch_data(cursor.fetchall()[0])
return fetch_data(cursor.get_row())
if EMAIL is not None:
cursor.execute("SELECT * FROM USERDATA WHERE EMAIL = %s", (EMAIL,))
return fetch_data(cursor.fetchall()[0])
return fetch_data(cursor.get_row())
else:
raise ValueError('USER not Valid')

Expand All @@ -35,6 +35,7 @@ def push_data(u: User):
ACTIVE = %s
WHERE ID = %s
"""

PARAM = (
u.EMAIL,
u.PASSWORD_HASH,
Expand Down
54 changes: 50 additions & 4 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
from pydantic import ValidationError
from starlette.responses import JSONResponse

from assets.database import openDBConnection
from functions.hashing import get_current_active_user, authenticate_user, get_password_hash
from functions.sessionkey import create_access_token
from functions.user import create_user, get_user, is_teacher, get_all_users, remove_passwordhash_obj
from models.token import Token
from models.user import User, preUser
from models.apikey import ApiKey
from models.mail import Mail

app = FastAPI()

Expand Down Expand Up @@ -50,7 +52,7 @@ async def read_user_by_id(id: int, current_user: User = Depends(get_current_acti
return get_user(ID=id)
else:
return JSONResponse(status_code=status.HTTP_403_FORBIDDEN,
content="Not sufficent Permissions to view other users")
content="Not sufficient Permissions to view other users")


@app.get("/users", description="returns all users (for admin dashboard)")
Expand All @@ -62,7 +64,7 @@ async def return_all_users(current_user: User = Depends(get_current_active_user)
return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=e.errors())
else:
return JSONResponse(status_code=status.HTTP_403_FORBIDDEN,
content="Not sufficent Permissions to view other users")
content="Not sufficient Permissions to view other users")


# Put
Expand All @@ -76,7 +78,7 @@ async def admin_create_user(user: User, current_user: User = Depends(get_current
else:
return JSONResponse(
status_code=status.HTTP_403_FORBIDDEN,
content="Not sufficent Permissions to create other users"
content="Not sufficient Permissions to create other users"
)


Expand All @@ -90,6 +92,21 @@ async def form_create_user(api_key: str, user: preUser):
content=e.errors()
)

db = openDBConnection()
cursor = db.cursor()

SQL = "SELECT EMAIL FROM USERDATA WHERE EMAIL = %s"
PAR = (user.EMAIL,)

cursor.execute(SQL, PAR)
data = cursor.fetchone()

if data is None:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="User is already registered"
)

PERMISSION_LEVEL = 0
if is_teacher(user.EMAIL):
PERMISSION_LEVEL = 1
Expand Down Expand Up @@ -118,5 +135,34 @@ async def form_create_user(api_key: str, user: preUser):
create_user(account)
return JSONResponse(
status_code=status.HTTP_200_OK,
content="User succesfully created"
content="User successfully created"
)


# Mail API

@app.post("/sendmail")
async def api_send_mail(apikey: ApiKey, mail: Mail):
try:
if apikey.PERMISSION >= 2:
if mail.send():
return JSONResponse(
status_code=status.HTTP_200_OK,
content="E-Mail was sent successfully"
)
else:
return HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Internal Server Error with Processing the Mail"
)
else:
return HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You are not allowed to send Mails"
)
except ValidationError as e:
return JSONResponse(
status_code=status.HTTP_403_FORBIDDEN,
content=e.errors()
)

22 changes: 22 additions & 0 deletions models/mail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from pydantic import BaseModel, validator
from typing import Optional
from assets.mail import sendMail
import re


class Mail(BaseModel):
to: str
subject: str
message: str
html: Optional[bool] = False

@validator('to')
def is_valid_email(cls, email):
regex = r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
if re.search(regex, email):
return email
else:
raise ValueError('EMAIL not Valid')

def send(self):
return sendMail(to=self.to, subject=self.subject, message=self.message, html=self.html)
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ pydantic~=1.6.1
fastapi~=0.61.1
mysql-connector-python~=8.0.21
passlib~=1.7.2
uvicorn~=0.11.8
bcrypt~=3.2.0
uvicorn~=0.12.2
python-jose~=3.2.0
python-multipart~=0.0.5

0 comments on commit ceb8267

Please sign in to comment.