@studious-lms/server 1.0.6 → 1.0.7

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.
Files changed (101) hide show
  1. package/API_SPECIFICATION.md +1117 -0
  2. package/dist/exportType.js +1 -2
  3. package/dist/index.js +25 -30
  4. package/dist/lib/fileUpload.d.ts.map +1 -1
  5. package/dist/lib/fileUpload.js +31 -29
  6. package/dist/lib/googleCloudStorage.js +9 -14
  7. package/dist/lib/prisma.js +4 -7
  8. package/dist/lib/thumbnailGenerator.js +12 -20
  9. package/dist/middleware/auth.d.ts.map +1 -1
  10. package/dist/middleware/auth.js +17 -22
  11. package/dist/middleware/logging.js +5 -9
  12. package/dist/routers/_app.d.ts +3483 -1801
  13. package/dist/routers/_app.d.ts.map +1 -1
  14. package/dist/routers/_app.js +28 -27
  15. package/dist/routers/agenda.d.ts +13 -8
  16. package/dist/routers/agenda.d.ts.map +1 -1
  17. package/dist/routers/agenda.js +14 -17
  18. package/dist/routers/announcement.d.ts +4 -3
  19. package/dist/routers/announcement.d.ts.map +1 -1
  20. package/dist/routers/announcement.js +28 -31
  21. package/dist/routers/assignment.d.ts +282 -196
  22. package/dist/routers/assignment.d.ts.map +1 -1
  23. package/dist/routers/assignment.js +256 -202
  24. package/dist/routers/attendance.d.ts +5 -4
  25. package/dist/routers/attendance.d.ts.map +1 -1
  26. package/dist/routers/attendance.js +31 -34
  27. package/dist/routers/auth.d.ts +1 -0
  28. package/dist/routers/auth.d.ts.map +1 -1
  29. package/dist/routers/auth.js +80 -75
  30. package/dist/routers/class.d.ts +284 -14
  31. package/dist/routers/class.d.ts.map +1 -1
  32. package/dist/routers/class.js +435 -164
  33. package/dist/routers/event.d.ts +47 -38
  34. package/dist/routers/event.d.ts.map +1 -1
  35. package/dist/routers/event.js +76 -79
  36. package/dist/routers/file.d.ts +71 -1
  37. package/dist/routers/file.d.ts.map +1 -1
  38. package/dist/routers/file.js +267 -32
  39. package/dist/routers/folder.d.ts +296 -0
  40. package/dist/routers/folder.d.ts.map +1 -0
  41. package/dist/routers/folder.js +693 -0
  42. package/dist/routers/notifications.d.ts +103 -0
  43. package/dist/routers/notifications.d.ts.map +1 -0
  44. package/dist/routers/notifications.js +91 -0
  45. package/dist/routers/school.d.ts +208 -0
  46. package/dist/routers/school.d.ts.map +1 -0
  47. package/dist/routers/school.js +481 -0
  48. package/dist/routers/section.d.ts +1 -0
  49. package/dist/routers/section.d.ts.map +1 -1
  50. package/dist/routers/section.js +30 -33
  51. package/dist/routers/user.d.ts +2 -1
  52. package/dist/routers/user.d.ts.map +1 -1
  53. package/dist/routers/user.js +21 -24
  54. package/dist/seedDatabase.d.ts +22 -0
  55. package/dist/seedDatabase.d.ts.map +1 -0
  56. package/dist/seedDatabase.js +57 -0
  57. package/dist/socket/handlers.js +26 -30
  58. package/dist/trpc.d.ts +5 -0
  59. package/dist/trpc.d.ts.map +1 -1
  60. package/dist/trpc.js +35 -26
  61. package/dist/types/trpc.js +1 -2
  62. package/dist/utils/email.js +2 -8
  63. package/dist/utils/generateInviteCode.js +1 -5
  64. package/dist/utils/logger.d.ts.map +1 -1
  65. package/dist/utils/logger.js +13 -9
  66. package/dist/utils/prismaErrorHandler.d.ts +9 -0
  67. package/dist/utils/prismaErrorHandler.d.ts.map +1 -0
  68. package/dist/utils/prismaErrorHandler.js +234 -0
  69. package/dist/utils/prismaWrapper.d.ts +14 -0
  70. package/dist/utils/prismaWrapper.d.ts.map +1 -0
  71. package/dist/utils/prismaWrapper.js +64 -0
  72. package/package.json +11 -4
  73. package/prisma/migrations/20250807062924_init/migration.sql +436 -0
  74. package/prisma/migrations/migration_lock.toml +3 -0
  75. package/prisma/schema.prisma +67 -0
  76. package/src/index.ts +2 -2
  77. package/src/lib/fileUpload.ts +16 -7
  78. package/src/middleware/auth.ts +0 -2
  79. package/src/routers/_app.ts +5 -1
  80. package/src/routers/assignment.ts +82 -22
  81. package/src/routers/auth.ts +80 -54
  82. package/src/routers/class.ts +330 -36
  83. package/src/routers/file.ts +283 -20
  84. package/src/routers/folder.ts +755 -0
  85. package/src/routers/notifications.ts +93 -0
  86. package/src/seedDatabase.ts +66 -0
  87. package/src/socket/handlers.ts +4 -4
  88. package/src/trpc.ts +13 -0
  89. package/src/utils/logger.ts +14 -4
  90. package/src/utils/prismaErrorHandler.ts +275 -0
  91. package/src/utils/prismaWrapper.ts +91 -0
  92. package/tests/auth.test.ts +25 -0
  93. package/tests/class.test.ts +281 -0
  94. package/tests/setup.ts +98 -0
  95. package/tests/startup.test.ts +5 -0
  96. package/tsconfig.json +2 -1
  97. package/vitest.config.ts +11 -0
  98. package/dist/logger.d.ts +0 -26
  99. package/dist/logger.d.ts.map +0 -1
  100. package/dist/logger.js +0 -135
  101. package/src/logger.ts +0 -163
@@ -1,31 +1,28 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.userRouter = void 0;
4
- const zod_1 = require("zod");
5
- const trpc_1 = require("../trpc");
6
- const server_1 = require("@trpc/server");
7
- const prisma_1 = require("../lib/prisma");
8
- const fileUpload_1 = require("../lib/fileUpload");
9
- const fileSchema = zod_1.z.object({
10
- name: zod_1.z.string(),
11
- type: zod_1.z.string(),
12
- size: zod_1.z.number(),
13
- data: zod_1.z.string(), // base64 encoded file data
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure } from "../trpc";
3
+ import { TRPCError } from "@trpc/server";
4
+ import { prisma } from "../lib/prisma";
5
+ import { uploadFiles } from "../lib/fileUpload";
6
+ const fileSchema = z.object({
7
+ name: z.string(),
8
+ type: z.string(),
9
+ size: z.number(),
10
+ data: z.string(), // base64 encoded file data
14
11
  });
15
- const updateProfileSchema = zod_1.z.object({
16
- profile: zod_1.z.record(zod_1.z.any()),
12
+ const updateProfileSchema = z.object({
13
+ profile: z.record(z.any()),
17
14
  profilePicture: fileSchema.optional(),
18
15
  });
19
- exports.userRouter = (0, trpc_1.createTRPCRouter)({
20
- getProfile: trpc_1.protectedProcedure
16
+ export const userRouter = createTRPCRouter({
17
+ getProfile: protectedProcedure
21
18
  .query(async ({ ctx }) => {
22
19
  if (!ctx.user) {
23
- throw new server_1.TRPCError({
20
+ throw new TRPCError({
24
21
  code: "UNAUTHORIZED",
25
22
  message: "User must be authenticated",
26
23
  });
27
24
  }
28
- const user = await prisma_1.prisma.user.findUnique({
25
+ const user = await prisma.user.findUnique({
29
26
  where: { id: ctx.user.id },
30
27
  select: {
31
28
  id: true,
@@ -34,18 +31,18 @@ exports.userRouter = (0, trpc_1.createTRPCRouter)({
34
31
  },
35
32
  });
36
33
  if (!user) {
37
- throw new server_1.TRPCError({
34
+ throw new TRPCError({
38
35
  code: "NOT_FOUND",
39
36
  message: "User not found",
40
37
  });
41
38
  }
42
39
  return user;
43
40
  }),
44
- updateProfile: trpc_1.protectedProcedure
41
+ updateProfile: protectedProcedure
45
42
  .input(updateProfileSchema)
46
43
  .mutation(async ({ ctx, input }) => {
47
44
  if (!ctx.user) {
48
- throw new server_1.TRPCError({
45
+ throw new TRPCError({
49
46
  code: "UNAUTHORIZED",
50
47
  message: "User must be authenticated",
51
48
  });
@@ -53,12 +50,12 @@ exports.userRouter = (0, trpc_1.createTRPCRouter)({
53
50
  let uploadedFiles = [];
54
51
  if (input.profilePicture) {
55
52
  // Store profile picture in a user-specific directory
56
- uploadedFiles = await (0, fileUpload_1.uploadFiles)([input.profilePicture], ctx.user.id, `users/${ctx.user.id}/profile`);
53
+ uploadedFiles = await uploadFiles([input.profilePicture], ctx.user.id, `users/${ctx.user.id}/profile`);
57
54
  // Add profile picture path to profile data
58
55
  input.profile.profilePicture = uploadedFiles[0].path;
59
56
  input.profile.profilePictureThumbnail = uploadedFiles[0].thumbnailId;
60
57
  }
61
- const updatedUser = await prisma_1.prisma.user.update({
58
+ const updatedUser = await prisma.user.update({
62
59
  where: { id: ctx.user.id },
63
60
  data: {
64
61
  profile: input.profile,
@@ -0,0 +1,22 @@
1
+ export declare function clearDatabase(): Promise<void>;
2
+ export declare function createUser(email: string, password: string, username: string): Promise<{
3
+ id: string;
4
+ username: string;
5
+ email: string;
6
+ password: string;
7
+ verified: boolean;
8
+ role: import(".prisma/client").$Enums.UserRole;
9
+ profileId: string | null;
10
+ schoolId: string | null;
11
+ }>;
12
+ export declare function addNotification(userId: string, title: string, content: string): Promise<{
13
+ id: string;
14
+ title: string;
15
+ content: string;
16
+ createdAt: Date;
17
+ read: boolean;
18
+ receiverId: string;
19
+ senderId: string | null;
20
+ }>;
21
+ export declare const seedDatabase: () => Promise<void>;
22
+ //# sourceMappingURL=seedDatabase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seedDatabase.d.ts","sourceRoot":"","sources":["../src/seedDatabase.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,kBAKlC;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;GAOjF;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;GAQnF;AAED,eAAO,MAAM,YAAY,qBA6BxB,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { prisma } from "./lib/prisma";
2
+ import { hash } from "bcryptjs";
3
+ import { logger } from "./utils/logger";
4
+ export async function clearDatabase() {
5
+ await prisma.class.deleteMany();
6
+ await prisma.userProfile.deleteMany();
7
+ await prisma.session.deleteMany();
8
+ await prisma.user.deleteMany();
9
+ }
10
+ export async function createUser(email, password, username) {
11
+ logger.debug("Creating user", { email, username, password });
12
+ const hashedPassword = await hash(password, 10);
13
+ return await prisma.user.create({
14
+ data: { email, password: hashedPassword, username, verified: true },
15
+ });
16
+ }
17
+ export async function addNotification(userId, title, content) {
18
+ return await prisma.notification.create({
19
+ data: {
20
+ receiverId: userId,
21
+ title,
22
+ content,
23
+ },
24
+ });
25
+ }
26
+ export const seedDatabase = async () => {
27
+ await clearDatabase();
28
+ logger.info('Cleared database');
29
+ // create two test users
30
+ const teacher1 = await createUser('teacher1@studious.sh', '123456', 'teacher1');
31
+ const student1 = await createUser('student1@studious.sh', '123456', 'student1');
32
+ // create a class
33
+ const class1 = await prisma.class.create({
34
+ data: {
35
+ name: 'Class 1',
36
+ subject: 'Math',
37
+ section: 'A',
38
+ teachers: {
39
+ connect: {
40
+ id: teacher1.id,
41
+ }
42
+ },
43
+ students: {
44
+ connect: {
45
+ id: student1.id,
46
+ }
47
+ }
48
+ },
49
+ });
50
+ await addNotification(teacher1.id, 'Welcome to Studious', 'Welcome to Studious');
51
+ await addNotification(student1.id, 'Welcome to Studious', 'Welcome to Studious');
52
+ };
53
+ (async () => {
54
+ logger.info('Seeding database');
55
+ await seedDatabase();
56
+ logger.info('Database seeded');
57
+ })();
@@ -1,33 +1,30 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setupSocketHandlers = void 0;
4
- const logger_1 = require("../utils/logger");
5
- const setupSocketHandlers = (io) => {
1
+ import { logger } from '../utils/logger';
2
+ export const setupSocketHandlers = (io) => {
6
3
  io.on('connection', (socket) => {
7
- logger_1.logger.info('Client connected', { socketId: socket.id });
4
+ logger.info('Client connected', { socketId: socket.id });
8
5
  socket.on('disconnect', (reason) => {
9
- logger_1.logger.info('Client disconnected', { socketId: socket.id, reason });
6
+ logger.info('Client disconnected', { socketId: socket.id, reason });
10
7
  });
11
8
  socket.on('error', (error) => {
12
- logger_1.logger.error('Socket error', { socketId: socket.id, error });
9
+ logger.error('Socket error', { socketId: socket.id, error });
13
10
  });
14
11
  // Class Room Management
15
12
  socket.on('create-class', (data) => {
16
13
  io.emit('class-created', data.class);
17
- logger_1.logger.info('Class created', { class: data.class });
14
+ logger.info('Class created', { class: data.class });
18
15
  });
19
16
  socket.on('delete-class', (data) => {
20
17
  io.emit('class-deleted', data.classId);
21
- logger_1.logger.info('Class deleted', { classId: data.classId });
18
+ logger.info('Class deleted', { classId: data.classId });
22
19
  });
23
20
  socket.on('update-class', (data) => {
24
21
  io.emit('class-updated', data.class);
25
- logger_1.logger.info('Class updated', { class: data.class });
22
+ logger.info('Class updated', { class: data.class });
26
23
  });
27
24
  socket.on('join-class', (classId, callback) => {
28
25
  try {
29
26
  socket.join(`class-${classId}`);
30
- logger_1.logger.info('Client joined class room', { socketId: socket.id, classId });
27
+ logger.info('Client joined class room', { socketId: socket.id, classId });
31
28
  if (callback) {
32
29
  callback(classId);
33
30
  }
@@ -36,9 +33,9 @@ const setupSocketHandlers = (io) => {
36
33
  }
37
34
  }
38
35
  catch (error) {
39
- logger_1.logger.error('Error joining class room', { socketId: socket.id, classId, error });
36
+ logger.error('Error joining class room', { socketId: socket.id, classId, error });
40
37
  if (callback) {
41
- callback(null);
38
+ callback(classId);
42
39
  }
43
40
  }
44
41
  });
@@ -46,56 +43,56 @@ const setupSocketHandlers = (io) => {
46
43
  socket.on('assignment-create', (data) => {
47
44
  if (data.classId && data.assignment) {
48
45
  io.in(`class-${data.classId}`).emit('assignment-created', data.assignment);
49
- logger_1.logger.info('Assignment created', { classId: data.classId, assignmentId: data.assignment.id });
46
+ logger.info('Assignment created', { classId: data.classId, assignmentId: data.assignment.id });
50
47
  }
51
48
  else {
52
- logger_1.logger.error('Invalid assignment data format', { data });
49
+ logger.error('Invalid assignment data format', { data });
53
50
  }
54
51
  });
55
52
  socket.on('assignment-update', (data) => {
56
53
  io.in(`class-${data.classId}`).emit('assignment-updated', data.assignment);
57
- logger_1.logger.info('Assignment updated', { classId: data.classId, assignmentId: data.assignment.id });
54
+ logger.info('Assignment updated', { classId: data.classId, assignmentId: data.assignment.id });
58
55
  });
59
56
  socket.on('assignment-delete', (data) => {
60
57
  io.in(`class-${data.classId}`).emit('assignment-deleted', data.assignmentId);
61
- logger_1.logger.info('Assignment deleted', { classId: data.classId, assignmentId: data.assignmentId });
58
+ logger.info('Assignment deleted', { classId: data.classId, assignmentId: data.assignmentId });
62
59
  });
63
60
  // Submission Events
64
61
  socket.on('submission-update', (data) => {
65
62
  io.in(`class-${data.classId}`).emit('submission-updated', data.submission);
66
- logger_1.logger.info('Submission updated', { classId: data.classId, submissionId: data.submission.id });
63
+ logger.info('Submission updated', { classId: data.classId, submissionId: data.submission.id });
67
64
  });
68
65
  // Announcement Events
69
66
  socket.on('new-announcement', (data) => {
70
67
  io.in(`class-${data.classId}`).emit('announcement-created', data.announcement);
71
- logger_1.logger.info('New announcement created', { classId: data.classId, announcementId: data.announcement.id });
68
+ logger.info('New announcement created', { classId: data.classId, announcementId: data.announcement.id });
72
69
  });
73
70
  // Section Events
74
71
  socket.on('section-create', (data) => {
75
72
  io.in(`class-${data.classId}`).emit('section-created', data.section);
76
- logger_1.logger.info('Section created', { classId: data.classId, sectionId: data.section.id });
73
+ logger.info('Section created', { classId: data.classId, sectionId: data.section.id });
77
74
  });
78
75
  socket.on('section-update', (data) => {
79
76
  io.in(`class-${data.classId}`).emit('section-updated', data.section);
80
- logger_1.logger.info('Section updated', { classId: data.classId, sectionId: data.section.id });
77
+ logger.info('Section updated', { classId: data.classId, sectionId: data.section.id });
81
78
  });
82
79
  socket.on('section-delete', (data) => {
83
80
  io.in(`class-${data.classId}`).emit('section-deleted', data.sectionId);
84
- logger_1.logger.info('Section deleted', { classId: data.classId, sectionId: data.sectionId });
81
+ logger.info('Section deleted', { classId: data.classId, sectionId: data.sectionId });
85
82
  });
86
83
  // Member Events
87
84
  socket.on('member-update', (data) => {
88
85
  io.in(`class-${data.classId}`).emit('member-updated', data.member);
89
- logger_1.logger.info('Member updated', { classId: data.classId, memberId: data.member.id });
86
+ logger.info('Member updated', { classId: data.classId, memberId: data.member.id });
90
87
  });
91
88
  socket.on('member-delete', (data) => {
92
89
  io.in(`class-${data.classId}`).emit('member-deleted', data.memberId);
93
- logger_1.logger.info('Member deleted', { classId: data.classId, memberId: data.memberId });
90
+ logger.info('Member deleted', { classId: data.classId, memberId: data.memberId });
94
91
  });
95
92
  // Attendance Events
96
93
  socket.on('attendance-update', (data) => {
97
94
  io.in(`class-${data.classId}`).emit('attendance-updated', data.attendance);
98
- logger_1.logger.info('Attendance updated', { classId: data.classId, attendanceId: data.attendance.id });
95
+ logger.info('Attendance updated', { classId: data.classId, attendanceId: data.attendance.id });
99
96
  });
100
97
  // Event Events
101
98
  socket.on('event-create', (data) => {
@@ -105,7 +102,7 @@ const setupSocketHandlers = (io) => {
105
102
  else {
106
103
  io.emit('event-created', data.event);
107
104
  }
108
- logger_1.logger.info('Event created', { classId: data.classId, eventId: data.event.id });
105
+ logger.info('Event created', { classId: data.classId, eventId: data.event.id });
109
106
  });
110
107
  socket.on('event-update', (data) => {
111
108
  if (data.classId) {
@@ -114,7 +111,7 @@ const setupSocketHandlers = (io) => {
114
111
  else {
115
112
  io.emit('event-updated', data.event);
116
113
  }
117
- logger_1.logger.info('Event updated', { classId: data.classId, eventId: data.event.id });
114
+ logger.info('Event updated', { classId: data.classId, eventId: data.event.id });
118
115
  });
119
116
  socket.on('event-delete', (data) => {
120
117
  if (data.classId) {
@@ -123,8 +120,7 @@ const setupSocketHandlers = (io) => {
123
120
  else {
124
121
  io.emit('event-deleted', data.eventId);
125
122
  }
126
- logger_1.logger.info('Event deleted', { classId: data.classId, eventId: data.eventId });
123
+ logger.info('Event deleted', { classId: data.classId, eventId: data.eventId });
127
124
  });
128
125
  });
129
126
  };
130
- exports.setupSocketHandlers = setupSocketHandlers;
package/dist/trpc.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { TRPCError } from '@trpc/server';
2
2
  import { Request, Response } from 'express';
3
3
  import { z } from 'zod';
4
+ import { PrismaErrorInfo } from './utils/prismaErrorHandler';
4
5
  interface CreateContextOptions {
5
6
  req: Request;
6
7
  res: Response;
@@ -28,6 +29,7 @@ export declare const t: import("@trpc/server").TRPCRootObject<Context, object, {
28
29
  }): {
29
30
  data: {
30
31
  zodError: z.typeToFlattenedError<any, string> | null;
32
+ prismaError: PrismaErrorInfo | null;
31
33
  code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
32
34
  httpStatus: number;
33
35
  path?: string;
@@ -42,6 +44,7 @@ export declare const t: import("@trpc/server").TRPCRootObject<Context, object, {
42
44
  errorShape: {
43
45
  data: {
44
46
  zodError: z.typeToFlattenedError<any, string> | null;
47
+ prismaError: PrismaErrorInfo | null;
45
48
  code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
46
49
  httpStatus: number;
47
50
  path?: string;
@@ -58,6 +61,7 @@ export declare const createTRPCRouter: import("@trpc/server").TRPCRouterBuilder<
58
61
  errorShape: {
59
62
  data: {
60
63
  zodError: z.typeToFlattenedError<any, string> | null;
64
+ prismaError: PrismaErrorInfo | null;
61
65
  code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
62
66
  httpStatus: number;
63
67
  path?: string;
@@ -86,6 +90,7 @@ export declare const createCallerFactory: import("@trpc/server").TRPCRouterCalle
86
90
  errorShape: {
87
91
  data: {
88
92
  zodError: z.typeToFlattenedError<any, string> | null;
93
+ prismaError: PrismaErrorInfo | null;
89
94
  code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
90
95
  httpStatus: number;
91
96
  path?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../src/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAE,MAAM,cAAc,CAAC;AAMnD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,UAAU,oBAAoB;IAC5B,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,MAAM,oBAAoB,KAAG,OAAO,CAAC,OAAO,CAwBnF,CAAC;AAEF,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkBZ,CAAC;AAOH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAAW,CAAC;AACzC,eAAO,MAAM,eAAe,yOAAqC,CAAC;AAGlE,eAAO,MAAM,kBAAkB,yOAAgC,CAAC;AAChE,eAAO,MAAM,6BAA6B;;;;uHAEnB,CAAC;AACxB,eAAO,MAAM,yBAAyB;;;;uHAEd,CAAC;AAIzB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAAwB,CAAC"}
1
+ {"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../src/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAE,MAAM,cAAc,CAAC;AAMnD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAqB,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAEhF,UAAU,oBAAoB;IAC5B,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,MAAM,oBAAoB,KAAG,OAAO,CAAC,OAAO,CAwBnF,CAAC;AAEF,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BZ,CAAC;AAOH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;EAAW,CAAC;AACzC,eAAO,MAAM,eAAe,yOAAqC,CAAC;AAGlE,eAAO,MAAM,kBAAkB,yOAAgC,CAAC;AAChE,eAAO,MAAM,6BAA6B;;;;uHAEnB,CAAC;AACxB,eAAO,MAAM,yBAAyB;;;;uHAEd,CAAC;AAIzB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;EAAwB,CAAC"}
package/dist/trpc.js CHANGED
@@ -1,18 +1,16 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createCallerFactory = exports.protectedTeacherProcedure = exports.protectedClassMemberProcedure = exports.protectedProcedure = exports.publicProcedure = exports.createTRPCRouter = exports.t = exports.createTRPCContext = void 0;
4
- const server_1 = require("@trpc/server");
5
- const zod_1 = require("zod");
6
- const logger_1 = require("./utils/logger");
7
- const prisma_1 = require("./lib/prisma");
8
- const logging_1 = require("./middleware/logging");
9
- const auth_1 = require("./middleware/auth");
10
- const zod_2 = require("zod");
11
- const createTRPCContext = async (opts) => {
1
+ import { initTRPC } from '@trpc/server';
2
+ import { ZodError } from 'zod';
3
+ import { logger } from './utils/logger';
4
+ import { prisma } from './lib/prisma';
5
+ import { createLoggingMiddleware } from './middleware/logging';
6
+ import { createAuthMiddleware } from './middleware/auth';
7
+ import { z } from 'zod';
8
+ import { handlePrismaError } from './utils/prismaErrorHandler';
9
+ export const createTRPCContext = async (opts) => {
12
10
  const { req, res } = opts;
13
11
  // Get user from session/token
14
12
  const token = req.headers.authorization?.split(' ')[1];
15
- const user = token ? await prisma_1.prisma.user.findFirst({
13
+ const user = token ? await prisma.user.findFirst({
16
14
  where: {
17
15
  sessions: {
18
16
  some: {
@@ -31,37 +29,48 @@ const createTRPCContext = async (opts) => {
31
29
  meta: {},
32
30
  };
33
31
  };
34
- exports.createTRPCContext = createTRPCContext;
35
- exports.t = server_1.initTRPC.context().create({
32
+ export const t = initTRPC.context().create({
36
33
  errorFormatter({ shape, error }) {
37
- logger_1.logger.error('tRPC Error', {
34
+ // Handle Prisma errors specifically
35
+ let prismaErrorInfo = null;
36
+ if (error.cause) {
37
+ try {
38
+ prismaErrorInfo = handlePrismaError(error.cause);
39
+ }
40
+ catch (e) {
41
+ // If Prisma error handling fails, continue with normal error handling
42
+ }
43
+ }
44
+ logger.error('tRPC Error', {
38
45
  code: shape.code,
39
46
  message: error.message,
40
47
  cause: error.cause,
41
48
  stack: error.stack,
49
+ prismaError: prismaErrorInfo,
42
50
  });
43
51
  return {
44
52
  ...shape,
45
53
  data: {
46
54
  ...shape.data,
47
- zodError: error.cause instanceof zod_1.ZodError ? error.cause.flatten() : null,
55
+ zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
56
+ prismaError: prismaErrorInfo,
48
57
  },
49
58
  };
50
59
  },
51
60
  });
52
61
  // Create middleware
53
- const loggingMiddleware = (0, logging_1.createLoggingMiddleware)(exports.t);
54
- const { isAuthed, isMemberInClass, isTeacherInClass } = (0, auth_1.createAuthMiddleware)(exports.t);
62
+ const loggingMiddleware = createLoggingMiddleware(t);
63
+ const { isAuthed, isMemberInClass, isTeacherInClass } = createAuthMiddleware(t);
55
64
  // Base procedures
56
- exports.createTRPCRouter = exports.t.router;
57
- exports.publicProcedure = exports.t.procedure.use(loggingMiddleware);
65
+ export const createTRPCRouter = t.router;
66
+ export const publicProcedure = t.procedure.use(loggingMiddleware);
58
67
  // Protected procedures
59
- exports.protectedProcedure = exports.publicProcedure.use(isAuthed);
60
- exports.protectedClassMemberProcedure = exports.protectedProcedure
61
- .input(zod_2.z.object({ classId: zod_2.z.string() }).passthrough())
68
+ export const protectedProcedure = publicProcedure.use(isAuthed);
69
+ export const protectedClassMemberProcedure = protectedProcedure
70
+ .input(z.object({ classId: z.string() }).passthrough())
62
71
  .use(isMemberInClass);
63
- exports.protectedTeacherProcedure = exports.protectedProcedure
64
- .input(zod_2.z.object({ classId: zod_2.z.string() }).passthrough())
72
+ export const protectedTeacherProcedure = protectedProcedure
73
+ .input(z.object({ classId: z.string() }).passthrough())
65
74
  .use(isTeacherInClass);
66
75
  // Create caller factory
67
- exports.createCallerFactory = exports.t.createCallerFactory;
76
+ export const createCallerFactory = t.createCallerFactory;
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
@@ -1,11 +1,5 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.transport = void 0;
7
- const nodemailer_1 = __importDefault(require("nodemailer"));
8
- exports.transport = nodemailer_1.default.createTransport({
1
+ import nodemailer from 'nodemailer';
2
+ export const transport = nodemailer.createTransport({
9
3
  host: process.env.EMAIL_HOST,
10
4
  port: 587,
11
5
  secure: false,
@@ -1,11 +1,7 @@
1
- "use strict";
2
1
  /**
3
2
  * Generates a random invite code
4
3
  * @returns {string} The invite code with length 5
5
4
  */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.generateInviteCode = void 0;
8
- const generateInviteCode = () => {
5
+ export const generateInviteCode = () => {
9
6
  return (Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)).slice(0, 5);
10
7
  };
11
- exports.generateInviteCode = generateInviteCode;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,oBAAY,QAAQ;IAClB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,KAAK,UAAU;IACf,KAAK,UAAU;CAChB;AAED,KAAK,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAwB3D,cAAM,MAAM;IACV,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,WAAW,CAA2B;IAE9C,OAAO;WAoBO,WAAW,IAAI,MAAM;IAO5B,OAAO,CAAC,IAAI,EAAE,OAAO;IAI5B,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,GAAG;IA8BJ,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAInD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAInD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAIpD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;CAG5D;AAED,eAAO,MAAM,MAAM,QAAuB,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,oBAAY,QAAQ;IAClB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,KAAK,UAAU;IACf,KAAK,UAAU;CAChB;AAED,KAAK,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAwB3D,cAAM,MAAM;IACV,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,WAAW,CAA2B;IAE9C,OAAO;WAuBO,WAAW,IAAI,MAAM;IAO5B,OAAO,CAAC,IAAI,EAAE,OAAO;IAI5B,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,GAAG;IAqCJ,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAInD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAInD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAIpD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;CAG5D;AAED,eAAO,MAAM,MAAM,QAAuB,CAAC"}
@@ -1,13 +1,10 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.logger = exports.LogLevel = void 0;
4
- var LogLevel;
1
+ export var LogLevel;
5
2
  (function (LogLevel) {
6
3
  LogLevel["INFO"] = "info";
7
4
  LogLevel["WARN"] = "warn";
8
5
  LogLevel["ERROR"] = "error";
9
6
  LogLevel["DEBUG"] = "debug";
10
- })(LogLevel || (exports.LogLevel = LogLevel = {}));
7
+ })(LogLevel || (LogLevel = {}));
11
8
  // ANSI color codes
12
9
  const colors = {
13
10
  reset: '\x1b[0m',
@@ -24,7 +21,8 @@ const colors = {
24
21
  };
25
22
  class Logger {
26
23
  constructor() {
27
- this.isDevelopment = process.env.NODE_ENV === 'development';
24
+ // this.isDevelopment = process.env.NODE_ENV === 'development';
25
+ this.isDevelopment = true;
28
26
  this.mode = process.env.LOG_MODE || 'normal';
29
27
  this.levelColors = {
30
28
  [LogLevel.INFO]: colors.blue,
@@ -83,8 +81,14 @@ class Logger {
83
81
  return `${timestampStr} ${levelStr} ${emojiStr}${messageStr}${contextStr}`;
84
82
  }
85
83
  log(level, message, context) {
86
- // if (this.shouldLog(level))
87
- // return;
84
+ if (!this.shouldLog(level))
85
+ return;
86
+ if (level == LogLevel.WARN || level == LogLevel.ERROR) {
87
+ if (level == LogLevel.ERROR) {
88
+ // alert me
89
+ }
90
+ // store in database
91
+ }
88
92
  const logMessage = {
89
93
  level,
90
94
  message,
@@ -121,4 +125,4 @@ class Logger {
121
125
  this.log(LogLevel.DEBUG, message, context);
122
126
  }
123
127
  }
124
- exports.logger = Logger.getInstance();
128
+ export const logger = Logger.getInstance();
@@ -0,0 +1,9 @@
1
+ export interface PrismaErrorInfo {
2
+ message: string;
3
+ code?: string;
4
+ meta?: any;
5
+ details?: string;
6
+ }
7
+ export declare function handlePrismaError(error: unknown): PrismaErrorInfo;
8
+ export declare function getFieldDisplayName(fieldName: string): string;
9
+ //# sourceMappingURL=prismaErrorHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prismaErrorHandler.d.ts","sourceRoot":"","sources":["../../src/utils/prismaErrorHandler.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CA4BjE;AAiMD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAwC7D"}