feat: full site build — Project/Melody schema (Option A), admin CRUD, public sections, uploads, email+SMTP, internal analytics, legal pages, docs
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { prisma } from "@/lib/db";
|
||||
import { analyticsEventSchema, type AnalyticsEventInput } from "@/lib/validations/analytics";
|
||||
|
||||
export async function recordAnalyticsEvent(input: unknown) {
|
||||
const parsed = analyticsEventSchema.safeParse(input);
|
||||
if (!parsed.success) return false;
|
||||
|
||||
const { type, path, entityId } = parsed.data;
|
||||
await prisma.analyticsEvent.create({
|
||||
data: {
|
||||
type,
|
||||
path: path || null,
|
||||
projectId: type === "PROJECT_OPEN" || type === "PROJECT_LINK_CLICK" ? entityId || null : null,
|
||||
melodyId: type === "MELODY_PLAY" ? entityId || null : null,
|
||||
},
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function getAnalyticsSummary() {
|
||||
const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
||||
const [total, grouped, recent] = await Promise.all([
|
||||
prisma.analyticsEvent.count(),
|
||||
prisma.analyticsEvent.groupBy({
|
||||
by: ["type"],
|
||||
where: { createdAt: { gte: since } },
|
||||
_count: { _all: true },
|
||||
orderBy: { _count: { type: "desc" } },
|
||||
}),
|
||||
prisma.analyticsEvent.findMany({
|
||||
where: { createdAt: { gte: since } },
|
||||
orderBy: { createdAt: "desc" },
|
||||
take: 12,
|
||||
select: { id: true, type: true, path: true, createdAt: true },
|
||||
}),
|
||||
]);
|
||||
|
||||
return { total, since, grouped, recent };
|
||||
}
|
||||
|
||||
export type { AnalyticsEventInput };
|
||||
Reference in New Issue
Block a user