Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions PatchNotes.Api/Routes/EmailTemplateRoutes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public static async Task SeedDefaultTemplatesAsync(PatchNotesDbContext db)
You're all set to receive release notifications for the packages you care about.
</Text>
<Text style={{ color: "#4a4a4a", fontSize: "16px", lineHeight: "26px" }}>
Head to your <Link href="https://patchnotes.dev" style={{ color: "#5469d4" }}>dashboard</Link> to start watching packages.
Head to your <Link href="https://app.myreleasenotes.ai" style={{ color: "#5469d4" }}>dashboard</Link> to start watching packages.
</Text>
</Section>
<Hr style={{ borderColor: "#e6ebf1", margin: "32px 0" }} />
Expand Down Expand Up @@ -197,7 +197,7 @@ Your Weekly PatchNotes Digest
<Text style={{ color: "#4a4a4a", fontSize: "16px" }}>No new releases this week.</Text>
)}
<Text style={{ color: "#4a4a4a", fontSize: "16px", lineHeight: "26px" }}>
<Link href="https://patchnotes.dev" style={{ color: "#5469d4" }}>View all updates on PatchNotes</Link>
<Link href="https://app.myreleasenotes.ai" style={{ color: "#5469d4" }}>View all updates on PatchNotes</Link>
</Text>
</Section>
<Hr style={{ borderColor: "#e6ebf1", margin: "32px 0" }} />
Expand Down
27 changes: 18 additions & 9 deletions PatchNotes.Api/Routes/SubscriptionRoutes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ namespace PatchNotes.Api.Routes;

public static class SubscriptionRoutes
{
private static readonly HashSet<string> AllowedOrigins = new(StringComparer.OrdinalIgnoreCase)
private static readonly string[] DevOrigins =
{
"https://app.myreleasenotes.ai",
"http://localhost:5173",
"http://localhost:3000",
};
Expand All @@ -23,15 +22,25 @@ public static class SubscriptionRoutes
"billing.stripe.com",
};

private static string GetValidatedOrigin(HttpContext httpContext)
private static HashSet<string> BuildAllowedOrigins(IConfiguration configuration)
{
var baseUrl = configuration["App:BaseUrl"] ?? "https://app.myreleasenotes.ai";
var origins = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { baseUrl };
foreach (var dev in DevOrigins)
origins.Add(dev);
return origins;
}

private static string GetValidatedOrigin(HttpContext httpContext, IConfiguration configuration)
{
var allowedOrigins = BuildAllowedOrigins(configuration);
var origin = httpContext.Request.Headers.Origin.FirstOrDefault();
if (!string.IsNullOrEmpty(origin) && AllowedOrigins.Contains(origin))
if (!string.IsNullOrEmpty(origin) && allowedOrigins.Contains(origin))
{
return origin;
}
// Fallback to first allowed origin in production
return AllowedOrigins.First();
// Fallback to configured base URL in production
return configuration["App:BaseUrl"] ?? "https://app.myreleasenotes.ai";
}

private static bool IsValidStripeRedirectUrl(string? url)
Expand Down Expand Up @@ -80,7 +89,7 @@ public static WebApplication MapSubscriptionRoutes(this WebApplication app)
}

// Determine the base URL for success/cancel redirects
var origin = GetValidatedOrigin(httpContext);
var origin = GetValidatedOrigin(httpContext, configuration);

var sessionOptions = new SessionCreateOptions
{
Expand Down Expand Up @@ -126,7 +135,7 @@ public static WebApplication MapSubscriptionRoutes(this WebApplication app)
.WithName("CreateCheckoutSession");

// POST /api/subscription/portal - Create Stripe Customer Portal session
group.MapPost("/portal", async (HttpContext httpContext, PatchNotesDbContext db) =>
group.MapPost("/portal", async (HttpContext httpContext, PatchNotesDbContext db, IConfiguration configuration) =>
{
var stytchUserId = httpContext.Items["StytchUserId"] as string;
if (string.IsNullOrEmpty(stytchUserId))
Expand All @@ -145,7 +154,7 @@ public static WebApplication MapSubscriptionRoutes(this WebApplication app)
return Results.BadRequest(new ApiError("No subscription found"));
}

var origin = GetValidatedOrigin(httpContext);
var origin = GetValidatedOrigin(httpContext, configuration);

var portalOptions = new Stripe.BillingPortal.SessionCreateOptions
{
Expand Down
3 changes: 3 additions & 0 deletions PatchNotes.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
}
},
"AllowedHosts": "*",
"App": {
"BaseUrl": "https://app.myreleasenotes.ai"
},
"AI": {
"BaseUrl": "https://ollama.com/v1/",
"Model": "gemma3:27b"
Expand Down
1 change: 1 addition & 0 deletions patchnotes-email/src/functions/sendDigest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { mockSend, mockFindMany } = vi.hoisted(() => ({
vi.mock("../lib/resend", () => ({
resend: { emails: { send: mockSend } },
FROM_ADDRESS: "PatchNotes <notifications@patchnotes.dev>",
APP_BASE_URL: "https://app.myreleasenotes.ai",
escapeHtml: (s: string) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"),
emailFooter: () => "<footer/>",
sanitizeSubject: (s: string) => s.replace(/[\r\n]+/g, " ").trim(),
Expand Down
4 changes: 2 additions & 2 deletions patchnotes-email/src/functions/sendDigest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { app, InvocationContext, Timer } from "@azure/functions";
import { resend, FROM_ADDRESS, escapeHtml, emailFooter, sanitizeSubject, isValidEmail } from "../lib/resend";
import { resend, FROM_ADDRESS, APP_BASE_URL, escapeHtml, emailFooter, sanitizeSubject, isValidEmail } from "../lib/resend";
import { getPrismaClient } from "../lib/prisma";

const DIGEST_WINDOW_DAYS = 7;
Expand Down Expand Up @@ -106,7 +106,7 @@ export async function sendDigest(
<h1>Your Weekly PatchNotes Digest</h1>
<p>Hi ${escapeHtml(user.Name ?? "there")}, here's what happened this week with the packages you're watching:</p>
<ul>${releaseList}</ul>
<p><a href="https://myreleasenotes.ai">View all updates on PatchNotes</a></p>
<p><a href="${APP_BASE_URL}">View all updates on PatchNotes</a></p>
${emailFooter()}
`;

Expand Down
4 changes: 3 additions & 1 deletion patchnotes-email/src/lib/resend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export function isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

export const SETTINGS_URL = "https://myreleasenotes.ai/settings";
export const APP_BASE_URL = process.env.APP_BASE_URL || "https://app.myreleasenotes.ai";

export const SETTINGS_URL = `${APP_BASE_URL}/settings`;

export function emailFooter(): string {
return `
Expand Down