@goscribe/server 1.5.0 → 1.6.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/dist/context.d.ts +14 -1
- package/dist/context.js +23 -2
- package/dist/lib/ai/index.d.ts +3 -2
- package/dist/lib/ai/index.js +3 -2
- package/dist/lib/ai/llm-client.d.ts +1 -0
- package/dist/lib/ai/llm-client.js +17 -0
- package/dist/routers/_app.d.ts +40 -80
- package/dist/routers/auth.js +1 -1
- package/dist/routers/flashcards.d.ts +12 -1
- package/dist/routers/payment.d.ts +1 -12
- package/dist/routers/workspace.d.ts +27 -67
- package/dist/routers/workspace.js +1 -0
- package/dist/services/billing/payment.service.d.ts +1 -12
- package/dist/services/billing/payment.service.js +3 -6
- package/dist/services/billing/usage.service.d.ts +30 -10
- package/dist/services/billing/usage.service.js +87 -15
- package/dist/services/content/copilot.service.js +15 -29
- package/dist/services/content/flashcard-progress.service.js +9 -9
- package/dist/services/content/flashcard.service.d.ts +45 -1
- package/dist/services/content/flashcard.service.js +81 -68
- package/dist/services/content/media-analysis.service.js +27 -27
- package/dist/services/content/worksheet-generation.service.test.js +2 -2
- package/dist/services/workspace/workspace.service.d.ts +23 -67
- package/dist/services/workspace/workspace.service.js +69 -62
- package/dist/trpc.d.ts +12 -4
- package/dist/trpc.js +5 -11
- package/package.json +1 -1
- package/src/context.ts +33 -3
- package/src/lib/ai/index.ts +3 -0
- package/src/lib/ai/llm-client.ts +23 -0
- package/src/routers/auth.ts +1 -1
- package/src/routers/workspace.ts +4 -0
- package/src/services/billing/payment.service.ts +3 -6
- package/src/services/billing/usage.service.ts +190 -77
- package/src/services/content/copilot.service.ts +23 -32
- package/src/services/content/flashcard-progress.service.ts +12 -9
- package/src/services/content/flashcard.service.ts +89 -66
- package/src/services/content/media-analysis.service.ts +34 -29
- package/src/services/content/worksheet-generation.service.test.ts +2 -2
- package/src/services/workspace/workspace.service.ts +73 -66
- package/src/trpc.ts +5 -13
package/dist/context.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
|
|
2
2
|
import { prisma } from "./lib/prisma.js";
|
|
3
3
|
import cookie from "cookie";
|
|
4
|
+
export type SessionUser = {
|
|
5
|
+
id: string;
|
|
6
|
+
emailVerified: boolean;
|
|
7
|
+
isSystemAdmin: boolean;
|
|
8
|
+
};
|
|
4
9
|
export declare function createContext({ req, res }: CreateExpressContextOptions): Promise<{
|
|
5
10
|
db: import("@prisma/client").PrismaClient<import("@prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
|
|
6
|
-
session:
|
|
11
|
+
session: null;
|
|
12
|
+
req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
13
|
+
res: import("express").Response<any, Record<string, any>>;
|
|
14
|
+
cookies: cookie.Cookies;
|
|
15
|
+
} | {
|
|
16
|
+
db: import("@prisma/client").PrismaClient<import("@prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
|
|
17
|
+
session: {
|
|
18
|
+
user: SessionUser;
|
|
19
|
+
};
|
|
7
20
|
req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
8
21
|
res: import("express").Response<any, Record<string, any>>;
|
|
9
22
|
cookies: cookie.Cookies;
|
package/dist/context.js
CHANGED
|
@@ -3,10 +3,31 @@ import { verifyCustomAuthCookie } from "./lib/auth.js";
|
|
|
3
3
|
import cookie from "cookie";
|
|
4
4
|
export async function createContext({ req, res }) {
|
|
5
5
|
const cookies = cookie.parse(req.headers.cookie ?? "");
|
|
6
|
-
// Only use custom auth cookie
|
|
7
6
|
const custom = verifyCustomAuthCookie(cookies["auth_token"]);
|
|
8
7
|
if (custom) {
|
|
9
|
-
|
|
8
|
+
const user = await prisma.user.findUnique({
|
|
9
|
+
where: { id: custom.userId },
|
|
10
|
+
select: {
|
|
11
|
+
id: true,
|
|
12
|
+
emailVerified: true,
|
|
13
|
+
role: { select: { name: true } },
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
if (!user) {
|
|
17
|
+
return { db: prisma, session: null, req, res, cookies };
|
|
18
|
+
}
|
|
19
|
+
const sessionUser = {
|
|
20
|
+
id: user.id,
|
|
21
|
+
emailVerified: !!user.emailVerified,
|
|
22
|
+
isSystemAdmin: user.role?.name === "System Admin",
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
db: prisma,
|
|
26
|
+
session: { user: sessionUser },
|
|
27
|
+
req,
|
|
28
|
+
res,
|
|
29
|
+
cookies,
|
|
30
|
+
};
|
|
10
31
|
}
|
|
11
32
|
return { db: prisma, session: null, req, res, cookies };
|
|
12
33
|
}
|
package/dist/lib/ai/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { aiConfig } from './config.js';
|
|
2
|
-
import { complete, completeText } from './llm-client.js';
|
|
2
|
+
import { complete, completeText, streamComplete } from './llm-client.js';
|
|
3
3
|
import { DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, toVectorLiteral } from './embedding-client.js';
|
|
4
4
|
import { extractJson, parseJsonField } from './json-parse.js';
|
|
5
5
|
import { isAiMockMode, mockDelay } from './mock.js';
|
|
@@ -28,6 +28,7 @@ export declare const ai: {
|
|
|
28
28
|
llm: {
|
|
29
29
|
complete: typeof complete;
|
|
30
30
|
completeText: typeof completeText;
|
|
31
|
+
streamComplete: typeof streamComplete;
|
|
31
32
|
};
|
|
32
33
|
embed: {
|
|
33
34
|
texts: typeof embedTexts;
|
|
@@ -44,4 +45,4 @@ export declare const ai: {
|
|
|
44
45
|
};
|
|
45
46
|
export type { ChatMessage, ChatRole, CompleteOptions } from './types.js';
|
|
46
47
|
export type { AISession, ProcessFileResult, PodcastSpeaker, StudyGuideSegment, WorksheetGenerationOptions, } from './inference-backend/types.js';
|
|
47
|
-
export { aiConfig, complete, completeText, DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, extractJson, inferenceBackend, isAiMockMode, mockDelay, parseJsonField, toVectorLiteral, };
|
|
48
|
+
export { aiConfig, complete, completeText, streamComplete, DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, extractJson, inferenceBackend, isAiMockMode, mockDelay, parseJsonField, toVectorLiteral, };
|
package/dist/lib/ai/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { aiConfig } from './config.js';
|
|
2
|
-
import { complete, completeText, } from './llm-client.js';
|
|
2
|
+
import { complete, completeText, streamComplete, } from './llm-client.js';
|
|
3
3
|
import { DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, toVectorLiteral, } from './embedding-client.js';
|
|
4
4
|
import { extractJson, parseJsonField } from './json-parse.js';
|
|
5
5
|
import { isAiMockMode, mockDelay } from './mock.js';
|
|
@@ -11,6 +11,7 @@ export const ai = {
|
|
|
11
11
|
llm: {
|
|
12
12
|
complete,
|
|
13
13
|
completeText,
|
|
14
|
+
streamComplete,
|
|
14
15
|
},
|
|
15
16
|
embed: {
|
|
16
17
|
texts: embedTexts,
|
|
@@ -25,4 +26,4 @@ export const ai = {
|
|
|
25
26
|
parseJsonField,
|
|
26
27
|
},
|
|
27
28
|
};
|
|
28
|
-
export { aiConfig, complete, completeText, DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, extractJson, inferenceBackend, isAiMockMode, mockDelay, parseJsonField, toVectorLiteral, };
|
|
29
|
+
export { aiConfig, complete, completeText, streamComplete, DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, extractJson, inferenceBackend, isAiMockMode, mockDelay, parseJsonField, toVectorLiteral, };
|
|
@@ -2,5 +2,6 @@ import type { ChatCompletion } from 'openai/resources/chat/completions';
|
|
|
2
2
|
import type { ChatMessage, CompleteOptions } from './types.js';
|
|
3
3
|
export declare function complete(messages: ChatMessage[], options?: CompleteOptions): Promise<ChatCompletion>;
|
|
4
4
|
export declare function completeText(messages: ChatMessage[], options?: CompleteOptions): Promise<string>;
|
|
5
|
+
export declare function streamComplete(messages: ChatMessage[], onDelta: (delta: string) => void | Promise<void>, options?: CompleteOptions): Promise<string>;
|
|
5
6
|
/** @deprecated Use `complete()` from the ai module instead. */
|
|
6
7
|
export default complete;
|
|
@@ -15,5 +15,22 @@ export async function completeText(messages, options = {}) {
|
|
|
15
15
|
const response = await complete(messages, options);
|
|
16
16
|
return response.choices?.[0]?.message?.content ?? '';
|
|
17
17
|
}
|
|
18
|
+
export async function streamComplete(messages, onDelta, options = {}) {
|
|
19
|
+
const stream = await client.chat.completions.create({
|
|
20
|
+
model: options.model ?? aiConfig.llm.model,
|
|
21
|
+
messages,
|
|
22
|
+
stream: true,
|
|
23
|
+
...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
|
|
24
|
+
});
|
|
25
|
+
let content = '';
|
|
26
|
+
for await (const chunk of stream) {
|
|
27
|
+
const delta = chunk.choices?.[0]?.delta?.content ?? '';
|
|
28
|
+
if (!delta)
|
|
29
|
+
continue;
|
|
30
|
+
content += delta;
|
|
31
|
+
await onDelta(delta);
|
|
32
|
+
}
|
|
33
|
+
return content;
|
|
34
|
+
}
|
|
18
35
|
/** @deprecated Use `complete()` from the ai module instead. */
|
|
19
36
|
export default complete;
|
package/dist/routers/_app.d.ts
CHANGED
|
@@ -231,35 +231,26 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
231
231
|
folders: {
|
|
232
232
|
name: string;
|
|
233
233
|
id: string;
|
|
234
|
-
createdAt: Date;
|
|
235
234
|
updatedAt: Date;
|
|
236
|
-
ownerId: string;
|
|
237
235
|
parentId: string | null;
|
|
238
236
|
color: string;
|
|
239
237
|
markerColor: string | null;
|
|
240
238
|
}[];
|
|
241
|
-
workspaces:
|
|
242
|
-
uploads: {
|
|
243
|
-
name: string;
|
|
244
|
-
id: string;
|
|
245
|
-
createdAt: Date;
|
|
246
|
-
mimeType: string;
|
|
247
|
-
}[];
|
|
248
|
-
} & {
|
|
239
|
+
workspaces: {
|
|
249
240
|
id: string;
|
|
250
|
-
createdAt: Date;
|
|
251
241
|
updatedAt: Date;
|
|
252
242
|
title: string;
|
|
253
|
-
description: string | null;
|
|
254
|
-
ownerId: string;
|
|
255
243
|
color: string;
|
|
256
244
|
markerColor: string | null;
|
|
257
245
|
icon: string;
|
|
258
246
|
folderId: string | null;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
247
|
+
uploads: {
|
|
248
|
+
name: string;
|
|
249
|
+
id: string;
|
|
250
|
+
createdAt: Date;
|
|
251
|
+
mimeType: string;
|
|
252
|
+
}[];
|
|
253
|
+
}[];
|
|
263
254
|
};
|
|
264
255
|
meta: object;
|
|
265
256
|
}>;
|
|
@@ -344,74 +335,43 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
344
335
|
id: string;
|
|
345
336
|
};
|
|
346
337
|
output: {
|
|
338
|
+
id: string;
|
|
339
|
+
createdAt: Date;
|
|
340
|
+
updatedAt: Date;
|
|
341
|
+
title: string;
|
|
342
|
+
description: string | null;
|
|
347
343
|
folder: {
|
|
348
344
|
name: string;
|
|
349
345
|
id: string;
|
|
350
|
-
createdAt: Date;
|
|
351
|
-
updatedAt: Date;
|
|
352
|
-
ownerId: string;
|
|
353
|
-
parentId: string | null;
|
|
354
346
|
color: string;
|
|
355
|
-
markerColor: string | null;
|
|
356
347
|
} | null;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
difficulty: import("@prisma/client").$Enums.Difficulty | null;
|
|
366
|
-
estimatedTime: string | null;
|
|
367
|
-
createdById: string | null;
|
|
368
|
-
description: string | null;
|
|
369
|
-
generating: boolean;
|
|
370
|
-
generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
|
|
371
|
-
worksheetConfig: import("@prisma/client/runtime/library").JsonValue | null;
|
|
372
|
-
imageObjectKey: string | null;
|
|
373
|
-
}[];
|
|
348
|
+
ownerId: string;
|
|
349
|
+
color: string;
|
|
350
|
+
markerColor: string | null;
|
|
351
|
+
icon: string;
|
|
352
|
+
folderId: string | null;
|
|
353
|
+
fileBeingAnalyzed: boolean;
|
|
354
|
+
analysisProgress: import("@prisma/client/runtime/library").JsonValue;
|
|
355
|
+
needsAnalysis: boolean;
|
|
374
356
|
uploads: {
|
|
375
|
-
meta: import("@prisma/client/runtime/library").JsonValue | null;
|
|
376
357
|
name: string;
|
|
377
358
|
id: string;
|
|
378
359
|
createdAt: Date;
|
|
379
|
-
workspaceId: string | null;
|
|
380
|
-
userId: string | null;
|
|
381
360
|
mimeType: string;
|
|
382
361
|
size: number;
|
|
383
|
-
bucket: string | null;
|
|
384
362
|
objectKey: string | null;
|
|
385
|
-
url: string | null;
|
|
386
|
-
checksum: string | null;
|
|
387
|
-
aiTranscription: import("@prisma/client/runtime/library").JsonValue | null;
|
|
388
363
|
}[];
|
|
389
|
-
} & {
|
|
390
|
-
id: string;
|
|
391
|
-
createdAt: Date;
|
|
392
|
-
updatedAt: Date;
|
|
393
|
-
title: string;
|
|
394
|
-
description: string | null;
|
|
395
|
-
ownerId: string;
|
|
396
|
-
color: string;
|
|
397
|
-
markerColor: string | null;
|
|
398
|
-
icon: string;
|
|
399
|
-
folderId: string | null;
|
|
400
|
-
fileBeingAnalyzed: boolean;
|
|
401
|
-
analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
|
|
402
|
-
needsAnalysis: boolean;
|
|
403
364
|
};
|
|
404
365
|
meta: object;
|
|
405
366
|
}>;
|
|
406
367
|
getStats: import("@trpc/server").TRPCQueryProcedure<{
|
|
407
368
|
input: void;
|
|
408
|
-
output:
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
};
|
|
369
|
+
output: import("../services/billing/usage.service.js").AccountStats;
|
|
370
|
+
meta: object;
|
|
371
|
+
}>;
|
|
372
|
+
getAccountSummary: import("@trpc/server").TRPCQueryProcedure<{
|
|
373
|
+
input: void;
|
|
374
|
+
output: import("../services/billing/usage.service.js").AccountSummary;
|
|
415
375
|
meta: object;
|
|
416
376
|
}>;
|
|
417
377
|
getStudyAnalytics: import("@trpc/server").TRPCQueryProcedure<{
|
|
@@ -866,7 +826,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
866
826
|
input: {
|
|
867
827
|
workspaceId: string;
|
|
868
828
|
};
|
|
869
|
-
output: boolean
|
|
829
|
+
output: boolean;
|
|
870
830
|
meta: object;
|
|
871
831
|
}>;
|
|
872
832
|
createCard: import("@trpc/server").TRPCMutationProcedure<{
|
|
@@ -949,6 +909,17 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
949
909
|
};
|
|
950
910
|
output: {
|
|
951
911
|
artifact: {
|
|
912
|
+
flashcards: {
|
|
913
|
+
id: string;
|
|
914
|
+
createdAt: Date;
|
|
915
|
+
artifactId: string;
|
|
916
|
+
tags: string[];
|
|
917
|
+
order: number;
|
|
918
|
+
front: string;
|
|
919
|
+
back: string;
|
|
920
|
+
acceptedAnswers: string[];
|
|
921
|
+
}[];
|
|
922
|
+
} & {
|
|
952
923
|
id: string;
|
|
953
924
|
createdAt: Date;
|
|
954
925
|
updatedAt: Date;
|
|
@@ -2665,18 +2636,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
2665
2636
|
input: void;
|
|
2666
2637
|
output: {
|
|
2667
2638
|
usage: import("../services/billing/usage.service.js").UserUsage;
|
|
2668
|
-
limits:
|
|
2669
|
-
id: string;
|
|
2670
|
-
planId: string;
|
|
2671
|
-
maxStorageBytes: bigint;
|
|
2672
|
-
maxWorksheets: number;
|
|
2673
|
-
maxFlashcards: number;
|
|
2674
|
-
maxPodcasts: number;
|
|
2675
|
-
maxStudyGuides: number;
|
|
2676
|
-
createdAt: Date;
|
|
2677
|
-
updatedAt: Date;
|
|
2678
|
-
isFallbackPlan: boolean;
|
|
2679
|
-
};
|
|
2639
|
+
limits: import("../services/billing/usage.service.js").UserPlanLimits;
|
|
2680
2640
|
hasActivePlan: boolean;
|
|
2681
2641
|
};
|
|
2682
2642
|
meta: object;
|
package/dist/routers/auth.js
CHANGED
|
@@ -26,7 +26,7 @@ const passwordFieldSchema = z
|
|
|
26
26
|
.regex(/[0-9]/, 'Password must contain at least one number')
|
|
27
27
|
.regex(/[^a-zA-Z0-9]/, 'Password must contain at least one special character');
|
|
28
28
|
export const auth = router({
|
|
29
|
-
updateProfile:
|
|
29
|
+
updateProfile: authedProcedure
|
|
30
30
|
.input(z.object({ name: z.string().min(1) }))
|
|
31
31
|
.mutation(({ ctx, input }) => new AuthService(ctx.db).updateProfile(ctx.session.user.id, input)),
|
|
32
32
|
changePassword: authedProcedure
|
|
@@ -85,7 +85,7 @@ export declare const flashcards: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
85
85
|
input: {
|
|
86
86
|
workspaceId: string;
|
|
87
87
|
};
|
|
88
|
-
output: boolean
|
|
88
|
+
output: boolean;
|
|
89
89
|
meta: object;
|
|
90
90
|
}>;
|
|
91
91
|
createCard: import("@trpc/server").TRPCMutationProcedure<{
|
|
@@ -168,6 +168,17 @@ export declare const flashcards: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
168
168
|
};
|
|
169
169
|
output: {
|
|
170
170
|
artifact: {
|
|
171
|
+
flashcards: {
|
|
172
|
+
id: string;
|
|
173
|
+
createdAt: Date;
|
|
174
|
+
artifactId: string;
|
|
175
|
+
tags: string[];
|
|
176
|
+
order: number;
|
|
177
|
+
front: string;
|
|
178
|
+
back: string;
|
|
179
|
+
acceptedAnswers: string[];
|
|
180
|
+
}[];
|
|
181
|
+
} & {
|
|
171
182
|
id: string;
|
|
172
183
|
createdAt: Date;
|
|
173
184
|
updatedAt: Date;
|
|
@@ -61,18 +61,7 @@ export declare const paymentRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
61
61
|
input: void;
|
|
62
62
|
output: {
|
|
63
63
|
usage: import("../services/billing/usage.service.js").UserUsage;
|
|
64
|
-
limits:
|
|
65
|
-
id: string;
|
|
66
|
-
planId: string;
|
|
67
|
-
maxStorageBytes: bigint;
|
|
68
|
-
maxWorksheets: number;
|
|
69
|
-
maxFlashcards: number;
|
|
70
|
-
maxPodcasts: number;
|
|
71
|
-
maxStudyGuides: number;
|
|
72
|
-
createdAt: Date;
|
|
73
|
-
updatedAt: Date;
|
|
74
|
-
isFallbackPlan: boolean;
|
|
75
|
-
};
|
|
64
|
+
limits: import("../services/billing/usage.service.js").UserPlanLimits;
|
|
76
65
|
hasActivePlan: boolean;
|
|
77
66
|
};
|
|
78
67
|
meta: object;
|
|
@@ -53,35 +53,26 @@ export declare const workspace: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
53
53
|
folders: {
|
|
54
54
|
name: string;
|
|
55
55
|
id: string;
|
|
56
|
-
createdAt: Date;
|
|
57
56
|
updatedAt: Date;
|
|
58
|
-
ownerId: string;
|
|
59
57
|
parentId: string | null;
|
|
60
58
|
color: string;
|
|
61
59
|
markerColor: string | null;
|
|
62
60
|
}[];
|
|
63
|
-
workspaces:
|
|
64
|
-
uploads: {
|
|
65
|
-
name: string;
|
|
66
|
-
id: string;
|
|
67
|
-
createdAt: Date;
|
|
68
|
-
mimeType: string;
|
|
69
|
-
}[];
|
|
70
|
-
} & {
|
|
61
|
+
workspaces: {
|
|
71
62
|
id: string;
|
|
72
|
-
createdAt: Date;
|
|
73
63
|
updatedAt: Date;
|
|
74
64
|
title: string;
|
|
75
|
-
description: string | null;
|
|
76
|
-
ownerId: string;
|
|
77
65
|
color: string;
|
|
78
66
|
markerColor: string | null;
|
|
79
67
|
icon: string;
|
|
80
68
|
folderId: string | null;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
69
|
+
uploads: {
|
|
70
|
+
name: string;
|
|
71
|
+
id: string;
|
|
72
|
+
createdAt: Date;
|
|
73
|
+
mimeType: string;
|
|
74
|
+
}[];
|
|
75
|
+
}[];
|
|
85
76
|
};
|
|
86
77
|
meta: object;
|
|
87
78
|
}>;
|
|
@@ -166,74 +157,43 @@ export declare const workspace: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
166
157
|
id: string;
|
|
167
158
|
};
|
|
168
159
|
output: {
|
|
160
|
+
id: string;
|
|
161
|
+
createdAt: Date;
|
|
162
|
+
updatedAt: Date;
|
|
163
|
+
title: string;
|
|
164
|
+
description: string | null;
|
|
169
165
|
folder: {
|
|
170
166
|
name: string;
|
|
171
167
|
id: string;
|
|
172
|
-
createdAt: Date;
|
|
173
|
-
updatedAt: Date;
|
|
174
|
-
ownerId: string;
|
|
175
|
-
parentId: string | null;
|
|
176
168
|
color: string;
|
|
177
|
-
markerColor: string | null;
|
|
178
169
|
} | null;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
difficulty: import("@prisma/client").$Enums.Difficulty | null;
|
|
188
|
-
estimatedTime: string | null;
|
|
189
|
-
createdById: string | null;
|
|
190
|
-
description: string | null;
|
|
191
|
-
generating: boolean;
|
|
192
|
-
generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
|
|
193
|
-
worksheetConfig: import("@prisma/client/runtime/library").JsonValue | null;
|
|
194
|
-
imageObjectKey: string | null;
|
|
195
|
-
}[];
|
|
170
|
+
ownerId: string;
|
|
171
|
+
color: string;
|
|
172
|
+
markerColor: string | null;
|
|
173
|
+
icon: string;
|
|
174
|
+
folderId: string | null;
|
|
175
|
+
fileBeingAnalyzed: boolean;
|
|
176
|
+
analysisProgress: import("@prisma/client/runtime/library").JsonValue;
|
|
177
|
+
needsAnalysis: boolean;
|
|
196
178
|
uploads: {
|
|
197
|
-
meta: import("@prisma/client/runtime/library").JsonValue | null;
|
|
198
179
|
name: string;
|
|
199
180
|
id: string;
|
|
200
181
|
createdAt: Date;
|
|
201
|
-
workspaceId: string | null;
|
|
202
|
-
userId: string | null;
|
|
203
182
|
mimeType: string;
|
|
204
183
|
size: number;
|
|
205
|
-
bucket: string | null;
|
|
206
184
|
objectKey: string | null;
|
|
207
|
-
url: string | null;
|
|
208
|
-
checksum: string | null;
|
|
209
|
-
aiTranscription: import("@prisma/client/runtime/library").JsonValue | null;
|
|
210
185
|
}[];
|
|
211
|
-
} & {
|
|
212
|
-
id: string;
|
|
213
|
-
createdAt: Date;
|
|
214
|
-
updatedAt: Date;
|
|
215
|
-
title: string;
|
|
216
|
-
description: string | null;
|
|
217
|
-
ownerId: string;
|
|
218
|
-
color: string;
|
|
219
|
-
markerColor: string | null;
|
|
220
|
-
icon: string;
|
|
221
|
-
folderId: string | null;
|
|
222
|
-
fileBeingAnalyzed: boolean;
|
|
223
|
-
analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
|
|
224
|
-
needsAnalysis: boolean;
|
|
225
186
|
};
|
|
226
187
|
meta: object;
|
|
227
188
|
}>;
|
|
228
189
|
getStats: import("@trpc/server").TRPCQueryProcedure<{
|
|
229
190
|
input: void;
|
|
230
|
-
output:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
};
|
|
191
|
+
output: import("../services/billing/usage.service.js").AccountStats;
|
|
192
|
+
meta: object;
|
|
193
|
+
}>;
|
|
194
|
+
getAccountSummary: import("@trpc/server").TRPCQueryProcedure<{
|
|
195
|
+
input: void;
|
|
196
|
+
output: import("../services/billing/usage.service.js").AccountSummary;
|
|
237
197
|
meta: object;
|
|
238
198
|
}>;
|
|
239
199
|
getStudyAnalytics: import("@trpc/server").TRPCQueryProcedure<{
|
|
@@ -40,6 +40,7 @@ export const workspace = router({
|
|
|
40
40
|
.input(z.object({ id: z.string() }))
|
|
41
41
|
.query(({ ctx, input }) => new WorkspaceService(ctx.db).get(ctx.session.user.id, input.id)),
|
|
42
42
|
getStats: authedProcedure.query(({ ctx }) => new WorkspaceService(ctx.db).getStats(ctx.session.user.id)),
|
|
43
|
+
getAccountSummary: authedProcedure.query(({ ctx }) => new WorkspaceService(ctx.db).getAccountSummary(ctx.session.user.id)),
|
|
43
44
|
getStudyAnalytics: authedProcedure.query(({ ctx }) => new WorkspaceAnalyticsService(ctx.db).getStudyAnalytics(ctx.session.user.id)),
|
|
44
45
|
update: authedProcedure
|
|
45
46
|
.input(z.object({
|
|
@@ -32,18 +32,7 @@ export declare class PaymentService extends BaseService {
|
|
|
32
32
|
}>;
|
|
33
33
|
getUsageOverview(userId: string): Promise<{
|
|
34
34
|
usage: import("./usage.service.js").UserUsage;
|
|
35
|
-
limits:
|
|
36
|
-
id: string;
|
|
37
|
-
planId: string;
|
|
38
|
-
maxStorageBytes: bigint;
|
|
39
|
-
maxWorksheets: number;
|
|
40
|
-
maxFlashcards: number;
|
|
41
|
-
maxPodcasts: number;
|
|
42
|
-
maxStudyGuides: number;
|
|
43
|
-
createdAt: Date;
|
|
44
|
-
updatedAt: Date;
|
|
45
|
-
isFallbackPlan: boolean;
|
|
46
|
-
};
|
|
35
|
+
limits: import("./usage.service.js").UserPlanLimits;
|
|
47
36
|
hasActivePlan: boolean;
|
|
48
37
|
}>;
|
|
49
38
|
getResourcePrices(): import("@prisma/client").Prisma.PrismaPromise<{
|
|
@@ -2,7 +2,7 @@ import { TRPCError } from '@trpc/server';
|
|
|
2
2
|
import { BaseService } from '../base.service.js';
|
|
3
3
|
import { stripe } from '../../lib/stripe.js';
|
|
4
4
|
import { env } from '../../lib/env.js';
|
|
5
|
-
import {
|
|
5
|
+
import { getAccountSummary } from './usage.service.js';
|
|
6
6
|
import { notifyPaymentSucceeded, notifySubscriptionActivated, notifySubscriptionPaymentSucceeded, } from '../notifications/notification.service.js';
|
|
7
7
|
import { upsertSubscriptionFromStripe } from './subscription.service.js';
|
|
8
8
|
/** Stripe Checkout URLs contain the session id (cs_…); we only store the URL in DB. */
|
|
@@ -356,11 +356,8 @@ export class PaymentService extends BaseService {
|
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
358
|
async getUsageOverview(userId) {
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
getUserPlanLimits(userId),
|
|
362
|
-
]);
|
|
363
|
-
return { usage, limits, hasActivePlan: !!limits };
|
|
359
|
+
const { usage, limits, hasActivePlan } = await getAccountSummary(userId);
|
|
360
|
+
return { usage, limits, hasActivePlan };
|
|
364
361
|
}
|
|
365
362
|
getResourcePrices() {
|
|
366
363
|
return this.db.resourcePrice.findMany();
|
|
@@ -5,15 +5,14 @@ export interface UserUsage {
|
|
|
5
5
|
podcasts: number;
|
|
6
6
|
storageBytes: number;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export declare function getUserPlanLimits(userId: string): Promise<{
|
|
8
|
+
export interface AccountStats {
|
|
9
|
+
workspaces: number;
|
|
10
|
+
folders: number;
|
|
11
|
+
lastUpdated: Date | null;
|
|
12
|
+
spaceUsed: number;
|
|
13
|
+
spaceTotal: number;
|
|
14
|
+
}
|
|
15
|
+
export interface UserPlanLimits {
|
|
17
16
|
id: string;
|
|
18
17
|
planId: string;
|
|
19
18
|
maxStorageBytes: bigint;
|
|
@@ -24,4 +23,25 @@ export declare function getUserPlanLimits(userId: string): Promise<{
|
|
|
24
23
|
createdAt: Date;
|
|
25
24
|
updatedAt: Date;
|
|
26
25
|
isFallbackPlan: boolean;
|
|
27
|
-
}
|
|
26
|
+
}
|
|
27
|
+
export interface AccountSummary {
|
|
28
|
+
stats: AccountStats;
|
|
29
|
+
usage: UserUsage;
|
|
30
|
+
limits: UserPlanLimits;
|
|
31
|
+
hasActivePlan: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** Bust cached usage/limit reads after creates, deletes, or billing changes. */
|
|
34
|
+
export declare function invalidateUserBillingCache(userId: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Counts all resources consumed by a user across the platform.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getUserUsage(userId: string): Promise<UserUsage>;
|
|
39
|
+
/**
|
|
40
|
+
* Retrieves the specific plan limits for a user based on their active subscription
|
|
41
|
+
* PLUS any extra credits purchased.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getUserPlanLimits(userId: string): Promise<UserPlanLimits>;
|
|
44
|
+
/**
|
|
45
|
+
* Sidebar/dashboard summary: stats + usage + limits in one parallel DB round-trip batch.
|
|
46
|
+
*/
|
|
47
|
+
export declare function getAccountSummary(userId: string): Promise<AccountSummary>;
|