@studious-lms/server 1.2.47 → 1.2.49
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/index.js +22 -18
- package/dist/index.js.map +1 -1
- package/dist/middleware/security.d.ts.map +1 -1
- package/dist/middleware/security.js +4 -4
- package/dist/middleware/security.js.map +1 -1
- package/dist/routers/_app.d.ts +186 -0
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/agenda.d.ts +52 -0
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/agenda.js +4 -2
- package/dist/routers/agenda.js.map +1 -1
- package/dist/routers/announcement.d.ts +20 -0
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +9 -2
- package/dist/routers/announcement.js.map +1 -1
- package/dist/routers/assignment.d.ts +3 -0
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +5 -2
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/class.d.ts +18 -0
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +56 -2
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/newtonChat.d.ts.map +1 -1
- package/dist/routers/newtonChat.js +5 -6
- package/dist/routers/newtonChat.js.map +1 -1
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/seedDatabase.js +34 -8
- package/dist/seedDatabase.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +24 -22
- package/src/middleware/security.ts +2 -2
- package/src/routers/agenda.ts +2 -0
- package/src/routers/announcement.ts +7 -0
- package/src/routers/assignment.ts +3 -0
- package/src/routers/class.ts +55 -0
- package/src/routers/newtonChat.ts +7 -9
- package/src/seedDatabase.ts +38 -6
package/src/index.ts
CHANGED
|
@@ -34,8 +34,6 @@ app.use((req, res, next) => {
|
|
|
34
34
|
next();
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
app.use(generalLimiter);
|
|
38
|
-
|
|
39
37
|
const allowedOrigins = env.NODE_ENV === 'production'
|
|
40
38
|
? [
|
|
41
39
|
'https://www.studious.sh',
|
|
@@ -59,9 +57,13 @@ app.use(cors({
|
|
|
59
57
|
credentials: true,
|
|
60
58
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
61
59
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'x-user'],
|
|
62
|
-
|
|
60
|
+
preflightContinue: false, // Important: stop further handling of OPTIONS
|
|
61
|
+
optionsSuccessStatus: 204, // Recommended for modern browsers
|
|
62
|
+
|
|
63
63
|
}));
|
|
64
64
|
|
|
65
|
+
app.use(generalLimiter);
|
|
66
|
+
|
|
65
67
|
// CORS debugging middleware
|
|
66
68
|
app.use((req, res, next) => {
|
|
67
69
|
if (req.method === 'OPTIONS' || req.path.includes('trpc')) {
|
|
@@ -90,25 +92,25 @@ app.use((req, res, next) => {
|
|
|
90
92
|
next();
|
|
91
93
|
});
|
|
92
94
|
|
|
93
|
-
app.use("/panel", async (_, res) => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
95
|
+
// app.use("/panel", async (_, res) => {
|
|
96
|
+
// if (env.NODE_ENV !== "development") {
|
|
97
|
+
// return res.status(404).send("Not Found");
|
|
98
|
+
// }
|
|
99
|
+
|
|
100
|
+
// // Dynamically import renderTrpcPanel only in development
|
|
101
|
+
// const { renderTrpcPanel } = await import("trpc-ui");
|
|
102
|
+
|
|
103
|
+
// return res.send(
|
|
104
|
+
// renderTrpcPanel(appRouter, {
|
|
105
|
+
// url: "/trpc", // Base url of your trpc server
|
|
106
|
+
// meta: {
|
|
107
|
+
// title: "Studious Backend",
|
|
108
|
+
// description:
|
|
109
|
+
// "This is the backend for the Studious application.",
|
|
110
|
+
// },
|
|
111
|
+
// })
|
|
112
|
+
// );
|
|
113
|
+
// });
|
|
112
114
|
|
|
113
115
|
|
|
114
116
|
// Create HTTP server
|
|
@@ -19,7 +19,7 @@ const rateLimitHandler = (req: Request, res: Response) => {
|
|
|
19
19
|
|
|
20
20
|
// General API rate limiter - applies to all routes
|
|
21
21
|
export const generalLimiter = rateLimit({
|
|
22
|
-
windowMs: 10 * 60
|
|
22
|
+
windowMs: 10 * 60, // 10 minutes
|
|
23
23
|
max: 100, // Limit each IP to 100 requests per windowMs
|
|
24
24
|
message: 'Too many requests from this IP, please try again later.',
|
|
25
25
|
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
|
@@ -27,7 +27,7 @@ export const generalLimiter = rateLimit({
|
|
|
27
27
|
handler: rateLimitHandler,
|
|
28
28
|
skip: (req) => {
|
|
29
29
|
// Skip rate limiting for health checks
|
|
30
|
-
return req.path === '/health';
|
|
30
|
+
return req.path === '/health' || req.method === 'OPTIONS';
|
|
31
31
|
},
|
|
32
32
|
});
|
|
33
33
|
|
package/src/routers/agenda.ts
CHANGED
|
@@ -37,6 +37,7 @@ export const agendaRouter = createTRPCRouter({
|
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
include: {
|
|
40
|
+
assignmentsAttached: true,
|
|
40
41
|
class: true,
|
|
41
42
|
},
|
|
42
43
|
}),
|
|
@@ -67,6 +68,7 @@ export const agendaRouter = createTRPCRouter({
|
|
|
67
68
|
},
|
|
68
69
|
},
|
|
69
70
|
include: {
|
|
71
|
+
assignmentsAttached: true,
|
|
70
72
|
class: true,
|
|
71
73
|
},
|
|
72
74
|
}),
|
|
@@ -1015,6 +1015,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1015
1015
|
endTime: true,
|
|
1016
1016
|
location: true,
|
|
1017
1017
|
remarks: true,
|
|
1018
|
+
color: true,
|
|
1018
1019
|
}
|
|
1019
1020
|
},
|
|
1020
1021
|
markScheme: {
|
|
@@ -1835,6 +1836,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1835
1836
|
name: true,
|
|
1836
1837
|
startTime: true,
|
|
1837
1838
|
endTime: true,
|
|
1839
|
+
color: true,
|
|
1838
1840
|
}
|
|
1839
1841
|
}
|
|
1840
1842
|
}
|
|
@@ -1969,6 +1971,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1969
1971
|
endTime: true,
|
|
1970
1972
|
location: true,
|
|
1971
1973
|
remarks: true,
|
|
1974
|
+
color: true,
|
|
1972
1975
|
},
|
|
1973
1976
|
orderBy: {
|
|
1974
1977
|
startTime: 'asc'
|
package/src/routers/class.ts
CHANGED
|
@@ -26,12 +26,39 @@ export const classRouter = createTRPCRouter({
|
|
|
26
26
|
template: false,
|
|
27
27
|
},
|
|
28
28
|
select: {
|
|
29
|
+
|
|
29
30
|
id: true,
|
|
30
31
|
title: true,
|
|
31
32
|
type: true,
|
|
32
33
|
dueDate: true,
|
|
33
34
|
},
|
|
34
35
|
},
|
|
36
|
+
students: {
|
|
37
|
+
select: {
|
|
38
|
+
id: true,
|
|
39
|
+
username: true,
|
|
40
|
+
profile: {
|
|
41
|
+
select: {
|
|
42
|
+
displayName: true,
|
|
43
|
+
profilePicture: true,
|
|
44
|
+
profilePictureThumbnail: true,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
teachers: {
|
|
50
|
+
select: {
|
|
51
|
+
id: true,
|
|
52
|
+
username: true,
|
|
53
|
+
profile: {
|
|
54
|
+
select: {
|
|
55
|
+
displayName: true,
|
|
56
|
+
profilePicture: true,
|
|
57
|
+
profilePictureThumbnail: true,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
35
62
|
},
|
|
36
63
|
}),
|
|
37
64
|
prisma.class.findMany({
|
|
@@ -57,6 +84,32 @@ export const classRouter = createTRPCRouter({
|
|
|
57
84
|
dueDate: true,
|
|
58
85
|
},
|
|
59
86
|
},
|
|
87
|
+
students: {
|
|
88
|
+
select: {
|
|
89
|
+
id: true,
|
|
90
|
+
username: true,
|
|
91
|
+
profile: {
|
|
92
|
+
select: {
|
|
93
|
+
displayName: true,
|
|
94
|
+
profilePicture: true,
|
|
95
|
+
profilePictureThumbnail: true,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
teachers: {
|
|
101
|
+
select: {
|
|
102
|
+
id: true,
|
|
103
|
+
username: true,
|
|
104
|
+
profile: {
|
|
105
|
+
select: {
|
|
106
|
+
displayName: true,
|
|
107
|
+
profilePicture: true,
|
|
108
|
+
profilePictureThumbnail: true,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
60
113
|
},
|
|
61
114
|
}),
|
|
62
115
|
]);
|
|
@@ -69,6 +122,7 @@ export const classRouter = createTRPCRouter({
|
|
|
69
122
|
subject: cls.subject,
|
|
70
123
|
dueToday: cls.assignments,
|
|
71
124
|
assignments: cls.assignments,
|
|
125
|
+
members: [...cls.students, ...cls.teachers],
|
|
72
126
|
color: cls.color,
|
|
73
127
|
})),
|
|
74
128
|
studentInClass: studentClasses.map(cls => ({
|
|
@@ -78,6 +132,7 @@ export const classRouter = createTRPCRouter({
|
|
|
78
132
|
subject: cls.subject,
|
|
79
133
|
dueToday: cls.assignments,
|
|
80
134
|
assignments: cls.assignments,
|
|
135
|
+
members: [...cls.students, ...cls.teachers],
|
|
81
136
|
color: cls.color,
|
|
82
137
|
})),
|
|
83
138
|
};
|
|
@@ -120,19 +120,17 @@ export const newtonChatRouter = createTRPCRouter({
|
|
|
120
120
|
title: 'Session with Newton Tutor',
|
|
121
121
|
},
|
|
122
122
|
});
|
|
123
|
+
generateAndSendNewtonIntroduction(
|
|
124
|
+
newtonChat.id,
|
|
125
|
+
newtonChat.conversationId,
|
|
126
|
+
submission.id
|
|
127
|
+
).catch(error => {
|
|
128
|
+
logger.error('Failed to generate AI introduction:', { error, newtonChatId: result.id });
|
|
129
|
+
});
|
|
123
130
|
|
|
124
131
|
return newtonChat;
|
|
125
132
|
});
|
|
126
133
|
|
|
127
|
-
// Generate AI introduction message in parallel (don't await - fire and forget)
|
|
128
|
-
generateAndSendNewtonIntroduction(
|
|
129
|
-
result.id,
|
|
130
|
-
result.conversationId,
|
|
131
|
-
submission.id
|
|
132
|
-
).catch(error => {
|
|
133
|
-
logger.error('Failed to generate AI introduction:', { error, newtonChatId: result.id });
|
|
134
|
-
});
|
|
135
|
-
|
|
136
134
|
return {
|
|
137
135
|
conversationId: result.conversationId,
|
|
138
136
|
newtonChatId: result.id,
|
package/src/seedDatabase.ts
CHANGED
|
@@ -8,6 +8,21 @@ export async function clearDatabase() {
|
|
|
8
8
|
logger.info('Clearing database');
|
|
9
9
|
await prisma.notification.deleteMany();
|
|
10
10
|
|
|
11
|
+
// Delete worksheet-related records
|
|
12
|
+
await prisma.studentQuestionProgress.deleteMany();
|
|
13
|
+
await prisma.studentWorksheetResponse.deleteMany();
|
|
14
|
+
await prisma.worksheetQuestion.deleteMany();
|
|
15
|
+
await prisma.worksheet.deleteMany();
|
|
16
|
+
|
|
17
|
+
// Delete reactions (they reference announcements and comments)
|
|
18
|
+
await prisma.reaction.deleteMany();
|
|
19
|
+
|
|
20
|
+
// Delete comments (they reference announcements and users)
|
|
21
|
+
await prisma.comment.deleteMany();
|
|
22
|
+
|
|
23
|
+
// Delete NewtonChat (they reference submissions and conversations)
|
|
24
|
+
await prisma.newtonChat.deleteMany();
|
|
25
|
+
|
|
11
26
|
// Delete chat-related records
|
|
12
27
|
await prisma.mention.deleteMany();
|
|
13
28
|
await prisma.message.deleteMany();
|
|
@@ -39,10 +54,19 @@ export async function clearDatabase() {
|
|
|
39
54
|
// Delete schools (which reference files for logos) - this will cascade delete the file references
|
|
40
55
|
await prisma.school.deleteMany();
|
|
41
56
|
|
|
57
|
+
// Delete marketing-related records
|
|
58
|
+
await prisma.schoolDevelopementProgram.deleteMany();
|
|
59
|
+
await prisma.earlyAccessRequest.deleteMany();
|
|
60
|
+
|
|
42
61
|
// Finally delete all files
|
|
43
62
|
await prisma.file.deleteMany();
|
|
44
63
|
}
|
|
45
64
|
|
|
65
|
+
// Helper function to generate DiceBear avatar URL
|
|
66
|
+
function getDiceBearAvatar(seed: string): string {
|
|
67
|
+
return `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(seed)}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
46
70
|
export async function createUser(email: string, password: string, username: string) {
|
|
47
71
|
logger.debug("Creating user", { email, username, password });
|
|
48
72
|
|
|
@@ -109,7 +133,7 @@ export const seedDatabase = async () => {
|
|
|
109
133
|
createUser('charlotte.walker@student.riverside.edu', 'student123', 'charlotte.walker'),
|
|
110
134
|
]);
|
|
111
135
|
|
|
112
|
-
// 4. Create User Profiles
|
|
136
|
+
// 4. Create User Profiles with DiceBear avatars
|
|
113
137
|
await Promise.all([
|
|
114
138
|
prisma.userProfile.create({
|
|
115
139
|
data: {
|
|
@@ -118,6 +142,7 @@ export const seedDatabase = async () => {
|
|
|
118
142
|
bio: 'Biology teacher with 15 years of experience. Passionate about making science accessible to all students.',
|
|
119
143
|
location: 'Riverside, CA',
|
|
120
144
|
website: 'https://sarahjohnson-bio.com',
|
|
145
|
+
profilePicture: getDiceBearAvatar(teachers[0].username),
|
|
121
146
|
}
|
|
122
147
|
}),
|
|
123
148
|
prisma.userProfile.create({
|
|
@@ -126,6 +151,7 @@ export const seedDatabase = async () => {
|
|
|
126
151
|
displayName: 'Mr. Michael Chen',
|
|
127
152
|
bio: 'Mathematics teacher and department head. Specializes in AP Calculus and Statistics.',
|
|
128
153
|
location: 'Riverside, CA',
|
|
154
|
+
profilePicture: getDiceBearAvatar(teachers[1].username),
|
|
129
155
|
}
|
|
130
156
|
}),
|
|
131
157
|
prisma.userProfile.create({
|
|
@@ -134,18 +160,24 @@ export const seedDatabase = async () => {
|
|
|
134
160
|
displayName: 'Ms. Emma Davis',
|
|
135
161
|
bio: 'English Literature teacher. Loves fostering creative writing and critical thinking.',
|
|
136
162
|
location: 'Riverside, CA',
|
|
163
|
+
profilePicture: getDiceBearAvatar(teachers[2].username),
|
|
137
164
|
}
|
|
138
165
|
}),
|
|
139
166
|
]);
|
|
140
167
|
|
|
141
|
-
// Add profiles for
|
|
142
|
-
await Promise.all(students.
|
|
143
|
-
const names = [
|
|
168
|
+
// Add profiles for all students with DiceBear avatars
|
|
169
|
+
await Promise.all(students.map((student, index) => {
|
|
170
|
+
const names = [
|
|
171
|
+
'Alex Martinez', 'Sophia Williams', 'James Brown', 'Olivia Taylor',
|
|
172
|
+
'Ethan Anderson', 'Ava Thomas', 'Noah Jackson', 'Isabella White',
|
|
173
|
+
'Liam Harris', 'Mia Clark', 'Lucas Lewis', 'Charlotte Walker'
|
|
174
|
+
];
|
|
144
175
|
return prisma.userProfile.create({
|
|
145
176
|
data: {
|
|
146
177
|
userId: student.id,
|
|
147
|
-
displayName: names[index],
|
|
148
|
-
bio: `Grade 11 student at Riverside High School
|
|
178
|
+
displayName: names[index] || student.username,
|
|
179
|
+
bio: index < 6 ? `Grade 11 student at Riverside High School.` : undefined,
|
|
180
|
+
profilePicture: getDiceBearAvatar(student.username),
|
|
149
181
|
}
|
|
150
182
|
});
|
|
151
183
|
}));
|