@studious-lms/server 1.0.4 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API_SPECIFICATION.md +1117 -0
- package/dist/exportType.js +1 -2
- package/dist/index.js +25 -30
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +31 -29
- package/dist/lib/googleCloudStorage.js +9 -14
- package/dist/lib/prisma.js +4 -7
- package/dist/lib/thumbnailGenerator.js +12 -20
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +17 -22
- package/dist/middleware/logging.js +5 -9
- package/dist/routers/_app.d.ts +3483 -1801
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +28 -27
- package/dist/routers/agenda.d.ts +13 -8
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/agenda.js +14 -17
- package/dist/routers/announcement.d.ts +4 -3
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +28 -31
- package/dist/routers/assignment.d.ts +282 -196
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +256 -202
- package/dist/routers/attendance.d.ts +5 -4
- package/dist/routers/attendance.d.ts.map +1 -1
- package/dist/routers/attendance.js +31 -34
- package/dist/routers/auth.d.ts +1 -0
- package/dist/routers/auth.d.ts.map +1 -1
- package/dist/routers/auth.js +80 -75
- package/dist/routers/class.d.ts +284 -14
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +435 -164
- package/dist/routers/event.d.ts +47 -38
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/event.js +76 -79
- package/dist/routers/file.d.ts +71 -1
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/file.js +267 -32
- package/dist/routers/folder.d.ts +296 -0
- package/dist/routers/folder.d.ts.map +1 -0
- package/dist/routers/folder.js +693 -0
- package/dist/routers/notifications.d.ts +103 -0
- package/dist/routers/notifications.d.ts.map +1 -0
- package/dist/routers/notifications.js +91 -0
- package/dist/routers/school.d.ts +208 -0
- package/dist/routers/school.d.ts.map +1 -0
- package/dist/routers/school.js +481 -0
- package/dist/routers/section.d.ts +1 -0
- package/dist/routers/section.d.ts.map +1 -1
- package/dist/routers/section.js +30 -33
- package/dist/routers/user.d.ts +2 -1
- package/dist/routers/user.d.ts.map +1 -1
- package/dist/routers/user.js +21 -24
- package/dist/seedDatabase.d.ts +22 -0
- package/dist/seedDatabase.d.ts.map +1 -0
- package/dist/seedDatabase.js +57 -0
- package/dist/socket/handlers.js +26 -30
- package/dist/trpc.d.ts +5 -0
- package/dist/trpc.d.ts.map +1 -1
- package/dist/trpc.js +35 -26
- package/dist/types/trpc.js +1 -2
- package/dist/utils/email.js +2 -8
- package/dist/utils/generateInviteCode.js +1 -5
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +13 -9
- package/dist/utils/prismaErrorHandler.d.ts +9 -0
- package/dist/utils/prismaErrorHandler.d.ts.map +1 -0
- package/dist/utils/prismaErrorHandler.js +234 -0
- package/dist/utils/prismaWrapper.d.ts +14 -0
- package/dist/utils/prismaWrapper.d.ts.map +1 -0
- package/dist/utils/prismaWrapper.js +64 -0
- package/package.json +17 -4
- package/prisma/migrations/20250807062924_init/migration.sql +436 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +67 -0
- package/src/index.ts +2 -2
- package/src/lib/fileUpload.ts +16 -7
- package/src/middleware/auth.ts +0 -2
- package/src/routers/_app.ts +5 -1
- package/src/routers/assignment.ts +82 -22
- package/src/routers/auth.ts +80 -54
- package/src/routers/class.ts +330 -36
- package/src/routers/file.ts +283 -20
- package/src/routers/folder.ts +755 -0
- package/src/routers/notifications.ts +93 -0
- package/src/seedDatabase.ts +66 -0
- package/src/socket/handlers.ts +4 -4
- package/src/trpc.ts +13 -0
- package/src/utils/logger.ts +14 -4
- package/src/utils/prismaErrorHandler.ts +275 -0
- package/src/utils/prismaWrapper.ts +91 -0
- package/tests/auth.test.ts +25 -0
- package/tests/class.test.ts +281 -0
- package/tests/setup.ts +98 -0
- package/tests/startup.test.ts +5 -0
- package/tsconfig.json +2 -1
- package/vitest.config.ts +11 -0
- package/dist/logger.d.ts +0 -26
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -135
- package/src/logger.ts +0 -163
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { PrismaClientKnownRequestError, PrismaClientUnknownRequestError, PrismaClientValidationError } from '@prisma/client/runtime/library';
|
|
2
|
+
export function handlePrismaError(error) {
|
|
3
|
+
// PrismaClientKnownRequestError - Database constraint violations, etc.
|
|
4
|
+
if (error instanceof PrismaClientKnownRequestError) {
|
|
5
|
+
return handleKnownRequestError(error);
|
|
6
|
+
}
|
|
7
|
+
// PrismaClientValidationError - Invalid data format, missing required fields, etc.
|
|
8
|
+
if (error instanceof PrismaClientValidationError) {
|
|
9
|
+
return handleValidationError(error);
|
|
10
|
+
}
|
|
11
|
+
// PrismaClientUnknownRequestError - Unknown database errors
|
|
12
|
+
if (error instanceof PrismaClientUnknownRequestError) {
|
|
13
|
+
return handleUnknownRequestError(error);
|
|
14
|
+
}
|
|
15
|
+
// Generic error fallback
|
|
16
|
+
if (error instanceof Error) {
|
|
17
|
+
return {
|
|
18
|
+
message: error.message,
|
|
19
|
+
details: error.stack
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
message: 'An unknown database error occurred',
|
|
24
|
+
details: String(error)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function handleKnownRequestError(error) {
|
|
28
|
+
const { code, meta, message } = error;
|
|
29
|
+
switch (code) {
|
|
30
|
+
case 'P2002':
|
|
31
|
+
const target = Array.isArray(meta?.target) ? meta.target.join(', ') : meta?.target || 'field';
|
|
32
|
+
return {
|
|
33
|
+
message: `A record with this ${target} already exists`,
|
|
34
|
+
code,
|
|
35
|
+
meta,
|
|
36
|
+
details: `Unique constraint violation on ${target}`
|
|
37
|
+
};
|
|
38
|
+
case 'P2003':
|
|
39
|
+
const fieldName = meta?.field_name || 'related field';
|
|
40
|
+
return {
|
|
41
|
+
message: `Cannot delete this record because it's referenced by other records`,
|
|
42
|
+
code,
|
|
43
|
+
meta,
|
|
44
|
+
details: `Foreign key constraint violation on ${fieldName}`
|
|
45
|
+
};
|
|
46
|
+
case 'P2025':
|
|
47
|
+
return {
|
|
48
|
+
message: 'The record you are trying to update or delete does not exist',
|
|
49
|
+
code,
|
|
50
|
+
meta,
|
|
51
|
+
details: 'Record not found in database'
|
|
52
|
+
};
|
|
53
|
+
case 'P2014':
|
|
54
|
+
return {
|
|
55
|
+
message: 'The change you are trying to make would violate the required relationship',
|
|
56
|
+
code,
|
|
57
|
+
meta,
|
|
58
|
+
details: 'Required relation violation'
|
|
59
|
+
};
|
|
60
|
+
case 'P2011':
|
|
61
|
+
return {
|
|
62
|
+
message: 'A required field is missing or empty',
|
|
63
|
+
code,
|
|
64
|
+
meta,
|
|
65
|
+
details: 'Null constraint violation'
|
|
66
|
+
};
|
|
67
|
+
case 'P2012':
|
|
68
|
+
return {
|
|
69
|
+
message: 'The data you provided is not in the correct format',
|
|
70
|
+
code,
|
|
71
|
+
meta,
|
|
72
|
+
details: 'Data validation error'
|
|
73
|
+
};
|
|
74
|
+
case 'P2013':
|
|
75
|
+
return {
|
|
76
|
+
message: 'The data you provided is too long for this field',
|
|
77
|
+
code,
|
|
78
|
+
meta,
|
|
79
|
+
details: 'String length constraint violation'
|
|
80
|
+
};
|
|
81
|
+
case 'P2015':
|
|
82
|
+
return {
|
|
83
|
+
message: 'The record you are looking for could not be found',
|
|
84
|
+
code,
|
|
85
|
+
meta,
|
|
86
|
+
details: 'Record not found'
|
|
87
|
+
};
|
|
88
|
+
case 'P2016':
|
|
89
|
+
return {
|
|
90
|
+
message: 'The query you are trying to execute is not valid',
|
|
91
|
+
code,
|
|
92
|
+
meta,
|
|
93
|
+
details: 'Query interpretation error'
|
|
94
|
+
};
|
|
95
|
+
case 'P2017':
|
|
96
|
+
return {
|
|
97
|
+
message: 'The relationship between records is not properly connected',
|
|
98
|
+
code,
|
|
99
|
+
meta,
|
|
100
|
+
details: 'Relation connection error'
|
|
101
|
+
};
|
|
102
|
+
case 'P2018':
|
|
103
|
+
return {
|
|
104
|
+
message: 'The connected record you are looking for does not exist',
|
|
105
|
+
code,
|
|
106
|
+
meta,
|
|
107
|
+
details: 'Connected record not found'
|
|
108
|
+
};
|
|
109
|
+
case 'P2019':
|
|
110
|
+
return {
|
|
111
|
+
message: 'The input you provided is not valid',
|
|
112
|
+
code,
|
|
113
|
+
meta,
|
|
114
|
+
details: 'Input error'
|
|
115
|
+
};
|
|
116
|
+
case 'P2020':
|
|
117
|
+
return {
|
|
118
|
+
message: 'The value you provided is outside the allowed range',
|
|
119
|
+
code,
|
|
120
|
+
meta,
|
|
121
|
+
details: 'Value out of range'
|
|
122
|
+
};
|
|
123
|
+
case 'P2021':
|
|
124
|
+
return {
|
|
125
|
+
message: 'The table you are trying to access does not exist',
|
|
126
|
+
code,
|
|
127
|
+
meta,
|
|
128
|
+
details: 'Table does not exist'
|
|
129
|
+
};
|
|
130
|
+
case 'P2022':
|
|
131
|
+
return {
|
|
132
|
+
message: 'The column you are trying to access does not exist',
|
|
133
|
+
code,
|
|
134
|
+
meta,
|
|
135
|
+
details: 'Column does not exist'
|
|
136
|
+
};
|
|
137
|
+
case 'P2023':
|
|
138
|
+
return {
|
|
139
|
+
message: 'The column data is not valid',
|
|
140
|
+
code,
|
|
141
|
+
meta,
|
|
142
|
+
details: 'Column data validation error'
|
|
143
|
+
};
|
|
144
|
+
case 'P2024':
|
|
145
|
+
return {
|
|
146
|
+
message: 'The database connection pool is exhausted',
|
|
147
|
+
code,
|
|
148
|
+
meta,
|
|
149
|
+
details: 'Connection pool timeout'
|
|
150
|
+
};
|
|
151
|
+
case 'P2026':
|
|
152
|
+
return {
|
|
153
|
+
message: 'The current database provider does not support this feature',
|
|
154
|
+
code,
|
|
155
|
+
meta,
|
|
156
|
+
details: 'Feature not supported by database provider'
|
|
157
|
+
};
|
|
158
|
+
case 'P2027':
|
|
159
|
+
return {
|
|
160
|
+
message: 'Multiple errors occurred during the database operation',
|
|
161
|
+
code,
|
|
162
|
+
meta,
|
|
163
|
+
details: 'Multiple errors in query execution'
|
|
164
|
+
};
|
|
165
|
+
default:
|
|
166
|
+
return {
|
|
167
|
+
message: 'A database constraint was violated',
|
|
168
|
+
code,
|
|
169
|
+
meta,
|
|
170
|
+
details: message
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function handleValidationError(error) {
|
|
175
|
+
return {
|
|
176
|
+
message: 'The data you provided is not valid',
|
|
177
|
+
details: error.message,
|
|
178
|
+
meta: {
|
|
179
|
+
type: 'validation_error',
|
|
180
|
+
originalMessage: error.message
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function handleUnknownRequestError(error) {
|
|
185
|
+
return {
|
|
186
|
+
message: 'An unexpected database error occurred',
|
|
187
|
+
details: error.message,
|
|
188
|
+
meta: {
|
|
189
|
+
type: 'unknown_request_error',
|
|
190
|
+
originalMessage: error.message
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// Helper function to get user-friendly field names
|
|
195
|
+
export function getFieldDisplayName(fieldName) {
|
|
196
|
+
const fieldMap = {
|
|
197
|
+
'username': 'username',
|
|
198
|
+
'email': 'email address',
|
|
199
|
+
'password': 'password',
|
|
200
|
+
'name': 'name',
|
|
201
|
+
'title': 'title',
|
|
202
|
+
'content': 'content',
|
|
203
|
+
'description': 'description',
|
|
204
|
+
'subject': 'subject',
|
|
205
|
+
'section': 'section',
|
|
206
|
+
'color': 'color',
|
|
207
|
+
'location': 'location',
|
|
208
|
+
'startTime': 'start time',
|
|
209
|
+
'endTime': 'end time',
|
|
210
|
+
'dueDate': 'due date',
|
|
211
|
+
'maxGrade': 'maximum grade',
|
|
212
|
+
'grade': 'grade',
|
|
213
|
+
'feedback': 'feedback',
|
|
214
|
+
'remarks': 'remarks',
|
|
215
|
+
'syllabus': 'syllabus',
|
|
216
|
+
'path': 'file path',
|
|
217
|
+
'size': 'file size',
|
|
218
|
+
'type': 'file type',
|
|
219
|
+
'uploadedAt': 'upload date',
|
|
220
|
+
'verified': 'verification status',
|
|
221
|
+
'profileId': 'profile',
|
|
222
|
+
'schoolId': 'school',
|
|
223
|
+
'classId': 'class',
|
|
224
|
+
'assignmentId': 'assignment',
|
|
225
|
+
'submissionId': 'submission',
|
|
226
|
+
'userId': 'user',
|
|
227
|
+
'eventId': 'event',
|
|
228
|
+
'sessionId': 'session',
|
|
229
|
+
'thumbnailId': 'thumbnail',
|
|
230
|
+
'annotationId': 'annotation',
|
|
231
|
+
'logoId': 'logo'
|
|
232
|
+
};
|
|
233
|
+
return fieldMap[fieldName] || fieldName;
|
|
234
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function withPrismaErrorHandling<T>(operation: () => Promise<T>, context?: string): Promise<T>;
|
|
2
|
+
export declare const prismaWrapper: {
|
|
3
|
+
findUnique: <T>(operation: () => Promise<T | null>, context?: string) => Promise<T | null>;
|
|
4
|
+
findFirst: <T>(operation: () => Promise<T | null>, context?: string) => Promise<T | null>;
|
|
5
|
+
findMany: <T>(operation: () => Promise<T[]>, context?: string) => Promise<T[]>;
|
|
6
|
+
create: <T>(operation: () => Promise<T>, context?: string) => Promise<T>;
|
|
7
|
+
update: <T>(operation: () => Promise<T>, context?: string) => Promise<T>;
|
|
8
|
+
delete: <T>(operation: () => Promise<T>, context?: string) => Promise<T>;
|
|
9
|
+
deleteMany: <T>(operation: () => Promise<T>, context?: string) => Promise<T>;
|
|
10
|
+
upsert: <T>(operation: () => Promise<T>, context?: string) => Promise<T>;
|
|
11
|
+
count: <T>(operation: () => Promise<T>, context?: string) => Promise<T>;
|
|
12
|
+
aggregate: <T>(operation: () => Promise<T>, context?: string) => Promise<T>;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=prismaWrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prismaWrapper.d.ts","sourceRoot":"","sources":["../../src/utils/prismaWrapper.ts"],"names":[],"mappings":"AAGA,wBAAsB,uBAAuB,CAAC,CAAC,EAC7C,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,GAAE,MAA6B,GACrC,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAgCD,eAAO,MAAM,aAAa;iBACX,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,MAAM;gBAGxD,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,MAAM;eAGxD,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,EAAE,CAAC,YAAY,MAAM;aAGpD,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,MAAM;aAGhD,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,MAAM;aAGhD,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,MAAM;iBAG5C,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,MAAM;aAGpD,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,MAAM;YAGjD,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,MAAM;gBAG5C,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,MAAM;CAE7D,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
|
2
|
+
import { handlePrismaError } from './prismaErrorHandler';
|
|
3
|
+
export async function withPrismaErrorHandling(operation, context = 'database operation') {
|
|
4
|
+
try {
|
|
5
|
+
return await operation();
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
const prismaErrorInfo = handlePrismaError(error);
|
|
9
|
+
// Log the detailed error for debugging
|
|
10
|
+
console.error(`Prisma error in ${context}:`, {
|
|
11
|
+
userMessage: prismaErrorInfo.message,
|
|
12
|
+
details: prismaErrorInfo.details,
|
|
13
|
+
code: prismaErrorInfo.code,
|
|
14
|
+
meta: prismaErrorInfo.meta,
|
|
15
|
+
originalError: error
|
|
16
|
+
});
|
|
17
|
+
// Throw a tRPC error with the user-friendly message
|
|
18
|
+
throw new TRPCError({
|
|
19
|
+
code: getTRPCCode(prismaErrorInfo.code),
|
|
20
|
+
message: prismaErrorInfo.message,
|
|
21
|
+
cause: error
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getTRPCCode(prismaCode) {
|
|
26
|
+
if (!prismaCode)
|
|
27
|
+
return 'INTERNAL_SERVER_ERROR';
|
|
28
|
+
// Map Prisma error codes to tRPC error codes
|
|
29
|
+
const codeMap = {
|
|
30
|
+
'P2002': 'CONFLICT', // Unique constraint violation
|
|
31
|
+
'P2003': 'BAD_REQUEST', // Foreign key constraint violation
|
|
32
|
+
'P2025': 'NOT_FOUND', // Record not found
|
|
33
|
+
'P2014': 'BAD_REQUEST', // Required relation violation
|
|
34
|
+
'P2011': 'BAD_REQUEST', // Null constraint violation
|
|
35
|
+
'P2012': 'BAD_REQUEST', // Data validation error
|
|
36
|
+
'P2013': 'BAD_REQUEST', // String length constraint violation
|
|
37
|
+
'P2015': 'NOT_FOUND', // Record not found
|
|
38
|
+
'P2016': 'BAD_REQUEST', // Query interpretation error
|
|
39
|
+
'P2017': 'BAD_REQUEST', // Relation connection error
|
|
40
|
+
'P2018': 'NOT_FOUND', // Connected record not found
|
|
41
|
+
'P2019': 'BAD_REQUEST', // Input error
|
|
42
|
+
'P2020': 'BAD_REQUEST', // Value out of range
|
|
43
|
+
'P2021': 'INTERNAL_SERVER_ERROR', // Table does not exist
|
|
44
|
+
'P2022': 'INTERNAL_SERVER_ERROR', // Column does not exist
|
|
45
|
+
'P2023': 'BAD_REQUEST', // Column data validation error
|
|
46
|
+
'P2024': 'INTERNAL_SERVER_ERROR', // Connection pool timeout
|
|
47
|
+
'P2026': 'INTERNAL_SERVER_ERROR', // Feature not supported
|
|
48
|
+
'P2027': 'INTERNAL_SERVER_ERROR', // Multiple errors
|
|
49
|
+
};
|
|
50
|
+
return codeMap[prismaCode] || 'INTERNAL_SERVER_ERROR';
|
|
51
|
+
}
|
|
52
|
+
// Convenience functions for common operations
|
|
53
|
+
export const prismaWrapper = {
|
|
54
|
+
findUnique: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
55
|
+
findFirst: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
56
|
+
findMany: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
57
|
+
create: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
58
|
+
update: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
59
|
+
delete: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
60
|
+
deleteMany: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
61
|
+
upsert: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
62
|
+
count: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
63
|
+
aggregate: (operation, context) => withPrismaErrorHandling(operation, context),
|
|
64
|
+
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studious-lms/server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Backend server for Studious application",
|
|
5
5
|
"main": "dist/exportType.js",
|
|
6
6
|
"types": "dist/exportType.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/exportType.d.ts",
|
|
11
|
+
"default": "./dist/exportType.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
7
14
|
"scripts": {
|
|
8
|
-
"dev": "ts-node-dev
|
|
15
|
+
"dev": "ts-node-dev --respawn --transpile-only --loader ts-node/esm src/index.ts",
|
|
9
16
|
"build": "tsc",
|
|
10
17
|
"start": "node dist/index.js",
|
|
11
18
|
"generate": "npx prisma generate",
|
|
12
|
-
"prepublishOnly": "npm run generate && npm run build"
|
|
19
|
+
"prepublishOnly": "npm run generate && npm run build",
|
|
20
|
+
"test": "vitest",
|
|
21
|
+
"seed": "ts-node src/seedDatabase.ts"
|
|
13
22
|
},
|
|
14
23
|
"dependencies": {
|
|
15
24
|
"@google-cloud/storage": "^7.16.0",
|
|
@@ -23,6 +32,7 @@
|
|
|
23
32
|
"nodemailer": "^7.0.4",
|
|
24
33
|
"prisma": "^6.7.0",
|
|
25
34
|
"sharp": "^0.34.2",
|
|
35
|
+
"socket.io": "^4.8.1",
|
|
26
36
|
"superjson": "^2.2.2",
|
|
27
37
|
"uuid": "^11.1.0",
|
|
28
38
|
"zod": "^3.22.4"
|
|
@@ -32,10 +42,13 @@
|
|
|
32
42
|
"@types/express": "^4.17.21",
|
|
33
43
|
"@types/node": "^20.11.24",
|
|
34
44
|
"@types/nodemailer": "^6.4.17",
|
|
45
|
+
"@types/supertest": "^6.0.3",
|
|
35
46
|
"@types/uuid": "^10.0.0",
|
|
47
|
+
"supertest": "^7.1.4",
|
|
36
48
|
"ts-node": "^10.9.2",
|
|
37
49
|
"ts-node-dev": "^2.0.0",
|
|
38
50
|
"tsconfig-paths": "^4.2.0",
|
|
39
|
-
"typescript": "^5.3.3"
|
|
51
|
+
"typescript": "^5.3.3",
|
|
52
|
+
"vitest": "^3.2.4"
|
|
40
53
|
}
|
|
41
54
|
}
|