mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: email sending and invites backend code
This commit is contained in:
parent
287fa8f41e
commit
79ddfb1ce1
9 changed files with 155 additions and 21 deletions
|
|
@ -174,6 +174,7 @@ async def api_get_invite_codes(
|
|||
"""
|
||||
return await get_invite_codes(request, org_id, current_user, db_session)
|
||||
|
||||
|
||||
@router.get("/{org_id}/invites/code/{invite_code}")
|
||||
async def api_get_invite_code(
|
||||
request: Request,
|
||||
|
|
@ -186,7 +187,7 @@ async def api_get_invite_code(
|
|||
Get invite code
|
||||
"""
|
||||
print(f"org_id: {org_id}, invite_code: {invite_code}")
|
||||
return await get_invite_code(request, org_id,invite_code, current_user, db_session)
|
||||
return await get_invite_code(request, org_id, invite_code, current_user, db_session)
|
||||
|
||||
|
||||
@router.delete("/{org_id}/invites/{org_invite_code_uuid}")
|
||||
|
|
@ -209,14 +210,17 @@ async def api_delete_invite_code(
|
|||
async def api_invite_batch_users(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
users: str,
|
||||
emails: str,
|
||||
invite_code_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session: Session = Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Invite batch users
|
||||
Invite batch users by emails
|
||||
"""
|
||||
return await invite_batch_users(request, org_id, users, db_session, current_user)
|
||||
return await invite_batch_users(
|
||||
request, org_id, emails, invite_code_uuid, db_session, current_user
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{org_id}/invites/users")
|
||||
|
|
@ -231,6 +235,7 @@ async def api_get_org_users_invites(
|
|||
"""
|
||||
return await get_list_of_invited_users(request, org_id, db_session, current_user)
|
||||
|
||||
|
||||
@router.delete("/{org_id}/invites/users/{email}")
|
||||
async def api_delete_org_users_invites(
|
||||
request: Request,
|
||||
|
|
|
|||
0
apps/api/src/services/email/__init__.py
Normal file
0
apps/api/src/services/email/__init__.py
Normal file
19
apps/api/src/services/email/utils.py
Normal file
19
apps/api/src/services/email/utils.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import resend
|
||||
from config.config import get_learnhouse_config
|
||||
|
||||
def send_email(to: str, subject: str, body: str):
|
||||
lh_config = get_learnhouse_config()
|
||||
params = {
|
||||
"from": f"LearnHouse <"
|
||||
+ lh_config.mailing_config.system_email_address
|
||||
+ ">",
|
||||
"to": [to],
|
||||
"subject": subject,
|
||||
"html": body,
|
||||
}
|
||||
|
||||
|
||||
resend.api_key = lh_config.mailing_config.resend_api_key
|
||||
email = resend.Emails.send(params)
|
||||
|
||||
return email
|
||||
|
|
@ -5,11 +5,13 @@ import uuid
|
|||
import redis
|
||||
from datetime import datetime, timedelta
|
||||
from sqlmodel import Session, select
|
||||
from src.services.email.utils import send_email
|
||||
from config.config import get_learnhouse_config
|
||||
from src.services.orgs.orgs import rbac_check
|
||||
from src.db.users import AnonymousUser, PublicUser
|
||||
from src.db.users import AnonymousUser, PublicUser, UserRead
|
||||
from src.db.organizations import (
|
||||
Organization,
|
||||
OrganizationRead,
|
||||
)
|
||||
from fastapi import HTTPException, Request
|
||||
|
||||
|
|
@ -142,6 +144,7 @@ async def get_invite_codes(
|
|||
|
||||
return invite_codes_list
|
||||
|
||||
|
||||
async def get_invite_code(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
|
|
@ -181,23 +184,21 @@ async def get_invite_code(
|
|||
status_code=500,
|
||||
detail="Could not connect to Redis",
|
||||
)
|
||||
|
||||
|
||||
# Get invite code
|
||||
invite_code = r.keys(f"org_invite_code_*:org:{org.org_uuid}:code:{invite_code}") # type: ignore
|
||||
invite_code = r.keys(f"org_invite_code_*:org:{org.org_uuid}:code:{invite_code}") # type: ignore
|
||||
|
||||
if not invite_code:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Invite code not found",
|
||||
)
|
||||
|
||||
invite_code = r.get(invite_code[0]) # type: ignore
|
||||
|
||||
invite_code = r.get(invite_code[0]) # type: ignore
|
||||
invite_code = json.loads(invite_code)
|
||||
|
||||
return invite_code
|
||||
|
||||
|
||||
|
||||
|
||||
async def delete_invite_code(
|
||||
request: Request,
|
||||
|
|
@ -251,3 +252,57 @@ async def delete_invite_code(
|
|||
)
|
||||
|
||||
return keys
|
||||
|
||||
|
||||
def send_invite_email(
|
||||
org: OrganizationRead,
|
||||
invite_code_uuid: str,
|
||||
user: UserRead,
|
||||
email: str,
|
||||
):
|
||||
LH_CONFIG = get_learnhouse_config()
|
||||
redis_conn_string = LH_CONFIG.redis_config.redis_connection_string
|
||||
|
||||
if not redis_conn_string:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Redis connection string not found",
|
||||
)
|
||||
|
||||
# Connect to Redis
|
||||
r = redis.Redis.from_url(redis_conn_string)
|
||||
|
||||
if not r:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Could not connect to Redis",
|
||||
)
|
||||
|
||||
# Get invite code
|
||||
invite = r.keys(f"{invite_code_uuid}:org:{org.org_uuid}:code:*") # type: ignore
|
||||
|
||||
# Send email
|
||||
if invite:
|
||||
invite = r.get(invite[0])
|
||||
invite = json.loads(invite)
|
||||
|
||||
# send email
|
||||
send_email(
|
||||
to=email,
|
||||
subject=f"You have been invited to {org.name}",
|
||||
body=f"""
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello {email}</p>
|
||||
<p>You have been invited to {org.name} by @{user.username}. Your invite code is {invite['invite_code']}.</p>
|
||||
<p>Click <a href="{org.slug}.learnhouse.io/signup?inviteCode={invite['invite_code']}">here</a> to sign up.</p>
|
||||
<p>Thank you</p>
|
||||
</body>
|
||||
</html>
|
||||
""",
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import logging
|
|||
import redis
|
||||
from fastapi import HTTPException, Request
|
||||
from sqlmodel import Session, select
|
||||
from src.services.orgs.invites import send_invite_email
|
||||
from config.config import get_learnhouse_config
|
||||
from src.services.orgs.orgs import rbac_check
|
||||
from src.db.roles import Role, RoleRead
|
||||
|
|
@ -12,6 +13,7 @@ from src.db.users import AnonymousUser, PublicUser, User, UserRead
|
|||
from src.db.user_organizations import UserOrganization
|
||||
from src.db.organizations import (
|
||||
Organization,
|
||||
OrganizationRead,
|
||||
OrganizationUser,
|
||||
)
|
||||
|
||||
|
|
@ -259,6 +261,10 @@ async def invite_batch_users(
|
|||
detail="Organization not found",
|
||||
)
|
||||
|
||||
# get User sender
|
||||
statement = select(User).where(User.id == current_user.id)
|
||||
user = db_session.exec(statement).first()
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, org.org_uuid, current_user, "create", db_session)
|
||||
|
||||
|
|
@ -287,12 +293,22 @@ async def invite_batch_users(
|
|||
# skip this user
|
||||
continue
|
||||
|
||||
org = OrganizationRead.from_orm(org)
|
||||
user = UserRead.from_orm(user)
|
||||
|
||||
isEmailSent = send_invite_email(
|
||||
org,
|
||||
invite_code_uuid,
|
||||
user,
|
||||
email,
|
||||
)
|
||||
|
||||
invited_user_object = {
|
||||
"email": email,
|
||||
"org_id": org.id,
|
||||
"invite_code_uuid": invite_code_uuid,
|
||||
"pending": True,
|
||||
"email_sent": False,
|
||||
"email_sent": isEmailSent,
|
||||
"expires": ttl,
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"created_by": current_user.user_uuid,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue