From fbd1b7b633864dc2b4a744b818554e99aac01ca2 Mon Sep 17 00:00:00 2001 From: Takala Date: Thu, 26 Dec 2024 01:50:07 +0800 Subject: [PATCH] 80 8 offensive language in discussions (#98) * refactor: move Firestore utility functions to server module and update imports * feat: add warning fields to conversation and group schemas, update related types and functions * feat: update conversation and group schemas to include nullable warning fields, enhance moderation checks in discussions * fix: update session ID in test script and refine moderation handling in discussion API * feat: add moderation field to group schema and update discussion handling * feat: integrate content moderation check in discussion addition endpoint * feat: add warning field for moderation and off-topic checks in group creation * feat: simplify warning structure by removing off-topic field in group creation --- scripts/createTestStudent.ts | 2 +- src/lib/schema/conversation.ts | 8 +- src/lib/schema/group.ts | 8 +- src/lib/server/firebase.ts | 173 +++++++++++++++++ src/lib/server/llm.ts | 17 +- src/lib/{utils => server}/types.ts | 13 +- src/lib/utils/firestore.ts | 174 ------------------ .../[id]/conversations/keywords/+server.ts | 2 +- .../most-active-participants/+server.ts | 2 +- src/routes/api/session/[id]/group/+server.ts | 1 + .../[group_number]/conversations/+server.ts | 6 +- .../conversations/[conv_id]/chat/+server.ts | 10 +- .../[conv_id]/remove/warning/+server.ts | 2 +- .../[conv_id]/summary/+server.ts | 4 +- .../[group_number]/discussions/add/+server.ts | 18 +- .../discussions/summary/+server.ts | 16 +- .../leave/[participant]/+server.ts | 3 +- 17 files changed, 236 insertions(+), 223 deletions(-) rename src/lib/{utils => server}/types.ts (75%) delete mode 100644 src/lib/utils/firestore.ts diff --git a/scripts/createTestStudent.ts b/scripts/createTestStudent.ts index 290cf76..9ace4d8 100644 --- a/scripts/createTestStudent.ts +++ b/scripts/createTestStudent.ts @@ -6,7 +6,7 @@ import { GroupSchema } from '../src/lib/schema/group'; import { ProfileSchema } from '../src/lib/schema/profile'; import { adminDb } from '../src/lib/server/firebase'; // 假設有一個 Firebase store 模組 -const sessionId = 'HtxeyVYERenrLEjfyo5D'; +const sessionId = 'tWwNvzhumqTFS4YMF6Cu'; const numberOfStudents = 40; const numberOfGroups = 8; diff --git a/src/lib/schema/conversation.ts b/src/lib/schema/conversation.ts index bf60320..4d6e6b0 100644 --- a/src/lib/schema/conversation.ts +++ b/src/lib/schema/conversation.ts @@ -13,7 +13,13 @@ export const ConversationSchema = z.object({ z.object({ role: z.enum(['system', 'user', 'assistant']), content: z.string(), - audio: z.string().nullable() // to find the raw file + audio: z.string().nullable(), // to find the raw file + warning: z + .object({ + moderation: z.boolean().default(false), + offTopic: z.boolean().default(false) + }) + .nullable() }) ), warning: z.object({ diff --git a/src/lib/schema/group.ts b/src/lib/schema/group.ts index 21d9eb9..cab6185 100644 --- a/src/lib/schema/group.ts +++ b/src/lib/schema/group.ts @@ -10,14 +10,16 @@ export const GroupSchema = z.object({ z.object({ content: z.string(), id: z.string().nullable(), - speaker: z.string(), // i am not sure if this is going to be used - audio: z.string().nullable() // to find the raw file + speaker: z.string(), + audio: z.string().nullable(), // to find the raw file + moderation: z.boolean().default(false) }) ), updatedAt: z.date().nullable(), status: z.enum(['discussion', 'summarize', 'end']).default('discussion'), summary: z.string().nullable(), // lock on stage 2 finalize transaction - keywords: z.record(z.string(), z.number().min(1).max(5)) + keywords: z.record(z.string(), z.number().min(1).max(5)), + moderation: z.boolean().default(false) }); export interface GroupDiscussionMessage { diff --git a/src/lib/server/firebase.ts b/src/lib/server/firebase.ts index 795d241..a220a12 100644 --- a/src/lib/server/firebase.ts +++ b/src/lib/server/firebase.ts @@ -1,4 +1,9 @@ import { env } from '$env/dynamic/private'; +import type { Conversation } from '$lib/schema/conversation'; +import type { Group } from '$lib/schema/group'; +import type { Session } from '$lib/schema/session'; +import type { LLMChatMessage } from '$lib/server/types'; +import { error } from '@sveltejs/kit'; import { cert, getApps, initializeApp, type ServiceAccount } from 'firebase-admin/app'; import { getAuth } from 'firebase-admin/auth'; import { getFirestore } from 'firebase-admin/firestore'; @@ -22,3 +27,171 @@ if (!getApps().length) { export const adminAuth = getAuth(); export const adminDb = getFirestore(); + +export async function createConversation( + id: string, + group_number: string, + userId: string, + task: string, + subtasks: string[], + history: LLMChatMessage[], + resources: { name: string; content: string }[] +) { + const conversationsRef = adminDb + .collection('sessions') + .doc(id) + .collection('groups') + .doc(group_number) + .collection('conversations'); + + const existingConversations = await conversationsRef.get(); + if (!existingConversations.empty) { + return existingConversations.docs[0].id; + } + + const conversationRef = conversationsRef.doc(); + await conversationRef.set({ + userId: userId, + task: task, + subtasks: subtasks, + resources: resources, + history: history, + subtaskCompleted: new Array(subtasks.length).fill(false), + warning: { moderation: false, off_topic: 0 } + }); + + return conversationRef.id; +} +export function getConversationRef(id: string, group_number: string, conv_id: string) { + return adminDb + .collection('sessions') + .doc(id) + .collection('groups') + .doc(group_number) + .collection('conversations') + .doc(conv_id); +} + +export function getConversationsRef(id: string, group_number: string) { + return adminDb + .collection('sessions') + .doc(id) + .collection('groups') + .doc(group_number) + .collection('conversations'); +} + +export async function getConversationData( + conversation_ref: FirebaseFirestore.DocumentReference +): Promise { + const conversation = await conversation_ref.get(); + if (!conversation.exists) { + throw error(404, 'Conversation not found'); + } + + return conversation.data() as Conversation; +} + +export async function getConversationsData( + conversations_ref: FirebaseFirestore.CollectionReference< + FirebaseFirestore.DocumentData, + FirebaseFirestore.DocumentData + > +) { + const conversations = await conversations_ref.get(); + if (conversations.empty) { + throw error(404, 'Conversations not found'); + } + + return conversations.docs.map((doc) => doc.data() as Conversation); +} + +export function getGroupRef(id: string, group_number: string) { + return adminDb.collection('sessions').doc(id).collection('groups').doc(group_number); +} + +export function getGroupsRef(id: string) { + return adminDb.collection('sessions').doc(id).collection('groups'); +} + +export async function getGroupData( + group_ref: FirebaseFirestore.DocumentReference +): Promise { + const group = await group_ref.get(); + if (!group.exists) { + throw error(404, 'Group not found'); + } + + return group.data() as Group; +} + +export async function getGroupsData( + groups_ref: FirebaseFirestore.CollectionReference +): Promise { + const groups = await groups_ref.get(); + if (groups.empty) { + throw error(404, 'Groups not found'); + } + + return groups.docs.map((doc) => doc.data() as Group); +} + +export function getSessionRef(id: string) { + return adminDb.collection('sessions').doc(id); +} + +export async function getSessionData( + session_ref: FirebaseFirestore.DocumentReference +): Promise { + const session = await session_ref.get(); + if (!session.exists) { + throw error(404, 'Session not found'); + } + + return session.data() as Session; +} + +export async function getConversationsFromAllParticipantsData( + id: string +): Promise> { + // 先獲取所有群組 + const groupsRef = getGroupsRef(id); + const groups = await groupsRef.get(); + + if (groups.empty) { + throw error(404, 'No groups found'); + } + + // 獲取每個群組中的所有對話 + const conversationsPromises = groups.docs.map(async (groupDoc) => { + const conversationsRef = getConversationsRef(id, groupDoc.id); + const conversations = await conversationsRef.get(); + + return conversations.docs.map((doc) => ({ + ...(doc.data() as Conversation), + groupId: groupDoc.id, + conversationId: doc.id + })); + }); + + const allConversations = await Promise.all(conversationsPromises); + const flattenedConversations = allConversations.flat(); + + if (flattenedConversations.length === 0) { + throw error(404, 'No conversations found'); + } + + return flattenedConversations; +} + +export async function checkRemoveParticipantPermission( + sessionId: string, + userId: string, + participantToRemove: string +): Promise { + const sessionRef = getSessionRef(sessionId); + const session = await getSessionData(sessionRef); + + // 檢查是否為 session host 或是要被移除的參與者本人 + return session.host === userId || userId === participantToRemove; +} diff --git a/src/lib/server/llm.ts b/src/lib/server/llm.ts index 396b7ef..f3491e1 100644 --- a/src/lib/server/llm.ts +++ b/src/lib/server/llm.ts @@ -1,6 +1,6 @@ import { env } from '$env/dynamic/private'; import type { Resource } from '$lib/schema/resource'; -import type { LLMChatMessage, StudentSpeak } from '$lib/utils/types'; +import type { Discussion, LLMChatMessage } from '$lib/server/types'; import fs from 'fs/promises'; import { OpenAI } from 'openai'; import { zodResponseFormat } from 'openai/helpers/zod'; @@ -19,7 +19,7 @@ const openai = new OpenAI({ baseURL: env.OPENAI_BASE_URL }); -async function isHarmfulContent( +export async function isHarmfulContent( content: string ): Promise<{ success: boolean; harmful: boolean; error?: string }> { console.log('Checking content for harmful content:', { contentLength: content.length }); @@ -399,16 +399,13 @@ export async function summarizeConcepts( } } -export async function summarizeGroupOpinions(student_opinion: StudentSpeak[]): Promise<{ - success: boolean; - summary: string; - keywords: Record; - error?: string; -}> { +export async function summarizeGroupOpinions( + student_opinion: Discussion[] +): Promise<{ success: boolean; summary: string; keywords: string[]; error?: string }> { try { const formatted_opinions = student_opinion - .filter((opinion) => opinion.role !== '摘要小幫手') - .map((opinion) => `${opinion.role}: ${opinion.content}`) + .filter((opinion) => opinion.speaker !== '摘要小幫手') + .map((opinion) => `${opinion.speaker}: ${opinion.content}`) .join('\n'); const system_prompt = GROUP_OPINION_SUMMARY_PROMPT.replace( diff --git a/src/lib/utils/types.ts b/src/lib/server/types.ts similarity index 75% rename from src/lib/utils/types.ts rename to src/lib/server/types.ts index a6eb2e9..f58bb5f 100644 --- a/src/lib/utils/types.ts +++ b/src/lib/server/types.ts @@ -8,17 +8,20 @@ export interface DBChatMessage { role: 'user' | 'assistant' | 'system'; content: string; audio: string | null; + warning: { + moderation: boolean; + offTopic: boolean; + } | null; } export interface Discussion { id: string | null; content: string; speaker: string; -} - -export interface StudentSpeak { - role: string; - content: string; + warning: { + moderation: boolean; + offTopic: boolean; + }; } export interface SummaryData { diff --git a/src/lib/utils/firestore.ts b/src/lib/utils/firestore.ts deleted file mode 100644 index 5c2b785..0000000 --- a/src/lib/utils/firestore.ts +++ /dev/null @@ -1,174 +0,0 @@ -import type { Conversation } from '$lib/schema/conversation'; -import type { Group } from '$lib/schema/group'; -import type { Session } from '$lib/schema/session'; -import { adminDb } from '$lib/server/firebase'; -import { error } from '@sveltejs/kit'; -import type { LLMChatMessage } from './types'; - -export async function createConversation( - id: string, - group_number: string, - userId: string, - task: string, - subtasks: string[], - history: LLMChatMessage[], - resources: { name: string; content: string }[] -) { - const conversationsRef = adminDb - .collection('sessions') - .doc(id) - .collection('groups') - .doc(group_number) - .collection('conversations'); - - const existingConversations = await conversationsRef.get(); - if (!existingConversations.empty) { - return existingConversations.docs[0].id; - } - - const conversationRef = conversationsRef.doc(); - await conversationRef.set({ - userId: userId, - task: task, - subtasks: subtasks, - resources: resources, - history: history, - subtaskCompleted: new Array(subtasks.length).fill(false), - warning: { moderation: false, off_topic: 0 } - }); - - return conversationRef.id; -} -export function getConversationRef(id: string, group_number: string, conv_id: string) { - return adminDb - .collection('sessions') - .doc(id) - .collection('groups') - .doc(group_number) - .collection('conversations') - .doc(conv_id); -} - -export function getConversationsRef(id: string, group_number: string) { - return adminDb - .collection('sessions') - .doc(id) - .collection('groups') - .doc(group_number) - .collection('conversations'); -} - -export async function getConversationData( - conversation_ref: FirebaseFirestore.DocumentReference -): Promise { - const conversation = await conversation_ref.get(); - if (!conversation.exists) { - throw error(404, 'Conversation not found'); - } - - return conversation.data() as Conversation; -} - -export async function getConversationsData( - conversations_ref: FirebaseFirestore.CollectionReference< - FirebaseFirestore.DocumentData, - FirebaseFirestore.DocumentData - > -) { - const conversations = await conversations_ref.get(); - if (conversations.empty) { - throw error(404, 'Conversations not found'); - } - - return conversations.docs.map((doc) => doc.data() as Conversation); -} - -export function getGroupRef(id: string, group_number: string) { - return adminDb.collection('sessions').doc(id).collection('groups').doc(group_number); -} - -export function getGroupsRef(id: string) { - return adminDb.collection('sessions').doc(id).collection('groups'); -} - -export async function getGroupData( - group_ref: FirebaseFirestore.DocumentReference -): Promise { - const group = await group_ref.get(); - if (!group.exists) { - throw error(404, 'Group not found'); - } - - return group.data() as Group; -} - -export async function getGroupsData( - groups_ref: FirebaseFirestore.CollectionReference -): Promise { - const groups = await groups_ref.get(); - if (groups.empty) { - throw error(404, 'Groups not found'); - } - - return groups.docs.map((doc) => doc.data() as Group); -} - -export function getSessionRef(id: string) { - return adminDb.collection('sessions').doc(id); -} - -export async function getSessionData( - session_ref: FirebaseFirestore.DocumentReference -): Promise { - const session = await session_ref.get(); - if (!session.exists) { - throw error(404, 'Session not found'); - } - - return session.data() as Session; -} - -export async function getConversationsFromAllParticipantsData( - id: string -): Promise> { - // 先獲取所有群組 - const groupsRef = getGroupsRef(id); - const groups = await groupsRef.get(); - - if (groups.empty) { - throw error(404, 'No groups found'); - } - - // 獲取每個群組中的所有對話 - const conversationsPromises = groups.docs.map(async (groupDoc) => { - const conversationsRef = getConversationsRef(id, groupDoc.id); - const conversations = await conversationsRef.get(); - - return conversations.docs.map((doc) => ({ - ...(doc.data() as Conversation), - groupId: groupDoc.id, - conversationId: doc.id - })); - }); - - const allConversations = await Promise.all(conversationsPromises); - const flattenedConversations = allConversations.flat(); - - if (flattenedConversations.length === 0) { - throw error(404, 'No conversations found'); - } - - return flattenedConversations; -} - -export async function checkRemoveParticipantPermission( - sessionId: string, - userId: string, - participantToRemove: string -): Promise { - const sessionRef = getSessionRef(sessionId); - const session = await getSessionData(sessionRef); - - // 檢查是否為 session host 或是要被移除的參與者本人 - return session.host === userId || userId === participantToRemove; -} diff --git a/src/routes/api/session/[id]/conversations/keywords/+server.ts b/src/routes/api/session/[id]/conversations/keywords/+server.ts index 5405d53..6594c25 100644 --- a/src/routes/api/session/[id]/conversations/keywords/+server.ts +++ b/src/routes/api/session/[id]/conversations/keywords/+server.ts @@ -1,4 +1,4 @@ -import { getGroupsData, getGroupsRef } from '$lib/utils/firestore'; +import { getGroupsData, getGroupsRef } from '$lib/server/firebase'; import { error, json } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; diff --git a/src/routes/api/session/[id]/conversations/most-active-participants/+server.ts b/src/routes/api/session/[id]/conversations/most-active-participants/+server.ts index 4d6c626..fef1178 100644 --- a/src/routes/api/session/[id]/conversations/most-active-participants/+server.ts +++ b/src/routes/api/session/[id]/conversations/most-active-participants/+server.ts @@ -1,4 +1,4 @@ -import { getConversationsFromAllParticipantsData } from '$lib/utils/firestore'; +import { getConversationsFromAllParticipantsData } from '$lib/server/firebase'; import { error, json } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; diff --git a/src/routes/api/session/[id]/group/+server.ts b/src/routes/api/session/[id]/group/+server.ts index a92af70..9f17e98 100644 --- a/src/routes/api/session/[id]/group/+server.ts +++ b/src/routes/api/session/[id]/group/+server.ts @@ -34,6 +34,7 @@ export const POST: RequestHandler = async ({ params, locals }) => { number: groupNumber, createdAt: new Date(), status: 'discussion', + moderation: false, updatedAt: new Date() }; diff --git a/src/routes/api/session/[id]/group/[group_number]/conversations/+server.ts b/src/routes/api/session/[id]/group/[group_number]/conversations/+server.ts index 6786c0d..8b40333 100644 --- a/src/routes/api/session/[id]/group/[group_number]/conversations/+server.ts +++ b/src/routes/api/session/[id]/group/[group_number]/conversations/+server.ts @@ -1,12 +1,12 @@ -import { chatWithLLMByDocs } from '$lib/server/llm'; import { createConversation, getGroupData, getGroupRef, getSessionData, getSessionRef -} from '$lib/utils/firestore'; -import type { LLMChatMessage } from '$lib/utils/types'; +} from '$lib/server/firebase'; +import { chatWithLLMByDocs } from '$lib/server/llm'; +import type { LLMChatMessage } from '$lib/server/types'; import type { RequestHandler } from '@sveltejs/kit'; import { error, json, redirect } from '@sveltejs/kit'; diff --git a/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/chat/+server.ts b/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/chat/+server.ts index 9f04bdf..d8ee8dd 100644 --- a/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/chat/+server.ts +++ b/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/chat/+server.ts @@ -1,6 +1,6 @@ +import { getConversationData, getConversationRef } from '$lib/server/firebase'; import { chatWithLLMByDocs } from '$lib/server/llm'; -import { getConversationData, getConversationRef } from '$lib/utils/firestore'; -import type { DBChatMessage, LLMChatMessage } from '$lib/utils/types'; +import type { DBChatMessage, LLMChatMessage } from '$lib/server/types'; import type { RequestHandler } from '@sveltejs/kit'; import { error, json, redirect } from '@sveltejs/kit'; import { z } from 'zod'; @@ -79,7 +79,11 @@ export const POST: RequestHandler = async ({ request, params, locals }) => { { role: 'user', content: content, - audio: audio + audio: audio, + warning: { + moderation: response.warning.moderation, + offTopic: response.warning.off_topic + } }, { role: 'assistant', diff --git a/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/remove/warning/+server.ts b/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/remove/warning/+server.ts index 0a476a9..56ee2d4 100644 --- a/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/remove/warning/+server.ts +++ b/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/remove/warning/+server.ts @@ -1,4 +1,4 @@ -import { getConversationRef, getSessionData, getSessionRef } from '$lib/utils/firestore'; +import { getConversationRef, getSessionData, getSessionRef } from '$lib/server/firebase'; import type { RequestHandler } from '@sveltejs/kit'; import { error, json, redirect } from '@sveltejs/kit'; diff --git a/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/summary/+server.ts b/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/summary/+server.ts index 704ac13..924f7c5 100644 --- a/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/summary/+server.ts +++ b/src/routes/api/session/[id]/group/[group_number]/conversations/[conv_id]/summary/+server.ts @@ -1,11 +1,11 @@ -import { summarizeConcepts, summarizeStudentChat } from '$lib/server/llm'; import { getConversationData, getConversationRef, getConversationsData, getConversationsRef, getGroupRef -} from '$lib/utils/firestore'; +} from '$lib/server/firebase'; +import { summarizeConcepts, summarizeStudentChat } from '$lib/server/llm'; import type { RequestHandler } from '@sveltejs/kit'; import { error, redirect } from '@sveltejs/kit'; diff --git a/src/routes/api/session/[id]/group/[group_number]/discussions/add/+server.ts b/src/routes/api/session/[id]/group/[group_number]/discussions/add/+server.ts index 7a966b1..55a028d 100644 --- a/src/routes/api/session/[id]/group/[group_number]/discussions/add/+server.ts +++ b/src/routes/api/session/[id]/group/[group_number]/discussions/add/+server.ts @@ -1,5 +1,6 @@ -import { adminDb } from '$lib/server/firebase'; -import { getGroupRef } from '$lib/utils/firestore'; +import type { Group } from '$lib/schema/group'; +import { adminDb, getGroupRef } from '$lib/server/firebase'; +import { isHarmfulContent } from '$lib/server/llm'; import type { RequestHandler } from '@sveltejs/kit'; import { error, json, redirect } from '@sveltejs/kit'; import { z } from 'zod'; @@ -23,15 +24,18 @@ export const POST: RequestHandler = async ({ request, params, locals }) => { throw error(400, 'Missing parameters'); } + const { content, speaker, audio } = await getRequestData(request); + const moderation = await isHarmfulContent(content); + const group_ref = getGroupRef(id, group_number); await adminDb.runTransaction(async (t) => { const doc = await t.get(group_ref); - const data = doc.data(); + const data = doc.data() as Group; if (!data || !data.discussions) { throw error(400, 'Discussions not found'); } const { discussions } = data; - const { content, speaker, audio } = await getRequestData(request); + t.update(group_ref, { discussions: [ ...discussions, @@ -39,10 +43,12 @@ export const POST: RequestHandler = async ({ request, params, locals }) => { id: locals.user?.uid, content: content, speaker: speaker, - audio: audio + audio: audio, + moderation: moderation.harmful } ], - updatedAt: new Date() + updatedAt: new Date(), + moderation: moderation.harmful || data.moderation }); }); diff --git a/src/routes/api/session/[id]/group/[group_number]/discussions/summary/+server.ts b/src/routes/api/session/[id]/group/[group_number]/discussions/summary/+server.ts index b42ca03..c5534b3 100644 --- a/src/routes/api/session/[id]/group/[group_number]/discussions/summary/+server.ts +++ b/src/routes/api/session/[id]/group/[group_number]/discussions/summary/+server.ts @@ -1,6 +1,6 @@ +import { getGroupData, getGroupRef } from '$lib/server/firebase'; import { summarizeGroupOpinions } from '$lib/server/llm'; -import { getGroupData, getGroupRef } from '$lib/utils/firestore'; -import type { Discussion, StudentSpeak } from '$lib/utils/types'; +import type { Discussion } from '$lib/server/types'; import type { RequestHandler } from '@sveltejs/kit'; import { error, json } from '@sveltejs/kit'; import { z } from 'zod'; @@ -26,7 +26,10 @@ export const GET: RequestHandler = async ({ params, locals }) => { status: 'summarize' }); - const student_opinions = discussion2StudentSpeak(discussions); + const student_opinions = discussions.map((discussion) => { + discussion.speaker = discussion.speaker ? discussion.speaker : 'student'; + return discussion as Discussion; + }); const response = await summarizeGroupOpinions(student_opinions); if (!response.success) { @@ -79,13 +82,6 @@ export const PUT: RequestHandler = async ({ request, params, locals }) => { } }; -function discussion2StudentSpeak(discussions: Discussion[]): StudentSpeak[] { - return discussions.map((discussion) => ({ - role: discussion.speaker ? discussion.speaker : 'student', - content: discussion.content - })); -} - async function getRequestData(request: Request): Promise> { const data = await request.json(); const result = requestDataFormat.parse(data); diff --git a/src/routes/api/session/[id]/group/[group_number]/leave/[participant]/+server.ts b/src/routes/api/session/[id]/group/[group_number]/leave/[participant]/+server.ts index 42d3d43..34559b3 100644 --- a/src/routes/api/session/[id]/group/[group_number]/leave/[participant]/+server.ts +++ b/src/routes/api/session/[id]/group/[group_number]/leave/[participant]/+server.ts @@ -1,5 +1,4 @@ -import { adminDb } from '$lib/server/firebase'; -import { checkRemoveParticipantPermission } from '$lib/utils/firestore'; +import { adminDb, checkRemoveParticipantPermission } from '$lib/server/firebase'; import { error, json } from '@sveltejs/kit'; import type { RequestHandler } from './$types';