@studious-lms/server 1.1.24 → 1.2.26

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 (49) hide show
  1. package/dist/lib/fileUpload.d.ts +2 -2
  2. package/dist/lib/fileUpload.d.ts.map +1 -1
  3. package/dist/lib/fileUpload.js +76 -14
  4. package/dist/lib/googleCloudStorage.d.ts +7 -0
  5. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  6. package/dist/lib/googleCloudStorage.js +19 -0
  7. package/dist/lib/notificationHandler.d.ts +25 -0
  8. package/dist/lib/notificationHandler.d.ts.map +1 -0
  9. package/dist/lib/notificationHandler.js +28 -0
  10. package/dist/routers/_app.d.ts +818 -78
  11. package/dist/routers/_app.d.ts.map +1 -1
  12. package/dist/routers/announcement.d.ts +290 -3
  13. package/dist/routers/announcement.d.ts.map +1 -1
  14. package/dist/routers/announcement.js +896 -10
  15. package/dist/routers/assignment.d.ts +70 -4
  16. package/dist/routers/assignment.d.ts.map +1 -1
  17. package/dist/routers/assignment.js +265 -131
  18. package/dist/routers/auth.js +1 -1
  19. package/dist/routers/file.d.ts +2 -0
  20. package/dist/routers/file.d.ts.map +1 -1
  21. package/dist/routers/file.js +9 -6
  22. package/dist/routers/labChat.d.ts.map +1 -1
  23. package/dist/routers/labChat.js +13 -5
  24. package/dist/routers/notifications.d.ts +8 -8
  25. package/dist/routers/section.d.ts +16 -0
  26. package/dist/routers/section.d.ts.map +1 -1
  27. package/dist/routers/section.js +139 -30
  28. package/dist/seedDatabase.d.ts +2 -2
  29. package/dist/seedDatabase.d.ts.map +1 -1
  30. package/dist/seedDatabase.js +2 -1
  31. package/dist/utils/logger.d.ts +1 -0
  32. package/dist/utils/logger.d.ts.map +1 -1
  33. package/dist/utils/logger.js +27 -2
  34. package/package.json +2 -2
  35. package/prisma/migrations/20251109122857_annuoncements_comments/migration.sql +30 -0
  36. package/prisma/migrations/20251109135555_reactions_announcements_comments/migration.sql +35 -0
  37. package/prisma/schema.prisma +50 -0
  38. package/src/lib/fileUpload.ts +79 -14
  39. package/src/lib/googleCloudStorage.ts +19 -0
  40. package/src/lib/notificationHandler.ts +36 -0
  41. package/src/routers/announcement.ts +1007 -10
  42. package/src/routers/assignment.ts +230 -82
  43. package/src/routers/auth.ts +1 -1
  44. package/src/routers/file.ts +10 -7
  45. package/src/routers/labChat.ts +15 -6
  46. package/src/routers/section.ts +158 -36
  47. package/src/seedDatabase.ts +2 -1
  48. package/src/utils/logger.ts +29 -2
  49. package/tests/setup.ts +3 -9
@@ -49,7 +49,7 @@ export declare function getFileUrl(filePath: string): Promise<string>;
49
49
  * @param submissionId Optional submission ID to associate the file with
50
50
  * @returns The direct upload file information with signed URL
51
51
  */
52
- export declare function createDirectUploadFile(file: DirectFileData, userId: string, directory?: string, assignmentId?: string, submissionId?: string): Promise<DirectUploadFile>;
52
+ export declare function createDirectUploadFile(file: DirectFileData, userId: string, directory?: string, assignmentId?: string, submissionId?: string, announcementId?: string): Promise<DirectUploadFile>;
53
53
  /**
54
54
  * Confirms a direct upload was successful
55
55
  * @param fileId The ID of the file record
@@ -72,5 +72,5 @@ export declare function updateUploadProgress(fileId: string, progress: number):
72
72
  * @param submissionId Optional submission ID to associate files with
73
73
  * @returns Array of direct upload file information
74
74
  */
75
- export declare function createDirectUploadFiles(files: DirectFileData[], userId: string, directory?: string, assignmentId?: string, submissionId?: string): Promise<DirectUploadFile[]>;
75
+ export declare function createDirectUploadFiles(files: DirectFileData[], userId: string, directory?: string, assignmentId?: string, submissionId?: string, announcementId?: string): Promise<DirectUploadFile[]>;
76
76
  //# sourceMappingURL=fileUpload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fileUpload.d.ts","sourceRoot":"","sources":["../../src/lib/fileUpload.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAKD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,CAAC,CAKvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAKzB;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUlE;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAqF3B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,OAAO,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,cAAc,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAa7B"}
1
+ {"version":3,"file":"fileUpload.d.ts","sourceRoot":"","sources":["../../src/lib/fileUpload.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAKD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,CAAC,CAKvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAKzB;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUlE;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,EACrB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,gBAAgB,CAAC,CA8F3B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,OAAO,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA+Df;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,cAAc,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,EACrB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiB7B"}
@@ -1,7 +1,8 @@
1
1
  import { TRPCError } from "@trpc/server";
2
2
  import { v4 as uuidv4 } from "uuid";
3
- import { getSignedUrl } from "./googleCloudStorage.js";
3
+ import { getSignedUrl, objectExists } from "./googleCloudStorage.js";
4
4
  import { prisma } from "./prisma.js";
5
+ import { logger } from "../utils/logger.js";
5
6
  // DEPRECATED: These functions are no longer used - files are uploaded directly to GCS
6
7
  // Use createDirectUploadFile() and createDirectUploadFiles() instead
7
8
  /**
@@ -48,7 +49,7 @@ export async function getFileUrl(filePath) {
48
49
  * @param submissionId Optional submission ID to associate the file with
49
50
  * @returns The direct upload file information with signed URL
50
51
  */
51
- export async function createDirectUploadFile(file, userId, directory, assignmentId, submissionId) {
52
+ export async function createDirectUploadFile(file, userId, directory, assignmentId, submissionId, announcementId) {
52
53
  try {
53
54
  // Validate file extension matches MIME type
54
55
  const fileExtension = file.name.split('.').pop()?.toLowerCase();
@@ -105,6 +106,11 @@ export async function createDirectUploadFile(file, userId, directory, assignment
105
106
  submission: {
106
107
  connect: { id: submissionId }
107
108
  }
109
+ }),
110
+ ...(announcementId && {
111
+ announcement: {
112
+ connect: { id: announcementId }
113
+ }
108
114
  })
109
115
  },
110
116
  });
@@ -120,7 +126,11 @@ export async function createDirectUploadFile(file, userId, directory, assignment
120
126
  };
121
127
  }
122
128
  catch (error) {
123
- console.error('Error creating direct upload file:', error);
129
+ logger.error('Error creating direct upload file:', { error: error instanceof Error ? {
130
+ name: error.name,
131
+ message: error.message,
132
+ stack: error.stack,
133
+ } : error });
124
134
  throw new TRPCError({
125
135
  code: 'INTERNAL_SERVER_ERROR',
126
136
  message: 'Failed to create direct upload file',
@@ -135,15 +145,48 @@ export async function createDirectUploadFile(file, userId, directory, assignment
135
145
  */
136
146
  export async function confirmDirectUpload(fileId, uploadSuccess, errorMessage) {
137
147
  try {
148
+ // First fetch the file record to get the object path
149
+ const fileRecord = await prisma.file.findUnique({
150
+ where: { id: fileId },
151
+ select: { path: true }
152
+ });
153
+ if (!fileRecord) {
154
+ throw new TRPCError({
155
+ code: 'NOT_FOUND',
156
+ message: 'File record not found',
157
+ });
158
+ }
159
+ let actualUploadSuccess = uploadSuccess;
160
+ let actualErrorMessage = errorMessage;
161
+ // If uploadSuccess is true, verify the object actually exists in GCS
162
+ if (uploadSuccess) {
163
+ try {
164
+ const exists = await objectExists(process.env.GOOGLE_CLOUD_BUCKET_NAME, fileRecord.path);
165
+ if (!exists) {
166
+ actualUploadSuccess = false;
167
+ actualErrorMessage = 'File upload reported as successful but object not found in Google Cloud Storage';
168
+ logger.error(`File upload verification failed for ${fileId}: object ${fileRecord.path} not found in GCS`);
169
+ }
170
+ }
171
+ catch (error) {
172
+ logger.error(`Error verifying file existence in GCS for ${fileId}:`, { error: error instanceof Error ? {
173
+ name: error.name,
174
+ message: error.message,
175
+ stack: error.stack,
176
+ } : error });
177
+ actualUploadSuccess = false;
178
+ actualErrorMessage = 'Failed to verify file existence in Google Cloud Storage';
179
+ }
180
+ }
138
181
  const updateData = {
139
- uploadStatus: uploadSuccess ? 'COMPLETED' : 'FAILED',
140
- uploadProgress: uploadSuccess ? 100 : 0,
182
+ uploadStatus: actualUploadSuccess ? 'COMPLETED' : 'FAILED',
183
+ uploadProgress: actualUploadSuccess ? 100 : 0,
141
184
  };
142
- if (!uploadSuccess && errorMessage) {
143
- updateData.uploadError = errorMessage;
185
+ if (!actualUploadSuccess && actualErrorMessage) {
186
+ updateData.uploadError = actualErrorMessage;
144
187
  updateData.uploadRetryCount = { increment: 1 };
145
188
  }
146
- if (uploadSuccess) {
189
+ if (actualUploadSuccess) {
147
190
  updateData.uploadedAt = new Date();
148
191
  }
149
192
  await prisma.file.update({
@@ -152,7 +195,7 @@ export async function confirmDirectUpload(fileId, uploadSuccess, errorMessage) {
152
195
  });
153
196
  }
154
197
  catch (error) {
155
- console.error('Error confirming direct upload:', error);
198
+ logger.error('Error confirming direct upload:', { error });
156
199
  throw new TRPCError({
157
200
  code: 'INTERNAL_SERVER_ERROR',
158
201
  message: 'Failed to confirm upload',
@@ -166,16 +209,31 @@ export async function confirmDirectUpload(fileId, uploadSuccess, errorMessage) {
166
209
  */
167
210
  export async function updateUploadProgress(fileId, progress) {
168
211
  try {
212
+ // await prisma.file.update({
213
+ // where: { id: fileId },
214
+ // data: {
215
+ // uploadStatus: 'UPLOADING',
216
+ // uploadProgress: Math.min(100, Math.max(0, progress))
217
+ // }
218
+ // });
219
+ const current = await prisma.file.findUnique({ where: { id: fileId }, select: { uploadStatus: true } });
220
+ if (!current || ['COMPLETED', 'FAILED', 'CANCELLED'].includes(current.uploadStatus))
221
+ return;
222
+ const clamped = Math.min(100, Math.max(0, progress));
169
223
  await prisma.file.update({
170
224
  where: { id: fileId },
171
225
  data: {
172
226
  uploadStatus: 'UPLOADING',
173
- uploadProgress: Math.min(100, Math.max(0, progress))
227
+ uploadProgress: clamped
174
228
  }
175
229
  });
176
230
  }
177
231
  catch (error) {
178
- console.error('Error updating upload progress:', error);
232
+ logger.error('Error updating upload progress:', { error: error instanceof Error ? {
233
+ name: error.name,
234
+ message: error.message,
235
+ stack: error.stack,
236
+ } : error });
179
237
  throw new TRPCError({
180
238
  code: 'INTERNAL_SERVER_ERROR',
181
239
  message: 'Failed to update upload progress',
@@ -191,13 +249,17 @@ export async function updateUploadProgress(fileId, progress) {
191
249
  * @param submissionId Optional submission ID to associate files with
192
250
  * @returns Array of direct upload file information
193
251
  */
194
- export async function createDirectUploadFiles(files, userId, directory, assignmentId, submissionId) {
252
+ export async function createDirectUploadFiles(files, userId, directory, assignmentId, submissionId, announcementId) {
195
253
  try {
196
- const uploadPromises = files.map(file => createDirectUploadFile(file, userId, directory, assignmentId, submissionId));
254
+ const uploadPromises = files.map(file => createDirectUploadFile(file, userId, directory, assignmentId, submissionId, announcementId));
197
255
  return await Promise.all(uploadPromises);
198
256
  }
199
257
  catch (error) {
200
- console.error('Error creating direct upload files:', error);
258
+ logger.error('Error creating direct upload files:', { error: error instanceof Error ? {
259
+ name: error.name,
260
+ message: error.message,
261
+ stack: error.stack,
262
+ } : error });
201
263
  throw new TRPCError({
202
264
  code: 'INTERNAL_SERVER_ERROR',
203
265
  message: 'Failed to create direct upload files',
@@ -10,4 +10,11 @@ export declare function getSignedUrl(filePath: string, action?: 'read' | 'write'
10
10
  * @param filePath The path of the file to delete
11
11
  */
12
12
  export declare function deleteFile(filePath: string): Promise<void>;
13
+ /**
14
+ * Checks if an object exists in Google Cloud Storage
15
+ * @param bucketName The name of the bucket (unused, uses default bucket)
16
+ * @param objectPath The path of the object to check
17
+ * @returns Promise<boolean> True if the object exists, false otherwise
18
+ */
19
+ export declare function objectExists(bucketName: string, objectPath: string): Promise<boolean>;
13
20
  //# sourceMappingURL=googleCloudStorage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"googleCloudStorage.d.ts","sourceRoot":"","sources":["../../src/lib/googleCloudStorage.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,MAAM,wCAAwD,CAAC;AAQ5E;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,OAAgB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB7H;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhE"}
1
+ {"version":3,"file":"googleCloudStorage.d.ts","sourceRoot":"","sources":["../../src/lib/googleCloudStorage.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,MAAM,wCAAwD,CAAC;AAQ5E;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,OAAgB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB7H;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhE;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAW3F"}
@@ -57,3 +57,22 @@ export async function deleteFile(filePath) {
57
57
  });
58
58
  }
59
59
  }
60
+ /**
61
+ * Checks if an object exists in Google Cloud Storage
62
+ * @param bucketName The name of the bucket (unused, uses default bucket)
63
+ * @param objectPath The path of the object to check
64
+ * @returns Promise<boolean> True if the object exists, false otherwise
65
+ */
66
+ export async function objectExists(bucketName, objectPath) {
67
+ try {
68
+ const [exists] = await bucket.file(objectPath).exists();
69
+ return exists;
70
+ }
71
+ catch (error) {
72
+ console.error('Error checking if object exists in Google Cloud Storage:', error);
73
+ throw new TRPCError({
74
+ code: 'INTERNAL_SERVER_ERROR',
75
+ message: 'Failed to check object existence',
76
+ });
77
+ }
78
+ }
@@ -0,0 +1,25 @@
1
+ interface notificationData {
2
+ title: string;
3
+ content: string;
4
+ }
5
+ export declare function sendNotification(receiver: string, data: notificationData): Promise<{
6
+ id: string;
7
+ title: string;
8
+ content: string;
9
+ createdAt: Date;
10
+ senderId: string | null;
11
+ receiverId: string;
12
+ read: boolean;
13
+ }>;
14
+ export declare function sendNotifications(receiverIds: Array<string>, data: notificationData): Promise<import(".prisma/client").Prisma.BatchPayload>;
15
+ export declare function markRead(id: string, read?: boolean): Promise<{
16
+ id: string;
17
+ title: string;
18
+ content: string;
19
+ createdAt: Date;
20
+ senderId: string | null;
21
+ receiverId: string;
22
+ read: boolean;
23
+ }>;
24
+ export {};
25
+ //# sourceMappingURL=notificationHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notificationHandler.d.ts","sourceRoot":"","sources":["../../src/lib/notificationHandler.ts"],"names":[],"mappings":"AAEA,UAAU,gBAAgB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAA;CAClB;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB;;;;;;;;GAS9E;AAED,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,gBAAgB,yDASzF;AAED,wBAAsB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,OAAc;;;;;;;;GAM9D"}
@@ -0,0 +1,28 @@
1
+ import { prisma } from "./prisma.js";
2
+ export async function sendNotification(receiver, data) {
3
+ const notification = await prisma.notification.create({
4
+ data: {
5
+ receiverId: receiver,
6
+ title: data.title,
7
+ content: data.content,
8
+ },
9
+ });
10
+ return notification;
11
+ }
12
+ export async function sendNotifications(receiverIds, data) {
13
+ const notifications = await prisma.notification.createMany({
14
+ data: receiverIds.map(receiverId => ({
15
+ receiverId: receiverId,
16
+ title: data.title,
17
+ content: data.content,
18
+ })),
19
+ });
20
+ return notifications;
21
+ }
22
+ export async function markRead(id, read = true) {
23
+ const notification = await prisma.notification.update({
24
+ where: { id },
25
+ data: { read: read },
26
+ });
27
+ return notification;
28
+ }