@goscribe/server 1.0.11 → 1.1.0
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/ANALYSIS_PROGRESS_SPEC.md +463 -0
- package/PROGRESS_QUICK_REFERENCE.md +239 -0
- package/dist/lib/ai-session.d.ts +20 -9
- package/dist/lib/ai-session.js +316 -80
- package/dist/lib/auth.d.ts +35 -2
- package/dist/lib/auth.js +88 -15
- package/dist/lib/env.d.ts +32 -0
- package/dist/lib/env.js +46 -0
- package/dist/lib/errors.d.ts +33 -0
- package/dist/lib/errors.js +78 -0
- package/dist/lib/inference.d.ts +4 -1
- package/dist/lib/inference.js +9 -11
- package/dist/lib/logger.d.ts +62 -0
- package/dist/lib/logger.js +342 -0
- package/dist/lib/podcast-prompts.d.ts +43 -0
- package/dist/lib/podcast-prompts.js +135 -0
- package/dist/lib/pusher.d.ts +1 -0
- package/dist/lib/pusher.js +14 -2
- package/dist/lib/storage.d.ts +3 -3
- package/dist/lib/storage.js +51 -47
- package/dist/lib/validation.d.ts +51 -0
- package/dist/lib/validation.js +64 -0
- package/dist/routers/_app.d.ts +697 -111
- package/dist/routers/_app.js +5 -0
- package/dist/routers/auth.d.ts +11 -1
- package/dist/routers/chat.d.ts +11 -1
- package/dist/routers/flashcards.d.ts +205 -6
- package/dist/routers/flashcards.js +144 -66
- package/dist/routers/members.d.ts +165 -0
- package/dist/routers/members.js +531 -0
- package/dist/routers/podcast.d.ts +78 -63
- package/dist/routers/podcast.js +330 -393
- package/dist/routers/studyguide.d.ts +11 -1
- package/dist/routers/worksheets.d.ts +124 -13
- package/dist/routers/worksheets.js +123 -50
- package/dist/routers/workspace.d.ts +213 -26
- package/dist/routers/workspace.js +303 -181
- package/dist/server.js +12 -4
- package/dist/services/flashcard-progress.service.d.ts +183 -0
- package/dist/services/flashcard-progress.service.js +383 -0
- package/dist/services/flashcard.service.d.ts +183 -0
- package/dist/services/flashcard.service.js +224 -0
- package/dist/services/podcast-segment-reorder.d.ts +0 -0
- package/dist/services/podcast-segment-reorder.js +107 -0
- package/dist/services/podcast.service.d.ts +0 -0
- package/dist/services/podcast.service.js +326 -0
- package/dist/services/worksheet.service.d.ts +0 -0
- package/dist/services/worksheet.service.js +295 -0
- package/dist/trpc.d.ts +13 -2
- package/dist/trpc.js +55 -6
- package/dist/types/index.d.ts +126 -0
- package/dist/types/index.js +1 -0
- package/package.json +3 -2
- package/prisma/schema.prisma +142 -4
- package/src/lib/ai-session.ts +356 -85
- package/src/lib/auth.ts +113 -19
- package/src/lib/env.ts +59 -0
- package/src/lib/errors.ts +92 -0
- package/src/lib/inference.ts +11 -11
- package/src/lib/logger.ts +405 -0
- package/src/lib/pusher.ts +15 -3
- package/src/lib/storage.ts +56 -51
- package/src/lib/validation.ts +75 -0
- package/src/routers/_app.ts +5 -0
- package/src/routers/chat.ts +2 -23
- package/src/routers/flashcards.ts +108 -24
- package/src/routers/members.ts +586 -0
- package/src/routers/podcast.ts +385 -420
- package/src/routers/worksheets.ts +117 -35
- package/src/routers/workspace.ts +328 -195
- package/src/server.ts +13 -4
- package/src/services/flashcard-progress.service.ts +541 -0
- package/src/trpc.ts +59 -6
- package/src/types/index.ts +165 -0
- package/AUTH_FRONTEND_SPEC.md +0 -21
- package/CHAT_FRONTEND_SPEC.md +0 -474
- package/DATABASE_SETUP.md +0 -165
- package/MEETINGSUMMARY_FRONTEND_SPEC.md +0 -28
- package/PODCAST_FRONTEND_SPEC.md +0 -595
- package/STUDYGUIDE_FRONTEND_SPEC.md +0 -18
- package/WORKSHEETS_FRONTEND_SPEC.md +0 -26
- package/WORKSPACE_FRONTEND_SPEC.md +0 -47
- package/test-ai-integration.js +0 -134
package/dist/trpc.js
CHANGED
|
@@ -1,25 +1,74 @@
|
|
|
1
1
|
import { initTRPC, TRPCError } from "@trpc/server";
|
|
2
2
|
import superjson from "superjson";
|
|
3
|
+
import { logger } from "./lib/logger.js";
|
|
4
|
+
import { toTRPCError } from "./lib/errors.js";
|
|
3
5
|
const t = initTRPC.context().create({
|
|
4
6
|
transformer: superjson,
|
|
5
|
-
errorFormatter({ shape }) {
|
|
6
|
-
|
|
7
|
+
errorFormatter({ shape, error }) {
|
|
8
|
+
// Log errors in development
|
|
9
|
+
if (process.env.NODE_ENV === 'development') {
|
|
10
|
+
logger.error('TRPC Error', 'TRPC', {
|
|
11
|
+
code: error.code,
|
|
12
|
+
message: error.message,
|
|
13
|
+
cause: error.cause,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
...shape,
|
|
18
|
+
data: {
|
|
19
|
+
...shape.data,
|
|
20
|
+
zodError: error.cause instanceof Error ? error.cause.message : null,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
7
23
|
},
|
|
8
24
|
});
|
|
9
25
|
export const router = t.router;
|
|
10
26
|
export const middleware = t.middleware;
|
|
11
27
|
export const publicProcedure = t.procedure;
|
|
12
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Logging middleware
|
|
30
|
+
*/
|
|
31
|
+
const loggingMiddleware = middleware(async ({ ctx, next, path, type }) => {
|
|
32
|
+
const start = Date.now();
|
|
33
|
+
const result = await next();
|
|
34
|
+
const duration = Date.now() - start;
|
|
35
|
+
logger.info(`TRPC ${type} ${path}`, 'TRPC', {
|
|
36
|
+
duration: `${duration}ms`,
|
|
37
|
+
userId: ctx.session?.user?.id,
|
|
38
|
+
});
|
|
39
|
+
return result;
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Middleware that enforces authentication
|
|
43
|
+
*/
|
|
13
44
|
const isAuthed = middleware(({ ctx, next }) => {
|
|
14
45
|
const hasUser = Boolean(ctx.session?.user?.id);
|
|
15
46
|
if (!ctx.session || !hasUser) {
|
|
16
|
-
throw new TRPCError({
|
|
47
|
+
throw new TRPCError({
|
|
48
|
+
code: "UNAUTHORIZED",
|
|
49
|
+
message: "You must be logged in to access this resource"
|
|
50
|
+
});
|
|
17
51
|
}
|
|
18
52
|
return next({
|
|
19
53
|
ctx: {
|
|
20
54
|
session: ctx.session,
|
|
55
|
+
userId: ctx.session.user.id,
|
|
21
56
|
},
|
|
22
57
|
});
|
|
23
58
|
});
|
|
24
|
-
/**
|
|
25
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Error handling middleware
|
|
61
|
+
*/
|
|
62
|
+
const errorHandler = middleware(async ({ next }) => {
|
|
63
|
+
try {
|
|
64
|
+
return await next();
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw toTRPCError(error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
/** Exported procedures with middleware */
|
|
71
|
+
export const authedProcedure = publicProcedure
|
|
72
|
+
.use(loggingMiddleware)
|
|
73
|
+
.use(errorHandler)
|
|
74
|
+
.use(isAuthed);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Prisma } from '@prisma/client';
|
|
2
|
+
/**
|
|
3
|
+
* Common types for the application
|
|
4
|
+
*/
|
|
5
|
+
export type PrismaTransaction = Omit<Prisma.TransactionClient, '$connect' | '$disconnect' | '$on' | '$transaction' | '$use'>;
|
|
6
|
+
/**
|
|
7
|
+
* User types
|
|
8
|
+
*/
|
|
9
|
+
export interface UserSession {
|
|
10
|
+
id: string;
|
|
11
|
+
name?: string | null;
|
|
12
|
+
email?: string | null;
|
|
13
|
+
image?: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface AuthContext {
|
|
16
|
+
session: {
|
|
17
|
+
user: UserSession;
|
|
18
|
+
} | null;
|
|
19
|
+
userId?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Artifact types
|
|
23
|
+
*/
|
|
24
|
+
export type ArtifactTypeEnum = 'STUDY_GUIDE' | 'FLASHCARD_SET' | 'WORKSHEET' | 'MEETING_SUMMARY' | 'PODCAST_EPISODE';
|
|
25
|
+
export type DifficultyEnum = 'EASY' | 'MEDIUM' | 'HARD';
|
|
26
|
+
export type QuestionTypeEnum = 'MULTIPLE_CHOICE' | 'TEXT' | 'NUMERIC' | 'TRUE_FALSE' | 'MATCHING' | 'FILL_IN_THE_BLANK';
|
|
27
|
+
/**
|
|
28
|
+
* Worksheet types
|
|
29
|
+
*/
|
|
30
|
+
export interface MarkSchemePoint {
|
|
31
|
+
point: number;
|
|
32
|
+
requirements: number;
|
|
33
|
+
}
|
|
34
|
+
export interface MarkScheme {
|
|
35
|
+
points: MarkSchemePoint[];
|
|
36
|
+
totalPoints: number;
|
|
37
|
+
}
|
|
38
|
+
export interface UserMarkSchemePoint extends MarkSchemePoint {
|
|
39
|
+
achievedPoints: number;
|
|
40
|
+
feedback: string;
|
|
41
|
+
}
|
|
42
|
+
export interface UserMarkScheme {
|
|
43
|
+
points: UserMarkSchemePoint[];
|
|
44
|
+
totalPoints: number;
|
|
45
|
+
}
|
|
46
|
+
export interface WorksheetQuestionMeta {
|
|
47
|
+
options?: string[];
|
|
48
|
+
completed?: boolean;
|
|
49
|
+
userAnswer?: string;
|
|
50
|
+
completedAt?: Date | null;
|
|
51
|
+
mark_scheme?: MarkScheme;
|
|
52
|
+
userMarkScheme?: UserMarkScheme;
|
|
53
|
+
}
|
|
54
|
+
export interface WorksheetQuestionProgressMeta {
|
|
55
|
+
userMarkScheme?: UserMarkScheme;
|
|
56
|
+
}
|
|
57
|
+
export interface CreateWorksheetQuestionInput {
|
|
58
|
+
prompt: string;
|
|
59
|
+
answer?: string;
|
|
60
|
+
type: QuestionTypeEnum;
|
|
61
|
+
difficulty?: DifficultyEnum;
|
|
62
|
+
order?: number;
|
|
63
|
+
meta?: WorksheetQuestionMeta;
|
|
64
|
+
}
|
|
65
|
+
export interface UpdateWorksheetQuestionInput extends Partial<CreateWorksheetQuestionInput> {
|
|
66
|
+
id: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Flashcard types
|
|
70
|
+
*/
|
|
71
|
+
export interface CreateFlashcardInput {
|
|
72
|
+
front: string;
|
|
73
|
+
back: string;
|
|
74
|
+
tags?: string[];
|
|
75
|
+
order?: number;
|
|
76
|
+
}
|
|
77
|
+
export interface UpdateFlashcardInput extends Partial<CreateFlashcardInput> {
|
|
78
|
+
id: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* File upload types
|
|
82
|
+
*/
|
|
83
|
+
export interface FileUploadResult {
|
|
84
|
+
id: string;
|
|
85
|
+
name: string;
|
|
86
|
+
url: string;
|
|
87
|
+
mimeType: string;
|
|
88
|
+
size: number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Pagination types
|
|
92
|
+
*/
|
|
93
|
+
export interface PaginationInput {
|
|
94
|
+
page?: number;
|
|
95
|
+
limit?: number;
|
|
96
|
+
}
|
|
97
|
+
export interface PaginatedResult<T> {
|
|
98
|
+
data: T[];
|
|
99
|
+
pagination: {
|
|
100
|
+
page: number;
|
|
101
|
+
limit: number;
|
|
102
|
+
total: number;
|
|
103
|
+
totalPages: number;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Search types
|
|
108
|
+
*/
|
|
109
|
+
export interface SearchInput extends PaginationInput {
|
|
110
|
+
query: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Response types
|
|
114
|
+
*/
|
|
115
|
+
export type SuccessResponse<T = void> = {
|
|
116
|
+
success: true;
|
|
117
|
+
data: T;
|
|
118
|
+
message?: string;
|
|
119
|
+
};
|
|
120
|
+
export type ErrorResponse = {
|
|
121
|
+
success: false;
|
|
122
|
+
error: {
|
|
123
|
+
code: string;
|
|
124
|
+
message: string;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goscribe/server",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@auth/express": "^0.11.0",
|
|
24
24
|
"@auth/prisma-adapter": "^2.10.0",
|
|
25
|
-
"@google-cloud/storage": "^7.17.0",
|
|
26
25
|
"@goscribe/server": "^1.0.8",
|
|
27
26
|
"@prisma/client": "^6.14.0",
|
|
27
|
+
"@supabase/supabase-js": "^2.76.1",
|
|
28
28
|
"@trpc/server": "^11.5.0",
|
|
29
29
|
"@types/uuid": "^10.0.0",
|
|
30
30
|
"@vingeray/editorjs-markdown-converter": "^0.1.2",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"express": "^5.1.0",
|
|
37
37
|
"helmet": "^8.1.0",
|
|
38
38
|
"morgan": "^1.10.1",
|
|
39
|
+
"openai": "^6.3.0",
|
|
39
40
|
"prisma": "^6.14.0",
|
|
40
41
|
"pusher": "^5.2.0",
|
|
41
42
|
"pusher-js": "^8.4.0",
|
package/prisma/schema.prisma
CHANGED
|
@@ -50,11 +50,18 @@ model User {
|
|
|
50
50
|
// Ownership
|
|
51
51
|
folders Folder[] @relation("UserFolders")
|
|
52
52
|
workspaces Workspace[] @relation("UserWorkspaces")
|
|
53
|
-
invitedInWorkspaces Workspace[] @relation("WorkspaceSharedWith") // many-to-many
|
|
53
|
+
invitedInWorkspaces Workspace[] @relation("WorkspaceSharedWith") // many-to-many (deprecated)
|
|
54
|
+
workspaceMemberships WorkspaceMember[] // proper member management
|
|
54
55
|
uploads FileAsset[] @relation("UserUploads")
|
|
55
56
|
artifacts Artifact[] @relation("UserArtifacts")
|
|
56
57
|
versions ArtifactVersion[] @relation("UserArtifactVersions")
|
|
58
|
+
|
|
59
|
+
// Progress tracking
|
|
60
|
+
flashcardProgress FlashcardProgress[] @relation("UserFlashcardProgress")
|
|
57
61
|
worksheetQuestionProgress WorksheetQuestionProgress[]
|
|
62
|
+
|
|
63
|
+
// Invitations
|
|
64
|
+
sentInvitations WorkspaceInvitation[] @relation("UserInvitations")
|
|
58
65
|
|
|
59
66
|
chats Chat[]
|
|
60
67
|
createdAt DateTime @default(now())
|
|
@@ -109,6 +116,7 @@ model Workspace {
|
|
|
109
116
|
ownerId String
|
|
110
117
|
owner User @relation("UserWorkspaces", fields: [ownerId], references: [id], onDelete: Cascade)
|
|
111
118
|
icon String @default("📄")
|
|
119
|
+
color String @default("#9D00FF")
|
|
112
120
|
|
|
113
121
|
// A workspace (file) lives in a folder (nullable = root)
|
|
114
122
|
folderId String?
|
|
@@ -116,15 +124,20 @@ model Workspace {
|
|
|
116
124
|
|
|
117
125
|
channels Channel[]
|
|
118
126
|
|
|
119
|
-
|
|
127
|
+
sharedWith User[] @relation("WorkspaceSharedWith") // many-to-many for sharing (deprecated)
|
|
128
|
+
members WorkspaceMember[] // proper member management with roles
|
|
129
|
+
fileBeingAnalyzed Boolean @default(false)
|
|
120
130
|
|
|
121
|
-
|
|
131
|
+
analysisProgress Json?
|
|
122
132
|
|
|
123
133
|
// Raw uploads attached to this workspace
|
|
124
134
|
uploads FileAsset[]
|
|
125
135
|
|
|
126
136
|
// AI outputs for this workspace (study guides, flashcards, etc.)
|
|
127
137
|
artifacts Artifact[]
|
|
138
|
+
|
|
139
|
+
// Invitations
|
|
140
|
+
invitations WorkspaceInvitation[]
|
|
128
141
|
|
|
129
142
|
createdAt DateTime @default(now())
|
|
130
143
|
updatedAt DateTime @updatedAt
|
|
@@ -201,10 +214,14 @@ model Artifact {
|
|
|
201
214
|
title String
|
|
202
215
|
isArchived Boolean @default(false)
|
|
203
216
|
|
|
217
|
+
generating Boolean @default(false)
|
|
218
|
+
generatingMetadata Json?
|
|
219
|
+
|
|
204
220
|
// Worksheet-specific fields
|
|
205
221
|
difficulty Difficulty? // only meaningful for WORKSHEET
|
|
206
222
|
estimatedTime String? // only meaningful for WORKSHEET
|
|
207
223
|
|
|
224
|
+
imageObjectKey String?
|
|
208
225
|
description String?
|
|
209
226
|
|
|
210
227
|
createdById String?
|
|
@@ -213,6 +230,7 @@ model Artifact {
|
|
|
213
230
|
versions ArtifactVersion[] // text/transcript versions etc.
|
|
214
231
|
flashcards Flashcard[] // only meaningful for FLASHCARD_SET
|
|
215
232
|
questions WorksheetQuestion[] // only meaningful for WORKSHEET
|
|
233
|
+
podcastSegments PodcastSegment[] // only meaningful for PODCAST_EPISODE
|
|
216
234
|
|
|
217
235
|
createdAt DateTime @default(now())
|
|
218
236
|
updatedAt DateTime @updatedAt
|
|
@@ -256,11 +274,51 @@ model Flashcard {
|
|
|
256
274
|
tags String[] // optional keywords
|
|
257
275
|
order Int @default(0)
|
|
258
276
|
|
|
277
|
+
// User progress tracking
|
|
278
|
+
progress FlashcardProgress[]
|
|
279
|
+
|
|
259
280
|
createdAt DateTime @default(now())
|
|
260
281
|
|
|
261
282
|
@@index([artifactId])
|
|
262
283
|
}
|
|
263
284
|
|
|
285
|
+
//
|
|
286
|
+
// User Progress on Flashcards (spaced repetition, mastery tracking)
|
|
287
|
+
//
|
|
288
|
+
model FlashcardProgress {
|
|
289
|
+
id String @id @default(cuid())
|
|
290
|
+
userId String
|
|
291
|
+
user User @relation("UserFlashcardProgress", fields: [userId], references: [id], onDelete: Cascade)
|
|
292
|
+
|
|
293
|
+
flashcardId String
|
|
294
|
+
flashcard Flashcard @relation(fields: [flashcardId], references: [id], onDelete: Cascade)
|
|
295
|
+
|
|
296
|
+
// Study statistics
|
|
297
|
+
timesStudied Int @default(0)
|
|
298
|
+
timesCorrect Int @default(0)
|
|
299
|
+
timesIncorrect Int @default(0)
|
|
300
|
+
timesIncorrectConsecutive Int @default(0) // Track consecutive failures
|
|
301
|
+
|
|
302
|
+
// Spaced repetition data
|
|
303
|
+
easeFactor Float @default(2.5) // SM-2 algorithm ease factor
|
|
304
|
+
interval Int @default(0) // Days until next review
|
|
305
|
+
repetitions Int @default(0) // Consecutive correct answers
|
|
306
|
+
|
|
307
|
+
// Mastery level (0-100)
|
|
308
|
+
masteryLevel Int @default(0)
|
|
309
|
+
|
|
310
|
+
// Timestamps
|
|
311
|
+
lastStudiedAt DateTime?
|
|
312
|
+
nextReviewAt DateTime?
|
|
313
|
+
|
|
314
|
+
createdAt DateTime @default(now())
|
|
315
|
+
updatedAt DateTime @updatedAt
|
|
316
|
+
|
|
317
|
+
@@unique([userId, flashcardId])
|
|
318
|
+
@@index([userId, nextReviewAt])
|
|
319
|
+
@@index([flashcardId])
|
|
320
|
+
}
|
|
321
|
+
|
|
264
322
|
//
|
|
265
323
|
// Worksheet Questions (child items of a WORKSHEET Artifact)
|
|
266
324
|
//
|
|
@@ -294,8 +352,9 @@ model WorksheetQuestionProgress {
|
|
|
294
352
|
userId String
|
|
295
353
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
296
354
|
|
|
297
|
-
|
|
355
|
+
modified Boolean @default(false)
|
|
298
356
|
userAnswer String?
|
|
357
|
+
correct Boolean? @default(false)
|
|
299
358
|
completedAt DateTime?
|
|
300
359
|
attempts Int @default(0)
|
|
301
360
|
timeSpentSec Int?
|
|
@@ -306,4 +365,83 @@ model WorksheetQuestionProgress {
|
|
|
306
365
|
|
|
307
366
|
@@unique([worksheetQuestionId, userId])
|
|
308
367
|
@@index([userId])
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
//
|
|
371
|
+
// Workspace Members (with roles)
|
|
372
|
+
//
|
|
373
|
+
model WorkspaceMember {
|
|
374
|
+
id String @id @default(cuid())
|
|
375
|
+
workspaceId String
|
|
376
|
+
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
377
|
+
|
|
378
|
+
userId String
|
|
379
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
380
|
+
|
|
381
|
+
role String @default("member") // "owner", "admin", "member"
|
|
382
|
+
|
|
383
|
+
joinedAt DateTime @default(now())
|
|
384
|
+
updatedAt DateTime @updatedAt
|
|
385
|
+
|
|
386
|
+
@@unique([workspaceId, userId]) // One membership per user per workspace
|
|
387
|
+
@@index([workspaceId])
|
|
388
|
+
@@index([userId])
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
//
|
|
392
|
+
// Workspace Invitations
|
|
393
|
+
//
|
|
394
|
+
model WorkspaceInvitation {
|
|
395
|
+
id String @id @default(cuid())
|
|
396
|
+
workspaceId String
|
|
397
|
+
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
398
|
+
|
|
399
|
+
email String
|
|
400
|
+
role String @default("member") // "owner", "admin", "member"
|
|
401
|
+
token String @unique @default(cuid()) // UUID for invitation link
|
|
402
|
+
|
|
403
|
+
invitedById String
|
|
404
|
+
invitedBy User @relation("UserInvitations", fields: [invitedById], references: [id], onDelete: Cascade)
|
|
405
|
+
|
|
406
|
+
acceptedAt DateTime?
|
|
407
|
+
expiresAt DateTime @default(dbgenerated("NOW() + INTERVAL '7 days'"))
|
|
408
|
+
|
|
409
|
+
createdAt DateTime @default(now())
|
|
410
|
+
updatedAt DateTime @updatedAt
|
|
411
|
+
|
|
412
|
+
@@unique([workspaceId, email]) // One invitation per email per workspace
|
|
413
|
+
@@index([token])
|
|
414
|
+
@@index([workspaceId])
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
//
|
|
418
|
+
// Podcast Segments (child items of a PODCAST_EPISODE Artifact)
|
|
419
|
+
//
|
|
420
|
+
model PodcastSegment {
|
|
421
|
+
id String @id @default(cuid())
|
|
422
|
+
artifactId String
|
|
423
|
+
artifact Artifact @relation(fields: [artifactId], references: [id], onDelete: Cascade)
|
|
424
|
+
|
|
425
|
+
title String
|
|
426
|
+
content String // Full text content of the segment
|
|
427
|
+
startTime Int // Start time in seconds
|
|
428
|
+
duration Int // Duration in seconds
|
|
429
|
+
order Int // Display order within the episode
|
|
430
|
+
|
|
431
|
+
// Audio file reference
|
|
432
|
+
objectKey String? // Google Cloud Storage object key
|
|
433
|
+
audioUrl String? // Cached signed URL (temporary)
|
|
434
|
+
|
|
435
|
+
// Metadata
|
|
436
|
+
keyPoints String[] // Array of key points
|
|
437
|
+
meta Json? // Additional metadata (voice settings, etc.)
|
|
438
|
+
|
|
439
|
+
generating Boolean @default(false)
|
|
440
|
+
generatingMetadata Json? // Additional metadata (voice settings, etc.)
|
|
441
|
+
|
|
442
|
+
createdAt DateTime @default(now())
|
|
443
|
+
updatedAt DateTime @updatedAt
|
|
444
|
+
|
|
445
|
+
@@index([artifactId, order]) // For efficient ordering
|
|
446
|
+
@@index([artifactId, startTime]) // For time-based queries
|
|
309
447
|
}
|