@goscribe/server 1.1.2 → 1.1.4
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.
- package/dist/lib/ai-session.d.ts +13 -3
- package/dist/lib/ai-session.js +66 -146
- package/dist/lib/pusher.js +1 -1
- package/dist/routers/_app.d.ts +114 -7
- package/dist/routers/chat.js +2 -23
- package/dist/routers/flashcards.d.ts +25 -1
- package/dist/routers/flashcards.js +0 -14
- package/dist/routers/members.d.ts +18 -0
- package/dist/routers/members.js +14 -1
- package/dist/routers/worksheets.js +5 -4
- package/dist/routers/workspace.d.ts +89 -6
- package/dist/routers/workspace.js +389 -259
- package/dist/services/flashcard-progress.service.d.ts +25 -1
- package/dist/services/flashcard-progress.service.js +70 -31
- package/package.json +3 -2
- package/prisma/schema.prisma +196 -184
- package/src/lib/ai-session.ts +3 -21
- package/src/routers/auth.ts +50 -2
- package/src/routers/flashcards.ts +0 -16
- package/src/routers/members.ts +27 -6
- package/src/routers/workspace.ts +468 -439
- package/src/server.ts +13 -0
- package/ANALYSIS_PROGRESS_SPEC.md +0 -463
- package/PROGRESS_QUICK_REFERENCE.md +0 -239
- package/dist/lib/podcast-prompts.d.ts +0 -43
- package/dist/lib/podcast-prompts.js +0 -135
- package/dist/routers/ai-session.d.ts +0 -0
- package/dist/routers/ai-session.js +0 -1
- package/dist/services/flashcard.service.d.ts +0 -183
- package/dist/services/flashcard.service.js +0 -224
- package/dist/services/podcast-segment-reorder.d.ts +0 -0
- package/dist/services/podcast-segment-reorder.js +0 -107
- package/dist/services/podcast.service.d.ts +0 -0
- package/dist/services/podcast.service.js +0 -326
- package/dist/services/worksheet.service.d.ts +0 -0
- package/dist/services/worksheet.service.js +0 -295
package/dist/lib/ai-session.d.ts
CHANGED
|
@@ -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
|
-
|
|
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[];
|
package/dist/lib/ai-session.js
CHANGED
|
@@ -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
|
-
//
|
|
90
|
-
async
|
|
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 -
|
|
92
|
+
// Mock mode - return fake processing result
|
|
97
93
|
if (MOCK_MODE) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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',
|
|
107
|
-
formData.append('
|
|
108
|
-
formData.append('
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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');
|
package/dist/lib/pusher.js
CHANGED
|
@@ -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 =
|
|
97
|
+
const eventName = event;
|
|
98
98
|
await pusher.trigger(channel, eventName, data);
|
|
99
99
|
console.log(`📡 Pusher notification sent: ${eventName} to ${channel}`);
|
|
100
100
|
}
|
package/dist/routers/_app.d.ts
CHANGED
|
@@ -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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
}>;
|
package/dist/routers/chat.js
CHANGED
|
@@ -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
|
*/
|
package/dist/routers/members.js
CHANGED
|
@@ -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 (
|
|
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'
|