@studious-lms/server 1.14.0 → 1.15.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/.github/workflows/deploy.yml +1 -1
- package/dist/instrument.js +3 -3
- package/dist/instrument.js.map +1 -1
- package/dist/lib/inferDocumentFormat.d.ts +10 -0
- package/dist/lib/inferDocumentFormat.d.ts.map +1 -0
- package/dist/lib/inferDocumentFormat.js +48 -0
- package/dist/lib/inferDocumentFormat.js.map +1 -0
- package/dist/lib/legal.d.ts +3 -0
- package/dist/lib/legal.d.ts.map +1 -0
- package/dist/lib/legal.js +6 -0
- package/dist/lib/legal.js.map +1 -0
- package/dist/models/announcement.d.ts +47 -45
- package/dist/models/announcement.d.ts.map +1 -1
- package/dist/models/assignment.d.ts +75 -75
- package/dist/models/auth.d.ts +8 -0
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/auth.js +4 -2
- package/dist/models/auth.js.map +1 -1
- package/dist/models/class.d.ts +13 -13
- package/dist/models/comment.d.ts +6 -6
- package/dist/models/conversation.d.ts +23 -23
- package/dist/models/event.d.ts +20 -18
- package/dist/models/event.d.ts.map +1 -1
- package/dist/models/file.d.ts +33 -9
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +4 -4
- package/dist/models/marketing.d.ts +2 -15
- package/dist/models/marketing.d.ts.map +1 -1
- package/dist/models/marketing.js +12 -4
- package/dist/models/marketing.js.map +1 -1
- package/dist/models/message.d.ts +13 -13
- package/dist/models/newtonChat.d.ts +3 -3
- package/dist/models/notification.d.ts +4 -4
- package/dist/models/worksheet.d.ts +121 -121
- package/dist/pipelines/aiLabChat.d.ts +7 -1
- package/dist/pipelines/aiLabChat.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.js +153 -67
- package/dist/pipelines/aiLabChat.js.map +1 -1
- package/dist/pipelines/aiLabChatContract.d.ts +34 -195
- package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
- package/dist/pipelines/aiLabChatContract.js +9 -19
- package/dist/pipelines/aiLabChatContract.js.map +1 -1
- package/dist/pipelines/gradeWorksheet.d.ts +6 -6
- package/dist/pipelines/labChatPrompt.d.ts +8 -3
- package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
- package/dist/pipelines/labChatPrompt.js +52 -14
- package/dist/pipelines/labChatPrompt.js.map +1 -1
- package/dist/pipelines/pdfToWorksheet.d.ts +1 -1
- package/dist/routers/_app.d.ts +750 -546
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/announcement.d.ts +40 -40
- package/dist/routers/assignment.d.ts +131 -121
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/auth.d.ts +2 -0
- package/dist/routers/auth.d.ts.map +1 -1
- package/dist/routers/auth.js +15 -2
- package/dist/routers/auth.js.map +1 -1
- package/dist/routers/class.d.ts +12 -8
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/comment.d.ts +5 -5
- package/dist/routers/conversation.d.ts +6 -6
- package/dist/routers/discussion.d.ts +3 -3
- package/dist/routers/embedding.d.ts +6 -2
- package/dist/routers/embedding.d.ts.map +1 -1
- package/dist/routers/embedding.js +44 -36
- package/dist/routers/embedding.js.map +1 -1
- package/dist/routers/event.d.ts +20 -18
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/file.d.ts +2 -2
- package/dist/routers/labChat.d.ts +3 -3
- package/dist/routers/marketing.d.ts +0 -13
- package/dist/routers/marketing.d.ts.map +1 -1
- package/dist/routers/notifications.d.ts +4 -4
- package/dist/routers/school.d.ts +4 -0
- package/dist/routers/school.d.ts.map +1 -1
- package/dist/routers/search.d.ts +2 -2
- package/dist/routers/user.d.ts +89 -0
- package/dist/routers/user.d.ts.map +1 -1
- package/dist/routers/user.js +19 -2
- package/dist/routers/user.js.map +1 -1
- package/dist/routers/worksheet.d.ts +44 -44
- package/dist/routes/aiStream.d.ts.map +1 -1
- package/dist/routes/aiStream.js +41 -11
- package/dist/routes/aiStream.js.map +1 -1
- package/dist/seedDatabase.d.ts +3 -1
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/services/aiChatStream.d.ts +20 -0
- package/dist/services/aiChatStream.d.ts.map +1 -1
- package/dist/services/aiChatStream.js +110 -13
- package/dist/services/aiChatStream.js.map +1 -1
- package/dist/services/aiModifierTools.d.ts +0 -16
- package/dist/services/aiModifierTools.d.ts.map +1 -1
- package/dist/services/aiModifierTools.js +112 -3
- package/dist/services/aiModifierTools.js.map +1 -1
- package/dist/services/aiProposalTools.d.ts.map +1 -1
- package/dist/services/aiProposalTools.js +25 -25
- package/dist/services/aiProposalTools.js.map +1 -1
- package/dist/services/aiTools.js +3 -3
- package/dist/services/aiTools.js.map +1 -1
- package/dist/services/announcement.d.ts +34 -34
- package/dist/services/assignment.d.ts +121 -111
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/assignment.js +4 -2
- package/dist/services/assignment.js.map +1 -1
- package/dist/services/auth.d.ts +2 -0
- package/dist/services/auth.d.ts.map +1 -1
- package/dist/services/auth.js +13 -4
- package/dist/services/auth.js.map +1 -1
- package/dist/services/class.d.ts +10 -6
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +7 -3
- package/dist/services/class.js.map +1 -1
- package/dist/services/comment.d.ts +5 -5
- package/dist/services/conversation.d.ts +6 -6
- package/dist/services/discussion.d.ts.map +1 -1
- package/dist/services/discussion.js +9 -2
- package/dist/services/discussion.js.map +1 -1
- package/dist/services/embedding.d.ts +19 -1
- package/dist/services/embedding.d.ts.map +1 -1
- package/dist/services/embedding.js +154 -8
- package/dist/services/embedding.js.map +1 -1
- package/dist/services/event.d.ts +20 -18
- package/dist/services/event.d.ts.map +1 -1
- package/dist/services/file.d.ts +4 -4
- package/dist/services/folder.d.ts.map +1 -1
- package/dist/services/folder.js +32 -2
- package/dist/services/folder.js.map +1 -1
- package/dist/services/labChat.d.ts +3 -3
- package/dist/services/marketing.d.ts +1 -14
- package/dist/services/marketing.d.ts.map +1 -1
- package/dist/services/marketing.js +5 -5
- package/dist/services/marketing.js.map +1 -1
- package/dist/services/notification.d.ts +4 -4
- package/dist/services/openaiDocumentGen.d.ts +37 -0
- package/dist/services/openaiDocumentGen.d.ts.map +1 -0
- package/dist/services/openaiDocumentGen.js +262 -0
- package/dist/services/openaiDocumentGen.js.map +1 -0
- package/dist/services/school.d.ts +4 -0
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/search.d.ts +1 -1
- package/dist/services/search.d.ts.map +1 -1
- package/dist/services/search.js +27 -2
- package/dist/services/search.js.map +1 -1
- package/dist/services/userPrivacy.d.ts +86 -0
- package/dist/services/userPrivacy.d.ts.map +1 -0
- package/dist/services/userPrivacy.js +184 -0
- package/dist/services/userPrivacy.js.map +1 -0
- package/dist/services/worksheet.d.ts +66 -49
- package/dist/services/worksheet.d.ts.map +1 -1
- package/dist/services/worksheet.js +48 -3
- package/dist/services/worksheet.js.map +1 -1
- package/dist/utils/email.d.ts.map +1 -1
- package/dist/utils/email.js +7 -3
- package/dist/utils/email.js.map +1 -1
- package/dist/workers/processors/embedContent.d.ts.map +1 -1
- package/dist/workers/processors/embedContent.js +16 -25
- package/dist/workers/processors/embedContent.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260610140000_expand_embedding_source_types/migration.sql +4 -0
- package/prisma/migrations/20260611120000_add_user_terms_acceptance/migration.sql +3 -0
- package/prisma/schema.prisma +6 -0
- package/src/instrument.ts +1 -1
- package/src/lib/inferDocumentFormat.ts +58 -0
- package/src/lib/legal.ts +2 -0
- package/src/models/auth.ts +4 -0
- package/src/models/marketing.ts +10 -2
- package/src/pipelines/aiLabChat.ts +198 -74
- package/src/pipelines/aiLabChatContract.ts +7 -17
- package/src/pipelines/labChatPrompt.ts +64 -14
- package/src/routers/auth.ts +13 -0
- package/src/routers/embedding.ts +44 -39
- package/src/routers/user.ts +26 -0
- package/src/routes/aiStream.ts +47 -9
- package/src/services/aiChatStream.ts +133 -11
- package/src/services/aiModifierTools.ts +154 -1
- package/src/services/aiProposalTools.ts +27 -25
- package/src/services/aiTools.ts +1 -1
- package/src/services/assignment.ts +3 -0
- package/src/services/auth.ts +14 -2
- package/src/services/class.ts +7 -1
- package/src/services/discussion.ts +13 -0
- package/src/services/embedding.ts +167 -6
- package/src/services/folder.ts +31 -0
- package/src/services/marketing.ts +3 -3
- package/src/services/openaiDocumentGen.ts +319 -0
- package/src/services/search.ts +25 -1
- package/src/services/userPrivacy.ts +196 -0
- package/src/services/worksheet.ts +68 -1
- package/src/utils/email.ts +5 -1
- package/src/workers/processors/embedContent.ts +18 -22
- package/tests/helpers/registerInput.ts +20 -0
- package/tests/integration/globalSetup.ts +10 -18
- package/tests/integration/routers/auth.test.ts +13 -18
- package/tests/unit/lib/inferDocumentFormat.test.ts +33 -0
- package/tests/unit/services/aiChatStream.test.ts +92 -0
- package/tests/unit/services/openaiDocumentGen.test.ts +65 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** Export a portable snapshot of the authenticated user's personal data. */
|
|
2
|
+
export declare function exportUserData(userId: string): Promise<{
|
|
3
|
+
exportedAt: string;
|
|
4
|
+
user: {
|
|
5
|
+
id: string;
|
|
6
|
+
username: string;
|
|
7
|
+
email: string;
|
|
8
|
+
verified: boolean;
|
|
9
|
+
termsAcceptedAt: Date | null;
|
|
10
|
+
termsVersion: string | null;
|
|
11
|
+
profile: {
|
|
12
|
+
id: string;
|
|
13
|
+
userId: string;
|
|
14
|
+
displayName: string | null;
|
|
15
|
+
bio: string | null;
|
|
16
|
+
location: string | null;
|
|
17
|
+
website: string | null;
|
|
18
|
+
createdAt: Date;
|
|
19
|
+
updatedAt: Date;
|
|
20
|
+
profilePicture: string | null;
|
|
21
|
+
profilePictureThumbnail: string | null;
|
|
22
|
+
} | null;
|
|
23
|
+
teacherIn: {
|
|
24
|
+
section: string;
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
subject: string;
|
|
28
|
+
}[];
|
|
29
|
+
studentIn: {
|
|
30
|
+
section: string;
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
subject: string;
|
|
34
|
+
}[];
|
|
35
|
+
submissions: {
|
|
36
|
+
assignment: {
|
|
37
|
+
id: string;
|
|
38
|
+
classId: string;
|
|
39
|
+
title: string;
|
|
40
|
+
};
|
|
41
|
+
id: string;
|
|
42
|
+
createdAt: Date;
|
|
43
|
+
extendedResponse: string | null;
|
|
44
|
+
gradeReceived: number | null;
|
|
45
|
+
submittedAt: Date | null;
|
|
46
|
+
}[];
|
|
47
|
+
events: {
|
|
48
|
+
id: string;
|
|
49
|
+
location: string | null;
|
|
50
|
+
name: string | null;
|
|
51
|
+
startTime: Date;
|
|
52
|
+
endTime: Date;
|
|
53
|
+
remarks: string | null;
|
|
54
|
+
}[];
|
|
55
|
+
notificationsReceived: {
|
|
56
|
+
id: string;
|
|
57
|
+
createdAt: Date;
|
|
58
|
+
title: string;
|
|
59
|
+
content: string;
|
|
60
|
+
read: boolean;
|
|
61
|
+
}[];
|
|
62
|
+
schoolMemberships: {
|
|
63
|
+
status: import(".prisma/client").$Enums.SchoolMembershipStatus;
|
|
64
|
+
school: {
|
|
65
|
+
id: string;
|
|
66
|
+
name: string;
|
|
67
|
+
};
|
|
68
|
+
role: import(".prisma/client").$Enums.SchoolMembershipRole;
|
|
69
|
+
}[];
|
|
70
|
+
sentMessages: {
|
|
71
|
+
id: string;
|
|
72
|
+
createdAt: Date;
|
|
73
|
+
content: string;
|
|
74
|
+
conversationId: string;
|
|
75
|
+
}[];
|
|
76
|
+
};
|
|
77
|
+
}>;
|
|
78
|
+
/**
|
|
79
|
+
* Erase personal account data by anonymizing the user record and removing
|
|
80
|
+
* sessions and memberships. Educational records tied to classes (e.g.
|
|
81
|
+
* submissions) remain with an anonymized identity where required.
|
|
82
|
+
*/
|
|
83
|
+
export declare function deleteUserAccount(userId: string): Promise<{
|
|
84
|
+
success: boolean;
|
|
85
|
+
}>;
|
|
86
|
+
//# sourceMappingURL=userPrivacy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userPrivacy.d.ts","sourceRoot":"/","sources":["services/userPrivacy.ts"],"names":[],"mappings":"AA6DA,4EAA4E;AAC5E,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4ElD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM;;GAkDrD"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User privacy service — data export and account erasure (anonymization).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7184e3ea-1921-5a91-936e-6fa9a92f8a03")}catch(e){}}();
|
|
6
|
+
import { TRPCError } from "@trpc/server";
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { hash } from "bcryptjs";
|
|
9
|
+
import { prisma } from "../lib/prisma.js";
|
|
10
|
+
import { deleteSessionsByUserId } from "../models/auth.js";
|
|
11
|
+
async function assertAccountCanBeErased(userId) {
|
|
12
|
+
const user = await prisma.user.findUnique({
|
|
13
|
+
where: { id: userId },
|
|
14
|
+
select: {
|
|
15
|
+
id: true,
|
|
16
|
+
teacherIn: {
|
|
17
|
+
select: {
|
|
18
|
+
id: true,
|
|
19
|
+
name: true,
|
|
20
|
+
_count: { select: { students: true } },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
_count: {
|
|
24
|
+
select: {
|
|
25
|
+
assignments: true,
|
|
26
|
+
createdLabChats: true,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
if (!user) {
|
|
32
|
+
throw new TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
33
|
+
}
|
|
34
|
+
const classesWithStudents = user.teacherIn.filter((cls) => cls._count.students > 0);
|
|
35
|
+
if (classesWithStudents.length > 0) {
|
|
36
|
+
throw new TRPCError({
|
|
37
|
+
code: "PRECONDITION_FAILED",
|
|
38
|
+
message: "Delete or transfer your classes before deleting your account.",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (user._count.assignments > 0) {
|
|
42
|
+
throw new TRPCError({
|
|
43
|
+
code: "PRECONDITION_FAILED",
|
|
44
|
+
message: "Remove or reassign your assignments before deleting your account.",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (user._count.createdLabChats > 0) {
|
|
48
|
+
throw new TRPCError({
|
|
49
|
+
code: "PRECONDITION_FAILED",
|
|
50
|
+
message: "Remove your AI lab chats before deleting your account.",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Export a portable snapshot of the authenticated user's personal data. */
|
|
55
|
+
export async function exportUserData(userId) {
|
|
56
|
+
const user = await prisma.user.findUnique({
|
|
57
|
+
where: { id: userId },
|
|
58
|
+
select: {
|
|
59
|
+
id: true,
|
|
60
|
+
username: true,
|
|
61
|
+
email: true,
|
|
62
|
+
verified: true,
|
|
63
|
+
termsAcceptedAt: true,
|
|
64
|
+
termsVersion: true,
|
|
65
|
+
profile: true,
|
|
66
|
+
teacherIn: {
|
|
67
|
+
select: { id: true, name: true, subject: true, section: true },
|
|
68
|
+
},
|
|
69
|
+
studentIn: {
|
|
70
|
+
select: { id: true, name: true, subject: true, section: true },
|
|
71
|
+
},
|
|
72
|
+
schoolMemberships: {
|
|
73
|
+
select: {
|
|
74
|
+
role: true,
|
|
75
|
+
status: true,
|
|
76
|
+
school: { select: { id: true, name: true } },
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
submissions: {
|
|
80
|
+
select: {
|
|
81
|
+
id: true,
|
|
82
|
+
createdAt: true,
|
|
83
|
+
submittedAt: true,
|
|
84
|
+
gradeReceived: true,
|
|
85
|
+
extendedResponse: true,
|
|
86
|
+
assignment: { select: { id: true, title: true, classId: true } },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
events: {
|
|
90
|
+
select: {
|
|
91
|
+
id: true,
|
|
92
|
+
name: true,
|
|
93
|
+
startTime: true,
|
|
94
|
+
endTime: true,
|
|
95
|
+
location: true,
|
|
96
|
+
remarks: true,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
sentMessages: {
|
|
100
|
+
select: {
|
|
101
|
+
id: true,
|
|
102
|
+
content: true,
|
|
103
|
+
createdAt: true,
|
|
104
|
+
conversationId: true,
|
|
105
|
+
},
|
|
106
|
+
take: 500,
|
|
107
|
+
orderBy: { createdAt: "desc" },
|
|
108
|
+
},
|
|
109
|
+
notificationsReceived: {
|
|
110
|
+
select: {
|
|
111
|
+
id: true,
|
|
112
|
+
title: true,
|
|
113
|
+
content: true,
|
|
114
|
+
read: true,
|
|
115
|
+
createdAt: true,
|
|
116
|
+
},
|
|
117
|
+
take: 200,
|
|
118
|
+
orderBy: { createdAt: "desc" },
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
if (!user) {
|
|
123
|
+
throw new TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
exportedAt: new Date().toISOString(),
|
|
127
|
+
user,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Erase personal account data by anonymizing the user record and removing
|
|
132
|
+
* sessions and memberships. Educational records tied to classes (e.g.
|
|
133
|
+
* submissions) remain with an anonymized identity where required.
|
|
134
|
+
*/
|
|
135
|
+
export async function deleteUserAccount(userId) {
|
|
136
|
+
await assertAccountCanBeErased(userId);
|
|
137
|
+
await deleteSessionsByUserId(userId);
|
|
138
|
+
const anonUsername = `deleted_${userId.slice(0, 8)}`;
|
|
139
|
+
const anonEmail = `deleted+${userId}@studious.invalid`;
|
|
140
|
+
const randomPassword = await hash(randomBytes(32).toString("hex"), 10);
|
|
141
|
+
await prisma.$transaction([
|
|
142
|
+
prisma.conversationMember.deleteMany({ where: { userId } }),
|
|
143
|
+
prisma.notification.deleteMany({
|
|
144
|
+
where: { OR: [{ senderId: userId }, { receiverId: userId }] },
|
|
145
|
+
}),
|
|
146
|
+
prisma.schoolMembership.deleteMany({ where: { userId } }),
|
|
147
|
+
prisma.permission.deleteMany({
|
|
148
|
+
where: { OR: [{ userId }, { grantedById: userId }] },
|
|
149
|
+
}),
|
|
150
|
+
prisma.user.update({
|
|
151
|
+
where: { id: userId },
|
|
152
|
+
data: {
|
|
153
|
+
teacherIn: { set: [] },
|
|
154
|
+
studentIn: { set: [] },
|
|
155
|
+
departments: { set: [] },
|
|
156
|
+
tags: { set: [] },
|
|
157
|
+
specificAssignments: { set: [] },
|
|
158
|
+
presentAttendance: { set: [] },
|
|
159
|
+
lateAttendance: { set: [] },
|
|
160
|
+
absentAttendance: { set: [] },
|
|
161
|
+
schoolId: null,
|
|
162
|
+
username: anonUsername,
|
|
163
|
+
email: anonEmail,
|
|
164
|
+
password: randomPassword,
|
|
165
|
+
verified: false,
|
|
166
|
+
termsAcceptedAt: null,
|
|
167
|
+
termsVersion: null,
|
|
168
|
+
profile: {
|
|
169
|
+
update: {
|
|
170
|
+
displayName: null,
|
|
171
|
+
bio: null,
|
|
172
|
+
location: null,
|
|
173
|
+
website: null,
|
|
174
|
+
profilePicture: null,
|
|
175
|
+
profilePictureThumbnail: null,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
}),
|
|
180
|
+
]);
|
|
181
|
+
return { success: true };
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=userPrivacy.js.map
|
|
184
|
+
//# debugId=7184e3ea-1921-5a91-936e-6fa9a92f8a03
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userPrivacy.js","sources":["services/userPrivacy.ts"],"sourceRoot":"/","sourcesContent":["/**\n * User privacy service — data export and account erasure (anonymization).\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { randomBytes } from \"node:crypto\";\nimport { hash } from \"bcryptjs\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { deleteSessionsByUserId } from \"../models/auth.js\";\n\nasync function assertAccountCanBeErased(userId: string) {\n const user = await prisma.user.findUnique({\n where: { id: userId },\n select: {\n id: true,\n teacherIn: {\n select: {\n id: true,\n name: true,\n _count: { select: { students: true } },\n },\n },\n _count: {\n select: {\n assignments: true,\n createdLabChats: true,\n },\n },\n },\n });\n\n if (!user) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"User not found\" });\n }\n\n const classesWithStudents = user.teacherIn.filter(\n (cls) => cls._count.students > 0,\n );\n if (classesWithStudents.length > 0) {\n throw new TRPCError({\n code: \"PRECONDITION_FAILED\",\n message:\n \"Delete or transfer your classes before deleting your account.\",\n });\n }\n\n if (user._count.assignments > 0) {\n throw new TRPCError({\n code: \"PRECONDITION_FAILED\",\n message:\n \"Remove or reassign your assignments before deleting your account.\",\n });\n }\n\n if (user._count.createdLabChats > 0) {\n throw new TRPCError({\n code: \"PRECONDITION_FAILED\",\n message: \"Remove your AI lab chats before deleting your account.\",\n });\n }\n}\n\n/** Export a portable snapshot of the authenticated user's personal data. */\nexport async function exportUserData(userId: string) {\n const user = await prisma.user.findUnique({\n where: { id: userId },\n select: {\n id: true,\n username: true,\n email: true,\n verified: true,\n termsAcceptedAt: true,\n termsVersion: true,\n profile: true,\n teacherIn: {\n select: { id: true, name: true, subject: true, section: true },\n },\n studentIn: {\n select: { id: true, name: true, subject: true, section: true },\n },\n schoolMemberships: {\n select: {\n role: true,\n status: true,\n school: { select: { id: true, name: true } },\n },\n },\n submissions: {\n select: {\n id: true,\n createdAt: true,\n submittedAt: true,\n gradeReceived: true,\n extendedResponse: true,\n assignment: { select: { id: true, title: true, classId: true } },\n },\n },\n events: {\n select: {\n id: true,\n name: true,\n startTime: true,\n endTime: true,\n location: true,\n remarks: true,\n },\n },\n sentMessages: {\n select: {\n id: true,\n content: true,\n createdAt: true,\n conversationId: true,\n },\n take: 500,\n orderBy: { createdAt: \"desc\" },\n },\n notificationsReceived: {\n select: {\n id: true,\n title: true,\n content: true,\n read: true,\n createdAt: true,\n },\n take: 200,\n orderBy: { createdAt: \"desc\" },\n },\n },\n });\n\n if (!user) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"User not found\" });\n }\n\n return {\n exportedAt: new Date().toISOString(),\n user,\n };\n}\n\n/**\n * Erase personal account data by anonymizing the user record and removing\n * sessions and memberships. Educational records tied to classes (e.g.\n * submissions) remain with an anonymized identity where required.\n */\nexport async function deleteUserAccount(userId: string) {\n await assertAccountCanBeErased(userId);\n await deleteSessionsByUserId(userId);\n\n const anonUsername = `deleted_${userId.slice(0, 8)}`;\n const anonEmail = `deleted+${userId}@studious.invalid`;\n const randomPassword = await hash(randomBytes(32).toString(\"hex\"), 10);\n\n await prisma.$transaction([\n prisma.conversationMember.deleteMany({ where: { userId } }),\n prisma.notification.deleteMany({\n where: { OR: [{ senderId: userId }, { receiverId: userId }] },\n }),\n prisma.schoolMembership.deleteMany({ where: { userId } }),\n prisma.permission.deleteMany({\n where: { OR: [{ userId }, { grantedById: userId }] },\n }),\n prisma.user.update({\n where: { id: userId },\n data: {\n teacherIn: { set: [] },\n studentIn: { set: [] },\n departments: { set: [] },\n tags: { set: [] },\n specificAssignments: { set: [] },\n presentAttendance: { set: [] },\n lateAttendance: { set: [] },\n absentAttendance: { set: [] },\n schoolId: null,\n username: anonUsername,\n email: anonEmail,\n password: randomPassword,\n verified: false,\n termsAcceptedAt: null,\n termsVersion: null,\n profile: {\n update: {\n displayName: null,\n bio: null,\n location: null,\n website: null,\n profilePicture: null,\n profilePictureThumbnail: null,\n },\n },\n },\n }),\n ]);\n\n return { success: true };\n}\n"],"names":[],"mappings":"AAAA;;GAEG;;;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,KAAK,UAAU,wBAAwB,CAAC,MAAc;IACpD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QACxC,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,SAAS,EAAE;gBACT,MAAM,EAAE;oBACN,EAAE,EAAE,IAAI;oBACR,IAAI,EAAE,IAAI;oBACV,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;iBACvC;aACF;YACD,MAAM,EAAE;gBACN,MAAM,EAAE;oBACN,WAAW,EAAE,IAAI;oBACjB,eAAe,EAAE,IAAI;iBACtB;aACF;SACF;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAC/C,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,CAAC,CACjC,CAAC;IACF,IAAI,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EACL,+DAA+D;SAClE,CAAC,CAAC;IACL,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EACL,mEAAmE;SACtE,CAAC,CAAC;IACL,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE,wDAAwD;SAClE,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc;IACjD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QACxC,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,IAAI;YACd,eAAe,EAAE,IAAI;YACrB,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE;gBACT,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;aAC/D;YACD,SAAS,EAAE;gBACT,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;aAC/D;YACD,iBAAiB,EAAE;gBACjB,MAAM,EAAE;oBACN,IAAI,EAAE,IAAI;oBACV,MAAM,EAAE,IAAI;oBACZ,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;iBAC7C;aACF;YACD,WAAW,EAAE;gBACX,MAAM,EAAE;oBACN,EAAE,EAAE,IAAI;oBACR,SAAS,EAAE,IAAI;oBACf,WAAW,EAAE,IAAI;oBACjB,aAAa,EAAE,IAAI;oBACnB,gBAAgB,EAAE,IAAI;oBACtB,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;iBACjE;aACF;YACD,MAAM,EAAE;gBACN,MAAM,EAAE;oBACN,EAAE,EAAE,IAAI;oBACR,IAAI,EAAE,IAAI;oBACV,SAAS,EAAE,IAAI;oBACf,OAAO,EAAE,IAAI;oBACb,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,IAAI;iBACd;aACF;YACD,YAAY,EAAE;gBACZ,MAAM,EAAE;oBACN,EAAE,EAAE,IAAI;oBACR,OAAO,EAAE,IAAI;oBACb,SAAS,EAAE,IAAI;oBACf,cAAc,EAAE,IAAI;iBACrB;gBACD,IAAI,EAAE,GAAG;gBACT,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;aAC/B;YACD,qBAAqB,EAAE;gBACrB,MAAM,EAAE;oBACN,EAAE,EAAE,IAAI;oBACR,KAAK,EAAE,IAAI;oBACX,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,IAAI;oBACV,SAAS,EAAE,IAAI;iBAChB;gBACD,IAAI,EAAE,GAAG;gBACT,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;aAC/B;SACF;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO;QACL,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,IAAI;KACL,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc;IACpD,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAErC,MAAM,YAAY,GAAG,WAAW,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IACrD,MAAM,SAAS,GAAG,WAAW,MAAM,mBAAmB,CAAC;IACvD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IAEvE,MAAM,MAAM,CAAC,YAAY,CAAC;QACxB,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC;QAC3D,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC;YAC7B,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE;SAC9D,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC;QACzD,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;YAC3B,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,EAAE;SACrD,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YACjB,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;YACrB,IAAI,EAAE;gBACJ,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBACtB,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBACtB,WAAW,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBACxB,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBACjB,mBAAmB,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAChC,iBAAiB,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAC9B,cAAc,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAC3B,gBAAgB,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAC7B,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,YAAY;gBACtB,KAAK,EAAE,SAAS;gBAChB,QAAQ,EAAE,cAAc;gBACxB,QAAQ,EAAE,KAAK;gBACf,eAAe,EAAE,IAAI;gBACrB,YAAY,EAAE,IAAI;gBAClB,OAAO,EAAE;oBACP,MAAM,EAAE;wBACN,WAAW,EAAE,IAAI;wBACjB,GAAG,EAAE,IAAI;wBACT,QAAQ,EAAE,IAAI;wBACd,OAAO,EAAE,IAAI;wBACb,cAAc,EAAE,IAAI;wBACpB,uBAAuB,EAAE,IAAI;qBAC9B;iBACF;aACF;SACF,CAAC;KACH,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC","debug_id":"7184e3ea-1921-5a91-936e-6fa9a92f8a03"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type WorksheetQuestionType } from "@prisma/client";
|
|
1
|
+
import { Prisma, type WorksheetQuestionType } from "@prisma/client";
|
|
2
2
|
/** Get worksheet with questions and mark schemes. */
|
|
3
3
|
export declare function getWorksheet(worksheetId: string, userId: string): Promise<{
|
|
4
4
|
class: {
|
|
@@ -22,16 +22,16 @@ export declare function getWorksheet(worksheetId: string, userId: string): Promi
|
|
|
22
22
|
}[];
|
|
23
23
|
questions: {
|
|
24
24
|
type: import(".prisma/client").$Enums.WorksheetQuestionType;
|
|
25
|
-
options:
|
|
26
|
-
markScheme:
|
|
25
|
+
options: Prisma.JsonValue | null;
|
|
26
|
+
markScheme: Prisma.JsonValue | null;
|
|
27
27
|
id: string;
|
|
28
28
|
createdAt: Date;
|
|
29
29
|
updatedAt: Date;
|
|
30
|
-
|
|
31
|
-
worksheetId: string;
|
|
30
|
+
points: number;
|
|
32
31
|
question: string;
|
|
32
|
+
worksheetId: string;
|
|
33
|
+
order: number | null;
|
|
33
34
|
answer: string;
|
|
34
|
-
points: number;
|
|
35
35
|
}[];
|
|
36
36
|
} & {
|
|
37
37
|
status: import(".prisma/client").$Enums.JobStatus | null;
|
|
@@ -40,8 +40,8 @@ export declare function getWorksheet(worksheetId: string, userId: string): Promi
|
|
|
40
40
|
updatedAt: Date;
|
|
41
41
|
name: string;
|
|
42
42
|
classId: string;
|
|
43
|
-
sourceUrl: string | null;
|
|
44
43
|
runJobId: string | null;
|
|
44
|
+
sourceUrl: string | null;
|
|
45
45
|
}>;
|
|
46
46
|
/** Check if worksheet exists. */
|
|
47
47
|
export declare function worksheetExists(id: string): Promise<boolean>;
|
|
@@ -62,8 +62,8 @@ export declare function listWorksheets(classId: string): Promise<{
|
|
|
62
62
|
updatedAt: Date;
|
|
63
63
|
name: string;
|
|
64
64
|
classId: string;
|
|
65
|
-
sourceUrl: string | null;
|
|
66
65
|
runJobId: string | null;
|
|
66
|
+
sourceUrl: string | null;
|
|
67
67
|
}[]>;
|
|
68
68
|
export declare function updateWorksheetRecord(worksheetId: string, userId: string, data: {
|
|
69
69
|
name?: string;
|
|
@@ -74,8 +74,8 @@ export declare function updateWorksheetRecord(worksheetId: string, userId: strin
|
|
|
74
74
|
updatedAt: Date;
|
|
75
75
|
name: string;
|
|
76
76
|
classId: string;
|
|
77
|
-
sourceUrl: string | null;
|
|
78
77
|
runJobId: string | null;
|
|
78
|
+
sourceUrl: string | null;
|
|
79
79
|
}>;
|
|
80
80
|
export declare function deleteWorksheetRecord(worksheetId: string, userId: string): Promise<{
|
|
81
81
|
status: import(".prisma/client").$Enums.JobStatus | null;
|
|
@@ -84,8 +84,8 @@ export declare function deleteWorksheetRecord(worksheetId: string, userId: strin
|
|
|
84
84
|
updatedAt: Date;
|
|
85
85
|
name: string;
|
|
86
86
|
classId: string;
|
|
87
|
-
sourceUrl: string | null;
|
|
88
87
|
runJobId: string | null;
|
|
88
|
+
sourceUrl: string | null;
|
|
89
89
|
}>;
|
|
90
90
|
export declare function createWorksheetRecord(classId: string, name: string): Promise<{
|
|
91
91
|
status: import(".prisma/client").$Enums.JobStatus | null;
|
|
@@ -94,8 +94,8 @@ export declare function createWorksheetRecord(classId: string, name: string): Pr
|
|
|
94
94
|
updatedAt: Date;
|
|
95
95
|
name: string;
|
|
96
96
|
classId: string;
|
|
97
|
-
sourceUrl: string | null;
|
|
98
97
|
runJobId: string | null;
|
|
98
|
+
sourceUrl: string | null;
|
|
99
99
|
}>;
|
|
100
100
|
/**
|
|
101
101
|
* Duplicate a worksheet into another class. Caller must teach both the source worksheet's class
|
|
@@ -108,8 +108,8 @@ export declare function duplicateWorksheetRecord(worksheetId: string, classId: s
|
|
|
108
108
|
updatedAt: Date;
|
|
109
109
|
name: string;
|
|
110
110
|
classId: string;
|
|
111
|
-
sourceUrl: string | null;
|
|
112
111
|
runJobId: string | null;
|
|
112
|
+
sourceUrl: string | null;
|
|
113
113
|
}>;
|
|
114
114
|
/**
|
|
115
115
|
* Kick off PDF-to-worksheet conversion asynchronously.
|
|
@@ -157,17 +157,34 @@ export declare function addQuestionToWorksheet(worksheetId: string, userId: stri
|
|
|
157
157
|
order?: number;
|
|
158
158
|
}): Promise<{
|
|
159
159
|
type: import(".prisma/client").$Enums.WorksheetQuestionType;
|
|
160
|
-
options:
|
|
161
|
-
markScheme:
|
|
160
|
+
options: Prisma.JsonValue | null;
|
|
161
|
+
markScheme: Prisma.JsonValue | null;
|
|
162
162
|
id: string;
|
|
163
163
|
createdAt: Date;
|
|
164
164
|
updatedAt: Date;
|
|
165
|
-
|
|
165
|
+
points: number;
|
|
166
|
+
question: string;
|
|
166
167
|
worksheetId: string;
|
|
168
|
+
order: number | null;
|
|
169
|
+
answer: string;
|
|
170
|
+
}>;
|
|
171
|
+
/** A question's full editable state as captured before an AI modifier run. */
|
|
172
|
+
export interface WorksheetQuestionSnapshot {
|
|
173
|
+
id: string;
|
|
174
|
+
type: WorksheetQuestionType;
|
|
167
175
|
question: string;
|
|
168
176
|
answer: string;
|
|
177
|
+
options: unknown;
|
|
178
|
+
markScheme: unknown;
|
|
169
179
|
points: number;
|
|
170
|
-
|
|
180
|
+
order: number | null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Restore a worksheet's questions to a snapshot: deletes questions not in the
|
|
184
|
+
* snapshot, re-creates deleted ones (with their original ids), and reverts
|
|
185
|
+
* edited ones. Used by AI modifier undo.
|
|
186
|
+
*/
|
|
187
|
+
export declare function restoreWorksheetQuestions(worksheetId: string, userId: string, questions: WorksheetQuestionSnapshot[]): Promise<void>;
|
|
171
188
|
export declare function reorderWorksheetQuestions(worksheetId: string, userId: string, movedId: string, position: "before" | "after", targetId: string): Promise<{
|
|
172
189
|
id: string;
|
|
173
190
|
}[]>;
|
|
@@ -180,29 +197,29 @@ export declare function updateWorksheetQuestionRecord(worksheetId: string, quest
|
|
|
180
197
|
type?: WorksheetQuestionType;
|
|
181
198
|
}): Promise<{
|
|
182
199
|
type: import(".prisma/client").$Enums.WorksheetQuestionType;
|
|
183
|
-
options:
|
|
184
|
-
markScheme:
|
|
200
|
+
options: Prisma.JsonValue | null;
|
|
201
|
+
markScheme: Prisma.JsonValue | null;
|
|
185
202
|
id: string;
|
|
186
203
|
createdAt: Date;
|
|
187
204
|
updatedAt: Date;
|
|
188
|
-
|
|
189
|
-
worksheetId: string;
|
|
205
|
+
points: number;
|
|
190
206
|
question: string;
|
|
207
|
+
worksheetId: string;
|
|
208
|
+
order: number | null;
|
|
191
209
|
answer: string;
|
|
192
|
-
points: number;
|
|
193
210
|
}>;
|
|
194
211
|
export declare function deleteWorksheetQuestionRecord(worksheetId: string, questionId: string, userId: string): Promise<{
|
|
195
212
|
type: import(".prisma/client").$Enums.WorksheetQuestionType;
|
|
196
|
-
options:
|
|
197
|
-
markScheme:
|
|
213
|
+
options: Prisma.JsonValue | null;
|
|
214
|
+
markScheme: Prisma.JsonValue | null;
|
|
198
215
|
id: string;
|
|
199
216
|
createdAt: Date;
|
|
200
217
|
updatedAt: Date;
|
|
201
|
-
|
|
202
|
-
worksheetId: string;
|
|
218
|
+
points: number;
|
|
203
219
|
question: string;
|
|
220
|
+
worksheetId: string;
|
|
221
|
+
order: number | null;
|
|
204
222
|
answer: string;
|
|
205
|
-
points: number;
|
|
206
223
|
}>;
|
|
207
224
|
export declare function getWorksheetSubmission(worksheetId: string, submissionId: string, userId: string): Promise<{
|
|
208
225
|
responses: ({
|
|
@@ -216,14 +233,14 @@ export declare function getWorksheetSubmission(worksheetId: string, submissionId
|
|
|
216
233
|
createdAt: Date;
|
|
217
234
|
updatedAt: Date | null;
|
|
218
235
|
feedback: string | null;
|
|
219
|
-
studentId: string;
|
|
220
236
|
runJobId: string | null;
|
|
221
|
-
points: number;
|
|
222
237
|
response: string;
|
|
223
238
|
isCorrect: boolean;
|
|
224
|
-
markschemeState:
|
|
225
|
-
|
|
239
|
+
markschemeState: Prisma.JsonValue | null;
|
|
240
|
+
points: number;
|
|
241
|
+
studentId: string;
|
|
226
242
|
questionId: string;
|
|
243
|
+
studentWorksheetResponseId: string | null;
|
|
227
244
|
})[];
|
|
228
245
|
} & {
|
|
229
246
|
id: string;
|
|
@@ -231,9 +248,9 @@ export declare function getWorksheetSubmission(worksheetId: string, submissionId
|
|
|
231
248
|
updatedAt: Date;
|
|
232
249
|
submissionId: string | null;
|
|
233
250
|
studentId: string;
|
|
251
|
+
worksheetId: string;
|
|
234
252
|
submittedAt: Date | null;
|
|
235
253
|
submitted: boolean;
|
|
236
|
-
worksheetId: string;
|
|
237
254
|
}>;
|
|
238
255
|
export declare function answerWorksheetQuestion(worksheetResponseId: string, questionId: string, response: string, userId: string): Promise<{
|
|
239
256
|
responses: {
|
|
@@ -242,14 +259,14 @@ export declare function answerWorksheetQuestion(worksheetResponseId: string, que
|
|
|
242
259
|
createdAt: Date;
|
|
243
260
|
updatedAt: Date | null;
|
|
244
261
|
feedback: string | null;
|
|
245
|
-
studentId: string;
|
|
246
262
|
runJobId: string | null;
|
|
247
|
-
points: number;
|
|
248
263
|
response: string;
|
|
249
264
|
isCorrect: boolean;
|
|
250
|
-
markschemeState:
|
|
251
|
-
|
|
265
|
+
markschemeState: Prisma.JsonValue | null;
|
|
266
|
+
points: number;
|
|
267
|
+
studentId: string;
|
|
252
268
|
questionId: string;
|
|
269
|
+
studentWorksheetResponseId: string | null;
|
|
253
270
|
}[];
|
|
254
271
|
} & {
|
|
255
272
|
id: string;
|
|
@@ -257,9 +274,9 @@ export declare function answerWorksheetQuestion(worksheetResponseId: string, que
|
|
|
257
274
|
updatedAt: Date;
|
|
258
275
|
submissionId: string | null;
|
|
259
276
|
studentId: string;
|
|
277
|
+
worksheetId: string;
|
|
260
278
|
submittedAt: Date | null;
|
|
261
279
|
submitted: boolean;
|
|
262
|
-
worksheetId: string;
|
|
263
280
|
}>;
|
|
264
281
|
export declare function cancelGrading(worksheetResponseId: string, progressId: string, userId: string): Promise<{
|
|
265
282
|
status: import(".prisma/client").$Enums.JobStatus | null;
|
|
@@ -267,14 +284,14 @@ export declare function cancelGrading(worksheetResponseId: string, progressId: s
|
|
|
267
284
|
createdAt: Date;
|
|
268
285
|
updatedAt: Date | null;
|
|
269
286
|
feedback: string | null;
|
|
270
|
-
studentId: string;
|
|
271
287
|
runJobId: string | null;
|
|
272
|
-
points: number;
|
|
273
288
|
response: string;
|
|
274
289
|
isCorrect: boolean;
|
|
275
|
-
markschemeState:
|
|
276
|
-
|
|
290
|
+
markschemeState: Prisma.JsonValue | null;
|
|
291
|
+
points: number;
|
|
292
|
+
studentId: string;
|
|
277
293
|
questionId: string;
|
|
294
|
+
studentWorksheetResponseId: string | null;
|
|
278
295
|
}>;
|
|
279
296
|
export declare function regradeQuestion(worksheetResponseId: string, progressId: string, userId: string): Promise<{
|
|
280
297
|
status: import(".prisma/client").$Enums.JobStatus | null;
|
|
@@ -282,14 +299,14 @@ export declare function regradeQuestion(worksheetResponseId: string, progressId:
|
|
|
282
299
|
createdAt: Date;
|
|
283
300
|
updatedAt: Date | null;
|
|
284
301
|
feedback: string | null;
|
|
285
|
-
studentId: string;
|
|
286
302
|
runJobId: string | null;
|
|
287
|
-
points: number;
|
|
288
303
|
response: string;
|
|
289
304
|
isCorrect: boolean;
|
|
290
|
-
markschemeState:
|
|
291
|
-
|
|
305
|
+
markschemeState: Prisma.JsonValue | null;
|
|
306
|
+
points: number;
|
|
307
|
+
studentId: string;
|
|
292
308
|
questionId: string;
|
|
309
|
+
studentWorksheetResponseId: string | null;
|
|
293
310
|
} | null>;
|
|
294
311
|
export declare function gradeAnswer(questionId: string, studentWorksheetResponseId: string, userId: string, data: {
|
|
295
312
|
responseId?: string;
|
|
@@ -304,14 +321,14 @@ export declare function gradeAnswer(questionId: string, studentWorksheetResponse
|
|
|
304
321
|
createdAt: Date;
|
|
305
322
|
updatedAt: Date | null;
|
|
306
323
|
feedback: string | null;
|
|
307
|
-
studentId: string;
|
|
308
324
|
runJobId: string | null;
|
|
309
|
-
points: number;
|
|
310
325
|
response: string;
|
|
311
326
|
isCorrect: boolean;
|
|
312
|
-
markschemeState:
|
|
313
|
-
|
|
327
|
+
markschemeState: Prisma.JsonValue | null;
|
|
328
|
+
points: number;
|
|
329
|
+
studentId: string;
|
|
314
330
|
questionId: string;
|
|
331
|
+
studentWorksheetResponseId: string | null;
|
|
315
332
|
}>;
|
|
316
333
|
export declare function addCommentToResponse(responseId: string, comment: string, authorId: string): Promise<{
|
|
317
334
|
id: string;
|
|
@@ -319,8 +336,8 @@ export declare function addCommentToResponse(responseId: string, comment: string
|
|
|
319
336
|
content: string;
|
|
320
337
|
assignmentId: string | null;
|
|
321
338
|
submissionId: string | null;
|
|
322
|
-
modifiedAt: Date | null;
|
|
323
339
|
announcementId: string | null;
|
|
340
|
+
modifiedAt: Date | null;
|
|
324
341
|
authorId: string;
|
|
325
342
|
isPinned: boolean;
|
|
326
343
|
parentCommentId: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worksheet.d.ts","sourceRoot":"/","sources":["services/worksheet.ts"],"names":[],"mappings":"AAKA,OAAO,EAAa,KAAK,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"worksheet.d.ts","sourceRoot":"/","sources":["services/worksheet.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Integrates with gradeWorksheet pipeline for auto-grading.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
6
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ecf79b03-756f-53ee-81b5-52a53a17b0e8")}catch(e){}}();
|
|
7
7
|
import { TRPCError } from "@trpc/server";
|
|
8
|
-
import { JobStatus } from "@prisma/client";
|
|
8
|
+
import { JobStatus, Prisma } from "@prisma/client";
|
|
9
9
|
import { prisma } from "../lib/prisma.js";
|
|
10
10
|
import { getFileUrl } from "../lib/fileUpload.js";
|
|
11
11
|
import { cancelGradePipeline, regradeWorksheetPipeline } from "../pipelines/gradeWorksheet.js";
|
|
@@ -226,6 +226,51 @@ export async function addQuestionToWorksheet(worksheetId, userId, data) {
|
|
|
226
226
|
...data,
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
|
+
const toJsonInput = (value) => value === null || value === undefined
|
|
230
|
+
? Prisma.JsonNull
|
|
231
|
+
: value;
|
|
232
|
+
/**
|
|
233
|
+
* Restore a worksheet's questions to a snapshot: deletes questions not in the
|
|
234
|
+
* snapshot, re-creates deleted ones (with their original ids), and reverts
|
|
235
|
+
* edited ones. Used by AI modifier undo.
|
|
236
|
+
*/
|
|
237
|
+
export async function restoreWorksheetQuestions(worksheetId, userId, questions) {
|
|
238
|
+
const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
|
|
239
|
+
if (!worksheet) {
|
|
240
|
+
throw new TRPCError({
|
|
241
|
+
code: "FORBIDDEN",
|
|
242
|
+
message: "Only class teachers can modify worksheet questions",
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
await prisma.$transaction([
|
|
246
|
+
prisma.worksheetQuestion.deleteMany({
|
|
247
|
+
where: { worksheetId, id: { notIn: questions.map((q) => q.id) } },
|
|
248
|
+
}),
|
|
249
|
+
...questions.map((q) => prisma.worksheetQuestion.upsert({
|
|
250
|
+
where: { id: q.id },
|
|
251
|
+
create: {
|
|
252
|
+
id: q.id,
|
|
253
|
+
worksheetId,
|
|
254
|
+
type: q.type,
|
|
255
|
+
question: q.question,
|
|
256
|
+
answer: q.answer,
|
|
257
|
+
options: toJsonInput(q.options),
|
|
258
|
+
markScheme: toJsonInput(q.markScheme),
|
|
259
|
+
points: q.points,
|
|
260
|
+
order: q.order,
|
|
261
|
+
},
|
|
262
|
+
update: {
|
|
263
|
+
type: q.type,
|
|
264
|
+
question: q.question,
|
|
265
|
+
answer: q.answer,
|
|
266
|
+
options: toJsonInput(q.options),
|
|
267
|
+
markScheme: toJsonInput(q.markScheme),
|
|
268
|
+
points: q.points,
|
|
269
|
+
order: q.order,
|
|
270
|
+
},
|
|
271
|
+
})),
|
|
272
|
+
]);
|
|
273
|
+
}
|
|
229
274
|
export async function reorderWorksheetQuestions(worksheetId, userId, movedId, position, targetId) {
|
|
230
275
|
const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
|
|
231
276
|
if (!worksheet) {
|
|
@@ -449,4 +494,4 @@ export async function addCommentToResponse(responseId, comment, authorId) {
|
|
|
449
494
|
});
|
|
450
495
|
}
|
|
451
496
|
//# sourceMappingURL=worksheet.js.map
|
|
452
|
-
//# debugId=
|
|
497
|
+
//# debugId=ecf79b03-756f-53ee-81b5-52a53a17b0e8
|