Skip to content

Commit aa4457a

Browse files
committed
Remove email endpoints for future implementation
1 parent 9439b24 commit aa4457a

File tree

9 files changed

+4
-309
lines changed

9 files changed

+4
-309
lines changed

backend/poetry.lock

+1-62
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/pyproject.toml

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,12 @@ readme = 'README.md'
77
packages = [{include = 'src'}]
88

99
[tool.poetry.dependencies]
10+
python = '3.12.*'
1011
alembic = '^1.13.2'
1112
aiosqlite = '^0.20.0'
1213
asyncpg = '^0.30.0'
1314
email-validator = '^2.2.0'
1415
fastapi = {extras = ['standard'], version = '^0.112.0'}
15-
fastapi-mail = "^1.4.2"
16-
itsdangerous = "^2.2.0"
17-
python = '3.12.*'
1816
python-multipart = '^0.0.9'
1917
pydantic-settings = '^2.4.0'
2018
pyjwt = '^2.9.0'

backend/src/api/routes/users.py

+1-173
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@
77
CurrentUser,
88
SessionDep,
99
)
10-
from src.core.security import (
11-
create_url_safe_token,
12-
decode_url_safe_token,
13-
)
14-
from src.email.email import create_email_message, email
15-
from src.schemas.base import Email, Message
10+
from src.schemas.base import Message
1611
from src.schemas.users import (
1712
PasswordChange,
1813
UserRequestCreate,
@@ -128,170 +123,3 @@ async def delete_user_me(
128123
)
129124

130125
return Message(message='User deleted.')
131-
132-
133-
# -- Email verification routes --
134-
135-
136-
@router.get('/check-verification-status/{user_id}', response_model=Message)
137-
async def is_verified(
138-
session: SessionDep, current_user: CurrentUser, user_id: int
139-
) -> Message:
140-
"""
141-
Check if own account is verified.
142-
"""
143-
user_db = await user_service.get_user_by_id(
144-
session=session, user_id=user_id
145-
)
146-
147-
if not user_db:
148-
raise HTTPException(
149-
status_code=HTTPStatus.NOT_FOUND, detail='User not found.'
150-
)
151-
if current_user.id != user_id:
152-
raise HTTPException(
153-
status_code=HTTPStatus.FORBIDDEN, detail='Not enough permissions.'
154-
)
155-
156-
if user_db.is_verified:
157-
return Message(message='User is already verified.')
158-
159-
return Message(message='User has not been verified yet.')
160-
161-
162-
@router.get('/verify-account', response_model=Message)
163-
async def verify_account(current_user: CurrentUser) -> Message:
164-
"""
165-
Send an email to the user to verify their account.
166-
"""
167-
email_token = create_url_safe_token(data={'email': current_user.email})
168-
169-
link = f'http://127.0.0.1:8000/users/verify/{email_token}'
170-
171-
html_message = f"""
172-
<h1>Verify your account</h1>
173-
<p>Click in this <a href={link}>link</a> to verify your account </p>
174-
"""
175-
176-
message = create_email_message(
177-
recipients=[current_user.email],
178-
subject='Verify your email',
179-
body=html_message,
180-
)
181-
182-
await email.send_message(message)
183-
184-
return Message(message=f'Email sent to {current_user.email}')
185-
186-
187-
@router.get('/verify/{token}', response_model=Message)
188-
async def verify(
189-
session: SessionDep, current_user: CurrentUser, token: str
190-
) -> Message:
191-
"""
192-
Verify the user account based on the verification email sent.
193-
"""
194-
email_token = decode_url_safe_token(token)
195-
196-
if not email_token:
197-
raise HTTPException(
198-
status_code=HTTPStatus.UNAUTHORIZED,
199-
detail='Could not validate credentials.',
200-
)
201-
202-
user_email = email_token.get('email')
203-
204-
if not user_email or user_email != current_user.email:
205-
raise HTTPException(
206-
status_code=HTTPStatus.FORBIDDEN,
207-
detail='Could not validate account',
208-
)
209-
210-
user_updated = current_user
211-
user_updated.is_verified = True
212-
session.add(user_updated)
213-
await session.commit()
214-
215-
return Message(message='Account verified sucessfully!')
216-
217-
218-
@router.post('/test-email', response_model=Message)
219-
async def test_email(emails: Email) -> Message:
220-
recipient_email_addresses = emails.addresses
221-
html = '<h1>Testing Email</h1>'
222-
message = create_email_message(
223-
recipients=recipient_email_addresses,
224-
subject='Welcome to the test',
225-
body=html,
226-
)
227-
await email.send_message(message)
228-
229-
return Message(message='Email sent')
230-
231-
232-
# -- Email recovery access routes --
233-
234-
235-
@router.get('/recover-access', response_model=Message)
236-
async def recover_access(current_user: CurrentUser) -> Message:
237-
"""
238-
Recover account access (WIP).
239-
"""
240-
email_token = create_url_safe_token(data={'email': current_user.email})
241-
242-
link = f'http://127.0.0.1:8000/users/change-password/{email_token}'
243-
244-
html_message = f"""
245-
<h1>Reset Password</h1>
246-
<p>Click in this <a href={link}>link</a> to reset your password</p>
247-
"""
248-
249-
message = create_email_message(
250-
recipients=[current_user.email],
251-
subject='Reset Password',
252-
body=html_message,
253-
)
254-
255-
await email.send_message(message)
256-
257-
return Message(message=f'Email sent to {current_user.email}')
258-
259-
260-
@router.post('/change-password/{token}', response_model=Message)
261-
async def change_password_by_email(
262-
session: SessionDep,
263-
passwords: PasswordChange,
264-
current_user: CurrentUser,
265-
token: str,
266-
) -> Message:
267-
"""
268-
Change password when account recovery is requested (WIP).
269-
"""
270-
email_token = decode_url_safe_token(token)
271-
272-
if not email_token:
273-
raise HTTPException(
274-
status_code=HTTPStatus.UNAUTHORIZED,
275-
detail='Could not validate credentials.',
276-
)
277-
278-
user_email = email_token.get('email')
279-
280-
if not user_email or user_email != current_user.email:
281-
raise HTTPException(
282-
status_code=HTTPStatus.FORBIDDEN,
283-
detail='Password change could not be completed.',
284-
)
285-
286-
if passwords.password != passwords.password_confirmation:
287-
raise HTTPException(
288-
status_code=HTTPStatus.BAD_REQUEST, detail='Passwords dont match.'
289-
)
290-
291-
await user_service.change_password(
292-
session=session,
293-
user_to_update=current_user,
294-
password=passwords.password,
295-
)
296-
297-
return Message(message='Password has been changed!')

backend/src/app.py

-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
from fastapi import FastAPI
22
from fastapi.middleware.cors import CORSMiddleware
3-
from starlette.middleware.sessions import SessionMiddleware
43

54
from src.api.main import api_router
6-
from src.core.settings import settings
75
from src.schemas.base import Message
86

97
app = FastAPI()
@@ -18,8 +16,6 @@
1816
allow_headers=['*'],
1917
)
2018

21-
app.add_middleware(SessionMiddleware, secret_key=settings.SECRET_KEY)
22-
2319
app.include_router(api_router)
2420

2521

backend/src/core/security.py

-19
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from zoneinfo import ZoneInfo
44

55
from fastapi.security import OAuth2PasswordBearer
6-
from itsdangerous import URLSafeTimedSerializer
76
from jwt import encode
87
from pwdlib import PasswordHash
98

@@ -33,21 +32,3 @@ def create_access_token(data: dict[str, Any]) -> str:
3332
)
3433

3534
return encoded_jwt
36-
37-
38-
serializer = URLSafeTimedSerializer(
39-
secret_key=settings.SECRET_KEY, salt='email-configuration'
40-
)
41-
42-
43-
def create_url_safe_token(data: dict[str, str]) -> str:
44-
token = serializer.dumps(data)
45-
return token
46-
47-
48-
def decode_url_safe_token(token: str) -> dict[str, Any] | None:
49-
try:
50-
token_data: dict[str, Any] = serializer.loads(token)
51-
return token_data
52-
except Exception:
53-
return None

backend/src/core/settings.py

+1-16
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
from pathlib import Path
2-
3-
from pydantic import DirectoryPath, SecretStr
41
from pydantic_settings import BaseSettings, SettingsConfigDict
52

63

74
class Settings(BaseSettings):
85
model_config = SettingsConfigDict(
9-
env_file='.env', env_file_encoding='utf-8'
6+
env_file='.env', env_file_encoding='utf-8', extra='allow'
107
)
118

129
DATABASE_URL: str = 'sqlite+aiosqlite:///./database.db'
@@ -19,17 +16,5 @@ class Settings(BaseSettings):
1916
FIRST_SUPERUSER_EMAIL: str = 'admin@admin.com'
2017
FIRST_SUPERUSER_PASSWORD: str = 'admin'
2118

22-
EMAIL_USERNAME: str = ''
23-
EMAIL_PASSWORD: SecretStr = SecretStr('')
24-
EMAIL_FROM: str = ''
25-
EMAIL_PORT: int = 587
26-
EMAIL_SERVER: str = ''
27-
EMAIL_FROM_NAME: str = ''
28-
EMAIL_STARTTLS: bool = True
29-
EMAIL_SSL_TLS: bool = False
30-
USE_CREDENTIALS: bool = True
31-
VALIDATE_CERTS: bool = True
32-
TEMPLATE_FOLDER: DirectoryPath = Path('src/email/templates')
33-
3419

3520
settings = Settings()

backend/src/email/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)