Skip to content

Commit

Permalink
server/article: unsubscribe link and email header
Browse files Browse the repository at this point in the history
  • Loading branch information
zegl committed Dec 13, 2023
1 parent 80b6ffc commit 5e65031
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 7 deletions.
22 changes: 20 additions & 2 deletions clients/apps/web/src/app/(email)/email/article/[id]/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ const renderArticle = async (
req: NextRequest,
articleId: string,
injectMagicLinkToken?: string,
unsubscribeLink?: string,
): Promise<NextResponse> => {
let article: Article

Expand Down Expand Up @@ -200,6 +201,11 @@ const renderArticle = async (
return u.toString()
}

unsubscribeLink =
unsubscribeLink ??
// If we don't have a subscription token. Send user to the subscriptions page.
preAuthLink(`https://polar.sh/${post.organization.name}?tab=subscriptions`)

const html = render(
<Html lang="en">
<Tailwind config={twConfig}>
Expand Down Expand Up @@ -275,7 +281,7 @@ const renderArticle = async (

<hr />

<center className="py-6 text-xs text-gray-500">
<center className="py-3 pt-3 text-xs text-gray-500">
You received this email because you&apos;re a subscriber to{' '}
<a
href={preAuthLink(
Expand All @@ -288,6 +294,16 @@ const renderArticle = async (
</a>
. Thanks!
</center>

<center className="py-3 text-xs text-gray-500">
<a

Check warning

Code scanning / CodeQL

Potentially unsafe external link Medium

External links without noopener/noreferrer are a potential security risk.
href={unsubscribeLink}
target="_blank"
className="!underline underline-offset-1"
>
Unsubscribe
</a>
</center>
</Section>
</Container>
</Body>
Expand Down Expand Up @@ -329,5 +345,7 @@ export async function POST(
? input['inject_magic_link_token']
: undefined

return renderArticle(req, params.id, injectMagicLinkToken)
const unsubscribeLink = input ? input['unsubscribe_link'] : undefined

return renderArticle(req, params.id, injectMagicLinkToken, unsubscribeLink)
}
16 changes: 16 additions & 0 deletions server/polar/article/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,22 @@ def _get_subscribed_articles_statement(

return statement

async def get_subscriber(
self,
session: AsyncSession,
user_id: UUID,
subscribed_to_organization_id: UUID,
) -> ArticlesSubscription | None:
statement = (
sql.select(ArticlesSubscription)
.where(ArticlesSubscription.user_id == user_id)
.where(
ArticlesSubscription.organization_id == subscribed_to_organization_id
)
)
res = await session.execute(statement)
return res.scalars().unique().one_or_none()

async def unsubscribe(
self,
session: AsyncSession,
Expand Down
24 changes: 19 additions & 5 deletions server/polar/article/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,25 @@ async def articles_send_to_user(
expires_at=utc_now() + timedelta(hours=24),
)

email_headers: dict[str, str] = {}

render_data = {
# Add pre-authenticated tokens to the end of all links in the email
"inject_magic_link_token": magic_link_token,
}

# Get subscriber ID (if exists)
subscriber = await article_service.get_subscriber(
session, user_id, article.organization_id
)
if subscriber:
unsubscribe_link = f"https://polar.sh/unsubscribe?org={article.organization.name}&id={subscriber.id}"
render_data["unsubscribe_link"] = unsubscribe_link
email_headers["List-Unsubscribe"] = f"<{unsubscribe_link}>"

req = requests.post(
f"{settings.FRONTEND_BASE_URL}/email/article/{article.id}",
json={
# Add pre-authenticated tokens to the end of all links in the email
"inject_magic_link_token": magic_link_token,
},
json=render_data,
# Authenticating to the renderer as the user we're sending the email to
headers={"Cookie": f"polar_session={jwt};"},
)
Expand All @@ -87,9 +100,10 @@ async def articles_send_to_user(
email_sender.send_to_user(
to_email_addr=user.email,
subject=subject,
html_content=rendered, # .decode("utf8"),
html_content=rendered,
from_name=from_name,
from_email_addr=f"{article.organization.name}@posts.polar.sh",
email_headers=email_headers,
)


Expand Down
6 changes: 6 additions & 0 deletions server/polar/email/sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def send_to_user(
html_content: str,
from_name: str = "Polar",
from_email_addr: str = "[email protected]",
email_headers: dict[str, str] = {},
) -> None:
pass

Expand All @@ -33,6 +34,7 @@ def send_to_user(
html_content: str,
from_name: str = "Polar",
from_email_addr: str = "[email protected]",
email_headers: dict[str, str] = {},
) -> None:
log.info(
"logging email",
Expand All @@ -41,6 +43,7 @@ def send_to_user(
html_content=html_content,
from_name=from_name,
from_email_addr=from_email_addr,
email_headers=email_headers,
)


Expand All @@ -56,6 +59,7 @@ def send_to_user(
html_content: str,
from_name: str = "Polar",
from_email_addr: str = "[email protected]", # not used
email_headers: dict[str, str] = {}, # not used
) -> None:
from_email = Email(email="[email protected]", name=from_name)
to_email = To(to_email_addr)
Expand Down Expand Up @@ -83,12 +87,14 @@ def send_to_user(
html_content: str,
from_name: str = "Polar",
from_email_addr: str = "[email protected]",
email_headers: dict[str, str] = {},
) -> None:
params = {
"from": f"{from_name} <{from_email_addr}>",
"to": [to_email_addr],
"subject": subject,
"html": html_content,
"headers": email_headers,
}

email = resend.Emails.send(params)
Expand Down

0 comments on commit 5e65031

Please sign in to comment.