@studious-lms/server 1.1.22 → 1.1.23

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.
@@ -474,18 +474,6 @@ export declare const assignmentRouter: import("@trpc/server").TRPCBuiltRouter<{
474
474
  student: {
475
475
  id: string;
476
476
  username: string;
477
- profile: {
478
- id: string;
479
- location: string | null;
480
- userId: string;
481
- createdAt: Date;
482
- displayName: string | null;
483
- bio: string | null;
484
- website: string | null;
485
- profilePicture: string | null;
486
- profilePictureThumbnail: string | null;
487
- updatedAt: Date;
488
- } | null;
489
477
  };
490
478
  attachments: {
491
479
  path: string;
@@ -605,18 +593,6 @@ export declare const assignmentRouter: import("@trpc/server").TRPCBuiltRouter<{
605
593
  student: {
606
594
  id: string;
607
595
  username: string;
608
- profile: {
609
- id: string;
610
- location: string | null;
611
- userId: string;
612
- createdAt: Date;
613
- displayName: string | null;
614
- bio: string | null;
615
- website: string | null;
616
- profilePicture: string | null;
617
- profilePictureThumbnail: string | null;
618
- updatedAt: Date;
619
- } | null;
620
596
  };
621
597
  attachments: {
622
598
  path: string;
@@ -1587,6 +1563,37 @@ export declare const assignmentRouter: import("@trpc/server").TRPCBuiltRouter<{
1587
1563
  };
1588
1564
  meta: object;
1589
1565
  }>;
1566
+ getAnnotationUploadUrls: import("@trpc/server").TRPCMutationProcedure<{
1567
+ input: {
1568
+ [x: string]: unknown;
1569
+ classId: string;
1570
+ files: {
1571
+ type: string;
1572
+ name: string;
1573
+ size: number;
1574
+ }[];
1575
+ submissionId: string;
1576
+ };
1577
+ output: {
1578
+ success: boolean;
1579
+ uploadFiles: DirectUploadFile[];
1580
+ };
1581
+ meta: object;
1582
+ }>;
1583
+ confirmAnnotationUpload: import("@trpc/server").TRPCMutationProcedure<{
1584
+ input: {
1585
+ [x: string]: unknown;
1586
+ classId: string;
1587
+ fileId: string;
1588
+ uploadSuccess: boolean;
1589
+ errorMessage?: string | undefined;
1590
+ };
1591
+ output: {
1592
+ success: boolean;
1593
+ message: string;
1594
+ };
1595
+ meta: object;
1596
+ }>;
1590
1597
  updateUploadProgress: import("@trpc/server").TRPCMutationProcedure<{
1591
1598
  input: {
1592
1599
  fileId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"assignment.d.ts","sourceRoot":"","sources":["../../src/routers/assignment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAA2B,KAAK,gBAAgB,EAAgE,MAAM,sBAAsB,CAAC;AAoHpJ,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAk7D3B,CAAC"}
1
+ {"version":3,"file":"assignment.d.ts","sourceRoot":"","sources":["../../src/routers/assignment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAA2B,KAAK,gBAAgB,EAAgE,MAAM,sBAAsB,CAAC;AAgIpJ,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqgE3B,CAAC"}
@@ -100,6 +100,16 @@ const confirmSubmissionUploadSchema = z.object({
100
100
  uploadSuccess: z.boolean(),
101
101
  errorMessage: z.string().optional(),
102
102
  });
103
+ const getAnnotationUploadUrlsSchema = z.object({
104
+ submissionId: z.string(),
105
+ classId: z.string(),
106
+ files: z.array(directFileSchema),
107
+ });
108
+ const confirmAnnotationUploadSchema = z.object({
109
+ fileId: z.string(),
110
+ uploadSuccess: z.boolean(),
111
+ errorMessage: z.string().optional(),
112
+ });
103
113
  const updateUploadProgressSchema = z.object({
104
114
  fileId: z.string(),
105
115
  progress: z.number().min(0).max(100),
@@ -668,7 +678,6 @@ export const assignmentRouter = createTRPCRouter({
668
678
  select: {
669
679
  id: true,
670
680
  username: true,
671
- profile: true,
672
681
  },
673
682
  },
674
683
  assignment: {
@@ -770,7 +779,6 @@ export const assignmentRouter = createTRPCRouter({
770
779
  select: {
771
780
  id: true,
772
781
  username: true,
773
- profile: true,
774
782
  },
775
783
  },
776
784
  assignment: {
@@ -1167,30 +1175,13 @@ export const assignmentRouter = createTRPCRouter({
1167
1175
  },
1168
1176
  });
1169
1177
  }
1170
- let uploadedFiles = [];
1178
+ // NOTE: Teacher annotation files are now handled via direct upload endpoints
1179
+ // Use getAnnotationUploadUrls and confirmAnnotationUpload endpoints instead
1180
+ // The newAttachments field is deprecated for annotations
1171
1181
  if (newAttachments && newAttachments.length > 0) {
1172
- // Store files in a class and assignment specific directory
1173
- uploadedFiles = await createDirectUploadFiles(newAttachments, ctx.user.id, undefined, undefined, submission.id);
1174
- }
1175
- // Update submission with new file attachments
1176
- if (uploadedFiles.length > 0) {
1177
- await prisma.submission.update({
1178
- where: { id: submission.id },
1179
- data: {
1180
- annotations: {
1181
- create: uploadedFiles.map(file => ({
1182
- name: file.name,
1183
- type: file.type,
1184
- size: file.size,
1185
- path: file.path,
1186
- ...(file.thumbnailId && {
1187
- thumbnail: {
1188
- connect: { id: file.thumbnailId }
1189
- }
1190
- })
1191
- }))
1192
- }
1193
- }
1182
+ throw new TRPCError({
1183
+ code: "BAD_REQUEST",
1184
+ message: "Direct file upload is deprecated. Use getAnnotationUploadUrls endpoint instead.",
1194
1185
  });
1195
1186
  }
1196
1187
  // Connect existing files if provided
@@ -1864,6 +1855,89 @@ export const assignmentRouter = createTRPCRouter({
1864
1855
  message: uploadSuccess ? "Upload confirmed successfully" : "Upload failed",
1865
1856
  };
1866
1857
  }),
1858
+ getAnnotationUploadUrls: protectedTeacherProcedure
1859
+ .input(getAnnotationUploadUrlsSchema)
1860
+ .mutation(async ({ ctx, input }) => {
1861
+ const { submissionId, classId, files } = input;
1862
+ if (!ctx.user) {
1863
+ throw new TRPCError({
1864
+ code: "UNAUTHORIZED",
1865
+ message: "You must be logged in to upload files",
1866
+ });
1867
+ }
1868
+ // Verify submission exists and user is a teacher of the class
1869
+ const submission = await prisma.submission.findFirst({
1870
+ where: {
1871
+ id: submissionId,
1872
+ assignment: {
1873
+ classId: classId,
1874
+ class: {
1875
+ teachers: {
1876
+ some: {
1877
+ id: ctx.user.id,
1878
+ },
1879
+ },
1880
+ },
1881
+ },
1882
+ },
1883
+ });
1884
+ if (!submission) {
1885
+ throw new TRPCError({
1886
+ code: "NOT_FOUND",
1887
+ message: "Submission not found or you are not a teacher of this class",
1888
+ });
1889
+ }
1890
+ // Create direct upload files for annotations
1891
+ // Note: We pass submissionId as the 5th parameter, but these are annotations not submission files
1892
+ // We need to store them separately, so we'll use a different approach
1893
+ const directUploadFiles = await createDirectUploadFiles(files, ctx.user.id, undefined, // No specific directory
1894
+ undefined, // No assignment ID
1895
+ undefined // Don't link to submission yet (will be linked in confirmAnnotationUpload)
1896
+ );
1897
+ // Store the submissionId in the file record so we can link it to annotations later
1898
+ await Promise.all(directUploadFiles.map(file => prisma.file.update({
1899
+ where: { id: file.id },
1900
+ data: {
1901
+ annotationId: submissionId, // Store as annotation
1902
+ }
1903
+ })));
1904
+ return {
1905
+ success: true,
1906
+ uploadFiles: directUploadFiles,
1907
+ };
1908
+ }),
1909
+ confirmAnnotationUpload: protectedTeacherProcedure
1910
+ .input(confirmAnnotationUploadSchema)
1911
+ .mutation(async ({ ctx, input }) => {
1912
+ const { fileId, uploadSuccess, errorMessage } = input;
1913
+ if (!ctx.user) {
1914
+ throw new TRPCError({
1915
+ code: "UNAUTHORIZED",
1916
+ message: "You must be logged in",
1917
+ });
1918
+ }
1919
+ // Verify file belongs to user and is an annotation file
1920
+ const file = await prisma.file.findFirst({
1921
+ where: {
1922
+ id: fileId,
1923
+ userId: ctx.user.id,
1924
+ annotationId: {
1925
+ not: null,
1926
+ },
1927
+ },
1928
+ });
1929
+ if (!file) {
1930
+ throw new TRPCError({
1931
+ code: "NOT_FOUND",
1932
+ message: "File not found or you don't have permission",
1933
+ });
1934
+ }
1935
+ await confirmDirectUpload(fileId, uploadSuccess, errorMessage);
1936
+ return {
1937
+ success: true,
1938
+ message: uploadSuccess ? "Annotation upload confirmed successfully" : "Annotation upload failed",
1939
+ };
1940
+ }),
1867
1941
  updateUploadProgress: protectedProcedure
1868
1942
  .input(updateUploadProgressSchema)
1869
1943
  .mutation(async ({ ctx, input }) => {
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { type DirectUploadFile } from "../lib/fileUpload.js";
2
3
  export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
3
4
  ctx: import("../trpc.js").Context;
4
5
  meta: object;
@@ -316,5 +317,31 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
316
317
  }[];
317
318
  meta: object;
318
319
  }>;
320
+ getFolderUploadUrls: import("@trpc/server").TRPCMutationProcedure<{
321
+ input: {
322
+ [x: string]: unknown;
323
+ classId: string;
324
+ files: {
325
+ type: string;
326
+ name: string;
327
+ size: number;
328
+ }[];
329
+ folderId: string;
330
+ };
331
+ output: DirectUploadFile[];
332
+ meta: object;
333
+ }>;
334
+ confirmFolderUpload: import("@trpc/server").TRPCMutationProcedure<{
335
+ input: {
336
+ [x: string]: unknown;
337
+ classId: string;
338
+ fileId: string;
339
+ uploadSuccess: boolean;
340
+ };
341
+ output: {
342
+ success: boolean;
343
+ };
344
+ meta: object;
345
+ }>;
319
346
  }>>;
320
347
  //# sourceMappingURL=folder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"folder.d.ts","sourceRoot":"","sources":["../../src/routers/folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiCxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0vBvB,CAAC"}
1
+ {"version":3,"file":"folder.d.ts","sourceRoot":"","sources":["../../src/routers/folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAA2B,KAAK,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AA6BzG,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAu2BvB,CAAC"}
@@ -726,4 +726,97 @@ export const folderRouter = createTRPCRouter({
726
726
  }
727
727
  return parents;
728
728
  }),
729
+ getFolderUploadUrls: protectedTeacherProcedure
730
+ .input(z.object({
731
+ classId: z.string(),
732
+ folderId: z.string(),
733
+ files: z.array(directFileSchema),
734
+ }))
735
+ .mutation(async ({ ctx, input }) => {
736
+ const { classId, folderId, files } = input;
737
+ if (!ctx.user) {
738
+ throw new TRPCError({
739
+ code: "UNAUTHORIZED",
740
+ message: "You must be logged in to upload files",
741
+ });
742
+ }
743
+ // Verify user is a teacher of the class
744
+ const classData = await prisma.class.findFirst({
745
+ where: {
746
+ id: classId,
747
+ teachers: {
748
+ some: {
749
+ id: ctx.user.id,
750
+ },
751
+ },
752
+ },
753
+ });
754
+ if (!classData) {
755
+ throw new TRPCError({
756
+ code: "NOT_FOUND",
757
+ message: "Class not found or you are not a teacher",
758
+ });
759
+ }
760
+ // Verify folder exists
761
+ const folder = await prisma.folder.findUnique({
762
+ where: {
763
+ id: folderId,
764
+ },
765
+ });
766
+ if (!folder) {
767
+ throw new TRPCError({
768
+ code: "NOT_FOUND",
769
+ message: "Folder not found",
770
+ });
771
+ }
772
+ // Verify folder belongs to the class by traversing parent hierarchy
773
+ // Only root folders have classId, child folders use parentFolderId
774
+ let currentFolder = folder;
775
+ let belongsToClass = false;
776
+ while (currentFolder) {
777
+ // Check if we've reached a root folder with the matching classId
778
+ if (currentFolder.classId === classId) {
779
+ belongsToClass = true;
780
+ break;
781
+ }
782
+ // If this folder has a parent, traverse up
783
+ if (currentFolder.parentFolderId) {
784
+ currentFolder = await prisma.folder.findUnique({
785
+ where: { id: currentFolder.parentFolderId },
786
+ });
787
+ }
788
+ else {
789
+ // Reached a root folder without matching classId
790
+ break;
791
+ }
792
+ }
793
+ if (!belongsToClass) {
794
+ throw new TRPCError({
795
+ code: "FORBIDDEN",
796
+ message: "Folder does not belong to this class",
797
+ });
798
+ }
799
+ // Create direct upload files
800
+ const uploadFiles = await createDirectUploadFiles(files, ctx.user.id, folder.id);
801
+ return uploadFiles;
802
+ }),
803
+ confirmFolderUpload: protectedTeacherProcedure
804
+ .input(z.object({
805
+ fileId: z.string(),
806
+ uploadSuccess: z.boolean(),
807
+ }))
808
+ .mutation(async ({ ctx, input }) => {
809
+ const { fileId, uploadSuccess } = input;
810
+ if (!ctx.user) {
811
+ throw new TRPCError({
812
+ code: "UNAUTHORIZED",
813
+ message: "You must be logged in to confirm uploads",
814
+ });
815
+ }
816
+ // Import the confirmDirectUpload function
817
+ const { confirmDirectUpload } = await import("../lib/fileUpload.js");
818
+ // Confirm the upload
819
+ await confirmDirectUpload(fileId, uploadSuccess);
820
+ return { success: true };
821
+ }),
729
822
  });
@@ -1 +1 @@
1
- {"version":3,"file":"seedDatabase.d.ts","sourceRoot":"","sources":["../src/seedDatabase.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,kBAuClC;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,qBAq+CxB,CAAC"}
1
+ {"version":3,"file":"seedDatabase.d.ts","sourceRoot":"","sources":["../src/seedDatabase.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,kBAsClC;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,qBAq+CxB,CAAC"}
@@ -4,7 +4,6 @@ import { logger } from "./utils/logger.js";
4
4
  export async function clearDatabase() {
5
5
  // Delete in order to respect foreign key constraints
6
6
  // Delete notifications first (they reference users)
7
- logger.info('Clearing database');
8
7
  await prisma.notification.deleteMany();
9
8
  // Delete chat-related records
10
9
  await prisma.mention.deleteMany();
@@ -78,7 +77,7 @@ export const seedDatabase = async () => {
78
77
  ]);
79
78
  // 3. Create Students (realistic names)
80
79
  const students = await Promise.all([
81
- createUser('alex.martinez@student.rverside.eidu', 'student123', 'alex.martinez'),
80
+ createUser('alex.martinez@student.riverside.edu', 'student123', 'alex.martinez'),
82
81
  createUser('sophia.williams@student.riverside.edu', 'student123', 'sophia.williams'),
83
82
  createUser('james.brown@student.riverside.edu', 'student123', 'james.brown'),
84
83
  createUser('olivia.taylor@student.riverside.edu', 'student123', 'olivia.taylor'),
@@ -10,7 +10,6 @@ declare class Logger {
10
10
  private isDevelopment;
11
11
  private mode;
12
12
  private levelColors;
13
- private levelBgColors;
14
13
  private levelEmojis;
15
14
  private constructor();
16
15
  static getInstance(): Logger;
@@ -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;AAyC3D,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,aAAa,CAA2B;IAChD,OAAO,CAAC,WAAW,CAA2B;IAE9C,OAAO;WA8BO,WAAW,IAAI,MAAM;IAO5B,OAAO,CAAC,IAAI,EAAE,OAAO;IAI5B,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,aAAa;IAmBrB,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
+ {"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"}
@@ -17,24 +17,7 @@ const colors = {
17
17
  magenta: '\x1b[35m',
18
18
  cyan: '\x1b[36m',
19
19
  white: '\x1b[37m',
20
- gray: '\x1b[90m',
21
- // Background colors
22
- bgRed: '\x1b[41m',
23
- bgGreen: '\x1b[42m',
24
- bgYellow: '\x1b[43m',
25
- bgBlue: '\x1b[44m',
26
- bgMagenta: '\x1b[45m',
27
- bgCyan: '\x1b[46m',
28
- bgWhite: '\x1b[47m',
29
- bgGray: '\x1b[100m',
30
- // Bright background colors
31
- bgBrightRed: '\x1b[101m',
32
- bgBrightGreen: '\x1b[102m',
33
- bgBrightYellow: '\x1b[103m',
34
- bgBrightBlue: '\x1b[104m',
35
- bgBrightMagenta: '\x1b[105m',
36
- bgBrightCyan: '\x1b[106m',
37
- bgBrightWhite: '\x1b[107m'
20
+ gray: '\x1b[90m'
38
21
  };
39
22
  class Logger {
40
23
  constructor() {
@@ -47,12 +30,6 @@ class Logger {
47
30
  [LogLevel.ERROR]: colors.red,
48
31
  [LogLevel.DEBUG]: colors.magenta
49
32
  };
50
- this.levelBgColors = {
51
- [LogLevel.INFO]: colors.bgBlue,
52
- [LogLevel.WARN]: colors.bgYellow,
53
- [LogLevel.ERROR]: colors.bgRed,
54
- [LogLevel.DEBUG]: colors.bgMagenta
55
- };
56
33
  this.levelEmojis = {
57
34
  [LogLevel.INFO]: 'ℹ️',
58
35
  [LogLevel.WARN]: '⚠️',
@@ -93,11 +70,9 @@ class Logger {
93
70
  formatMessage(logMessage) {
94
71
  const { level, message, timestamp, context } = logMessage;
95
72
  const color = this.levelColors[level];
96
- const bgColor = this.levelBgColors[level];
97
73
  const emoji = this.levelEmojis[level];
98
74
  const timestampStr = colors.gray + `[${timestamp}]` + colors.reset;
99
- // Use background color for level badge like Vitest
100
- const levelStr = colors.white + bgColor + ` ${level.toUpperCase()} ` + colors.reset;
75
+ const levelStr = color + `[${level.toUpperCase()}]` + colors.reset;
101
76
  const emojiStr = emoji + ' ';
102
77
  const messageStr = colors.bright + message + colors.reset;
103
78
  const contextStr = context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studious-lms/server",
3
- "version": "1.1.22",
3
+ "version": "1.1.23",
4
4
  "description": "Backend server for Studious application",
5
5
  "main": "dist/exportType.js",
6
6
  "types": "dist/exportType.d.ts",
@@ -18,7 +18,7 @@
18
18
  "generate": "npx prisma generate",
19
19
  "prepublishOnly": "npm run generate && npm run build",
20
20
  "test": "vitest",
21
- "seed": "tsx src/seedDatabase.ts"
21
+ "seed": "node dist/seedDatabase.js"
22
22
  },
23
23
  "dependencies": {
24
24
  "@google-cloud/storage": "^7.16.0",
@@ -1,6 +1,6 @@
1
1
  import { TRPCError } from "@trpc/server";
2
2
  import { v4 as uuidv4 } from "uuid";
3
- import { getSignedUrl, objectExists } from "./googleCloudStorage.js";
3
+ import { getSignedUrl } from "./googleCloudStorage.js";
4
4
  import { generateMediaThumbnail } from "./thumbnailGenerator.js";
5
5
  import { prisma } from "./prisma.js";
6
6
  import { logger } from "../utils/logger.js";
@@ -182,11 +182,7 @@ export async function createDirectUploadFile(
182
182
  uploadSessionId
183
183
  };
184
184
  } catch (error) {
185
- logger.error('Error creating direct upload file:', {error: error instanceof Error ? {
186
- name: error.name,
187
- message: error.message,
188
- stack: error.stack,
189
- } : error});
185
+ console.error('Error creating direct upload file:', error);
190
186
  throw new TRPCError({
191
187
  code: 'INTERNAL_SERVER_ERROR',
192
188
  message: 'Failed to create direct upload file',
@@ -206,53 +202,17 @@ export async function confirmDirectUpload(
206
202
  errorMessage?: string
207
203
  ): Promise<void> {
208
204
  try {
209
- // First fetch the file record to get the object path
210
- const fileRecord = await prisma.file.findUnique({
211
- where: { id: fileId },
212
- select: { path: true }
213
- });
214
-
215
- if (!fileRecord) {
216
- throw new TRPCError({
217
- code: 'NOT_FOUND',
218
- message: 'File record not found',
219
- });
220
- }
221
-
222
- let actualUploadSuccess = uploadSuccess;
223
- let actualErrorMessage = errorMessage;
224
-
225
- // If uploadSuccess is true, verify the object actually exists in GCS
226
- if (uploadSuccess) {
227
- try {
228
- const exists = await objectExists(process.env.GOOGLE_CLOUD_BUCKET_NAME!, fileRecord.path);
229
- if (!exists) {
230
- actualUploadSuccess = false;
231
- actualErrorMessage = 'File upload reported as successful but object not found in Google Cloud Storage';
232
- logger.error(`File upload verification failed for ${fileId}: object ${fileRecord.path} not found in GCS`);
233
- }
234
- } catch (error) {
235
- logger.error(`Error verifying file existence in GCS for ${fileId}:`, {error: error instanceof Error ? {
236
- name: error.name,
237
- message: error.message,
238
- stack: error.stack,
239
- } : error});
240
- actualUploadSuccess = false;
241
- actualErrorMessage = 'Failed to verify file existence in Google Cloud Storage';
242
- }
243
- }
244
-
245
205
  const updateData: any = {
246
- uploadStatus: actualUploadSuccess ? 'COMPLETED' : 'FAILED',
247
- uploadProgress: actualUploadSuccess ? 100 : 0,
206
+ uploadStatus: uploadSuccess ? 'COMPLETED' : 'FAILED',
207
+ uploadProgress: uploadSuccess ? 100 : 0,
248
208
  };
249
209
 
250
- if (!actualUploadSuccess && actualErrorMessage) {
251
- updateData.uploadError = actualErrorMessage;
210
+ if (!uploadSuccess && errorMessage) {
211
+ updateData.uploadError = errorMessage;
252
212
  updateData.uploadRetryCount = { increment: 1 };
253
213
  }
254
214
 
255
- if (actualUploadSuccess) {
215
+ if (uploadSuccess) {
256
216
  updateData.uploadedAt = new Date();
257
217
  }
258
218
 
@@ -261,7 +221,7 @@ export async function confirmDirectUpload(
261
221
  data: updateData
262
222
  });
263
223
  } catch (error) {
264
- logger.error('Error confirming direct upload:', {error});
224
+ console.error('Error confirming direct upload:', error);
265
225
  throw new TRPCError({
266
226
  code: 'INTERNAL_SERVER_ERROR',
267
227
  message: 'Failed to confirm upload',
@@ -279,29 +239,15 @@ export async function updateUploadProgress(
279
239
  progress: number
280
240
  ): Promise<void> {
281
241
  try {
282
- // await prisma.file.update({
283
- // where: { id: fileId },
284
- // data: {
285
- // uploadStatus: 'UPLOADING',
286
- // uploadProgress: Math.min(100, Math.max(0, progress))
287
- // }
288
- // });
289
- const current = await prisma.file.findUnique({ where: { id: fileId }, select: { uploadStatus: true } });
290
- if (!current || ['COMPLETED','FAILED','CANCELLED'].includes(current.uploadStatus as string)) return;
291
- const clamped = Math.min(100, Math.max(0, progress));
292
242
  await prisma.file.update({
293
243
  where: { id: fileId },
294
244
  data: {
295
245
  uploadStatus: 'UPLOADING',
296
- uploadProgress: clamped
246
+ uploadProgress: Math.min(100, Math.max(0, progress))
297
247
  }
298
248
  });
299
249
  } catch (error) {
300
- logger.error('Error updating upload progress:', {error: error instanceof Error ? {
301
- name: error.name,
302
- message: error.message,
303
- stack: error.stack,
304
- } : error});
250
+ console.error('Error updating upload progress:', error);
305
251
  throw new TRPCError({
306
252
  code: 'INTERNAL_SERVER_ERROR',
307
253
  message: 'Failed to update upload progress',
@@ -331,11 +277,7 @@ export async function createDirectUploadFiles(
331
277
  );
332
278
  return await Promise.all(uploadPromises);
333
279
  } catch (error) {
334
- logger.error('Error creating direct upload files:', {error: error instanceof Error ? {
335
- name: error.name,
336
- message: error.message,
337
- stack: error.stack,
338
- } : error});
280
+ console.error('Error creating direct upload files:', error);
339
281
  throw new TRPCError({
340
282
  code: 'INTERNAL_SERVER_ERROR',
341
283
  message: 'Failed to create direct upload files',
@@ -62,23 +62,4 @@ export async function deleteFile(filePath: string): Promise<void> {
62
62
  message: 'Failed to delete file from storage',
63
63
  });
64
64
  }
65
- }
66
-
67
- /**
68
- * Checks if an object exists in Google Cloud Storage
69
- * @param bucketName The name of the bucket (unused, uses default bucket)
70
- * @param objectPath The path of the object to check
71
- * @returns Promise<boolean> True if the object exists, false otherwise
72
- */
73
- export async function objectExists(bucketName: string, objectPath: string): Promise<boolean> {
74
- try {
75
- const [exists] = await bucket.file(objectPath).exists();
76
- return exists;
77
- } catch (error) {
78
- console.error('Error checking if object exists in Google Cloud Storage:', error);
79
- throw new TRPCError({
80
- code: 'INTERNAL_SERVER_ERROR',
81
- message: 'Failed to check object existence',
82
- });
83
- }
84
65
  }