diff --git a/clients/apps/web/src/app/(email)/email/article/[id]/route.tsx b/clients/apps/web/src/app/(email)/email/article/[id]/route.tsx index c5aca2df8d..99350c8930 100644 --- a/clients/apps/web/src/app/(email)/email/article/[id]/route.tsx +++ b/clients/apps/web/src/app/(email)/email/article/[id]/route.tsx @@ -172,6 +172,7 @@ const renderArticle = async ( req: NextRequest, articleId: string, injectMagicLinkToken?: string, + unsubscribeLink?: string, ): Promise => { let article: Article @@ -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( @@ -275,7 +281,7 @@ const renderArticle = async (
-
+
You received this email because you're a subscriber to{' '} . Thanks!
+ +
+ + Unsubscribe + +
@@ -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) } diff --git a/server/polar/article/service.py b/server/polar/article/service.py index 861d4bdda9..e305e0605f 100644 --- a/server/polar/article/service.py +++ b/server/polar/article/service.py @@ -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, diff --git a/server/polar/article/tasks.py b/server/polar/article/tasks.py index 1252978635..560bee3bf9 100644 --- a/server/polar/article/tasks.py +++ b/server/polar/article/tasks.py @@ -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};"}, ) @@ -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, ) diff --git a/server/polar/email/sender.py b/server/polar/email/sender.py index c579ab3769..d90f9750db 100644 --- a/server/polar/email/sender.py +++ b/server/polar/email/sender.py @@ -21,6 +21,7 @@ def send_to_user( html_content: str, from_name: str = "Polar", from_email_addr: str = "notifications@polar.sh", + email_headers: dict[str, str] = {}, ) -> None: pass @@ -33,6 +34,7 @@ def send_to_user( html_content: str, from_name: str = "Polar", from_email_addr: str = "notifications@polar.sh", + email_headers: dict[str, str] = {}, ) -> None: log.info( "logging email", @@ -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, ) @@ -56,6 +59,7 @@ def send_to_user( html_content: str, from_name: str = "Polar", from_email_addr: str = "notifications@polar.sh", # not used + email_headers: dict[str, str] = {}, # not used ) -> None: from_email = Email(email="notifications@polar.sh", name=from_name) to_email = To(to_email_addr) @@ -83,12 +87,14 @@ def send_to_user( html_content: str, from_name: str = "Polar", from_email_addr: str = "polarsource@posts.polar.sh", + 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)