7c8e72cd94
Notify admins/curators (or configured notificationEmails) when a student leaves a comment under a lesson. New setting notifyOnComment (default on) + toggle in admin settings. Fires only for student comments, not admin/curator replies; comment text is HTML-escaped in the email. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
import { cache } from "react";
|
|
import { prisma } from "./prisma";
|
|
|
|
// ── Defaults ──────────────────────────────────────────────────────────────────
|
|
|
|
export const SETTINGS_DEFAULTS = {
|
|
// Basic
|
|
schoolName: "Second Brain",
|
|
schoolDescription: "Образовательная платформа Second Brain",
|
|
schoolKeywords: "",
|
|
maintenanceMode: "false",
|
|
registrationEnabled: "true",
|
|
|
|
// Notifications
|
|
notificationEmails: "", // newline-separated list
|
|
notifyOnHomework: "true",
|
|
notifyOnComment: "true",
|
|
notifyOnRegistration: "true",
|
|
notifyStudentOnFeedback: "true",
|
|
|
|
// Student profile
|
|
requireEmailVerification: "true",
|
|
lastNameField: "optional", // required | optional | hidden
|
|
phoneField: "hidden", // required | optional | hidden
|
|
|
|
// Legal
|
|
privacyPolicyUrl: "",
|
|
termsUrl: "",
|
|
offerUrl: "",
|
|
showTermsCheckbox: "false",
|
|
orgRequisites: "",
|
|
|
|
// Curator permissions
|
|
curatorHomeworkScope: "all", // all | assigned
|
|
curatorCanAnswerQuestions: "true",
|
|
curatorCanSeeStudents: "true",
|
|
|
|
// Logo
|
|
logoUrl: "",
|
|
showLogo: "true",
|
|
|
|
// Social networks
|
|
socialYoutube: "",
|
|
socialVk: "",
|
|
socialTelegram: "",
|
|
|
|
// Code injection
|
|
headCode: "",
|
|
bodyCode: "",
|
|
} as const;
|
|
|
|
export type SettingsKey = keyof typeof SETTINGS_DEFAULTS;
|
|
export type Settings = Record<SettingsKey, string>;
|
|
|
|
// ── Getters ───────────────────────────────────────────────────────────────────
|
|
|
|
export const getSettings = cache(async (): Promise<Settings> => {
|
|
try {
|
|
const rows = await prisma.settings.findMany();
|
|
const stored: Record<string, string> = {};
|
|
for (const row of rows) stored[row.key] = row.value;
|
|
return Object.fromEntries(
|
|
Object.entries(SETTINGS_DEFAULTS).map(([k, v]) => [k, stored[k] ?? v])
|
|
) as Settings;
|
|
} catch {
|
|
// DB unavailable at build time — return defaults
|
|
return { ...SETTINGS_DEFAULTS } as Settings;
|
|
}
|
|
});
|
|
|
|
export async function getSetting(key: SettingsKey): Promise<string> {
|
|
try {
|
|
const row = await prisma.settings.findUnique({ where: { key } });
|
|
return row?.value ?? SETTINGS_DEFAULTS[key];
|
|
} catch {
|
|
return SETTINGS_DEFAULTS[key];
|
|
}
|
|
}
|
|
|
|
// ── Convenience helpers ───────────────────────────────────────────────────────
|
|
|
|
/** Parse a boolean setting ("true" / "false") */
|
|
export function asBool(value: string): boolean {
|
|
return value === "true";
|
|
}
|
|
|
|
/** Parse notification emails (newline-separated string → array) */
|
|
export function parseNotificationEmails(value: string): string[] {
|
|
return value
|
|
.split("\n")
|
|
.map((e) => e.trim())
|
|
.filter(Boolean);
|
|
}
|