@studious-lms/server 1.1.22 → 1.1.24

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;
@@ -606,16 +594,8 @@ export declare const assignmentRouter: import("@trpc/server").TRPCBuiltRouter<{
606
594
  id: string;
607
595
  username: string;
608
596
  profile: {
609
- id: string;
610
- location: string | null;
611
- userId: string;
612
- createdAt: Date;
613
597
  displayName: string | null;
614
- bio: string | null;
615
- website: string | null;
616
598
  profilePicture: string | null;
617
- profilePictureThumbnail: string | null;
618
- updatedAt: Date;
619
599
  } | null;
620
600
  };
621
601
  attachments: {
@@ -1587,6 +1567,37 @@ export declare const assignmentRouter: import("@trpc/server").TRPCBuiltRouter<{
1587
1567
  };
1588
1568
  meta: object;
1589
1569
  }>;
1570
+ getAnnotationUploadUrls: import("@trpc/server").TRPCMutationProcedure<{
1571
+ input: {
1572
+ [x: string]: unknown;
1573
+ classId: string;
1574
+ files: {
1575
+ type: string;
1576
+ name: string;
1577
+ size: number;
1578
+ }[];
1579
+ submissionId: string;
1580
+ };
1581
+ output: {
1582
+ success: boolean;
1583
+ uploadFiles: DirectUploadFile[];
1584
+ };
1585
+ meta: object;
1586
+ }>;
1587
+ confirmAnnotationUpload: import("@trpc/server").TRPCMutationProcedure<{
1588
+ input: {
1589
+ [x: string]: unknown;
1590
+ classId: string;
1591
+ fileId: string;
1592
+ uploadSuccess: boolean;
1593
+ errorMessage?: string | undefined;
1594
+ };
1595
+ output: {
1596
+ success: boolean;
1597
+ message: string;
1598
+ };
1599
+ meta: object;
1600
+ }>;
1590
1601
  updateUploadProgress: import("@trpc/server").TRPCMutationProcedure<{
1591
1602
  input: {
1592
1603
  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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2gE3B,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,12 @@ export const assignmentRouter = createTRPCRouter({
770
779
  select: {
771
780
  id: true,
772
781
  username: true,
773
- profile: true,
782
+ profile: {
783
+ select: {
784
+ displayName: true,
785
+ profilePicture: true,
786
+ }
787
+ }
774
788
  },
775
789
  },
776
790
  assignment: {
@@ -1167,30 +1181,13 @@ export const assignmentRouter = createTRPCRouter({
1167
1181
  },
1168
1182
  });
1169
1183
  }
1170
- let uploadedFiles = [];
1184
+ // NOTE: Teacher annotation files are now handled via direct upload endpoints
1185
+ // Use getAnnotationUploadUrls and confirmAnnotationUpload endpoints instead
1186
+ // The newAttachments field is deprecated for annotations
1171
1187
  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
- }
1188
+ throw new TRPCError({
1189
+ code: "BAD_REQUEST",
1190
+ message: "Direct file upload is deprecated. Use getAnnotationUploadUrls endpoint instead.",
1194
1191
  });
1195
1192
  }
1196
1193
  // Connect existing files if provided
@@ -1864,6 +1861,89 @@ export const assignmentRouter = createTRPCRouter({
1864
1861
  message: uploadSuccess ? "Upload confirmed successfully" : "Upload failed",
1865
1862
  };
1866
1863
  }),
1864
+ getAnnotationUploadUrls: protectedTeacherProcedure
1865
+ .input(getAnnotationUploadUrlsSchema)
1866
+ .mutation(async ({ ctx, input }) => {
1867
+ const { submissionId, classId, files } = input;
1868
+ if (!ctx.user) {
1869
+ throw new TRPCError({
1870
+ code: "UNAUTHORIZED",
1871
+ message: "You must be logged in to upload files",
1872
+ });
1873
+ }
1874
+ // Verify submission exists and user is a teacher of the class
1875
+ const submission = await prisma.submission.findFirst({
1876
+ where: {
1877
+ id: submissionId,
1878
+ assignment: {
1879
+ classId: classId,
1880
+ class: {
1881
+ teachers: {
1882
+ some: {
1883
+ id: ctx.user.id,
1884
+ },
1885
+ },
1886
+ },
1887
+ },
1888
+ },
1889
+ });
1890
+ if (!submission) {
1891
+ throw new TRPCError({
1892
+ code: "NOT_FOUND",
1893
+ message: "Submission not found or you are not a teacher of this class",
1894
+ });
1895
+ }
1896
+ // Create direct upload files for annotations
1897
+ // Note: We pass submissionId as the 5th parameter, but these are annotations not submission files
1898
+ // We need to store them separately, so we'll use a different approach
1899
+ const directUploadFiles = await createDirectUploadFiles(files, ctx.user.id, undefined, // No specific directory
1900
+ undefined, // No assignment ID
1901
+ undefined // Don't link to submission yet (will be linked in confirmAnnotationUpload)
1902
+ );
1903
+ // Store the submissionId in the file record so we can link it to annotations later
1904
+ await Promise.all(directUploadFiles.map(file => prisma.file.update({
1905
+ where: { id: file.id },
1906
+ data: {
1907
+ annotationId: submissionId, // Store as annotation
1908
+ }
1909
+ })));
1910
+ return {
1911
+ success: true,
1912
+ uploadFiles: directUploadFiles,
1913
+ };
1914
+ }),
1915
+ confirmAnnotationUpload: protectedTeacherProcedure
1916
+ .input(confirmAnnotationUploadSchema)
1917
+ .mutation(async ({ ctx, input }) => {
1918
+ const { fileId, uploadSuccess, errorMessage } = input;
1919
+ if (!ctx.user) {
1920
+ throw new TRPCError({
1921
+ code: "UNAUTHORIZED",
1922
+ message: "You must be logged in",
1923
+ });
1924
+ }
1925
+ // Verify file belongs to user and is an annotation file
1926
+ const file = await prisma.file.findFirst({
1927
+ where: {
1928
+ id: fileId,
1929
+ userId: ctx.user.id,
1930
+ annotationId: {
1931
+ not: null,
1932
+ },
1933
+ },
1934
+ });
1935
+ if (!file) {
1936
+ throw new TRPCError({
1937
+ code: "NOT_FOUND",
1938
+ message: "File not found or you don't have permission",
1939
+ });
1940
+ }
1941
+ await confirmDirectUpload(fileId, uploadSuccess, errorMessage);
1942
+ return {
1943
+ success: true,
1944
+ message: uploadSuccess ? "Annotation upload confirmed successfully" : "Annotation upload failed",
1945
+ };
1946
+ }),
1867
1947
  updateUploadProgress: protectedProcedure
1868
1948
  .input(updateUploadProgressSchema)
1869
1949
  .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.24",
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
  }