@goscribe/server 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/lib/ai-session.d.ts +13 -3
  2. package/dist/lib/ai-session.js +66 -146
  3. package/dist/lib/pusher.js +1 -1
  4. package/dist/routers/_app.d.ts +114 -7
  5. package/dist/routers/chat.js +2 -23
  6. package/dist/routers/flashcards.d.ts +25 -1
  7. package/dist/routers/flashcards.js +0 -14
  8. package/dist/routers/members.d.ts +18 -0
  9. package/dist/routers/members.js +14 -1
  10. package/dist/routers/worksheets.js +5 -4
  11. package/dist/routers/workspace.d.ts +89 -6
  12. package/dist/routers/workspace.js +389 -259
  13. package/dist/services/flashcard-progress.service.d.ts +25 -1
  14. package/dist/services/flashcard-progress.service.js +70 -31
  15. package/package.json +2 -2
  16. package/prisma/schema.prisma +14 -1
  17. package/src/lib/ai-session.ts +97 -158
  18. package/src/routers/flashcards.ts +0 -16
  19. package/src/routers/members.ts +13 -2
  20. package/src/routers/podcast.ts +0 -1
  21. package/src/routers/worksheets.ts +3 -2
  22. package/src/routers/workspace.ts +516 -399
  23. package/ANALYSIS_PROGRESS_SPEC.md +0 -463
  24. package/PROGRESS_QUICK_REFERENCE.md +0 -239
  25. package/dist/lib/podcast-prompts.d.ts +0 -43
  26. package/dist/lib/podcast-prompts.js +0 -135
  27. package/dist/routers/ai-session.d.ts +0 -0
  28. package/dist/routers/ai-session.js +0 -1
  29. package/dist/services/flashcard.service.d.ts +0 -183
  30. package/dist/services/flashcard.service.js +0 -224
  31. package/dist/services/podcast-segment-reorder.d.ts +0 -0
  32. package/dist/services/podcast-segment-reorder.js +0 -107
  33. package/dist/services/podcast.service.d.ts +0 -0
  34. package/dist/services/podcast.service.js +0 -326
  35. package/dist/services/worksheet.service.d.ts +0 -0
  36. package/dist/services/worksheet.service.js +0 -295
@@ -8,10 +8,22 @@ export interface AISession {
8
8
  createdAt: Date;
9
9
  updatedAt: Date;
10
10
  }
11
+ export interface ProcessFileResult {
12
+ status: 'success' | 'error';
13
+ textContent: string | null;
14
+ imageDescriptions: Array<{
15
+ page: number;
16
+ description: string;
17
+ hasVisualContent: boolean;
18
+ }>;
19
+ comprehensiveDescription: string | null;
20
+ pageCount: number;
21
+ error?: string;
22
+ }
11
23
  export declare class AISessionService {
12
24
  private sessions;
13
25
  initSession(workspaceId: string, user: string): Promise<AISession>;
14
- uploadFile(sessionId: string, user: string, file: File, fileType: 'image' | 'pdf'): Promise<void>;
26
+ processFile(sessionId: string, user: string, fileUrl: string, fileType: 'image' | 'pdf', maxPages?: number): Promise<ProcessFileResult>;
15
27
  generateStudyGuide(sessionId: string, user: string): Promise<string>;
16
28
  generateFlashcardQuestions(sessionId: string, user: string, numQuestions: number, difficulty: 'easy' | 'medium' | 'hard'): Promise<string>;
17
29
  generateWorksheetQuestions(sessionId: string, user: string, numQuestions: number, difficulty: 'EASY' | 'MEDIUM' | 'HARD'): Promise<string>;
@@ -26,8 +38,6 @@ export declare class AISessionService {
26
38
  role: string;
27
39
  name?: string;
28
40
  }>, voiceId?: string): Promise<any>;
29
- analysePDF(sessionId: string, user: string): Promise<string>;
30
- analyseImage(sessionId: string, user: string): Promise<string>;
31
41
  generatePodcastImage(sessionId: string, user: string, summary: string): Promise<string>;
32
42
  getSession(sessionId: string): AISession | undefined;
33
43
  getSessionsByUserAndWorkspace(userId: string, workspaceId: string): AISession[];
@@ -86,58 +86,82 @@ export class AISessionService {
86
86
  message: `Failed to initialize AI session after ${maxRetries} attempts: ${lastError?.message || 'Unknown error'}`,
87
87
  });
88
88
  }
89
- // Upload file to AI session
90
- async uploadFile(sessionId, user, file, fileType) {
91
- const session = this.sessions.get(sessionId);
92
- if (!session) {
93
- throw new TRPCError({ code: 'NOT_FOUND', message: 'AI session not found' });
94
- }
89
+ // Process file (PDF/image) and return comprehensive text descriptions
90
+ async processFile(sessionId, user, fileUrl, fileType, maxPages) {
95
91
  await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
96
- // Mock mode - simulate successful file upload
92
+ // Mock mode - return fake processing result
97
93
  if (MOCK_MODE) {
98
- console.log(`🎭 MOCK MODE: Uploading ${fileType} file "${file.name}" to session ${sessionId}`);
99
- session.files.push(file.name);
100
- session.updatedAt = new Date();
101
- this.sessions.set(sessionId, session);
102
- return;
94
+ logger.info(`🎭 MOCK MODE: Processing ${fileType} file from URL for session ${sessionId}`);
95
+ const mockPageCount = fileType === 'pdf' ? 15 : 1;
96
+ return {
97
+ status: 'success',
98
+ textContent: `Mock extracted text content from ${fileType} file. This would contain the full text extracted from the document.`,
99
+ imageDescriptions: Array.from({ length: mockPageCount }, (_, i) => ({
100
+ page: i + 1,
101
+ description: `Page ${i + 1} contains educational content with diagrams and text.`,
102
+ hasVisualContent: true,
103
+ })),
104
+ comprehensiveDescription: `DOCUMENT SUMMARY (${mockPageCount} ${mockPageCount === 1 ? 'page' : 'pages'})\n\nTEXT CONTENT:\nMock extracted text content...\n\nVISUAL CONTENT DESCRIPTIONS:\nPage-by-page descriptions of visual elements.`,
105
+ pageCount: mockPageCount,
106
+ };
103
107
  }
104
- const command = fileType === 'image' ? 'append_image' : 'append_pdflike';
105
108
  const formData = new FormData();
106
- formData.append('command', command);
107
- formData.append('file', file);
108
- formData.append('session', sessionId);
109
- formData.append('user', user);
110
- try {
111
- const response = await fetch(AI_SERVICE_URL, {
112
- method: 'POST',
113
- body: formData,
114
- });
115
- if (!response.ok) {
116
- throw new Error(`AI service error: ${response.status} ${response.statusText}`);
109
+ formData.append('command', 'process_file');
110
+ formData.append('fileUrl', fileUrl);
111
+ formData.append('fileType', fileType);
112
+ if (maxPages) {
113
+ formData.append('maxPages', maxPages.toString());
114
+ }
115
+ console.log('formData', formData);
116
+ // Retry logic for file processing
117
+ const maxRetries = 3;
118
+ let lastError = null;
119
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
120
+ try {
121
+ logger.info(`📄 Processing ${fileType} file attempt ${attempt}/${maxRetries} for session ${sessionId}`);
122
+ // Set timeout for large files (5 minutes)
123
+ const controller = new AbortController();
124
+ const timeoutId = setTimeout(() => controller.abort(), 300000); // 5 min timeout
125
+ const response = await fetch(AI_SERVICE_URL, {
126
+ method: 'POST',
127
+ body: formData,
128
+ // signal: controller.signal,
129
+ });
130
+ clearTimeout(timeoutId);
131
+ if (!response.ok) {
132
+ const errorText = await response.text();
133
+ logger.error(`❌ File processing error response:`, errorText);
134
+ throw new Error(`AI service error: ${response.status} ${response.statusText} - ${errorText}`);
135
+ }
136
+ const result = await response.json();
137
+ logger.info(`📋 File processing result: status=${result.status}, pageCount=${result.pageCount}`);
138
+ if (result.status === 'error') {
139
+ throw new Error(result.error || 'File processing failed');
140
+ }
141
+ return result;
117
142
  }
118
- const result = await response.json();
119
- console.log(`📋 Upload result:`, result);
120
- if (!result.message) {
121
- throw new Error(`AI service error: No response message`);
143
+ catch (error) {
144
+ lastError = error instanceof Error ? error : new Error('Unknown error');
145
+ logger.error(`❌ File processing attempt ${attempt} failed:`, lastError.message);
146
+ if (attempt < maxRetries) {
147
+ const delay = Math.pow(2, attempt) * 1000; // Exponential backoff: 2s, 4s, 8s
148
+ logger.info(`⏳ Retrying file processing in ${delay}ms...`);
149
+ await new Promise(resolve => setTimeout(resolve, delay));
150
+ }
122
151
  }
123
- // Update session
124
- session.files.push(file.name);
125
- session.updatedAt = new Date();
126
- this.sessions.set(sessionId, session);
127
- }
128
- catch (error) {
129
- throw new TRPCError({
130
- code: 'INTERNAL_SERVER_ERROR',
131
- message: `Failed to upload file: ${error instanceof Error ? error.message : 'Unknown error'}`,
132
- });
133
152
  }
153
+ logger.error(`💥 All ${maxRetries} file processing attempts failed. Last error:`, lastError?.message);
154
+ return {
155
+ status: 'error',
156
+ textContent: null,
157
+ imageDescriptions: [],
158
+ comprehensiveDescription: null,
159
+ pageCount: 0,
160
+ error: `Failed to process file after ${maxRetries} attempts: ${lastError?.message || 'Unknown error'}`,
161
+ };
134
162
  }
135
163
  // Generate study guide
136
164
  async generateStudyGuide(sessionId, user) {
137
- const session = this.sessions.get(sessionId);
138
- if (!session) {
139
- throw new TRPCError({ code: 'NOT_FOUND', message: 'AI session not found' });
140
- }
141
165
  await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
142
166
  // Mock mode - return fake study guide
143
167
  if (MOCK_MODE) {
@@ -186,10 +210,6 @@ This mock study guide demonstrates the structure and format that would be genera
186
210
  }
187
211
  // Generate flashcard questions
188
212
  async generateFlashcardQuestions(sessionId, user, numQuestions, difficulty) {
189
- // const session = this.sessions.get(sessionId);
190
- // if (!session) {
191
- // throw new TRPCError({ code: 'NOT_FOUND', message: 'AI session not found' });
192
- // }
193
213
  await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
194
214
  // Mock mode - return fake flashcard questions
195
215
  if (MOCK_MODE) {
@@ -416,106 +436,6 @@ This mock study guide demonstrates the structure and format that would be genera
416
436
  });
417
437
  }
418
438
  }
419
- // Analyse PDF
420
- async analysePDF(sessionId, user) {
421
- const session = this.sessions.get(sessionId);
422
- if (!session) {
423
- throw new TRPCError({ code: 'NOT_FOUND', message: 'AI session not found' });
424
- }
425
- await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
426
- // Mock mode - return fake PDF analysis
427
- if (MOCK_MODE) {
428
- console.log(`🎭 MOCK MODE: Analysing PDF for session ${sessionId}`);
429
- return `Mock PDF Analysis Results:
430
-
431
- Document Type: Educational Material
432
- Pages: 15
433
- Language: English
434
- Key Topics Identified:
435
- - Introduction to Machine Learning
436
- - Data Preprocessing Techniques
437
- - Model Training and Validation
438
- - Performance Metrics
439
-
440
- Summary: This mock PDF analysis shows the structure and content that would be extracted from an uploaded PDF document. The analysis includes document metadata, key topics, and a summary of the content.
441
-
442
- Note: This is a mock response generated when DONT_TEST_INFERENCE=true`;
443
- }
444
- const formData = new FormData();
445
- formData.append('command', 'analyse_pdf');
446
- formData.append('session', sessionId);
447
- formData.append('user', user);
448
- try {
449
- const response = await fetch(AI_SERVICE_URL, {
450
- method: 'POST',
451
- body: formData,
452
- });
453
- if (!response.ok) {
454
- throw new Error(`AI service error: ${response.status} ${response.statusText}`);
455
- }
456
- const result = await response.json();
457
- return result.message || 'PDF analysis completed';
458
- }
459
- catch (error) {
460
- throw new TRPCError({
461
- code: 'INTERNAL_SERVER_ERROR',
462
- message: `Failed to analyse PDF: ${error instanceof Error ? error.message : 'Unknown error'}`,
463
- });
464
- }
465
- }
466
- // Analyse Image
467
- async analyseImage(sessionId, user) {
468
- const session = this.sessions.get(sessionId);
469
- if (!session) {
470
- throw new TRPCError({ code: 'NOT_FOUND', message: 'AI session not found' });
471
- }
472
- await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
473
- // Mock mode - return fake image analysis
474
- if (MOCK_MODE) {
475
- console.log(`🎭 MOCK MODE: Analysing image for session ${sessionId}`);
476
- return `Mock Image Analysis Results:
477
-
478
- Image Type: Educational Diagram
479
- Format: PNG
480
- Dimensions: 1920x1080
481
- Content Description:
482
- - Contains a flowchart or diagram
483
- - Shows a process or system architecture
484
- - Includes text labels and annotations
485
- - Educational or instructional content
486
-
487
- Key Elements Identified:
488
- - Process flow arrows
489
- - Decision points
490
- - Input/output elements
491
- - Descriptive text
492
-
493
- Summary: This mock image analysis demonstrates the type of content extraction that would be performed on uploaded images. The analysis identifies visual elements, text content, and overall structure.
494
-
495
- Note: This is a mock response generated when DONT_TEST_INFERENCE=true`;
496
- }
497
- const formData = new FormData();
498
- formData.append('command', 'analyse_img');
499
- formData.append('session', sessionId);
500
- formData.append('user', user);
501
- try {
502
- const response = await fetch(AI_SERVICE_URL, {
503
- method: 'POST',
504
- body: formData,
505
- });
506
- if (!response.ok) {
507
- throw new Error(`AI service error: ${response.status} ${response.statusText}`);
508
- }
509
- const result = await response.json();
510
- return result.message || 'Image analysis completed';
511
- }
512
- catch (error) {
513
- throw new TRPCError({
514
- code: 'INTERNAL_SERVER_ERROR',
515
- message: `Failed to analyse image: ${error instanceof Error ? error.message : 'Unknown error'}`,
516
- });
517
- }
518
- }
519
439
  async generatePodcastImage(sessionId, user, summary) {
520
440
  const formData = new FormData();
521
441
  formData.append('command', 'generate_podcast_image');
@@ -94,7 +94,7 @@ export class PusherService {
94
94
  static async emitChannelEvent(channelId, event, data) {
95
95
  try {
96
96
  const channel = channelId; // Use channelId directly as channel name
97
- const eventName = `${channelId}_${event}`;
97
+ const eventName = event;
98
98
  await pusher.trigger(channel, eventName, data);
99
99
  console.log(`📡 Pusher notification sent: ${eventName} to ${channel}`);
100
100
  }
@@ -129,6 +129,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
129
129
  folderId: string | null;
130
130
  fileBeingAnalyzed: boolean;
131
131
  analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
132
+ needsAnalysis: boolean;
132
133
  }[];
133
134
  folders: {
134
135
  name: string;
@@ -160,6 +161,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
160
161
  folderId: string | null;
161
162
  fileBeingAnalyzed: boolean;
162
163
  analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
164
+ needsAnalysis: boolean;
163
165
  };
164
166
  meta: object;
165
167
  }>;
@@ -239,6 +241,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
239
241
  objectKey: string | null;
240
242
  url: string | null;
241
243
  checksum: string | null;
244
+ aiTranscription: import("@prisma/client/runtime/library").JsonValue | null;
242
245
  }[];
243
246
  artifacts: {
244
247
  id: string;
@@ -268,6 +271,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
268
271
  folderId: string | null;
269
272
  fileBeingAnalyzed: boolean;
270
273
  analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
274
+ needsAnalysis: boolean;
271
275
  };
272
276
  meta: object;
273
277
  }>;
@@ -302,6 +306,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
302
306
  folderId: string | null;
303
307
  fileBeingAnalyzed: boolean;
304
308
  analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
309
+ needsAnalysis: boolean;
305
310
  };
306
311
  meta: object;
307
312
  }>;
@@ -338,6 +343,55 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
338
343
  };
339
344
  meta: object;
340
345
  }>;
346
+ getSharedWith: import("@trpc/server").TRPCQueryProcedure<{
347
+ input: {
348
+ id: string;
349
+ };
350
+ output: {
351
+ shared: {
352
+ id: string;
353
+ createdAt: Date;
354
+ updatedAt: Date;
355
+ ownerId: string;
356
+ color: string;
357
+ title: string;
358
+ description: string | null;
359
+ icon: string;
360
+ folderId: string | null;
361
+ fileBeingAnalyzed: boolean;
362
+ analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
363
+ needsAnalysis: boolean;
364
+ }[];
365
+ invitations: ({
366
+ workspace: {
367
+ id: string;
368
+ createdAt: Date;
369
+ updatedAt: Date;
370
+ ownerId: string;
371
+ color: string;
372
+ title: string;
373
+ description: string | null;
374
+ icon: string;
375
+ folderId: string | null;
376
+ fileBeingAnalyzed: boolean;
377
+ analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
378
+ needsAnalysis: boolean;
379
+ };
380
+ } & {
381
+ id: string;
382
+ email: string;
383
+ createdAt: Date;
384
+ updatedAt: Date;
385
+ workspaceId: string;
386
+ role: string;
387
+ token: string;
388
+ invitedById: string;
389
+ acceptedAt: Date | null;
390
+ expiresAt: Date;
391
+ })[];
392
+ };
393
+ meta: object;
394
+ }>;
341
395
  uploadFiles: import("@trpc/server").TRPCMutationProcedure<{
342
396
  input: {
343
397
  id: string;
@@ -361,15 +415,25 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
361
415
  output: boolean;
362
416
  meta: object;
363
417
  }>;
418
+ getFileUploadUrl: import("@trpc/server").TRPCQueryProcedure<{
419
+ input: {
420
+ workspaceId: string;
421
+ filename: string;
422
+ contentType: string;
423
+ size: number;
424
+ };
425
+ output: {
426
+ fileId: string;
427
+ uploadUrl: string;
428
+ };
429
+ meta: object;
430
+ }>;
364
431
  uploadAndAnalyzeMedia: import("@trpc/server").TRPCMutationProcedure<{
365
432
  input: {
366
433
  workspaceId: string;
367
- file: {
368
- filename: string;
369
- contentType: string;
370
- size: number;
371
- content: string;
372
- };
434
+ files: {
435
+ id: string;
436
+ }[];
373
437
  generateStudyGuide?: boolean | undefined;
374
438
  generateFlashcards?: boolean | undefined;
375
439
  generateWorksheet?: boolean | undefined;
@@ -401,6 +465,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
401
465
  folderId: string | null;
402
466
  fileBeingAnalyzed: boolean;
403
467
  analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
468
+ needsAnalysis: boolean;
404
469
  }[];
405
470
  meta: object;
406
471
  }>;
@@ -471,6 +536,24 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
471
536
  };
472
537
  meta: object;
473
538
  }>;
539
+ getInvitations: import("@trpc/server").TRPCQueryProcedure<{
540
+ input: {
541
+ workspaceId: string;
542
+ };
543
+ output: {
544
+ id: string;
545
+ email: string;
546
+ createdAt: Date;
547
+ updatedAt: Date;
548
+ workspaceId: string;
549
+ role: string;
550
+ token: string;
551
+ invitedById: string;
552
+ acceptedAt: Date | null;
553
+ expiresAt: Date;
554
+ }[];
555
+ meta: object;
556
+ }>;
474
557
  acceptInvite: import("@trpc/server").TRPCMutationProcedure<{
475
558
  input: {
476
559
  token: string;
@@ -779,7 +862,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
779
862
  input: {
780
863
  workspaceId: string;
781
864
  };
782
- output: ({
865
+ output: (({
783
866
  artifact: {
784
867
  id: string;
785
868
  createdAt: Date;
@@ -804,6 +887,30 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
804
887
  front: string;
805
888
  back: string;
806
889
  tags: string[];
890
+ }) | {
891
+ artifact: {
892
+ id: string;
893
+ createdAt: Date;
894
+ updatedAt: Date;
895
+ title: string;
896
+ description: string | null;
897
+ workspaceId: string;
898
+ type: import("@prisma/client").$Enums.ArtifactType;
899
+ isArchived: boolean;
900
+ generating: boolean;
901
+ generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
902
+ difficulty: import("@prisma/client").$Enums.Difficulty | null;
903
+ estimatedTime: string | null;
904
+ imageObjectKey: string | null;
905
+ createdById: string | null;
906
+ };
907
+ id: string;
908
+ createdAt: Date;
909
+ artifactId: string;
910
+ order: number;
911
+ front: string;
912
+ back: string;
913
+ tags: string[];
807
914
  })[];
808
915
  meta: object;
809
916
  }>;
@@ -215,18 +215,7 @@ export const chat = router({
215
215
  }
216
216
  });
217
217
  // Notify via Pusher
218
- await PusherService.emitChannelEvent(chat.channelId, "edit_message", {
219
- chatId: updatedChat.id,
220
- channelId: updatedChat.channelId,
221
- userId: updatedChat.userId,
222
- message: input.message,
223
- updatedAt: updatedChat.updatedAt,
224
- user: {
225
- id: ctx.session.user.id,
226
- name: updatedChat.user?.name,
227
- image: updatedChat.user?.image,
228
- },
229
- });
218
+ await PusherService.emitChannelEvent(chat.channelId, "edit_message", updatedChat);
230
219
  return updatedChat;
231
220
  }),
232
221
  deleteMessage: authedProcedure
@@ -254,17 +243,7 @@ export const chat = router({
254
243
  where: { id: input.chatId },
255
244
  });
256
245
  // Notify via Pusher
257
- await PusherService.emitChannelEvent(chat.channelId, "delete_message", {
258
- chatId: chat.id,
259
- channelId: chat.channelId,
260
- userId: chat.userId,
261
- deletedAt: new Date().toISOString(),
262
- user: {
263
- id: ctx.session.user.id,
264
- name: chat.user?.name,
265
- image: chat.user?.image,
266
- },
267
- });
246
+ await PusherService.emitChannelEvent(chat.channelId, "delete_message", chat);
268
247
  return { success: true };
269
248
  }),
270
249
  });
@@ -242,7 +242,7 @@ export declare const flashcards: import("@trpc/server").TRPCBuiltRouter<{
242
242
  input: {
243
243
  workspaceId: string;
244
244
  };
245
- output: ({
245
+ output: (({
246
246
  artifact: {
247
247
  id: string;
248
248
  createdAt: Date;
@@ -267,6 +267,30 @@ export declare const flashcards: import("@trpc/server").TRPCBuiltRouter<{
267
267
  front: string;
268
268
  back: string;
269
269
  tags: string[];
270
+ }) | {
271
+ artifact: {
272
+ id: string;
273
+ createdAt: Date;
274
+ updatedAt: Date;
275
+ title: string;
276
+ description: string | null;
277
+ workspaceId: string;
278
+ type: import("@prisma/client").$Enums.ArtifactType;
279
+ isArchived: boolean;
280
+ generating: boolean;
281
+ generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
282
+ difficulty: import("@prisma/client").$Enums.Difficulty | null;
283
+ estimatedTime: string | null;
284
+ imageObjectKey: string | null;
285
+ createdById: string | null;
286
+ };
287
+ id: string;
288
+ createdAt: Date;
289
+ artifactId: string;
290
+ order: number;
291
+ front: string;
292
+ back: string;
293
+ tags: string[];
270
294
  })[];
271
295
  meta: object;
272
296
  }>;
@@ -173,20 +173,6 @@ export const flashcards = router({
173
173
  data: { generating: true, generatingMetadata: { quantity: input.numCards, difficulty: input.difficulty.toLowerCase() } },
174
174
  });
175
175
  await PusherService.emitTaskComplete(input.workspaceId, 'flash_card_info', { status: 'generating', numCards: input.numCards, difficulty: input.difficulty });
176
- const formattedPreviousCards = flashcardCurrent?.flashcards.map((card) => ({
177
- front: card.front,
178
- back: card.back,
179
- }));
180
- const partialPrompt = `
181
- This is the users previous flashcards, avoid repeating any existing cards.
182
- Please generate ${input.numCards} new cards,
183
- Of a ${input.difficulty} difficulty,
184
- Of a ${input.tags?.join(', ')} tag,
185
- Of a ${input.title} title.
186
- ${formattedPreviousCards?.map((card) => `Front: ${card.front}\nBack: ${card.back}`).join('\n')}
187
-
188
- The user has also left you this prompt: ${input.prompt}
189
- `;
190
176
  const artifact = await ctx.db.artifact.create({
191
177
  data: {
192
178
  workspaceId: input.workspaceId,
@@ -85,6 +85,24 @@ export declare const members: import("@trpc/server").TRPCBuiltRouter<{
85
85
  };
86
86
  meta: object;
87
87
  }>;
88
+ getInvitations: import("@trpc/server").TRPCQueryProcedure<{
89
+ input: {
90
+ workspaceId: string;
91
+ };
92
+ output: {
93
+ id: string;
94
+ email: string;
95
+ createdAt: Date;
96
+ updatedAt: Date;
97
+ workspaceId: string;
98
+ role: string;
99
+ token: string;
100
+ invitedById: string;
101
+ acceptedAt: Date | null;
102
+ expiresAt: Date;
103
+ }[];
104
+ meta: object;
105
+ }>;
88
106
  /**
89
107
  * Accept an invitation (public endpoint)
90
108
  */
@@ -211,6 +211,16 @@ export const members = router({
211
211
  invitedByName: invitation.workspace.owner.name || invitation.workspace.owner.email,
212
212
  };
213
213
  }),
214
+ getInvitations: authedProcedure
215
+ .input(z.object({
216
+ workspaceId: z.string(),
217
+ }))
218
+ .query(async ({ ctx, input }) => {
219
+ const invitations = await ctx.db.workspaceInvitation.findMany({
220
+ where: { workspaceId: input.workspaceId },
221
+ });
222
+ return invitations;
223
+ }),
214
224
  /**
215
225
  * Accept an invitation (public endpoint)
216
226
  */
@@ -254,8 +264,11 @@ export const members = router({
254
264
  message: 'Please log in to accept this invitation'
255
265
  });
256
266
  }
267
+ const user = await ctx.db.user.findFirst({ where: { id: ctx.session.user.id } });
268
+ if (!user || !user.email)
269
+ throw new TRPCError({ code: 'NOT_FOUND' });
257
270
  // Check if the email matches the user's email
258
- if (ctx.session.user.email !== invitation.email) {
271
+ if (user.email !== invitation.email) {
259
272
  throw new TRPCError({
260
273
  code: 'BAD_REQUEST',
261
274
  message: 'This invitation was sent to a different email address'