import { Resend } from "resend"; import { getSetting } from "./settings"; function getResend() { return new Resend(process.env.RESEND_API_KEY); } const FROM = process.env.EMAIL_FROM ?? "noreply@mailsend.second-brain.ru"; const BASE_URL = process.env.BETTER_AUTH_URL ?? "https://school.second-brain.ru"; async function getSchoolName() { return getSetting("schoolName"); } // ── HTML template (inline styles for maximum email client compatibility) ─────── function base(content: string, schoolName = "Second Brain") { return `
|
| ${label} → |
|
${text.replace(/\n/g, " |
Привет, ${name}!
Вам открыт доступ к курсу «${courseTitle}».
Перейдите на платформу чтобы начать обучение:
${btn(`${BASE_URL}/dashboard`, "Перейти к курсам")} `, school), }).catch((e) => console.error("[email] sendCourseAccessEmail:", e)); } export async function sendHomeworkSubmittedEmail( to: string, curatorName: string, studentName: string, lessonTitle: string, submissionId: string ) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Новая работа на проверку — ${lessonTitle}`, html: base(`Привет, ${curatorName}!
Студент ${studentName} сдал работу по уроку «${lessonTitle}».
Откройте работу чтобы проверить и оставить фидбек:
${btn(`${BASE_URL}/curator/homework/${submissionId}`, "Проверить работу")} `, school), }).catch((e) => console.error("[email] sendHomeworkSubmittedEmail:", e)); } export async function sendCommentNotificationEmail( to: string, recipientName: string, studentName: string, lessonTitle: string, commentText: string, lessonUrl: string ) { const school = await getSchoolName(); const esc = (t: string) => t.replace(/&/g, "&").replace(//g, ">"); const preview = commentText.length > 400 ? commentText.slice(0, 400) + "…" : commentText; await getResend().emails.send({ from: FROM, to, subject: `Новый комментарий под уроком — ${lessonTitle}`, html: base(`Привет${recipientName ? `, ${esc(recipientName)}` : ""}!
${esc(studentName)} оставил комментарий под уроком «${lessonTitle}»:
${quote(esc(preview))}Открыть урок и ответить:
${btn(lessonUrl, "Открыть урок")} `, school), }).catch((e) => console.error("[email] sendCommentNotificationEmail:", e)); } export async function sendFeedbackReceivedEmail( to: string, studentName: string, lessonTitle: string, feedbackText: string, lessonUrl: string ) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Получен фидбек по уроку «${lessonTitle}»`, html: base(`Привет, ${studentName}!
Куратор проверил вашу работу по уроку «${lessonTitle}» и оставил обратную связь:
${quote(feedbackText)}Вернитесь к уроку чтобы увидеть полный фидбек:
${btn(lessonUrl, "Открыть урок")} `, school), }).catch((e) => console.error("[email] sendFeedbackReceivedEmail:", e)); } export async function sendWelcomeEmail(to: string, name: string) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Добро пожаловать в ${school}`, html: base(`Привет, ${name}!
Ваш аккаунт на образовательной платформе ${school} создан.
После того как администратор откроет вам доступ к курсу, вы получите письмо и сможете начать обучение.
${btn(`${BASE_URL}/dashboard`, "Перейти на платформу")} `, school), }).catch((e) => console.error("[email] sendWelcomeEmail:", e)); } export async function sendTestEmail(to: string) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Тест — ${school} LMS`, html: base(`Привет!
Это тестовое письмо от платформы ${school} LMS.
Так будут выглядеть все уведомления — доступ к курсу, фидбек по ДЗ и другие.
Если письмо отображается корректно, значит email-уведомления настроены и работают.
${btn(`${BASE_URL}/dashboard`, "Открыть платформу")} `, school), }).catch((e) => console.error("[email] sendTestEmail:", e)); } export async function sendAccessEmail( to: string, name: string, courseTitle: string, tempPassword: string ) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Доступ к курсу «${courseTitle}» открыт`, html: base(`Привет!
Вам открыт доступ к курсу «${courseTitle}» на образовательной платформе ${school}.
Ваши данные для входа:
${quote(`Логин: ${to}\nПароль: ${tempPassword}`)}После входа рекомендуем сразу сменить пароль в разделе «Профиль».
Если пароль не подходит — воспользуйтесь ссылкой «Забыли пароль?» на странице входа.
${btn(`${BASE_URL}/login`, "Войти на платформу")} `, school), }).catch((e) => console.error("[email] sendAccessEmail:", e)); } // Выдача доступа по факту оплаты (через payment-router /api/internal/grant). // tempPassword != null — новый покупатель (аккаунт создан с временным паролем, // force-change при первом входе). null — существующий (курсы добавлены). export async function sendCourseGrantEmail( to: string, name: string, courseTitles: string[], tempPassword: string | null, ) { const school = await getSchoolName(); const list = courseTitles .map((t) => `Данные для входа:
${quote(`Адрес: school.second-brain.ru\nEmail: ${to}\nПароль: ${tempPassword}`)}Это временный пароль — при первом входе система попросит задать свой.
` : `Войдите под своим обычным паролем — новые курсы уже на главной.
`; const subject = courseTitles.length > 1 ? `Доступ к курсам ${school} открыт` : `Доступ к курсу «${courseTitles[0] ?? school}» открыт`; await getResend() .emails.send({ from: FROM, to, subject, html: base( `Привет, ${name || ""} 👋🏽
Спасибо за покупку! Доступ уже открыт в вашем личном кабинете на school.second-brain.ru. Вам открыты курсы:
Если что-то не получается — просто ответьте на это письмо.
`, school, ), }) .catch((e) => console.error("[email] sendCourseGrantEmail:", e)); } export async function sendPasswordResetEmail(to: string, name: string, resetUrl: string) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Сброс пароля — ${school}`, html: base(`Привет, ${name}!
Вы запросили сброс пароля для вашего аккаунта на платформе ${school}.
Нажмите на кнопку ниже чтобы задать новый пароль. Ссылка действительна 1 час.
Если вы не запрашивали сброс — просто проигнорируйте это письмо.
${btn(resetUrl, "Задать новый пароль")} `, school), }).catch((e) => console.error("[email] sendPasswordResetEmail:", e)); } export async function sendVerificationEmail(to: string, name: string, verifyUrl: string) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Подтвердите email — ${school}`, html: base(`Привет, ${name}!
Вы зарегистрировались на образовательной платформе ${school}.
Нажмите на кнопку ниже, чтобы подтвердить email и активировать аккаунт. Ссылка действительна 24 часа.
Если вы не регистрировались — просто проигнорируйте это письмо.
${btn(verifyUrl, "Подтвердить email")} `, school), }).catch((e) => console.error("[email] sendVerificationEmail:", e)); } export async function sendQuestionCreatedEmail( to: string, recipientName: string, studentName: string, questionTitle: string, ) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Новый вопрос от ${studentName}`, html: base(`Привет, ${recipientName}!
Студент ${studentName} задал новый вопрос:
${quote(questionTitle)}Откройте панель, чтобы ответить:
${btn(`${BASE_URL}/admin/questions`, "Перейти к вопросам")} `, school), }).catch((e) => console.error("[email] sendQuestionCreatedEmail:", e)); } export async function sendQuestionReplyEmail( to: string, studentName: string, questionTitle: string, questionId: string, ) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Ответ на ваш вопрос`, html: base(`Привет, ${studentName}!
Школа ответила на ваш вопрос:
${quote(questionTitle)}Откройте тред чтобы прочитать ответ:
${btn(`${BASE_URL}/questions/${questionId}`, "Читать ответ")} `, school), }).catch((e) => console.error("[email] sendQuestionReplyEmail:", e)); } export async function sendQuestionFollowUpEmail( to: string, recipientName: string, studentName: string, questionTitle: string, ) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Новый ответ от студента — ${questionTitle}`, html: base(`Привет, ${recipientName}!
Студент ${studentName} добавил сообщение в вопрос:
${quote(questionTitle)}Откройте панель, чтобы ответить:
${btn(`${BASE_URL}/admin/questions`, "Перейти к вопросам")} `, school), }).catch((e) => console.error("[email] sendQuestionFollowUpEmail:", e)); } export async function sendHomeworkUpdatedEmail( to: string, recipientName: string, studentName: string, lessonTitle: string, submissionId: string, ) { const school = await getSchoolName(); await getResend().emails.send({ from: FROM, to, subject: `Студент обновил работу — ${lessonTitle}`, html: base(`Привет, ${recipientName}!
Студент ${studentName} обновил работу по уроку «${lessonTitle}».
Откройте работу чтобы проверить изменения:
${btn(`${BASE_URL}/curator/homework/${submissionId}`, "Открыть работу")} `, school), }).catch((e) => console.error("[email] sendHomeworkUpdatedEmail:", e)); }