Skip to content

Conversation

@lorenzocorallo
Copy link
Member

No description provided.

Copilot AI review requested due to automatic review settings November 1, 2025 14:28

This comment was marked as duplicate.

@lorenzocorallo lorenzocorallo marked this pull request as draft November 1, 2025 15:15
@coderabbitai
Copy link

coderabbitai bot commented Dec 26, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds React-based email templates, mailer wrappers, and Azure Graph email sending (client + sendEmail). Adds env vars and dependencies for email tooling, a /test/welcome-email route, WebSocketServer rename + close(), graceful shutdown and global error handlers, and a MaybeArray type.

Changes

Cohort / File(s) Change Summary
Dependencies & Build
backend/package.json, backend/tsconfig.json
Added email/react, Microsoft Graph, nodemailer, types, and an email npm script; enabled JSX (react-jsx) and included .tsx sources.
Environment Configuration
backend/src/env.ts
Expanded server env schema with PUBLIC_URL, TRUSTED_ORIGINS, ENCRYPTION_KEY, and Azure credentials: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_EMAIL_SENDER.
Azure Graph Client
backend/src/azure/client.ts
New Graph client using ClientSecretCredential + TokenCredentialAuthenticationProvider; exports client.
Azure Email Sending
backend/src/azure/functions.ts
New sendEmail(to, subject, component) rendering React component to HTML/plain, composing MIME via MailComposer, base64-encoding, and sending via Graph /users/{sender}/sendMail.
Email Templates & Components
Templates: backend/src/emails/templates/welcome.tsx, backend/src/emails/templates/otp.tsx
Components: backend/src/emails/components/logo.tsx, backend/src/emails/components/footer.tsx
Added Welcome and OTP email templates, plus Logo and Footer presentational components; templates expose PreviewProps.
Mailer Wrappers & Types
backend/src/emails/mailer.tsx, backend/src/emails/types/index.ts, backend/src/utils/types.ts
Added sendLoginOtpEmail and sendWelcomeEmail wrappers; introduced EmailTemplate<P> type and MaybeArray<T> utility.
Server & WebSocket
backend/src/server.ts, backend/src/websocket/index.ts
Renamed WebsocketServerWebSocketServer; added `close(): Promise<Error

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Backend
    participant Renderer
    participant GraphAPI
    Client->>Backend: POST /test/welcome-email (toAddress, firstName, assocNumber)
    Backend->>Renderer: Render WelcomeEmail -> HTML + plain text
    Renderer-->>Backend: HTML + plain text
    Backend->>Backend: Compose MIME via MailComposer, base64-encode
    Backend->>GraphAPI: POST /users/{sender}/sendMail (base64 MIME) with TokenCredential
    GraphAPI-->>Backend: Success / Error
    Backend-->>Client: JSON result (success: boolean)
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: automatic emails' directly and concisely describes the main change: implementing automatic email functionality, which aligns with the overall objective of adding email templates, mailer functions, and infrastructure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 470b167 and e24910b.

📒 Files selected for processing (3)
  • backend/src/azure/functions.ts
  • backend/src/emails/components/logo.tsx
  • backend/src/emails/templates/welcome.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/emails/components/logo.tsx
  • backend/src/azure/functions.ts
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/emails/templates/welcome.tsx (3)
backend/src/emails/types/index.ts (1)
  • EmailTemplate (1-1)
backend/src/emails/components/logo.tsx (1)
  • Logo (3-14)
backend/src/emails/components/footer.tsx (1)
  • Footer (3-17)
🔇 Additional comments (3)
backend/src/emails/templates/welcome.tsx (3)

1-93: LGTM! Well-structured email template.

The component is well-organized with proper use of React Email components and Tailwind styling. The EmailTemplate<Props> type correctly handles the PreviewProps static property typing, and Link components now include the required href attributes.


95-107: Fix for email without @ symbol is correct.

The optional chaining on line 97 (domain?.split(".") ?? []) properly handles the edge case where an email lacks an @ symbol, preventing the runtime TypeError that was previously identified. The fallback on line 99 gracefully handles malformed emails.


109-116: LGTM!

The PreviewProps static property is now properly typed via the EmailTemplate<Props> type, eliminating the need for the previous @ts-expect-error directive.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lorenzocorallo
Copy link
Member Author

@coderabbitai review

@lorenzocorallo lorenzocorallo marked this pull request as ready for review December 26, 2025 23:25
@coderabbitai
Copy link

coderabbitai bot commented Dec 26, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
backend/src/server.ts (1)

48-65: Security: Remove hardcoded personal email and credentials from test route.

This test endpoint contains a real email address and test credentials that shouldn't be in the codebase. Even for WIP code, this poses risks if accidentally deployed. The forceProd: true flag will send real emails via production SMTP.

Suggested improvements
 app.post("/test-email", async (c) => {
-  if (env.NODE_ENV === "production") return c.status(500)
+  if (env.NODE_ENV === "production") return c.text("Not available in production", 500)

   await sendEmail({
-    to: "lorenzo.corallo@mail.polimi.it",
+    to: env.TEST_EMAIL_RECIPIENT ?? "test@example.com",
     subject: "Welcome to PoliNetwork",
     email: WelcomeEmail,
-    props: { email: "example@polinetwork.org", password: "R@123123123123as", assocNum: 12, firstName: "Lorenzo" },
-    forceProd: true,
+    props: { email: "test@polinetwork.org", password: "TestPassword123!", assocNum: 1, firstName: "Test" },
   })
-  // await sendEmail({
-  //   to: "test@example.org",
-  //   subject: "Welcome to PoliNetwork",
-  //   email: WelcomeEmail,
-  //   props: { email: "example@polinetwork.org", password: "R@123123123123as", assocNum: 12, firstName: "Lorenzo" },
-  // })
   return c.text("Success!")
 })

Consider:

  1. Remove forceProd: true to use the Ethereal test transport in development
  2. Use environment variables for test recipients
  3. Remove commented-out code
backend/src/emails/index.tsx (1)

52-52: Missing closing angle bracket in email address.

The from address is malformed: <noreply@example.com should be <noreply@example.com>.

-        from: "Test PoliNetwork APS <noreply@example.com",
+        from: "Test PoliNetwork APS <noreply@example.com>",
🧹 Nitpick comments (4)
backend/package.json (1)

44-44: Move @types/react to devDependencies.

Type definitions are only needed at build/compile time, not at runtime. Placing them in dependencies unnecessarily increases production bundle size.

Suggested change
  "devDependencies": {
    "@biomejs/biome": "^2.2.4",
    "@react-email/preview-server": "4.3.2",
    "@types/node": "^24.5.2",
    "@types/nodemailer": "^7.0.3",
    "@types/pg": "^8.15.5",
+   "@types/react": "^19.2.2",
    "drizzle-kit": "^0.31.4",
    ...
  },
  "dependencies": {
    ...
-   "@types/react": "^19.2.2",
    ...
  }
backend/src/env.ts (2)

42-42: Add minimum length validation for SMTP_PASS.

Without a minimum length constraint, an effectively empty password could pass validation if emptyStringAsUndefined doesn't catch edge cases.

-    SMTP_PASS: z.string(),
+    SMTP_PASS: z.string().min(1),

43-43: Consider removing deprecated TLS versions.

TLSv1 and TLSv1.1 are deprecated and considered insecure. If Azure Communication Services or your SMTP provider supports it, limiting to TLSv1.2+ would improve security posture.

-    SMTP_TLS_VERSION: z.enum(["TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"]).default("TLSv1.2"),
+    SMTP_TLS_VERSION: z.enum(["TLSv1.3", "TLSv1.2"]).default("TLSv1.2"),
backend/src/emails/templates/welcome.tsx (1)

36-36: Consider hosting the logo on a stable CDN.

Using a GitHub raw URL (raw.githubusercontent.com) for the logo could break if the repository structure changes or if GitHub changes their raw content delivery. Consider hosting on a dedicated CDN or using a versioned asset URL.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5def01e and 16a2285.

⛔ Files ignored due to path filters (1)
  • backend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • backend/package.json
  • backend/src/emails/index.tsx
  • backend/src/emails/templates/welcome.tsx
  • backend/src/env.ts
  • backend/src/server.ts
  • backend/src/utils/types.ts
  • backend/src/websocket/index.ts
  • backend/tsconfig.json
🧰 Additional context used
🧬 Code graph analysis (3)
backend/src/emails/index.tsx (3)
backend/src/env.ts (1)
  • env (8-65)
backend/src/utils/types.ts (1)
  • MaybeArray (1-1)
backend/src/logger.ts (1)
  • logger (3-5)
backend/src/server.ts (2)
backend/src/env.ts (1)
  • env (8-65)
backend/src/emails/index.tsx (1)
  • sendEmail (34-66)
backend/src/env.ts (1)
backend/src/constants.ts (1)
  • TRUSTED_ORIGINS (5-12)
🔇 Additional comments (7)
backend/package.json (1)

14-14: LGTM!

The email dev script is well-configured, pointing to the correct directory for email templates.

backend/tsconfig.json (1)

5-5: LGTM!

The TypeScript configuration changes correctly enable JSX support for the React Email templates. Using "preserve" is appropriate here since the JSX transformation will be handled by the email rendering toolchain.

Also applies to: 10-10

backend/src/utils/types.ts (1)

1-1: LGTM!

Clean utility type that enables flexibility for single or multiple email recipients.

backend/src/websocket/index.ts (1)

14-14: LGTM!

Good naming consistency improvement - WebSocketServer correctly follows PascalCase convention.

backend/src/emails/index.tsx (1)

33-40: LGTM on the sendEmail API design.

The generic typing for email props and flexible recipient handling with MaybeArray is well-designed.

backend/src/env.ts (1)

38-41: LGTM on SMTP configuration structure.

Good defaults for Azure Communication Services and proper use of z.email() for the SMTP_USER validation.

backend/src/emails/templates/welcome.tsx (1)

25-105: Well-structured email template.

The template is clean with good use of Tailwind for styling, proper accessibility (alt text on images), and semantic structure. The zero-width non-joiner technique for email obfuscation is a nice touch for reducing spam scraping.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
backend/src/emails/templates/welcome.tsx (1)

108-119: Multi-part TLD issue remains unresolved.

The EmailWithoutHref component still splits the domain on the first . only, which causes multi-part TLDs like .co.uk to be handled incorrectly (e.g., user@example.co.uk would render with incomplete domain information).

This issue was previously flagged with a suggested fix that uses lastIndexOf to properly split the domain.

🧹 Nitpick comments (1)
backend/src/emails/templates/welcome.tsx (1)

58-63: Consider aligning the link URL with the link text.

The link text says "Microsoft Login" but the href points to https://outlook.office.com. While this works (Outlook redirects to the login page), it may be clearer to use https://login.microsoftonline.com/ to match the user's expectation from the text, or change the text to "Outlook" to match the URL.

🔎 Option 1: Update href to match "Microsoft Login" text
                To activate your Office 365 account, using a PC open{" "}
-                <Link href="https://outlook.office.com" className="text-[#1156AE] underline">
+                <Link href="https://login.microsoftonline.com/" className="text-[#1156AE] underline">
                  Microsoft Login
                </Link>
🔎 Option 2: Update text to match Outlook URL
                To activate your Office 365 account, using a PC open{" "}
                <Link href="https://outlook.office.com" className="text-[#1156AE] underline">
-                  Microsoft Login
+                  Outlook
                </Link>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16a2285 and 3704002.

⛔ Files ignored due to path filters (1)
  • backend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • backend/package.json
  • backend/src/emails/index.tsx
  • backend/src/emails/templates/welcome.tsx
  • backend/src/server.ts
  • backend/src/websocket/index.ts
  • backend/tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • backend/package.json
  • backend/tsconfig.json
  • backend/src/server.ts
  • backend/src/websocket/index.ts
  • backend/src/emails/index.tsx
🔇 Additional comments (9)
backend/src/emails/templates/welcome.tsx (9)

1-17: LGTM!

The imports are correctly structured with type-only React import (appropriate for React 19) and all @react-email/components are used in the template.


19-24: LGTM!

Props type definition is clear and appropriate for the welcome email template.


66-73: LGTM!

The credentials section is well-structured with appropriate visual styling to highlight the sensitive information.


85-90: LGTM!

The Telegram contact link is properly implemented with the correct href attribute.


33-43: LGTM!

The logo section is properly structured with appropriate sizing and alt text for accessibility.


46-56: LGTM!

The welcome message properly uses the provided props and has a clear, personalized greeting structure.


75-83: LGTM!

The instructions and important notice are clearly formatted with appropriate emphasis (bold text and red color) to draw attention to critical information.


96-101: LGTM!

The footer section is clean and appropriately styled with a working link to the organization's website.


129-129: LGTM!

Standard default export for the email template component.

@lorenzocorallo
Copy link
Member Author

@toto04 dovremmo esserci

@lorenzocorallo lorenzocorallo changed the title [WIP] feat: automatic emails feat: automatic emails Dec 27, 2025
@lorenzocorallo lorenzocorallo marked this pull request as draft December 29, 2025 14:27
@lorenzocorallo
Copy link
Member Author

Rimetto in draft perché voglio migrare a Microsoft Graph API che ci permette di avere limiti di invio molto superiori utilizzando un utente vero e proprio (licenza Microsoft Business 365 Basic tipo)

@toto04 toto04 marked this pull request as ready for review January 14, 2026 13:07
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@backend/src/emails/components/logo.tsx`:
- Around line 1-13: The Logo component currently uses an SVG which many major
email clients block; update the Logo() function to use a PNG as the primary
image source (replace the current SVG URL in the Img src prop with a PNG URL of
the same logo and size) and add a robust fallback strategy (include a srcSet or
additional src fallback that references the SVG only as a secondary option,
preserve alt text and sizing classes on the Img component, and ensure the
Section/Img remain unchanged otherwise); after making the change, test rendering
in Gmail, Outlook (desktop/web) and Apple Mail to confirm the PNG displays
everywhere.

In `@backend/src/emails/mailer.tsx`:
- Around line 10-26: sendWelcomeEmail currently embeds plaintext
accountCredentials.password in the Welcome component and email body; stop
sending raw passwords. Change sendWelcomeEmail to generate a time-limited
setup/reset token (e.g., createPasswordSetupToken(accountCredentials.email) or
reuse existing reset flow), pass a secure setup link (not the password) to
sendEmail/Welcome, remove the password prop from the Welcome component, and
ensure backend enforces one-time token validation and forces password
creation/change on first use.
🧹 Nitpick comments (3)
backend/package.json (1)

25-33: Consider version pinning consistency and potential unused types.

Two observations:

  1. @types/nodemailer is added, but given the migration to Microsoft Graph API mentioned in the PR, this may become unused if nodemailer is fully replaced.

  2. Version pinning is inconsistent: @react-email/preview-server and react-email use exact versions while @types/react and @microsoft/microsoft-graph-types use caret ranges. Consider aligning the strategy for react-email ecosystem packages.

backend/src/azure/client.ts (1)

19-28: Test request at module load has side effects on import.

The fire-and-forget test request is useful for early credential validation, but be aware this executes whenever the module is imported (including during tests or when the email feature isn't needed).

Consider wrapping this in an initialization function that can be called explicitly from the application startup, or gating it behind a condition:

♻️ Optional: Extract to explicit initialization
+export async function testAzureConnection(): Promise<boolean> {
+  try {
+    const r: Organization = await client.api(`/organization/${env.AZURE_TENANT_ID}`).get()
+    logger.info({ orgName: r.displayName }, "[Azure Graph API] Client connected successfully")
+    return true
+  } catch (e) {
+    const error = e as GraphError
+    if (error.code === "AuthenticationRequiredError")
+      logger.error({ error: error.message }, "[Azure Graph API] Authentication failed, check credentials")
+    else logger.error({ error }, "[Azure Graph API] Error on TEST request")
+    return false
+  }
+}
+
-// test request -- void because we do not want to wait
-void client
-  .api(`/organization/${env.AZURE_TENANT_ID}`)
-  .get()
-  .then((r: Organization) => logger.info({ orgName: r.displayName }, "[Azure Graph API] Client connected successfully"))
-  .catch((e: GraphError) => {
-    if (e.code === "AuthenticationRequiredError")
-      logger.error({ error: e.message }, "[Azure Graph API] Authentication failed, check credentials")
-    else logger.error({ error: e }, "[Azure Graph API] Error on TEST request")
-  })
backend/src/azure/functions.ts (1)

27-38: Consider validating the to parameter before sending.

The function accepts any string for to without validation. While the Graph API will reject invalid addresses, validating early provides better error messages and avoids unnecessary API calls.

♻️ Suggested validation
 export async function sendEmail(to: string, subject: string, component: JSX.Element) {
+  // Basic email format check before attempting to send
+  if (!to || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(to)) {
+    logger.error({ to }, "[Azure Graph API] Invalid recipient email address")
+    return false
+  }
+
   const html = await render(component)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a9e202 and d9e6bcd.

⛔ Files ignored due to path filters (1)
  • backend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • backend/package.json
  • backend/src/azure/client.ts
  • backend/src/azure/functions.ts
  • backend/src/emails/components/footer.tsx
  • backend/src/emails/components/logo.tsx
  • backend/src/emails/mailer.tsx
  • backend/src/emails/templates/otp.tsx
  • backend/src/emails/templates/welcome.tsx
  • backend/src/emails/types/index.ts
  • backend/src/env.ts
  • backend/src/server.ts
  • backend/src/websocket/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/src/emails/templates/welcome.tsx
  • backend/src/websocket/index.ts
  • backend/src/server.ts
🧰 Additional context used
🧬 Code graph analysis (5)
backend/src/azure/functions.ts (3)
backend/src/env.ts (1)
  • env (8-64)
backend/src/azure/client.ts (1)
  • client (17-17)
backend/src/logger.ts (1)
  • logger (3-5)
backend/src/azure/client.ts (2)
backend/src/env.ts (1)
  • env (8-64)
backend/src/logger.ts (1)
  • logger (3-5)
backend/src/emails/templates/otp.tsx (3)
backend/src/emails/types/index.ts (1)
  • EmailTemplate (1-1)
backend/src/emails/components/logo.tsx (1)
  • Logo (3-13)
backend/src/emails/components/footer.tsx (1)
  • Footer (3-17)
backend/src/emails/mailer.tsx (1)
backend/src/azure/functions.ts (1)
  • sendEmail (16-57)
backend/src/env.ts (1)
backend/src/constants.ts (1)
  • TRUSTED_ORIGINS (5-12)
🔇 Additional comments (13)
backend/package.json (3)

14-14: LGTM!

The email preview script is correctly configured for react-email development workflow.


54-54: nodemailer dependency is correctly needed for MIME composition with MS Graph API.

The dependency is actively used via MailComposer (line 3 of backend/src/azure/functions.ts) to generate MIME-formatted email content, which is required by the Microsoft Graph API's /sendMail endpoint. The old SMTP-based createTransport approach is fully commented out. The migration to MS Graph is complete and the dependency serves a specific purpose in the new implementation.

Likely an incorrect or invalid review comment.


57-58: No action required. React 19.2.0 and react-email 5.1.0 are compatible. React 19 support has been maintained through react-email's releases, including v5.x. While some @types/react version alignment issues may occur in edge cases, the current dependency versions in package.json are safe to use together.

backend/src/env.ts (3)

13-17: TRUSTED_ORIGINS transformation is well-implemented.

The pipeline correctly transforms a comma-separated string into a validated string array. The default value from TRUSTED_ORIGINS constant provides good development defaults.


21-25: ENCRYPTION_KEY validation looks appropriate.

The hex string validation with 64-1024 character bounds is reasonable for encryption key material (representing 32-512 bytes of key data).


38-42: Azure configuration uses Zod 4's standalone z.email() correctly.

The Azure environment variables are properly defined. Note that AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET are required without defaults, so the application will fail to start if these are missing. Consider whether this is intentional for all environments (including development).

Based on library documentation, z.email() is the correct standalone format schema in Zod 4.

backend/src/azure/client.ts (1)

8-17: Client credentials flow is correctly configured.

The ClientSecretCredential with TokenCredentialAuthenticationProvider using .default scope is the appropriate pattern for daemon/service applications using the client credentials OAuth flow.

backend/src/azure/functions.ts (2)

16-22: Email rendering and plain text conversion look correct.

The render() and toPlainText() usage from @react-email/components is appropriate. The selector to skip display:none elements prevents duplicate text in the plain text version.


37-56: MIME-based Graph API email sending is correctly implemented.

The approach of building MIME content with MailComposer, base64 encoding, and posting to /users/{sender}/sendMail with Content-Type: text/plain is the correct pattern for sending raw MIME messages via Microsoft Graph API.

One consideration: the error object structure isn't preserved in the return value. If callers need to distinguish between different failure modes (auth issues vs. recipient issues vs. rate limits), consider returning a more detailed result type.

backend/src/emails/components/footer.tsx (1)

1-17: LGTM! Clean footer component with appropriate disclaimer.

The footer provides good UX with the PoliNetwork link and clear "do not reply" messaging for automated emails. The component structure follows react-email patterns correctly.

backend/src/emails/types/index.ts (1)

1-1: LGTM!

Clean type definition that enforces readonly props and provides optional preview props support for email templates.

backend/src/emails/mailer.tsx (1)

5-8: LGTM!

Clean implementation for sending OTP emails.

backend/src/emails/templates/otp.tsx (1)

1-74: LGTM!

Well-structured OTP email template with:

  • Clean component architecture using React Email
  • Proper conditional rendering for optional expiration notice
  • Sensible preview props for development/testing
  • Good accessibility practices (meta tag to disable phone detection)

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 1 to 13
import { Img, Section } from "@react-email/components"

export function Logo() {
return (
<Section className="py-7 sm:py-10 text-center">
<Img
src="https://raw.githubusercontent.com/PoliNetworkOrg/Logo/a1e02bf150ac0b3c734a629901eada79fb4fd762/Logo.svg"
alt="PoliNetwork APS Logo"
className="mx-auto block w-24 h-24 sm:w-32 sm:h-32"
/>
</Section>
)
}
Copy link

@coderabbitai coderabbitai bot Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

email client SVG image support compatibility 2025

💡 Result:

Short summary (status as of 2025)

  • Major web/desktop clients: Microsoft announced that Outlook (Outlook for Web and the new Outlook for Windows) stopped rendering inline SVG embedded in email bodies during the Sep–Oct 2025 rollout. Inline SVGs will appear blank; SVG attachments still open from the attachment well. [1][2]
  • Gmail: historically and through 2024–2025 Gmail has not reliably displayed SVG images embedded in emails (many senders still avoid SVG for Gmail). [3]
  • Apple Mail / native macOS & iOS mail: generally more permissive and have supported SVG rendering where the client uses the system WebKit renderer (but behavior can vary by OS/version). Use CanIEmail for per-client tests. [4]
  • Other clients (Thunderbird, some webmail/UIs): mixed support; many ESPs and signature tools still warn that SVGs are not safe/compatible for mass email. [3][5]

Practical recommendations

  • Don't rely on inline SVG for critical imagery (logos, CTAs). Use PNG (2x for retina) as a fallback or as the primary asset. [5]
  • If you must use SVG, provide a robust fallback: or server-side/templating fallbacks and include alt text. Test rendering across clients (use Litmus / Email on Acid / CanIEmail for up-to-date client checks). [4][5]

Sources

  1. Microsoft 365 Message Center – MC1130385 (Retirement of inline SVG images in Microsoft Outlook). [Microsoft change notice summary].
  2. Reporting on the Outlook change (TechRadar / industry posts summarizing the Sep–Oct 2025 rollout).
  3. Community/FAQ notes and long-standing issues: Gmail/other clients not reliably supporting SVG in email (StackOverflow, vendor docs).
  4. CanIEmail — email feature support tracker (SVG/image notes & client scoreboard).
  5. Vendor help articles (email platforms / signature tools) recommending PNG/JPG instead of SVG for email.

If you want, I can run a quick CanIEmail/ Litmus check now and produce a per-client table (Gmail web/mobile, Outlook desktop/web/mobile, Apple Mail macOS/iOS, Thunderbird, Yahoo, AOL).


SVG logo will fail to render in major email clients—use PNG with fallback.

Recent changes (Sept–Oct 2025) have made SVG incompatibility a critical issue: Microsoft Outlook (desktop and web) now blocks inline SVG rendering entirely, causing images to appear blank. Gmail also does not reliably display SVG images in email. For a logo—critical imagery—relying solely on SVG is unacceptable.

Replace the SVG URL with a PNG asset and provide a robust fallback strategy. Use srcset with both formats, test across clients (Gmail, Outlook desktop/web, Apple Mail), or serve PNG as the primary asset. Do not ship the component until this is resolved.

🤖 Prompt for AI Agents
In `@backend/src/emails/components/logo.tsx` around lines 1 - 13, The Logo
component currently uses an SVG which many major email clients block; update the
Logo() function to use a PNG as the primary image source (replace the current
SVG URL in the Img src prop with a PNG URL of the same logo and size) and add a
robust fallback strategy (include a srcSet or additional src fallback that
references the SVG only as a secondary option, preserve alt text and sizing
classes on the Img component, and ensure the Section/Img remain unchanged
otherwise); after making the change, test rendering in Gmail, Outlook
(desktop/web) and Apple Mail to confirm the PNG displays everywhere.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lorenzocorallo questa è una cosa effettivamente rilevante
posso switchare al PNG allo stesso url di github, ma ci sono due issue rilevanti

  • il PNG è 3000x3000 e pesa 350kB che per l'amor di dio non è completamente folle ma è anche un po' inutile
  • sarebbe meglio avere il più possibile incorporato nell'email dato che client tipo Mail di iOS spesso bloccano il caricamento di risorse esterne prima di consenso esplicito dall'utente (che è anche giusto dato che renderebbe l'apertura dell'email tracciabile)

che ne dici di incorporare l'email renderizzando un PNG a grandezza ridotta e piantandocelo dentro come base64? sarebbe anche carino poter importare direttamente il file .png come modulo e averlo convertito in base64 da un loader ma non penso che litigare con dei bundler tipo webpack per un singolo PNG sia la migliore delle scelte ahah

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member Author

@lorenzocorallo lorenzocorallo Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

che ne dici di incorporare l'email renderizzando un PNG a grandezza ridotta e piantandocelo dentro come base64? sarebbe anche carino poter importare direttamente il file .png come modulo e averlo convertito in base64 da un loader ma non penso che litigare con dei bundler tipo webpack per un singolo PNG sia la migliore delle scelte ahah

Credo che sia per il momento sia la scelta ottimale, quantomeno per il logo che è un asset critico. @toto04

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anche se probabilmente darei un occhio a come react-emails gestisce le immagini: https://react.email/docs/components/image

@toto04 magari è quello che cercavi per evitare di hard-codarde il base64 e far fare tutto al codice

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react-emails non sembra fare alcuna magia arcana per fare embedding delle immagini, quello che dai al src è quello che viene mandato, quindi base64 a mano sia
ho piantato il png 192x192, da quanto ho estrapolato w-24 in tailwind vuol dire 96px (default 1rem = 16px), e sembra essere la scelta migliore avere immagini PNG a 2x

andrebbe testato per assicurarci che sia effettivamente bello e non si spacchi niente

(even though there should be no usecase for this)
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@backend/src/emails/templates/welcome.tsx`:
- Around line 95-107: The EmailWithoutHref component currently calls
email.split("@") and immediately destructures into user and domain, then calls
domain.split(".") which throws if email lacks "@"; update EmailWithoutHref to
first verify the presence of "@" (e.g., check email.includes("@") or
indexOf("@") !== -1) before splitting or handle the split result safely (guard
domain existence), and if the check fails return the fallback CodeInline with
the full email; ensure references to EmailWithoutHref, user, domain, domainName,
and domainTld remain but add the early guard so domain.split(".") is only
invoked when domain is defined.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9e6bcd and 30a0412.

📒 Files selected for processing (1)
  • backend/src/emails/templates/welcome.tsx
🔇 Additional comments (2)
backend/src/emails/templates/welcome.tsx (2)

27-93: LGTM!

The WelcomeEmail component is well-structured, follows React Email patterns, and properly uses Tailwind styling. The Link components now include href attributes (addressing previous feedback), and the email content is clear and professional.


109-114: LGTM!

The PreviewProps assignment is clean with appropriate sample data, and the previous TypeScript typing issue has been resolved.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@backend/src/azure/functions.ts`:
- Line 30: The sender email is interpolated directly into the Graph API path
which breaks for special characters; URL-encode the AZURE_EMAIL_SENDER value
before constructing the API path used in client.api(`/users/${sender}/sendMail`)
(i.e., replace usage of sender with encodeURIComponent(sender)) so the call to
client.api(...) with header("Content-Type", "text/plain").post(base64Encoded) is
made with a safely encoded path; keep the same header and body variables
(base64Encoded) unchanged.
🧹 Nitpick comments (3)
backend/src/azure/functions.ts (3)

8-14: Consider adding input validation.

The function accepts any string for to and subject without validation. Consider validating the email format and ensuring the subject is non-empty to fail fast rather than making an API call that will fail anyway.

💡 Optional validation example
 export async function sendEmail(to: string, subject: string, component: JSX.Element) {
+  if (!to || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(to)) {
+    logger.warn({ to }, "[Azure Graph API] Invalid recipient email")
+    return false
+  }
+  if (!subject.trim()) {
+    logger.warn("[Azure Graph API] Empty subject")
+    return false
+  }
+
   const html = await render(component)

34-37: Consider exposing error context to callers.

Returning false on failure doesn't let callers distinguish between transient issues (rate limits, network errors) and permanent failures (invalid credentials, bad recipient). This matters for retry logic or user feedback.

💡 Alternative: throw or return a result object
// Option 1: Throw on failure (let caller handle)
throw new Error(`Failed to send email: ${error instanceof Error ? error.message : "Unknown error"}`)

// Option 2: Return a result object
type EmailResult = { success: true } | { success: false; error: string }

29-31: The Graph API endpoint and MIME format are correct, but be aware of known limitations.

The endpoint /users/{sender}/sendMail with Content-Type: text/plain and base64-encoded MIME body is the documented approach and is implemented correctly. However, be aware of these Microsoft Graph API limitations:

  • Custom MIME headers (e.g., Return-Path, Auto-Submitted) may be removed or replaced by Graph.
  • Known issues exist with certain attachment types, particularly text/calendar (ICS) files, where Graph may modify Content-Type parameters.
  • MIME submissions always save to Sent Items (no option to override this behavior).

If your emails rely on specific custom headers or calendar-format attachments being preserved exactly as-is, consider testing thoroughly or using SMTP with OAuth2 for more granular control.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30a0412 and 708b15b.

📒 Files selected for processing (1)
  • backend/src/azure/functions.ts
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/azure/functions.ts (3)
backend/src/env.ts (1)
  • env (8-64)
backend/src/azure/client.ts (1)
  • client (17-17)
backend/src/logger.ts (1)
  • logger (3-5)
🔇 Additional comments (2)
backend/src/azure/functions.ts (2)

1-6: LGTM!

Imports are well-organized with appropriate type-only import for JSX.


15-27: LGTM!

Good use of MailComposer for MIME construction, and the comment clearly explains why raw MIME is needed for multipart emails with Graph API.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 1 to 13
import { Img, Section } from "@react-email/components"

export function Logo() {
return (
<Section className="py-7 sm:py-10 text-center">
<Img
src="https://raw.githubusercontent.com/PoliNetworkOrg/Logo/a1e02bf150ac0b3c734a629901eada79fb4fd762/Logo.svg"
alt="PoliNetwork APS Logo"
className="mx-auto block w-24 h-24 sm:w-32 sm:h-32"
/>
</Section>
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lorenzocorallo questa è una cosa effettivamente rilevante
posso switchare al PNG allo stesso url di github, ma ci sono due issue rilevanti

  • il PNG è 3000x3000 e pesa 350kB che per l'amor di dio non è completamente folle ma è anche un po' inutile
  • sarebbe meglio avere il più possibile incorporato nell'email dato che client tipo Mail di iOS spesso bloccano il caricamento di risorse esterne prima di consenso esplicito dall'utente (che è anche giusto dato che renderebbe l'apertura dell'email tracciabile)

che ne dici di incorporare l'email renderizzando un PNG a grandezza ridotta e piantandocelo dentro come base64? sarebbe anche carino poter importare direttamente il file .png come modulo e averlo convertito in base64 da un loader ma non penso che litigare con dei bundler tipo webpack per un singolo PNG sia la migliore delle scelte ahah

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants