Add upload email notifications with Seafile direct-upload watcher; v2.0.6

- Portal uploads send one email immediately (host, episode, passage,
  description, filenames) via Resend to NUGGETS_NOTIFY_EMAIL
- Seafile watcher polls /Episode Upload every 15 min for direct uploads
  and sends a notification for any new file not already seen via portal
- Files uploaded through the portal are marked seen so the watcher
  never sends a duplicate notification
- Credentials kept out of git: SEAFILE_TOKEN, SEAFILE_NUGGETS_REPO_ID,
  NUGGETS_NOTIFY_EMAIL now sourced from docker .env file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-09-08 13:54:46 -04:00
1 parent 6a03813914
commit 1e396644a9
7 files changed
+177 -5

No files matched your search

+52
View File
@@ -712,3 +712,55 @@ export async function sendStudyWeeklyDigestEmail(email, displayName, lessons, un
console.error('[study-digest] send exception:', err)
}
}
// ── Nuggets upload notifications ───────────────────────────────────────────
export async function sendNuggetsUploadNotification({ to, source, hostName, episodeName, passage, description, filename, infoFile }) {
if (!process.env.RESEND_API_KEY) return
const resend = new Resend(process.env.RESEND_API_KEY)
const from = process.env.RESEND_FROM ?? DEFAULT_RESEND_FROM
const sourceLabel = source === 'portal' ? 'Upload Portal' : 'Direct to Seafile'
const subject = `New Nuggets Episode Upload: ${episodeName || filename}`
const bodyHtml = `
<p style="margin:0 0 16px;font-family:Arial,sans-serif;font-size:15px;color:#2c2417;">A new episode file has arrived in Seafile.</p>
<table style="width:100%;border-collapse:collapse;font-family:Arial,sans-serif;font-size:14px;color:#2c2417;">
<tr><td style="padding:6px 0;color:#7a7060;width:120px;">Source</td><td style="padding:6px 0;font-weight:600;">${escapeHtml(sourceLabel)}</td></tr>
${hostName ? `<tr><td style="padding:6px 0;color:#7a7060;">Host</td><td style="padding:6px 0;">${escapeHtml(hostName)}</td></tr>` : ''}
${episodeName ? `<tr><td style="padding:6px 0;color:#7a7060;">Episode</td><td style="padding:6px 0;font-weight:600;">${escapeHtml(episodeName)}</td></tr>` : ''}
${passage ? `<tr><td style="padding:6px 0;color:#7a7060;">Passage</td><td style="padding:6px 0;">${escapeHtml(passage)}</td></tr>` : ''}
<tr><td style="padding:6px 0;color:#7a7060;">Audio File</td><td style="padding:6px 0;font-family:monospace;font-size:13px;">${escapeHtml(filename)}</td></tr>
${infoFile ? `<tr><td style="padding:6px 0;color:#7a7060;">Info File</td><td style="padding:6px 0;font-family:monospace;font-size:13px;">${escapeHtml(infoFile)}</td></tr>` : ''}
</table>
${description ? `<p style="margin:16px 0 0;font-family:Arial,sans-serif;font-size:14px;color:#2c2417;"><strong>Description:</strong><br/>${escapeHtml(description).replace(/\n/g, '<br/>')}</p>` : ''}
`
const textBody = [
`New Nuggets Episode Upload (${sourceLabel})`,
'',
hostName ? `Host: ${hostName}` : '',
episodeName ? `Episode: ${episodeName}` : '',
passage ? `Passage: ${passage}` : '',
`Audio File: ${filename}`,
infoFile ? `Info File: ${infoFile}` : '',
description ? `\nDescription:\n${description}` : '',
].filter(Boolean).join('\n')
try {
const { error } = await resend.emails.send({
from,
to: [to],
subject,
text: textBody,
html: buildBrandedEmailHtml({
title: 'New Episode Upload',
eyebrow: 'Nuggets',
bodyHtml,
footerHtml: '<p style="margin:0;font-family:Arial,sans-serif;font-size:12px;color:#7a7060;">This notification was sent because a file was uploaded to the Nuggets Seafile library.</p>',
}),
})
if (error) console.error('[nuggets-notify] send error:', error)
} catch (err) {
console.error('[nuggets-notify] send exception:', err)
}
}