返回 presentation-ai
fetchPresentations.ts
根目录 / src / app / _actions / notebook / presentation / fetchPresentations.ts
1 "use server";
2
3 import "server-only";
4
5 import { logger } from "@/lib/observability/server/logger";
6 import { DocumentType, type Prisma } from "@/prisma/client";
7 import { auth } from "@/server/auth";
8 import { db } from "@/server/db";
9
10 const ITEMS_PER_PAGE = 10;
11 const PRESENTATION_DOCUMENT_TYPES = [DocumentType.PRESENTATION] as const;
12 export type PresentationDocumentTypeFilter =
13 (typeof PRESENTATION_DOCUMENT_TYPES)[number];
14
15 type PresentationContentShape = {
16 slides?: unknown;
17 };
18
19 function hasSlideContent(value: Prisma.JsonValue): boolean {
20 if (!value || typeof value !== "object" || Array.isArray(value)) {
21 return false;
22 }
23
24 const content = value as PresentationContentShape;
25 return Array.isArray(content.slides) && content.slides.length > 0;
26 }
27
28 export async function fetchPresentations(
29 page = 0,
30 type?: PresentationDocumentTypeFilter,
31 options?: {
32 favoritesOnly?: boolean;
33 },
34 ) {
35 const actionName = "presentation.fetchPresentations.fetchPresentations";
36 const span = logger.startSpan(`notebook.server_action.${actionName}`, {
37 attributes: {
38 "allweone.scope": "notebook",
39 "allweone.action.type": "server_action",
40 "allweone.action.name": actionName,
41 },
42 });
43
44 try {
45 const session = await auth();
46 const userId = session?.user.id;
47
48 if (!userId) {
49 return {
50 items: [],
51 hasMore: false,
52 };
53 }
54
55 const skip = page * ITEMS_PER_PAGE;
56 const documentType = type ?? PRESENTATION_DOCUMENT_TYPES[0];
57
58 const rows = await db.baseDocument.findMany({
59 where: {
60 userId,
61 type: documentType,
62 ...(options?.favoritesOnly
63 ? {
64 favorites: {
65 some: { userId },
66 },
67 }
68 : {}),
69 },
70 orderBy: {
71 updatedAt: "desc",
72 },
73 skip,
74 take: ITEMS_PER_PAGE + 1,
75 include: {
76 favorites: {
77 where: { userId },
78 select: { id: true },
79 take: 1,
80 },
81 presentation: {
82 select: {
83 content: true,
84 },
85 },
86 },
87 });
88
89 const hasMore = rows.length > ITEMS_PER_PAGE;
90 const items = hasMore ? rows.slice(0, ITEMS_PER_PAGE) : rows;
91
92 return {
93 items: items.map((item) => ({
94 id: item.id,
95 title: item.title,
96 type: item.type,
97 thumbnailUrl: item.thumbnailUrl,
98 createdAt: item.createdAt,
99 updatedAt: item.updatedAt,
100 isOwnedByCurrentUser: true,
101 favorites: item.favorites,
102 hasSlides: hasSlideContent(item.presentation?.content ?? null),
103 hasContent: hasSlideContent(item.presentation?.content ?? null),
104 })),
105 hasMore,
106 };
107 } catch (error) {
108 span.error(error);
109 throw error;
110 } finally {
111 span.end();
112 }
113 }
114
114 lines TYPESCRIPT