@studious-lms/server 1.1.20 → 1.1.22
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/BASE64_REMOVAL_SUMMARY.md +153 -0
- package/dist/lib/fileUpload.d.ts +45 -12
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +181 -72
- package/dist/lib/googleCloudStorage.d.ts +7 -8
- package/dist/lib/googleCloudStorage.d.ts.map +1 -1
- package/dist/lib/googleCloudStorage.js +21 -33
- package/dist/lib/thumbnailGenerator.d.ts +0 -1
- package/dist/lib/thumbnailGenerator.d.ts.map +1 -1
- package/dist/lib/thumbnailGenerator.js +3 -17
- package/dist/routers/_app.d.ts +452 -10
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/assignment.d.ts +209 -4
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +243 -16
- package/dist/routers/auth.js +1 -1
- package/dist/routers/file.d.ts +18 -0
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/folder.d.ts +0 -1
- package/dist/routers/folder.d.ts.map +1 -1
- package/dist/routers/folder.js +12 -9
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +4 -2
- package/package.json +1 -1
- package/prisma/migrations/20251020151505_add_upload_tracking_fields/migration.sql +57 -0
- package/prisma/schema.prisma +19 -0
- package/src/lib/fileUpload.ts +229 -83
- package/src/lib/googleCloudStorage.ts +21 -40
- package/src/lib/thumbnailGenerator.ts +3 -18
- package/src/routers/assignment.ts +291 -16
- package/src/routers/auth.ts +1 -1
- package/src/routers/folder.ts +13 -9
- package/src/routers/labChat.ts +4 -2
- package/src/routers/user.ts +1 -1
|
@@ -2,14 +2,18 @@ import { z } from "zod";
|
|
|
2
2
|
import { createTRPCRouter, protectedProcedure, protectedClassMemberProcedure, protectedTeacherProcedure } from "../trpc.js";
|
|
3
3
|
import { TRPCError } from "@trpc/server";
|
|
4
4
|
import { prisma } from "../lib/prisma.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createDirectUploadFiles, type DirectUploadFile, confirmDirectUpload, updateUploadProgress, type UploadedFile } from "../lib/fileUpload.js";
|
|
6
6
|
import { deleteFile } from "../lib/googleCloudStorage.js";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// DEPRECATED: This schema is no longer used - files are uploaded directly to GCS
|
|
9
|
+
// Use directFileSchema instead
|
|
10
|
+
|
|
11
|
+
// New schema for direct file uploads (no base64 data)
|
|
12
|
+
const directFileSchema = z.object({
|
|
9
13
|
name: z.string(),
|
|
10
14
|
type: z.string(),
|
|
11
15
|
size: z.number(),
|
|
12
|
-
data
|
|
16
|
+
// No data field - for direct file uploads
|
|
13
17
|
});
|
|
14
18
|
|
|
15
19
|
const createAssignmentSchema = z.object({
|
|
@@ -17,7 +21,7 @@ const createAssignmentSchema = z.object({
|
|
|
17
21
|
title: z.string(),
|
|
18
22
|
instructions: z.string(),
|
|
19
23
|
dueDate: z.string(),
|
|
20
|
-
files: z.array(
|
|
24
|
+
files: z.array(directFileSchema).optional(), // Use direct file schema
|
|
21
25
|
existingFileIds: z.array(z.string()).optional(),
|
|
22
26
|
maxGrade: z.number().optional(),
|
|
23
27
|
graded: z.boolean().optional(),
|
|
@@ -35,7 +39,7 @@ const updateAssignmentSchema = z.object({
|
|
|
35
39
|
title: z.string().optional(),
|
|
36
40
|
instructions: z.string().optional(),
|
|
37
41
|
dueDate: z.string().optional(),
|
|
38
|
-
files: z.array(
|
|
42
|
+
files: z.array(directFileSchema).optional(), // Use direct file schema
|
|
39
43
|
existingFileIds: z.array(z.string()).optional(),
|
|
40
44
|
removedAttachments: z.array(z.string()).optional(),
|
|
41
45
|
maxGrade: z.number().optional(),
|
|
@@ -61,7 +65,7 @@ const submissionSchema = z.object({
|
|
|
61
65
|
classId: z.string(),
|
|
62
66
|
submissionId: z.string(),
|
|
63
67
|
submit: z.boolean().optional(),
|
|
64
|
-
newAttachments: z.array(
|
|
68
|
+
newAttachments: z.array(directFileSchema).optional(), // Use direct file schema
|
|
65
69
|
existingFileIds: z.array(z.string()).optional(),
|
|
66
70
|
removedAttachments: z.array(z.string()).optional(),
|
|
67
71
|
});
|
|
@@ -72,7 +76,7 @@ const updateSubmissionSchema = z.object({
|
|
|
72
76
|
submissionId: z.string(),
|
|
73
77
|
return: z.boolean().optional(),
|
|
74
78
|
gradeReceived: z.number().nullable().optional(),
|
|
75
|
-
newAttachments: z.array(
|
|
79
|
+
newAttachments: z.array(directFileSchema).optional(), // Use direct file schema
|
|
76
80
|
existingFileIds: z.array(z.string()).optional(),
|
|
77
81
|
removedAttachments: z.array(z.string()).optional(),
|
|
78
82
|
feedback: z.string().optional(),
|
|
@@ -84,6 +88,36 @@ const updateSubmissionSchema = z.object({
|
|
|
84
88
|
})).optional(),
|
|
85
89
|
});
|
|
86
90
|
|
|
91
|
+
// New schemas for direct upload functionality
|
|
92
|
+
const getAssignmentUploadUrlsSchema = z.object({
|
|
93
|
+
assignmentId: z.string(),
|
|
94
|
+
classId: z.string(),
|
|
95
|
+
files: z.array(directFileSchema),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const getSubmissionUploadUrlsSchema = z.object({
|
|
99
|
+
submissionId: z.string(),
|
|
100
|
+
classId: z.string(),
|
|
101
|
+
files: z.array(directFileSchema),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const confirmAssignmentUploadSchema = z.object({
|
|
105
|
+
fileId: z.string(),
|
|
106
|
+
uploadSuccess: z.boolean(),
|
|
107
|
+
errorMessage: z.string().optional(),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const confirmSubmissionUploadSchema = z.object({
|
|
111
|
+
fileId: z.string(),
|
|
112
|
+
uploadSuccess: z.boolean(),
|
|
113
|
+
errorMessage: z.string().optional(),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const updateUploadProgressSchema = z.object({
|
|
117
|
+
fileId: z.string(),
|
|
118
|
+
progress: z.number().min(0).max(100),
|
|
119
|
+
});
|
|
120
|
+
|
|
87
121
|
export const assignmentRouter = createTRPCRouter({
|
|
88
122
|
order: protectedTeacherProcedure
|
|
89
123
|
.input(z.object({
|
|
@@ -109,7 +143,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
109
143
|
targetSectionId: z.string(),
|
|
110
144
|
}))
|
|
111
145
|
.mutation(async ({ ctx, input }) => {
|
|
112
|
-
const { id, targetSectionId
|
|
146
|
+
const { id, targetSectionId } = input;
|
|
113
147
|
|
|
114
148
|
|
|
115
149
|
const assignments = await prisma.assignment.findMany({
|
|
@@ -288,11 +322,13 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
288
322
|
)
|
|
289
323
|
);
|
|
290
324
|
|
|
291
|
-
//
|
|
325
|
+
// NOTE: Files are now handled via direct upload endpoints
|
|
326
|
+
// The files field in the schema is for metadata only
|
|
327
|
+
// Actual file uploads should use getAssignmentUploadUrls endpoint
|
|
292
328
|
let uploadedFiles: UploadedFile[] = [];
|
|
293
329
|
if (files && files.length > 0) {
|
|
294
|
-
//
|
|
295
|
-
uploadedFiles = await
|
|
330
|
+
// Create direct upload files instead of processing base64
|
|
331
|
+
uploadedFiles = await createDirectUploadFiles(files, ctx.user.id, undefined, assignment.id);
|
|
296
332
|
}
|
|
297
333
|
|
|
298
334
|
// Update assignment with new file attachments
|
|
@@ -380,11 +416,11 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
380
416
|
});
|
|
381
417
|
}
|
|
382
418
|
|
|
383
|
-
//
|
|
419
|
+
// NOTE: Files are now handled via direct upload endpoints
|
|
384
420
|
let uploadedFiles: UploadedFile[] = [];
|
|
385
421
|
if (files && files.length > 0) {
|
|
386
|
-
//
|
|
387
|
-
uploadedFiles = await
|
|
422
|
+
// Create direct upload files instead of processing base64
|
|
423
|
+
uploadedFiles = await createDirectUploadFiles(files, ctx.user.id, undefined, input.id);
|
|
388
424
|
}
|
|
389
425
|
|
|
390
426
|
// Update assignment
|
|
@@ -955,7 +991,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
955
991
|
let uploadedFiles: UploadedFile[] = [];
|
|
956
992
|
if (newAttachments && newAttachments.length > 0) {
|
|
957
993
|
// Store files in a class and assignment specific directory
|
|
958
|
-
uploadedFiles = await
|
|
994
|
+
uploadedFiles = await createDirectUploadFiles(newAttachments, ctx.user.id, undefined, undefined, submission.id);
|
|
959
995
|
}
|
|
960
996
|
|
|
961
997
|
// Update submission with new file attachments
|
|
@@ -1237,7 +1273,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1237
1273
|
let uploadedFiles: UploadedFile[] = [];
|
|
1238
1274
|
if (newAttachments && newAttachments.length > 0) {
|
|
1239
1275
|
// Store files in a class and assignment specific directory
|
|
1240
|
-
uploadedFiles = await
|
|
1276
|
+
uploadedFiles = await createDirectUploadFiles(newAttachments, ctx.user.id, undefined, undefined, submission.id);
|
|
1241
1277
|
}
|
|
1242
1278
|
|
|
1243
1279
|
// Update submission with new file attachments
|
|
@@ -1813,5 +1849,244 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1813
1849
|
|
|
1814
1850
|
return updatedAssignment;
|
|
1815
1851
|
}),
|
|
1852
|
+
|
|
1853
|
+
// New direct upload endpoints
|
|
1854
|
+
getAssignmentUploadUrls: protectedTeacherProcedure
|
|
1855
|
+
.input(getAssignmentUploadUrlsSchema)
|
|
1856
|
+
.mutation(async ({ ctx, input }) => {
|
|
1857
|
+
const { assignmentId, classId, files } = input;
|
|
1858
|
+
|
|
1859
|
+
if (!ctx.user) {
|
|
1860
|
+
throw new TRPCError({
|
|
1861
|
+
code: "UNAUTHORIZED",
|
|
1862
|
+
message: "You must be logged in to upload files",
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// Verify user is a teacher of the class
|
|
1867
|
+
const classData = await prisma.class.findFirst({
|
|
1868
|
+
where: {
|
|
1869
|
+
id: classId,
|
|
1870
|
+
teachers: {
|
|
1871
|
+
some: {
|
|
1872
|
+
id: ctx.user.id,
|
|
1873
|
+
},
|
|
1874
|
+
},
|
|
1875
|
+
},
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
if (!classData) {
|
|
1879
|
+
throw new TRPCError({
|
|
1880
|
+
code: "NOT_FOUND",
|
|
1881
|
+
message: "Class not found or you are not a teacher",
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// Verify assignment exists and belongs to the class
|
|
1886
|
+
const assignment = await prisma.assignment.findFirst({
|
|
1887
|
+
where: {
|
|
1888
|
+
id: assignmentId,
|
|
1889
|
+
classId: classId,
|
|
1890
|
+
},
|
|
1891
|
+
});
|
|
1892
|
+
|
|
1893
|
+
if (!assignment) {
|
|
1894
|
+
throw new TRPCError({
|
|
1895
|
+
code: "NOT_FOUND",
|
|
1896
|
+
message: "Assignment not found",
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
// Create direct upload files
|
|
1901
|
+
const directUploadFiles = await createDirectUploadFiles(
|
|
1902
|
+
files,
|
|
1903
|
+
ctx.user.id,
|
|
1904
|
+
undefined, // No specific directory
|
|
1905
|
+
assignmentId
|
|
1906
|
+
);
|
|
1907
|
+
|
|
1908
|
+
return {
|
|
1909
|
+
success: true,
|
|
1910
|
+
uploadFiles: directUploadFiles,
|
|
1911
|
+
};
|
|
1912
|
+
}),
|
|
1913
|
+
|
|
1914
|
+
getSubmissionUploadUrls: protectedClassMemberProcedure
|
|
1915
|
+
.input(getSubmissionUploadUrlsSchema)
|
|
1916
|
+
.mutation(async ({ ctx, input }) => {
|
|
1917
|
+
const { submissionId, classId, files } = input;
|
|
1918
|
+
|
|
1919
|
+
if (!ctx.user) {
|
|
1920
|
+
throw new TRPCError({
|
|
1921
|
+
code: "UNAUTHORIZED",
|
|
1922
|
+
message: "You must be logged in to upload files",
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// Verify submission exists and user has access
|
|
1927
|
+
const submission = await prisma.submission.findFirst({
|
|
1928
|
+
where: {
|
|
1929
|
+
id: submissionId,
|
|
1930
|
+
assignment: {
|
|
1931
|
+
classId: classId,
|
|
1932
|
+
},
|
|
1933
|
+
},
|
|
1934
|
+
include: {
|
|
1935
|
+
assignment: {
|
|
1936
|
+
include: {
|
|
1937
|
+
class: {
|
|
1938
|
+
include: {
|
|
1939
|
+
students: true,
|
|
1940
|
+
teachers: true,
|
|
1941
|
+
},
|
|
1942
|
+
},
|
|
1943
|
+
},
|
|
1944
|
+
},
|
|
1945
|
+
},
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1948
|
+
if (!submission) {
|
|
1949
|
+
throw new TRPCError({
|
|
1950
|
+
code: "NOT_FOUND",
|
|
1951
|
+
message: "Submission not found",
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
// Check if user is the student who owns the submission or a teacher of the class
|
|
1956
|
+
const isStudent = submission.studentId === ctx.user.id;
|
|
1957
|
+
const isTeacher = submission.assignment.class.teachers.some(teacher => teacher.id === ctx.user?.id);
|
|
1958
|
+
|
|
1959
|
+
if (!isStudent && !isTeacher) {
|
|
1960
|
+
throw new TRPCError({
|
|
1961
|
+
code: "FORBIDDEN",
|
|
1962
|
+
message: "You don't have permission to upload files to this submission",
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
// Create direct upload files
|
|
1967
|
+
const directUploadFiles = await createDirectUploadFiles(
|
|
1968
|
+
files,
|
|
1969
|
+
ctx.user.id,
|
|
1970
|
+
undefined, // No specific directory
|
|
1971
|
+
undefined, // No assignment ID
|
|
1972
|
+
submissionId
|
|
1973
|
+
);
|
|
1974
|
+
|
|
1975
|
+
return {
|
|
1976
|
+
success: true,
|
|
1977
|
+
uploadFiles: directUploadFiles,
|
|
1978
|
+
};
|
|
1979
|
+
}),
|
|
1980
|
+
|
|
1981
|
+
confirmAssignmentUpload: protectedTeacherProcedure
|
|
1982
|
+
.input(confirmAssignmentUploadSchema)
|
|
1983
|
+
.mutation(async ({ ctx, input }) => {
|
|
1984
|
+
const { fileId, uploadSuccess, errorMessage } = input;
|
|
1985
|
+
|
|
1986
|
+
if (!ctx.user) {
|
|
1987
|
+
throw new TRPCError({
|
|
1988
|
+
code: "UNAUTHORIZED",
|
|
1989
|
+
message: "You must be logged in",
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// Verify file belongs to user and is an assignment file
|
|
1994
|
+
const file = await prisma.file.findFirst({
|
|
1995
|
+
where: {
|
|
1996
|
+
id: fileId,
|
|
1997
|
+
userId: ctx.user.id,
|
|
1998
|
+
assignment: {
|
|
1999
|
+
isNot: null,
|
|
2000
|
+
},
|
|
2001
|
+
},
|
|
2002
|
+
});
|
|
2003
|
+
|
|
2004
|
+
if (!file) {
|
|
2005
|
+
throw new TRPCError({
|
|
2006
|
+
code: "NOT_FOUND",
|
|
2007
|
+
message: "File not found or you don't have permission",
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
await confirmDirectUpload(fileId, uploadSuccess, errorMessage);
|
|
2012
|
+
|
|
2013
|
+
return {
|
|
2014
|
+
success: true,
|
|
2015
|
+
message: uploadSuccess ? "Upload confirmed successfully" : "Upload failed",
|
|
2016
|
+
};
|
|
2017
|
+
}),
|
|
2018
|
+
|
|
2019
|
+
confirmSubmissionUpload: protectedClassMemberProcedure
|
|
2020
|
+
.input(confirmSubmissionUploadSchema)
|
|
2021
|
+
.mutation(async ({ ctx, input }) => {
|
|
2022
|
+
const { fileId, uploadSuccess, errorMessage } = input;
|
|
2023
|
+
|
|
2024
|
+
if (!ctx.user) {
|
|
2025
|
+
throw new TRPCError({
|
|
2026
|
+
code: "UNAUTHORIZED",
|
|
2027
|
+
message: "You must be logged in",
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// Verify file belongs to user and is a submission file
|
|
2032
|
+
const file = await prisma.file.findFirst({
|
|
2033
|
+
where: {
|
|
2034
|
+
id: fileId,
|
|
2035
|
+
userId: ctx.user.id,
|
|
2036
|
+
submission: {
|
|
2037
|
+
isNot: null,
|
|
2038
|
+
},
|
|
2039
|
+
},
|
|
2040
|
+
});
|
|
2041
|
+
|
|
2042
|
+
if (!file) {
|
|
2043
|
+
throw new TRPCError({
|
|
2044
|
+
code: "NOT_FOUND",
|
|
2045
|
+
message: "File not found or you don't have permission",
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
await confirmDirectUpload(fileId, uploadSuccess, errorMessage);
|
|
2050
|
+
|
|
2051
|
+
return {
|
|
2052
|
+
success: true,
|
|
2053
|
+
message: uploadSuccess ? "Upload confirmed successfully" : "Upload failed",
|
|
2054
|
+
};
|
|
2055
|
+
}),
|
|
2056
|
+
|
|
2057
|
+
updateUploadProgress: protectedProcedure
|
|
2058
|
+
.input(updateUploadProgressSchema)
|
|
2059
|
+
.mutation(async ({ ctx, input }) => {
|
|
2060
|
+
const { fileId, progress } = input;
|
|
2061
|
+
|
|
2062
|
+
if (!ctx.user) {
|
|
2063
|
+
throw new TRPCError({
|
|
2064
|
+
code: "UNAUTHORIZED",
|
|
2065
|
+
message: "You must be logged in",
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// Verify file belongs to user
|
|
2070
|
+
const file = await prisma.file.findFirst({
|
|
2071
|
+
where: {
|
|
2072
|
+
id: fileId,
|
|
2073
|
+
userId: ctx.user.id,
|
|
2074
|
+
},
|
|
2075
|
+
});
|
|
2076
|
+
|
|
2077
|
+
if (!file) {
|
|
2078
|
+
throw new TRPCError({
|
|
2079
|
+
code: "NOT_FOUND",
|
|
2080
|
+
message: "File not found or you don't have permission",
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
await updateUploadProgress(fileId, progress);
|
|
2085
|
+
|
|
2086
|
+
return {
|
|
2087
|
+
success: true,
|
|
2088
|
+
progress,
|
|
2089
|
+
};
|
|
2090
|
+
}),
|
|
1816
2091
|
});
|
|
1817
2092
|
|
package/src/routers/auth.ts
CHANGED
package/src/routers/folder.ts
CHANGED
|
@@ -2,15 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { createTRPCRouter, protectedProcedure, protectedClassMemberProcedure, protectedTeacherProcedure } from "../trpc.js";
|
|
3
3
|
import { TRPCError } from "@trpc/server";
|
|
4
4
|
import { prisma } from "../lib/prisma.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createDirectUploadFiles, type DirectUploadFile, type UploadedFile } from "../lib/fileUpload.js";
|
|
6
6
|
import { type Folder } from "@prisma/client";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type: z.string(),
|
|
11
|
-
size: z.number(),
|
|
12
|
-
data: z.string(), // base64 encoded file data
|
|
13
|
-
});
|
|
8
|
+
// DEPRECATED: This schema is no longer used - files are uploaded directly to GCS
|
|
9
|
+
// Use directFileSchema instead
|
|
14
10
|
|
|
15
11
|
const createFolderSchema = z.object({
|
|
16
12
|
name: z.string(),
|
|
@@ -18,9 +14,17 @@ const createFolderSchema = z.object({
|
|
|
18
14
|
color: z.string().optional(),
|
|
19
15
|
});
|
|
20
16
|
|
|
17
|
+
// New schema for direct file uploads (no base64 data)
|
|
18
|
+
const directFileSchema = z.object({
|
|
19
|
+
name: z.string(),
|
|
20
|
+
type: z.string(),
|
|
21
|
+
size: z.number(),
|
|
22
|
+
// No data field - for direct file uploads
|
|
23
|
+
});
|
|
24
|
+
|
|
21
25
|
const uploadFilesToFolderSchema = z.object({
|
|
22
26
|
folderId: z.string(),
|
|
23
|
-
files: z.array(
|
|
27
|
+
files: z.array(directFileSchema), // Use direct file schema
|
|
24
28
|
});
|
|
25
29
|
|
|
26
30
|
const getRootFolderSchema = z.object({
|
|
@@ -469,7 +473,7 @@ export const folderRouter = createTRPCRouter({
|
|
|
469
473
|
}
|
|
470
474
|
|
|
471
475
|
// Upload files
|
|
472
|
-
const uploadedFiles = await
|
|
476
|
+
const uploadedFiles = await createDirectUploadFiles(files, ctx.user.id, folder.id);
|
|
473
477
|
|
|
474
478
|
// Create file records in database
|
|
475
479
|
// const fileRecords = await prisma.file.createMany({
|
package/src/routers/labChat.ts
CHANGED
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
} from '../utils/inference.js';
|
|
11
11
|
import { logger } from '../utils/logger.js';
|
|
12
12
|
import { isAIUser } from '../utils/aiUser.js';
|
|
13
|
-
|
|
13
|
+
// DEPRECATED: uploadFile removed - use direct upload instead
|
|
14
|
+
// import { uploadFile } from '../lib/googleCloudStorage.js';
|
|
14
15
|
import { createPdf } from "../lib/jsonConversion.js"
|
|
15
16
|
import OpenAI from 'openai';
|
|
16
17
|
import { v4 as uuidv4 } from "uuid";
|
|
@@ -924,7 +925,8 @@ WHEN CREATING COURSE MATERIALS (docs field):
|
|
|
924
925
|
|
|
925
926
|
|
|
926
927
|
logger.info(`PDF ${i + 1} generated successfully`, { labChatId, title: doc.title });
|
|
927
|
-
|
|
928
|
+
// DEPRECATED: Base64 upload removed - use direct upload instead
|
|
929
|
+
// const gcpResult = await uploadFile(Buffer.from(pdfBytes).toString('base64'), `class/generated/${fullLabChat.classId}/${filename}`, 'application/pdf');
|
|
928
930
|
logger.info(`PDF ${i + 1} uploaded successfully`, { labChatId, filename });
|
|
929
931
|
|
|
930
932
|
const file = await prisma.file.create({
|
package/src/routers/user.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { createTRPCRouter, protectedProcedure } from "../trpc.js";
|
|
3
3
|
import { TRPCError } from "@trpc/server";
|
|
4
4
|
import { prisma } from "../lib/prisma.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createDirectUploadFiles, type DirectUploadFile } from "../lib/fileUpload.js";
|
|
6
6
|
import { getSignedUrl } from "../lib/googleCloudStorage.js";
|
|
7
7
|
import { logger } from "../utils/logger.js";
|
|
8
8
|
import { bucket } from "../lib/googleCloudStorage.js";
|