@goscribe/server 1.3.0 → 1.3.1
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/context.d.ts +5 -1
- package/dist/lib/activity_human_description.d.ts +13 -0
- package/dist/lib/activity_human_description.js +221 -0
- package/dist/lib/activity_human_description.test.d.ts +1 -0
- package/dist/lib/activity_human_description.test.js +16 -0
- package/dist/lib/activity_log_service.d.ts +87 -0
- package/dist/lib/activity_log_service.js +276 -0
- package/dist/lib/activity_log_service.test.d.ts +1 -0
- package/dist/lib/activity_log_service.test.js +27 -0
- package/dist/lib/ai-session.d.ts +15 -2
- package/dist/lib/ai-session.js +147 -85
- package/dist/lib/constants.d.ts +13 -0
- package/dist/lib/constants.js +12 -0
- package/dist/lib/email.d.ts +11 -0
- package/dist/lib/email.js +193 -0
- package/dist/lib/env.d.ts +13 -0
- package/dist/lib/env.js +16 -0
- package/dist/lib/inference.d.ts +4 -1
- package/dist/lib/inference.js +3 -3
- package/dist/lib/logger.d.ts +4 -4
- package/dist/lib/logger.js +30 -8
- package/dist/lib/notification-service.d.ts +152 -0
- package/dist/lib/notification-service.js +473 -0
- package/dist/lib/notification-service.test.d.ts +1 -0
- package/dist/lib/notification-service.test.js +87 -0
- package/dist/lib/prisma.d.ts +2 -1
- package/dist/lib/prisma.js +5 -1
- package/dist/lib/pusher.d.ts +23 -0
- package/dist/lib/pusher.js +69 -5
- package/dist/lib/retry.d.ts +15 -0
- package/dist/lib/retry.js +37 -0
- package/dist/lib/storage.js +2 -2
- package/dist/lib/stripe.d.ts +9 -0
- package/dist/lib/stripe.js +36 -0
- package/dist/lib/subscription_service.d.ts +37 -0
- package/dist/lib/subscription_service.js +654 -0
- package/dist/lib/usage_service.d.ts +26 -0
- package/dist/lib/usage_service.js +59 -0
- package/dist/lib/worksheet-generation.d.ts +91 -0
- package/dist/lib/worksheet-generation.js +95 -0
- package/dist/lib/worksheet-generation.test.d.ts +1 -0
- package/dist/lib/worksheet-generation.test.js +20 -0
- package/dist/lib/workspace-access.d.ts +18 -0
- package/dist/lib/workspace-access.js +13 -0
- package/dist/routers/_app.d.ts +1349 -253
- package/dist/routers/_app.js +10 -0
- package/dist/routers/admin.d.ts +361 -0
- package/dist/routers/admin.js +633 -0
- package/dist/routers/annotations.d.ts +219 -0
- package/dist/routers/annotations.js +187 -0
- package/dist/routers/auth.d.ts +88 -7
- package/dist/routers/auth.js +339 -19
- package/dist/routers/chat.d.ts +6 -12
- package/dist/routers/copilot.d.ts +199 -0
- package/dist/routers/copilot.js +571 -0
- package/dist/routers/flashcards.d.ts +47 -81
- package/dist/routers/flashcards.js +143 -27
- package/dist/routers/members.d.ts +36 -7
- package/dist/routers/members.js +200 -19
- package/dist/routers/notifications.d.ts +99 -0
- package/dist/routers/notifications.js +127 -0
- package/dist/routers/payment.d.ts +89 -0
- package/dist/routers/payment.js +403 -0
- package/dist/routers/podcast.d.ts +8 -13
- package/dist/routers/podcast.js +54 -31
- package/dist/routers/studyguide.d.ts +1 -29
- package/dist/routers/studyguide.js +80 -71
- package/dist/routers/worksheets.d.ts +105 -38
- package/dist/routers/worksheets.js +258 -68
- package/dist/routers/workspace.d.ts +139 -60
- package/dist/routers/workspace.js +455 -315
- package/dist/scripts/purge-deleted-users.d.ts +1 -0
- package/dist/scripts/purge-deleted-users.js +149 -0
- package/dist/server.js +130 -10
- package/dist/services/flashcard-progress.service.d.ts +18 -66
- package/dist/services/flashcard-progress.service.js +51 -42
- package/dist/trpc.d.ts +20 -21
- package/dist/trpc.js +150 -1
- package/package.json +1 -1
package/dist/routers/podcast.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { TRPCError } from '@trpc/server';
|
|
3
|
-
import { router, authedProcedure } from '../trpc.js';
|
|
3
|
+
import { router, authedProcedure, limitedProcedure } from '../trpc.js';
|
|
4
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
5
|
import inference from '../lib/inference.js';
|
|
6
6
|
import { generateSignedUrl, deleteFromSupabase } from '../lib/storage.js';
|
|
7
7
|
import PusherService from '../lib/pusher.js';
|
|
8
8
|
import { aiSessionService } from '../lib/ai-session.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
FLASHCARD_SET: 'FLASHCARD_SET',
|
|
14
|
-
};
|
|
9
|
+
import { ArtifactType } from '../lib/constants.js';
|
|
10
|
+
import { workspaceAccessFilter } from '../lib/workspace-access.js';
|
|
11
|
+
import { logger } from '../lib/logger.js';
|
|
12
|
+
import { notifyArtifactFailed, notifyArtifactReady } from '../lib/notification-service.js';
|
|
15
13
|
// Podcast segment schema
|
|
16
14
|
const podcastSegmentSchema = z.object({
|
|
17
15
|
id: z.string(),
|
|
@@ -85,9 +83,9 @@ export const podcast = router({
|
|
|
85
83
|
},
|
|
86
84
|
orderBy: { updatedAt: 'desc' },
|
|
87
85
|
});
|
|
88
|
-
|
|
86
|
+
logger.debug(`Found ${artifacts.length} podcast artifacts`);
|
|
89
87
|
artifacts.forEach((artifact, i) => {
|
|
90
|
-
|
|
88
|
+
logger.debug(` Podcast ${i + 1}: "${artifact.title}" - ${artifact.podcastSegments.length} segments`);
|
|
91
89
|
});
|
|
92
90
|
// Transform to include segments with fresh signed URLs
|
|
93
91
|
const episodesWithUrls = await Promise.all(artifacts.map(async (artifact) => {
|
|
@@ -112,7 +110,7 @@ export const podcast = router({
|
|
|
112
110
|
};
|
|
113
111
|
}
|
|
114
112
|
catch (error) {
|
|
115
|
-
|
|
113
|
+
logger.error(`Failed to generate signed URL for segment ${segment.id}:`, error);
|
|
116
114
|
return {
|
|
117
115
|
id: segment.id,
|
|
118
116
|
title: segment.title,
|
|
@@ -138,11 +136,11 @@ export const podcast = router({
|
|
|
138
136
|
let metadata = null;
|
|
139
137
|
if (latestVersion) {
|
|
140
138
|
try {
|
|
141
|
-
|
|
139
|
+
logger.debug(JSON.stringify(latestVersion.data));
|
|
142
140
|
metadata = podcastMetadataSchema.parse(latestVersion.data);
|
|
143
141
|
}
|
|
144
142
|
catch (error) {
|
|
145
|
-
|
|
143
|
+
logger.error('Failed to parse podcast metadata:', error);
|
|
146
144
|
}
|
|
147
145
|
}
|
|
148
146
|
return {
|
|
@@ -172,7 +170,7 @@ export const podcast = router({
|
|
|
172
170
|
where: {
|
|
173
171
|
id: input.episodeId,
|
|
174
172
|
type: ArtifactType.PODCAST_EPISODE,
|
|
175
|
-
workspace:
|
|
173
|
+
workspace: workspaceAccessFilter(ctx.session.user.id)
|
|
176
174
|
},
|
|
177
175
|
include: {
|
|
178
176
|
versions: {
|
|
@@ -184,18 +182,18 @@ export const podcast = router({
|
|
|
184
182
|
},
|
|
185
183
|
},
|
|
186
184
|
});
|
|
187
|
-
|
|
185
|
+
logger.debug(JSON.stringify(episode));
|
|
188
186
|
if (!episode)
|
|
189
187
|
throw new TRPCError({ code: 'NOT_FOUND' });
|
|
190
188
|
const latestVersion = episode.versions[0];
|
|
191
189
|
if (!latestVersion)
|
|
192
190
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'No version found' });
|
|
193
|
-
|
|
191
|
+
logger.debug(JSON.stringify(latestVersion));
|
|
194
192
|
try {
|
|
195
193
|
const metadata = podcastMetadataSchema.parse(latestVersion.data);
|
|
196
194
|
}
|
|
197
195
|
catch (error) {
|
|
198
|
-
|
|
196
|
+
logger.error('Failed to parse podcast metadata:', error);
|
|
199
197
|
}
|
|
200
198
|
const metadata = podcastMetadataSchema.parse(latestVersion.data);
|
|
201
199
|
const imageUrl = episode.imageObjectKey ? await generateSignedUrl(episode.imageObjectKey, 24) : null;
|
|
@@ -217,7 +215,7 @@ export const podcast = router({
|
|
|
217
215
|
};
|
|
218
216
|
}
|
|
219
217
|
catch (error) {
|
|
220
|
-
|
|
218
|
+
logger.error(`Failed to generate signed URL for segment ${segment.id}:`, error);
|
|
221
219
|
return {
|
|
222
220
|
id: segment.id,
|
|
223
221
|
title: segment.title,
|
|
@@ -256,7 +254,7 @@ export const podcast = router({
|
|
|
256
254
|
};
|
|
257
255
|
}),
|
|
258
256
|
// Generate podcast episode from text input
|
|
259
|
-
generateEpisode:
|
|
257
|
+
generateEpisode: limitedProcedure
|
|
260
258
|
.input(z.object({
|
|
261
259
|
workspaceId: z.string(),
|
|
262
260
|
podcastData: podcastInputSchema,
|
|
@@ -378,7 +376,7 @@ export const podcast = router({
|
|
|
378
376
|
}
|
|
379
377
|
catch (audioError) {
|
|
380
378
|
const errorMessage = audioError instanceof Error ? audioError.message : 'Unknown error';
|
|
381
|
-
|
|
379
|
+
logger.error(`❌ Error generating audio for segment ${i + 1}:`, {
|
|
382
380
|
title: segment.title,
|
|
383
381
|
error: errorMessage,
|
|
384
382
|
stack: audioError instanceof Error ? audioError.stack : undefined,
|
|
@@ -401,9 +399,17 @@ export const podcast = router({
|
|
|
401
399
|
}
|
|
402
400
|
// Check if any segments were successfully generated
|
|
403
401
|
if (segments.length === 0) {
|
|
404
|
-
|
|
402
|
+
logger.error('No segments were successfully generated');
|
|
405
403
|
await PusherService.emitError(input.workspaceId, `Failed to generate any segments. ${failedSegments.length} segment(s) failed.`, 'podcast');
|
|
406
404
|
// Cleanup the artifact
|
|
405
|
+
await notifyArtifactFailed(ctx.db, {
|
|
406
|
+
userId: ctx.session.user.id,
|
|
407
|
+
workspaceId: input.workspaceId,
|
|
408
|
+
artifactType: ArtifactType.PODCAST_EPISODE,
|
|
409
|
+
artifactId: newArtifact.id,
|
|
410
|
+
title: input.podcastData.title,
|
|
411
|
+
message: `Failed to generate any audio segments. All ${failedSegments.length} attempts failed.`,
|
|
412
|
+
}).catch(() => { });
|
|
407
413
|
await ctx.db.artifact.delete({
|
|
408
414
|
where: { id: newArtifact.id },
|
|
409
415
|
});
|
|
@@ -447,7 +453,7 @@ export const podcast = router({
|
|
|
447
453
|
|
|
448
454
|
Podcast Title: ${structure.episodeTitle}
|
|
449
455
|
Segments: ${JSON.stringify(segments.map(s => ({ title: s.title, keyPoints: s.keyPoints })))}`;
|
|
450
|
-
const summaryResponse = await inference(summaryPrompt);
|
|
456
|
+
const summaryResponse = await inference([{ role: "user", content: summaryPrompt }]);
|
|
451
457
|
const summaryContent = summaryResponse.choices[0].message.content || '';
|
|
452
458
|
let episodeSummary;
|
|
453
459
|
try {
|
|
@@ -459,7 +465,7 @@ export const podcast = router({
|
|
|
459
465
|
episodeSummary = JSON.parse(jsonMatch[0]);
|
|
460
466
|
}
|
|
461
467
|
catch (parseError) {
|
|
462
|
-
|
|
468
|
+
logger.error('Failed to parse summary response:', summaryContent);
|
|
463
469
|
await PusherService.emitTaskComplete(input.workspaceId, 'podcast_summary_error', {
|
|
464
470
|
error: 'Failed to parse summary response'
|
|
465
471
|
});
|
|
@@ -533,7 +539,14 @@ export const podcast = router({
|
|
|
533
539
|
},
|
|
534
540
|
});
|
|
535
541
|
// Emit podcast generation completion notification
|
|
536
|
-
await PusherService.emitPodcastComplete(input.workspaceId,
|
|
542
|
+
await PusherService.emitPodcastComplete(input.workspaceId, newArtifact);
|
|
543
|
+
await notifyArtifactReady(ctx.db, {
|
|
544
|
+
userId: ctx.session.user.id,
|
|
545
|
+
workspaceId: input.workspaceId,
|
|
546
|
+
artifactId: newArtifact.id,
|
|
547
|
+
artifactType: ArtifactType.PODCAST_EPISODE,
|
|
548
|
+
title: metadata.title,
|
|
549
|
+
}).catch(() => { });
|
|
537
550
|
return {
|
|
538
551
|
id: newArtifact.id,
|
|
539
552
|
title: metadata.title,
|
|
@@ -543,7 +556,17 @@ export const podcast = router({
|
|
|
543
556
|
};
|
|
544
557
|
}
|
|
545
558
|
catch (error) {
|
|
546
|
-
|
|
559
|
+
logger.error('Error generating podcast episode:', error);
|
|
560
|
+
await notifyArtifactFailed(ctx.db, {
|
|
561
|
+
userId: ctx.session.user.id,
|
|
562
|
+
workspaceId: input.workspaceId,
|
|
563
|
+
artifactType: ArtifactType.PODCAST_EPISODE,
|
|
564
|
+
artifactId: newArtifact.id,
|
|
565
|
+
title: input.podcastData.title,
|
|
566
|
+
message: error instanceof Error
|
|
567
|
+
? error.message
|
|
568
|
+
: 'Podcast generation failed.',
|
|
569
|
+
}).catch(() => { });
|
|
547
570
|
await ctx.db.artifact.delete({
|
|
548
571
|
where: {
|
|
549
572
|
id: newArtifact.id,
|
|
@@ -570,7 +593,7 @@ export const podcast = router({
|
|
|
570
593
|
where: {
|
|
571
594
|
id: input.episodeId,
|
|
572
595
|
type: ArtifactType.PODCAST_EPISODE,
|
|
573
|
-
workspace:
|
|
596
|
+
workspace: workspaceAccessFilter(ctx.session.user.id)
|
|
574
597
|
},
|
|
575
598
|
include: {
|
|
576
599
|
versions: {
|
|
@@ -618,7 +641,7 @@ export const podcast = router({
|
|
|
618
641
|
where: {
|
|
619
642
|
id: input.episodeId,
|
|
620
643
|
type: ArtifactType.PODCAST_EPISODE,
|
|
621
|
-
workspace:
|
|
644
|
+
workspace: workspaceAccessFilter(ctx.session.user.id)
|
|
622
645
|
},
|
|
623
646
|
include: {
|
|
624
647
|
versions: {
|
|
@@ -667,7 +690,7 @@ export const podcast = router({
|
|
|
667
690
|
where: {
|
|
668
691
|
id: input.episodeId,
|
|
669
692
|
type: ArtifactType.PODCAST_EPISODE,
|
|
670
|
-
workspace:
|
|
693
|
+
workspace: workspaceAccessFilter(ctx.session.user.id)
|
|
671
694
|
},
|
|
672
695
|
include: {
|
|
673
696
|
versions: {
|
|
@@ -695,7 +718,7 @@ export const podcast = router({
|
|
|
695
718
|
await deleteFromSupabase(segment.objectKey);
|
|
696
719
|
}
|
|
697
720
|
catch (error) {
|
|
698
|
-
|
|
721
|
+
logger.error(`Failed to delete audio file ${segment.objectKey}:`, error);
|
|
699
722
|
}
|
|
700
723
|
}
|
|
701
724
|
}
|
|
@@ -719,7 +742,7 @@ export const podcast = router({
|
|
|
719
742
|
return true;
|
|
720
743
|
}
|
|
721
744
|
catch (error) {
|
|
722
|
-
|
|
745
|
+
logger.error('Error deleting episode:', error);
|
|
723
746
|
await PusherService.emitError(episode.workspaceId, `Failed to delete episode: ${error instanceof Error ? error.message : 'Unknown error'}`, 'podcast');
|
|
724
747
|
throw new TRPCError({
|
|
725
748
|
code: 'INTERNAL_SERVER_ERROR',
|
|
@@ -735,7 +758,7 @@ export const podcast = router({
|
|
|
735
758
|
where: {
|
|
736
759
|
id: input.segmentId,
|
|
737
760
|
artifact: {
|
|
738
|
-
workspace:
|
|
761
|
+
workspace: workspaceAccessFilter(ctx.session.user.id)
|
|
739
762
|
}
|
|
740
763
|
},
|
|
741
764
|
include: {
|
|
@@ -751,7 +774,7 @@ export const podcast = router({
|
|
|
751
774
|
audioUrl = await generateSignedUrl(segment.objectKey, 24); // 24 hours
|
|
752
775
|
}
|
|
753
776
|
catch (error) {
|
|
754
|
-
|
|
777
|
+
logger.error(`Failed to generate signed URL for segment ${segment.id}:`, error);
|
|
755
778
|
}
|
|
756
779
|
}
|
|
757
780
|
return {
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
export declare const studyguide: import("@trpc/server").TRPCBuiltRouter<{
|
|
2
|
-
ctx:
|
|
3
|
-
db: import("@prisma/client").PrismaClient<import("@prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
|
|
4
|
-
session: any;
|
|
5
|
-
req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
6
|
-
res: import("express").Response<any, Record<string, any>>;
|
|
7
|
-
cookies: Record<string, string | undefined>;
|
|
8
|
-
};
|
|
2
|
+
ctx: import("../context.js").Context;
|
|
9
3
|
meta: object;
|
|
10
4
|
errorShape: {
|
|
11
5
|
data: {
|
|
@@ -39,26 +33,4 @@ export declare const studyguide: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
39
33
|
};
|
|
40
34
|
meta: object;
|
|
41
35
|
}>;
|
|
42
|
-
edit: import("@trpc/server").TRPCMutationProcedure<{
|
|
43
|
-
input: {
|
|
44
|
-
workspaceId: string;
|
|
45
|
-
content: string;
|
|
46
|
-
studyGuideId?: string | undefined;
|
|
47
|
-
data?: Record<string, unknown> | undefined;
|
|
48
|
-
title?: string | undefined;
|
|
49
|
-
};
|
|
50
|
-
output: {
|
|
51
|
-
artifactId: string;
|
|
52
|
-
version: {
|
|
53
|
-
id: string;
|
|
54
|
-
createdAt: Date;
|
|
55
|
-
createdById: string | null;
|
|
56
|
-
artifactId: string;
|
|
57
|
-
content: string;
|
|
58
|
-
data: import("@prisma/client/runtime/library").JsonValue | null;
|
|
59
|
-
version: number;
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
meta: object;
|
|
63
|
-
}>;
|
|
64
36
|
}>>;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { TRPCError } from '@trpc/server';
|
|
2
3
|
import { router, authedProcedure } from '../trpc.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
4
|
+
import { ArtifactType } from '../lib/constants.js';
|
|
5
|
+
import { getUserUsage, getUserPlanLimits } from '../lib/usage_service.js';
|
|
6
|
+
import { workspaceAccessFilter } from '../lib/workspace-access.js';
|
|
7
7
|
const initializeEditorJsEmptyBlock = () => ({
|
|
8
8
|
time: Date.now(),
|
|
9
9
|
blocks: [
|
|
10
10
|
{
|
|
11
11
|
id: 'initial',
|
|
12
12
|
type: 'paragraph',
|
|
13
|
-
data: { text: '
|
|
13
|
+
data: { text: 'Upload some files to begin creating your revision workspace...' },
|
|
14
14
|
},
|
|
15
15
|
],
|
|
16
16
|
version: '2.27.0',
|
|
@@ -27,14 +27,23 @@ export const studyguide = router({
|
|
|
27
27
|
where: {
|
|
28
28
|
workspaceId: input.workspaceId,
|
|
29
29
|
type: ArtifactType.STUDY_GUIDE,
|
|
30
|
-
workspace:
|
|
30
|
+
workspace: workspaceAccessFilter(ctx.session.user.id),
|
|
31
31
|
},
|
|
32
32
|
include: {
|
|
33
33
|
versions: { orderBy: { version: 'desc' }, take: 1 },
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
|
-
console.log('artifact', artifact);
|
|
37
36
|
if (!artifact) {
|
|
37
|
+
const [usage, limits] = await Promise.all([
|
|
38
|
+
getUserUsage(ctx.session.user.id),
|
|
39
|
+
getUserPlanLimits(ctx.session.user.id)
|
|
40
|
+
]);
|
|
41
|
+
if (limits && usage.studyGuides >= limits.maxStudyGuides) {
|
|
42
|
+
throw new TRPCError({
|
|
43
|
+
code: 'FORBIDDEN',
|
|
44
|
+
message: 'Study Guide limit reached. Please upgrade your plan to create more workspaces with study guides.'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
38
47
|
artifact = await ctx.db.artifact.create({
|
|
39
48
|
data: {
|
|
40
49
|
workspaceId: input.workspaceId,
|
|
@@ -58,68 +67,68 @@ export const studyguide = router({
|
|
|
58
67
|
return { artifactId: artifact.id, title: artifact.title, latestVersion };
|
|
59
68
|
}),
|
|
60
69
|
// Edit study guide content by creating a new version, or create if doesn't exist
|
|
61
|
-
edit: authedProcedure
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}),
|
|
70
|
+
// edit: authedProcedure
|
|
71
|
+
// .input(
|
|
72
|
+
// z.object({
|
|
73
|
+
// workspaceId: z.string(),
|
|
74
|
+
// studyGuideId: z.string().optional(),
|
|
75
|
+
// content: z.string().min(1),
|
|
76
|
+
// data: z.record(z.string(), z.unknown()).optional(),
|
|
77
|
+
// title: z.string().min(1).optional(),
|
|
78
|
+
// })
|
|
79
|
+
// )
|
|
80
|
+
// .mutation(async ({ ctx, input }) => {
|
|
81
|
+
// let artifact;
|
|
82
|
+
// if (input.studyGuideId) {
|
|
83
|
+
// // Try to find existing study guide
|
|
84
|
+
// artifact = await ctx.db.artifact.findFirst({
|
|
85
|
+
// where: {
|
|
86
|
+
// id: input.studyGuideId,
|
|
87
|
+
// type: ArtifactType.STUDY_GUIDE,
|
|
88
|
+
// workspace: workspaceAccessFilter(ctx.session.user.id),
|
|
89
|
+
// },
|
|
90
|
+
// });
|
|
91
|
+
// } else {
|
|
92
|
+
// // Find by workspace if no specific studyGuideId provided
|
|
93
|
+
// artifact = await ctx.db.artifact.findFirst({
|
|
94
|
+
// where: {
|
|
95
|
+
// workspaceId: input.workspaceId,
|
|
96
|
+
// type: ArtifactType.STUDY_GUIDE,
|
|
97
|
+
// workspace: workspaceAccessFilter(ctx.session.user.id),
|
|
98
|
+
// },
|
|
99
|
+
// });
|
|
100
|
+
// }
|
|
101
|
+
// // If no study guide found, create a new one
|
|
102
|
+
// if (!artifact) {
|
|
103
|
+
// artifact = await ctx.db.artifact.create({
|
|
104
|
+
// data: {
|
|
105
|
+
// workspaceId: input.workspaceId,
|
|
106
|
+
// type: ArtifactType.STUDY_GUIDE,
|
|
107
|
+
// title: 'Study Guide',
|
|
108
|
+
// createdById: ctx.session.user.id,
|
|
109
|
+
// },
|
|
110
|
+
// });
|
|
111
|
+
// }
|
|
112
|
+
// const last = await ctx.db.artifactVersion.findFirst({
|
|
113
|
+
// where: { artifactId: artifact.id },
|
|
114
|
+
// orderBy: { version: 'desc' },
|
|
115
|
+
// });
|
|
116
|
+
// if (input.title && input.title !== artifact.title) {
|
|
117
|
+
// await ctx.db.artifact.update({
|
|
118
|
+
// where: { id: artifact.id },
|
|
119
|
+
// data: { title: input.title },
|
|
120
|
+
// });
|
|
121
|
+
// }
|
|
122
|
+
// const nextVersion = (last?.version ?? 0) + 1;
|
|
123
|
+
// const version = await ctx.db.artifactVersion.create({
|
|
124
|
+
// data: {
|
|
125
|
+
// artifactId: artifact.id,
|
|
126
|
+
// content: input.content,
|
|
127
|
+
// data: input.data ?? undefined,
|
|
128
|
+
// version: nextVersion,
|
|
129
|
+
// createdById: ctx.session.user.id,
|
|
130
|
+
// },
|
|
131
|
+
// });
|
|
132
|
+
// return { artifactId: artifact.id, version };
|
|
133
|
+
// }),
|
|
125
134
|
});
|