diff --git a/server.js b/server.js index 7d4bcd2..e56edc1 100644 --- a/server.js +++ b/server.js @@ -1877,28 +1877,85 @@ app.post('/api/contact', contactRateLimit, async (req, res) => { if (shouldSendWelcome && !USE_RESEND_AUTOMATION_WELCOME) { const greetingName = splitName(trimmedName).firstName?.trim() ?? '' + let publishedSiteContent = cachedSiteContent + if (!publishedSiteContent) { + try { + const published = await loadSiteContentFile(DATA_FILE) + publishedSiteContent = published?.siteContent ?? null + } catch { + publishedSiteContent = null + } + } + + const emailConfig = publishedSiteContent ?? {} + const welcomeBaseUrl = typeof emailConfig?.seo?.canonicalUrl === 'string' && emailConfig.seo.canonicalUrl.trim() + ? emailConfig.seo.canonicalUrl.trim() + : DEFAULT_SEO.canonicalUrl + const welcomeSubject = process.env.RESEND_WELCOME_SUBJECT ?? emailConfig.welcomeEmailSubject ?? 'Welcome to Verse by Verse with Nate' + const welcomeGreetingPrefix = emailConfig.welcomeEmailGreetingPrefix?.trim() || "Glad you're here" const welcomeHeading = greetingName - ? `Glad you’re here, ${escapeHtml(greetingName)}.` - : 'Glad you’re here.' - const welcomeSpotifyUrl = process.env.RESEND_WELCOME_SPOTIFY_URL ?? 'https://versebyversewithnate.us/spotify' - const welcomeAppleUrl = process.env.RESEND_WELCOME_APPLE_URL ?? 'https://versebyversewithnate.us/apple' - const welcomeAmazonUrl = process.env.RESEND_WELCOME_AMAZON_URL ?? 'https://versebyversewithnate.us/amazon' - const welcomeWebsiteUrl = process.env.RESEND_WELCOME_WEBSITE_URL ?? 'https://versebyversewithnate.us' - const welcomeEpisodeUrl = process.env.RESEND_WELCOME_EPISODE_URL ?? 'https://versebyversewithnate.us/start-here' - const welcomeImageUrl = process.env.RESEND_WELCOME_IMAGE_URL ?? 'https://versebyversewithnate.us/images/podcast-art.jpeg' + ? `${escapeHtml(welcomeGreetingPrefix)}, ${escapeHtml(greetingName)}.` + : `${escapeHtml(welcomeGreetingPrefix)}.` + const welcomeIntro = emailConfig.welcomeEmailIntro?.trim() + || 'Thanks for subscribing to Verse by Verse with Nate - a Bible teaching podcast where we slow down, dig into the text, and pull out the nuggets God has for us word by word.' + const welcomeCurrentSeries = emailConfig.welcomeEmailCurrentSeries?.trim() + || 'Right now we\'re working through the book of Titus - a short letter packed with practical wisdom about grace, godliness, and what the Christian life looks like when it\'s rooted in sound doctrine.' + const welcomeStartHereTitle = emailConfig.welcomeEmailStartHereTitle?.trim() || 'Episode 1 - Introduction to Titus' + const welcomeStartHereSummary = emailConfig.welcomeEmailStartHereSummary?.trim() || 'Who wrote it, who received it, and why it still matters.' + const welcomeExpect1 = emailConfig.welcomeEmailWhatToExpect1?.trim() || 'Verse-by-verse teaching - we go slow and let the text speak for itself.' + const welcomeExpect2 = emailConfig.welcomeEmailWhatToExpect2?.trim() || 'Greek word studies - the kind that open up meaning without being a lecture.' + const welcomeExpect3 = emailConfig.welcomeEmailWhatToExpect3?.trim() || 'New episodes + study notes delivered right to your inbox.' + const welcomeScripture = emailConfig.welcomeEmailScripture?.trim() || 'For the grace of God has appeared, bringing salvation to all people.' + const welcomeScriptureRef = emailConfig.welcomeEmailScriptureRef?.trim() || 'Titus 2:11 - BSB' + const welcomeSignoff = emailConfig.welcomeEmailSignoff?.trim() || 'Grace and peace,\nNate' + const welcomeSignoffHtml = escapeHtml(welcomeSignoff).replace(/\n/g, '
') + const welcomeSpotifyUrl = buildAbsoluteUrl( + welcomeBaseUrl, + process.env.RESEND_WELCOME_SPOTIFY_URL ?? emailConfig.welcomeEmailSpotifyUrl ?? '/spotify', + ) + const welcomeAppleUrl = buildAbsoluteUrl( + welcomeBaseUrl, + process.env.RESEND_WELCOME_APPLE_URL ?? emailConfig.welcomeEmailAppleUrl ?? '/apple', + ) + const welcomeAmazonUrl = buildAbsoluteUrl( + welcomeBaseUrl, + process.env.RESEND_WELCOME_AMAZON_URL ?? emailConfig.welcomeEmailAmazonUrl ?? '/amazon', + ) + const welcomeWebsiteUrl = buildAbsoluteUrl( + welcomeBaseUrl, + process.env.RESEND_WELCOME_WEBSITE_URL ?? emailConfig.welcomeEmailWebsiteUrl ?? '/', + ) + const welcomeEpisodeUrl = buildAbsoluteUrl( + welcomeBaseUrl, + process.env.RESEND_WELCOME_EPISODE_URL ?? emailConfig.welcomeEmailStartHereUrl ?? '/start-here', + ) + const welcomeImageUrl = buildAbsoluteUrl( + welcomeBaseUrl, + process.env.RESEND_WELCOME_IMAGE_URL ?? emailConfig.welcomeEmailImageUrl ?? '/images/podcast-art.jpeg', + ) const { error: welcomeError } = await resend.emails.send({ from: process.env.RESEND_FROM ?? 'Verse by Verse with Nate ', to: [trimmedEmail], - subject: process.env.RESEND_WELCOME_SUBJECT ?? 'Welcome to Verse by Verse with Nate', + subject: welcomeSubject, text: `Welcome to Verse by Verse with Nate!\n\n` + `${greetingName ? `Glad you're here, ${greetingName}.` : "Glad you're here."}\n\n` + + `${welcomeIntro}\n\n` + + `${welcomeCurrentSeries}\n\n` + `Start here: ${welcomeEpisodeUrl}\n` + + `${welcomeStartHereTitle}\n` + + `${welcomeStartHereSummary}\n` + `Spotify: ${welcomeSpotifyUrl}\n` + `Apple Podcasts: ${welcomeAppleUrl}\n` + - `Amazon Music: ${welcomeAmazonUrl}\n\n` + - `Grace and peace,\nNate`, + `Amazon Music: ${welcomeAmazonUrl}\n` + + `Website: ${welcomeWebsiteUrl}\n\n` + + `What to expect:\n` + + `- ${welcomeExpect1}\n` + + `- ${welcomeExpect2}\n` + + `- ${welcomeExpect3}\n\n` + + `${welcomeScripture}\n${welcomeScriptureRef}\n\n` + + `${welcomeSignoff}`, html: `
` + `` + @@ -1915,15 +1972,15 @@ app.post('/api/contact', contactRateLimit, async (req, res) => { `
` + `` + `` + `` + `` + `
` + - `

Thanks for subscribing to Verse by Verse with Nate - a Bible teaching podcast where we slow down, dig into the text, and pull out the nuggets God has for us word by word.

` + - `

Right now we’re working through the book of Titus - a short letter packed with practical wisdom about grace, godliness, and what the Christian life looks like when it’s rooted in sound doctrine.

` + + `

${escapeHtml(welcomeIntro)}

` + + `

${escapeHtml(welcomeCurrentSeries)}

` + `

If you’re just joining us, the best place to start is Episode 1. It sets the table for everything that follows.

` + `
` + `

Start here

` + - `

Episode 1 - Introduction to Titus

` + - `

Who wrote it, who received it, and why it still matters.

` + + `

${escapeHtml(welcomeStartHereTitle)}

` + + `

${escapeHtml(welcomeStartHereSummary)}

` + `` + `` + `` + @@ -1933,15 +1990,15 @@ app.post('/api/contact', contactRateLimit, async (req, res) => { `` + `` + `` + `` + `` + `
Listen on SpotifyApple Podcasts
` + `

What to expect

` + - `

Verse-by-verse teaching - we go slow and let the text speak for itself.

` + - `

Greek word studies - the kind that open up meaning without being a lecture.

` + - `

New episodes + study notes delivered right to your inbox.

` + + `

${escapeHtml(welcomeExpect1)}

` + + `

${escapeHtml(welcomeExpect2)}

` + + `

${escapeHtml(welcomeExpect3)}

` + `
` + `
` + - `

“For the grace of God has appeared, bringing salvation to all people.”

` + - `

Titus 2:11 - BSB

` + + `

“${escapeHtml(welcomeScripture)}”

` + + `

${escapeHtml(welcomeScriptureRef)}

` + `
` + `
` + @@ -1956,7 +2013,7 @@ app.post('/api/contact', contactRateLimit, async (req, res) => { `Website
` + `

You’re receiving this because you subscribed to Verse by Verse with Nate.

` + - `

“Your word is a lamp to my feet and a light to my path.” - Psalm 119:105

` + + `

${welcomeSignoffHtml}

` + `
` + `` + @@ -1966,6 +2023,11 @@ app.post('/api/contact', contactRateLimit, async (req, res) => { if (welcomeError) throw welcomeError } + if (shouldSendWelcome) { + res.json({ ok: true }) + return + } + const { error } = await resend.emails.send({ from: process.env.RESEND_FROM ?? 'Verse by Verse with Nate ', to: [process.env.RESEND_TO ?? 'hello@versebyversewithnate.us'], diff --git a/src/AdminPage.tsx b/src/AdminPage.tsx index b658bf5..db090d5 100644 --- a/src/AdminPage.tsx +++ b/src/AdminPage.tsx @@ -203,6 +203,24 @@ const FIELDS: Array<{ key: StringField; label: string; multiline?: boolean; sect { key: 'platformAmazonUrl', label: 'Platform — Amazon Music URL', section: 'global' }, { key: 'platformFacebookUrl', label: 'Platform — Facebook URL', section: 'global' }, { key: 'platformCreatorUrl', label: 'Platform — Creator Profile URL', section: 'global' }, + { key: 'welcomeEmailSubject', label: 'Welcome Email — Subject', section: 'global' }, + { key: 'welcomeEmailGreetingPrefix', label: 'Welcome Email — Greeting Prefix', section: 'global' }, + { key: 'welcomeEmailIntro', label: 'Welcome Email — Intro Paragraph', multiline: true, section: 'global' }, + { key: 'welcomeEmailCurrentSeries', label: 'Welcome Email — Current Series Paragraph', multiline: true, section: 'global' }, + { key: 'welcomeEmailStartHereTitle', label: 'Welcome Email — Start Here Title', section: 'global' }, + { key: 'welcomeEmailStartHereSummary', label: 'Welcome Email — Start Here Summary', multiline: true, section: 'global' }, + { key: 'welcomeEmailStartHereUrl', label: 'Welcome Email — Start Here URL', section: 'global' }, + { key: 'welcomeEmailSpotifyUrl', label: 'Welcome Email — Spotify URL', section: 'global' }, + { key: 'welcomeEmailAppleUrl', label: 'Welcome Email — Apple URL', section: 'global' }, + { key: 'welcomeEmailAmazonUrl', label: 'Welcome Email — Amazon URL', section: 'global' }, + { key: 'welcomeEmailWebsiteUrl', label: 'Welcome Email — Website URL', section: 'global' }, + { key: 'welcomeEmailImageUrl', label: 'Welcome Email — Image URL', section: 'global' }, + { key: 'welcomeEmailWhatToExpect1', label: 'Welcome Email — What to Expect 1', multiline: true, section: 'global' }, + { key: 'welcomeEmailWhatToExpect2', label: 'Welcome Email — What to Expect 2', multiline: true, section: 'global' }, + { key: 'welcomeEmailWhatToExpect3', label: 'Welcome Email — What to Expect 3', multiline: true, section: 'global' }, + { key: 'welcomeEmailScripture', label: 'Welcome Email — Scripture Text', multiline: true, section: 'global' }, + { key: 'welcomeEmailScriptureRef', label: 'Welcome Email — Scripture Reference', section: 'global' }, + { key: 'welcomeEmailSignoff', label: 'Welcome Email — Signoff HTML', multiline: true, section: 'global' }, ] export default function AdminPage({ content, onSave, onLogout }: Props) { @@ -1949,6 +1967,16 @@ export default function AdminPage({ content, onSave, onLogout }: Props) {
))} +

Welcome Email

+ {FIELDS.filter(f => f.section === 'global' && f.key.startsWith('welcomeEmail')).map(({ key, label, multiline }) => ( +
+ + {multiline + ?