@studious-lms/server 1.4.2 → 1.4.3

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 (145) hide show
  1. package/.coderabbit.yaml +4 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +67 -28
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/fileUpload.d.ts +10 -2
  6. package/dist/lib/fileUpload.d.ts.map +1 -1
  7. package/dist/lib/fileUpload.js +38 -6
  8. package/dist/lib/fileUpload.js.map +1 -1
  9. package/dist/middleware/auth.d.ts.map +1 -1
  10. package/dist/middleware/auth.js +18 -5
  11. package/dist/middleware/auth.js.map +1 -1
  12. package/dist/models/auth.d.ts +19 -14
  13. package/dist/models/auth.d.ts.map +1 -1
  14. package/dist/models/auth.js +53 -30
  15. package/dist/models/auth.js.map +1 -1
  16. package/dist/models/class.d.ts +3 -1
  17. package/dist/models/class.d.ts.map +1 -1
  18. package/dist/models/class.js +16 -4
  19. package/dist/models/class.js.map +1 -1
  20. package/dist/models/notification.d.ts +11 -10
  21. package/dist/models/notification.d.ts.map +1 -1
  22. package/dist/models/notification.js +13 -9
  23. package/dist/models/notification.js.map +1 -1
  24. package/dist/models/worksheet.d.ts +378 -7
  25. package/dist/models/worksheet.d.ts.map +1 -1
  26. package/dist/models/worksheet.js +168 -2
  27. package/dist/models/worksheet.js.map +1 -1
  28. package/dist/pipelines/aiLabChat.d.ts.map +1 -1
  29. package/dist/pipelines/aiLabChat.js +11 -3
  30. package/dist/pipelines/aiLabChat.js.map +1 -1
  31. package/dist/pipelines/aiLabChatContract.d.ts +3 -3
  32. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
  33. package/dist/pipelines/aiNewtonChat.js +25 -14
  34. package/dist/pipelines/aiNewtonChat.js.map +1 -1
  35. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
  36. package/dist/pipelines/gradeWorksheet.js +10 -3
  37. package/dist/pipelines/gradeWorksheet.js.map +1 -1
  38. package/dist/routers/_app.d.ts +124 -18
  39. package/dist/routers/_app.d.ts.map +1 -1
  40. package/dist/routers/class.d.ts +2 -1
  41. package/dist/routers/class.d.ts.map +1 -1
  42. package/dist/routers/class.js +3 -3
  43. package/dist/routers/class.js.map +1 -1
  44. package/dist/routers/labChat.d.ts +6 -0
  45. package/dist/routers/labChat.d.ts.map +1 -1
  46. package/dist/routers/labChat.js +5 -3
  47. package/dist/routers/labChat.js.map +1 -1
  48. package/dist/routers/message.d.ts +31 -0
  49. package/dist/routers/message.d.ts.map +1 -1
  50. package/dist/routers/message.js +27 -4
  51. package/dist/routers/message.js.map +1 -1
  52. package/dist/routers/newtonChat.d.ts +6 -0
  53. package/dist/routers/newtonChat.d.ts.map +1 -1
  54. package/dist/routers/newtonChat.js +5 -4
  55. package/dist/routers/newtonChat.js.map +1 -1
  56. package/dist/routers/notifications.d.ts +9 -2
  57. package/dist/routers/notifications.d.ts.map +1 -1
  58. package/dist/routers/notifications.js +4 -4
  59. package/dist/routers/notifications.js.map +1 -1
  60. package/dist/routers/worksheet.d.ts +8 -6
  61. package/dist/routers/worksheet.d.ts.map +1 -1
  62. package/dist/routers/worksheet.js +18 -16
  63. package/dist/routers/worksheet.js.map +1 -1
  64. package/dist/services/auth.d.ts +1 -1
  65. package/dist/services/auth.d.ts.map +1 -1
  66. package/dist/services/auth.js +19 -12
  67. package/dist/services/auth.js.map +1 -1
  68. package/dist/services/class.d.ts +1 -1
  69. package/dist/services/class.d.ts.map +1 -1
  70. package/dist/services/class.js +6 -3
  71. package/dist/services/class.js.map +1 -1
  72. package/dist/services/file.d.ts.map +1 -1
  73. package/dist/services/file.js +7 -2
  74. package/dist/services/file.js.map +1 -1
  75. package/dist/services/labChat.d.ts +6 -0
  76. package/dist/services/labChat.d.ts.map +1 -1
  77. package/dist/services/labChat.js +30 -18
  78. package/dist/services/labChat.js.map +1 -1
  79. package/dist/services/message.d.ts +30 -0
  80. package/dist/services/message.d.ts.map +1 -1
  81. package/dist/services/message.js +97 -21
  82. package/dist/services/message.js.map +1 -1
  83. package/dist/services/newtonChat.d.ts +6 -1
  84. package/dist/services/newtonChat.d.ts.map +1 -1
  85. package/dist/services/newtonChat.js +36 -18
  86. package/dist/services/newtonChat.js.map +1 -1
  87. package/dist/services/notification.d.ts +12 -5
  88. package/dist/services/notification.d.ts.map +1 -1
  89. package/dist/services/notification.js +24 -9
  90. package/dist/services/notification.js.map +1 -1
  91. package/dist/services/studentProgress.d.ts.map +1 -1
  92. package/dist/services/studentProgress.js +6 -3
  93. package/dist/services/studentProgress.js.map +1 -1
  94. package/dist/services/worksheet.d.ts +15 -15
  95. package/dist/services/worksheet.d.ts.map +1 -1
  96. package/dist/services/worksheet.js +138 -54
  97. package/dist/services/worksheet.js.map +1 -1
  98. package/dist/trpc.d.ts.map +1 -1
  99. package/dist/trpc.js +11 -17
  100. package/dist/trpc.js.map +1 -1
  101. package/dist/utils/inference.d.ts +9 -1
  102. package/dist/utils/inference.d.ts.map +1 -1
  103. package/dist/utils/inference.js +138 -50
  104. package/dist/utils/inference.js.map +1 -1
  105. package/package.json +1 -1
  106. package/prisma/migrations/20260415103500_add_session_type/migration.sql +33 -0
  107. package/prisma/migrations/20260415133000_add_ai_usage_logs/migration.sql +19 -0
  108. package/prisma/schema.prisma +27 -1
  109. package/scripts/test-pre-push.ts +6 -2
  110. package/src/index.ts +80 -28
  111. package/src/lib/fileUpload.ts +55 -5
  112. package/src/middleware/auth.ts +19 -4
  113. package/src/models/auth.ts +59 -29
  114. package/src/models/class.ts +15 -4
  115. package/src/models/notification.ts +13 -7
  116. package/src/models/worksheet.ts +202 -0
  117. package/src/pipelines/aiLabChat.ts +9 -1
  118. package/src/pipelines/aiNewtonChat.ts +24 -14
  119. package/src/pipelines/gradeWorksheet.ts +8 -0
  120. package/src/routers/class.ts +2 -2
  121. package/src/routers/labChat.ts +3 -1
  122. package/src/routers/message.ts +38 -1
  123. package/src/routers/newtonChat.ts +4 -2
  124. package/src/routers/notifications.ts +2 -2
  125. package/src/routers/worksheet.ts +31 -19
  126. package/src/services/auth.ts +26 -12
  127. package/src/services/class.ts +4 -1
  128. package/src/services/file.ts +6 -0
  129. package/src/services/labChat.ts +42 -18
  130. package/src/services/message.ts +163 -18
  131. package/src/services/newtonChat.ts +49 -18
  132. package/src/services/notification.ts +27 -8
  133. package/src/services/studentProgress.ts +4 -1
  134. package/src/services/worksheet.ts +191 -51
  135. package/src/trpc.ts +12 -18
  136. package/src/utils/inference.ts +209 -55
  137. package/tests/globalSetup.ts +13 -4
  138. package/tests/routers/auth.test.ts +120 -17
  139. package/tests/routers/class.test.ts +7 -1
  140. package/tests/routers/message.test.ts +236 -9
  141. package/tests/routers/newtonChat.test.ts +81 -0
  142. package/tests/routers/notification.test.ts +12 -0
  143. package/tests/routers/worksheet.test.ts +194 -0
  144. package/tests/setup.ts +22 -5
  145. package/tests/utils/inference.test.ts +218 -0
package/.coderabbit.yaml CHANGED
@@ -1,9 +1,12 @@
1
1
  # Raise the file limit
2
2
  reviews:
3
3
  review_status: true
4
+ auto_review:
5
+ base_branches:
6
+ - staging
4
7
  path_filters:
5
8
  # Exclude paths you don't want reviewed
6
9
  - "!**/node_modules/**"
7
10
  - "!**/dist/**"
8
11
  - "!**/*.lock"
9
- - "!**/package-lock.json"
12
+ - "!**/package-lock.json"
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"AAyBA,OAAO,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"AA2BA,OAAO,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0a09f7dd-016d-5f2f-bdf3-5a5a294b50cb")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3cfa0c9c-5588-5fbd-a283-4495b71df718")}catch(e){}}();
3
3
  import express from 'express';
4
4
  import { createServer } from 'http';
5
5
  import { Server } from 'socket.io';
@@ -7,9 +7,11 @@ import cors from 'cors';
7
7
  import { createExpressMiddleware } from '@trpc/server/adapters/express';
8
8
  import { appRouter } from './routers/_app.js';
9
9
  import { createTRPCContext, createCallerFactory } from './trpc.js';
10
+ import { findUserBySessionToken } from './models/auth.js';
10
11
  import { logger } from './utils/logger.js';
11
12
  import { setupSocketHandlers } from './socket/handlers.js';
12
13
  import { bucket } from './lib/googleCloudStorage.js';
14
+ import { validateDirectUploadIntent } from './lib/fileUpload.js';
13
15
  import { prisma } from './lib/prisma.js';
14
16
  import { pusher } from './lib/pusher.js';
15
17
  import { connectRedis, disconnectRedis } from './lib/redis.js';
@@ -94,6 +96,16 @@ app.use((req, res, next) => {
94
96
  // });
95
97
  // Create HTTP server
96
98
  const httpServer = createServer(app);
99
+ app.get('/', async (req, res) => {
100
+ res.status(200).json({
101
+ status: 'OK',
102
+ timestamp: new Date().toISOString(),
103
+ uptime: process.uptime(),
104
+ version: process.env.npm_package_version,
105
+ environment: process.env.NODE_ENV,
106
+ appUrl: process.env.NEXT_PUBLIC_APP_URL,
107
+ });
108
+ });
97
109
  app.get('/health', async (req, res) => {
98
110
  try {
99
111
  // Check database connectivity
@@ -130,10 +142,7 @@ app.post('/api/pusher/auth', async (req, res) => {
130
142
  if (!token) {
131
143
  return res.status(401).json({ error: 'Authentication required' });
132
144
  }
133
- const user = await prisma.user.findFirst({
134
- where: { sessions: { some: { id: token } } },
135
- select: { id: true, username: true },
136
- });
145
+ const user = await findUserBySessionToken(token);
137
146
  if (!user) {
138
147
  return res.status(401).json({ error: 'Invalid or expired session' });
139
148
  }
@@ -234,19 +243,7 @@ app.get('/api/files/:fileId', async (req, res) => {
234
243
  }
235
244
  const token = typeof userHeader === 'string' ? userHeader : userHeader[0];
236
245
  // Find user by session token
237
- const user = await prisma.user.findFirst({
238
- where: {
239
- sessions: {
240
- some: {
241
- id: token
242
- }
243
- }
244
- },
245
- select: {
246
- id: true,
247
- username: true,
248
- }
249
- });
246
+ const user = await findUserBySessionToken(token);
250
247
  if (!user) {
251
248
  return res.status(401).json({ error: 'Invalid or expired session' });
252
249
  }
@@ -405,14 +402,40 @@ app.get('/api/files/:fileId', async (req, res) => {
405
402
  });
406
403
  app.use('/trpc/auth.login', authLimiter);
407
404
  app.use('/trpc/auth.register', authLimiter);
405
+ async function getAuthenticatedRequestUser(req) {
406
+ const userHeader = req.headers['x-user'];
407
+ const tokenFromUserHeader = typeof userHeader === 'string' ? userHeader : userHeader?.[0];
408
+ const authorizationHeader = req.headers.authorization;
409
+ const tokenFromAuthorization = typeof authorizationHeader === 'string' && authorizationHeader.startsWith('Bearer ')
410
+ ? authorizationHeader.slice(7)
411
+ : null;
412
+ let token = tokenFromUserHeader ?? tokenFromAuthorization;
413
+ if (!token && req.headers.cookie) {
414
+ const cookieName = env.PUSHER_AUTH_COOKIE_NAME || 'token';
415
+ const match = req.headers.cookie.match(new RegExp(cookieName + '=([^;]+)'));
416
+ token = match?.[1]?.trim() ?? null;
417
+ }
418
+ if (!token) {
419
+ return null;
420
+ }
421
+ return findUserBySessionToken(token);
422
+ }
423
+ function getUploadSessionId(req) {
424
+ const queryValue = req.query.uploadSessionId;
425
+ if (typeof queryValue === 'string' && queryValue.trim()) {
426
+ return queryValue;
427
+ }
428
+ const headerValue = req.headers['x-upload-session-id'];
429
+ return typeof headerValue === 'string' ? headerValue : headerValue?.[0];
430
+ }
408
431
  // File upload endpoint for secure file uploads (supports both POST and PUT)
409
432
  app.post('/api/upload/:filePath', uploadLimiter, async (req, res) => {
410
- handleFileUpload(req, res);
433
+ await handleFileUpload(req, res);
411
434
  });
412
435
  app.put('/api/upload/:filePath', uploadLimiter, async (req, res) => {
413
- handleFileUpload(req, res);
436
+ await handleFileUpload(req, res);
414
437
  });
415
- function handleFileUpload(req, res) {
438
+ async function handleFileUpload(req, res) {
416
439
  try {
417
440
  const filePath = decodeURIComponent(req.params.filePath);
418
441
  // Set CORS headers for upload endpoint
@@ -424,19 +447,28 @@ function handleFileUpload(req, res) {
424
447
  res.header('Access-Control-Allow-Origin', origin);
425
448
  }
426
449
  res.header('Access-Control-Allow-Credentials', 'true');
427
- // Get content type from headers
450
+ const user = await getAuthenticatedRequestUser(req);
451
+ if (!user) {
452
+ return res.status(401).json({ error: 'Authentication required' });
453
+ }
454
+ const uploadSessionId = getUploadSessionId(req);
455
+ if (!uploadSessionId) {
456
+ return res.status(401).json({ error: 'Upload session is required' });
457
+ }
458
+ await validateDirectUploadIntent({
459
+ filePath,
460
+ uploadSessionId,
461
+ userId: user.id,
462
+ });
428
463
  const contentType = req.headers['content-type'] || 'application/octet-stream';
429
- // Create a new file in the bucket
430
464
  const file = bucket.file(filePath);
431
- // Create a write stream to Google Cloud Storage
432
465
  const writeStream = file.createWriteStream({
433
466
  metadata: {
434
467
  contentType,
435
468
  },
436
469
  });
437
- // Handle stream events
438
470
  writeStream.on('error', (error) => {
439
- logger.error('Error uploading file:', { error });
471
+ logger.error('Error uploading file:', { error, filePath, userId: user.id });
440
472
  if (!res.headersSent) {
441
473
  res.status(500).json({ error: 'Error uploading file' });
442
474
  }
@@ -448,11 +480,18 @@ function handleFileUpload(req, res) {
448
480
  message: 'File uploaded successfully'
449
481
  });
450
482
  });
451
- // Pipe the request body to the write stream
452
483
  req.pipe(writeStream);
453
484
  }
454
485
  catch (error) {
455
486
  logger.error('Error handling file upload:', { error });
487
+ const code = typeof error === 'object' && error && 'code' in error ? error.code : null;
488
+ const message = error instanceof Error ? error.message : 'Internal server error';
489
+ if (code === 'FORBIDDEN') {
490
+ return res.status(403).json({ error: message });
491
+ }
492
+ if (code === 'BAD_REQUEST') {
493
+ return res.status(400).json({ error: message });
494
+ }
456
495
  res.status(500).json({ error: 'Internal server error' });
457
496
  }
458
497
  }
@@ -531,4 +570,4 @@ const gracefulShutdown = (signal) => {
531
570
  process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
532
571
  process.on('SIGINT', () => gracefulShutdown('SIGINT'));
533
572
  //# sourceMappingURL=index.js.map
534
- //# debugId=0a09f7dd-016d-5f2f-bdf3-5a5a294b50cb
573
+ //# debugId=3cfa0c9c-5588-5fbd-a283-4495b71df718
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["index.ts"],"sourceRoot":"/","sourcesContent":["import express from 'express';\nimport type { Request, Response } from 'express';\nimport { createServer } from 'http';\nimport { Server } from 'socket.io';\nimport cors from 'cors';\nimport dotenv from 'dotenv';\nimport { createExpressMiddleware } from '@trpc/server/adapters/express';\nimport { appRouter } from './routers/_app.js';\nimport { createTRPCContext, createCallerFactory } from './trpc.js';\nimport { logger } from './utils/logger.js';\nimport { setupSocketHandlers } from './socket/handlers.js';\nimport { bucket } from './lib/googleCloudStorage.js';\nimport { prisma } from './lib/prisma.js';\nimport { pusher } from './lib/pusher.js';\nimport { connectRedis, disconnectRedis } from './lib/redis.js';\nimport { createCorsOriginMatcher } from './lib/config/cors.js';\n\nimport { authLimiter, generalLimiter, helmetConfig, uploadLimiter } from './middleware/security.js';\n\nimport * as Sentry from \"@sentry/node\";\nimport { env } from './lib/config/env.js';\nimport compression from 'compression';\nimport { v4 as uuidv4 } from 'uuid';\n\n\nimport \"./instrument.js\";\nimport { openAIClient } from './utils/inference.js';\n\nconst app = express();\n\napp.set('trust proxy', 1);\n\napp.use(helmetConfig);\napp.use(compression());\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\napp.use((req, res, next) => {\n const requestId = uuidv4();\n res.setHeader('X-Request-ID', requestId);\n next();\n});\n\nconst { allowedOrigins, allowedOriginPatterns, isAllowedOrigin } = createCorsOriginMatcher(env);\n\n// CORS middleware\napp.use(cors({\n origin: (origin, callback) => {\n if (!origin || isAllowedOrigin(origin)) {\n callback(null, true);\n return;\n }\n\n logger.warn('CORS origin rejected', { origin, allowedOrigins });\n callback(null, false);\n },\n credentials: true,\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'x-user'],\n preflightContinue: false,\n optionsSuccessStatus: 204,\n}));\n\napp.use(generalLimiter);\n\n// CORS debugging middleware\napp.use((req, res, next) => {\n if (req.method === 'OPTIONS' || req.path.includes('trpc')) {\n logger.info('CORS Request', {\n method: req.method,\n path: req.path,\n origin: req.headers.origin,\n userAgent: req.headers['user-agent']\n });\n }\n next();\n});\n\n// Response time logging middleware\napp.use((req, res, next) => {\n const start = Date.now();\n res.on('finish', () => {\n const duration = Date.now() - start;\n logger.info('Request completed', {\n method: req.method,\n path: req.path,\n statusCode: res.statusCode,\n duration: `${duration}ms`\n });\n });\n next();\n});\n\n// app.use(\"/panel\", async (_, res) => {\n// if (env.NODE_ENV !== \"development\") {\n// return res.status(404).send(\"Not Found\");\n// }\n\n// // Dynamically import renderTrpcPanel only in development\n// const { renderTrpcPanel } = await import(\"trpc-ui\");\n\n// return res.send(\n// renderTrpcPanel(appRouter, {\n// url: \"/trpc\", // Base url of your trpc server\n// meta: {\n// title: \"Studious Backend\",\n// description:\n// \"This is the backend for the Studious application.\",\n// },\n// })\n// );\n// });\n\n\n// Create HTTP server\nconst httpServer = createServer(app);\n\napp.get('/health', async (req, res) => {\n\n try {\n // Check database connectivity\n await prisma.$queryRaw`SELECT 1`;\n \n res.status(200).json({ \n status: 'OK',\n timestamp: new Date().toISOString(),\n uptime: process.uptime(),\n database: 'connected'\n });\n } catch (error) {\n res.status(503).json({ \n status: 'ERROR',\n database: 'disconnected',\n error: error instanceof Error ? error.message : 'Unknown error'\n });\n }\n});\n\n// Pusher channel auth (for private-* and presence-* channels)\n// Token from: x-user header, or cookie (same-origin requests send cookies automatically)\napp.post('/api/pusher/auth', async (req, res) => {\n try {\n let token = req.headers['x-user'] as string | undefined;\n if (!token && req.headers.cookie) {\n const cookieName = env.PUSHER_AUTH_COOKIE_NAME || 'token';\n const match = req.headers.cookie.match(new RegExp(`${cookieName}=([^;]+)`));\n token = match?.[1]?.trim();\n }\n const { socket_id, channel_name } = req.body as { socket_id?: string; channel_name?: string };\n\n if (!socket_id || !channel_name) {\n return res.status(400).json({ error: 'socket_id and channel_name required' });\n }\n\n if (!token) {\n return res.status(401).json({ error: 'Authentication required' });\n }\n\n const user = await prisma.user.findFirst({\n where: { sessions: { some: { id: token } } },\n select: { id: true, username: true },\n });\n\n if (!user) {\n return res.status(401).json({ error: 'Invalid or expired session' });\n }\n\n // Verify channel access for private-conversation-* channels\n if (channel_name.startsWith('private-conversation-')) {\n const conversationId = channel_name.replace('private-conversation-', '');\n const member = await prisma.conversationMember.findFirst({\n where: { conversationId, userId: user.id },\n });\n \n if (!member) {\n return res.status(403).json({ error: 'Not a member of this conversation' });\n }\n }\n\n if (channel_name.startsWith('private-worksheet-')) {\n const worksheetId = channel_name.replace('private-worksheet-', '');\n const worksheet = await prisma.studentWorksheetResponse.findFirst({\n where: { id: worksheetId, OR: [\n { studentId: user.id },\n { submission: { assignment: { class: { teachers: { some: { id: user.id } } } } } },\n ] },\n });\n if (!worksheet) {\n return res.status(403).json({ error: 'No access to this worksheet' });\n }\n }\n\n if (channel_name.startsWith('private-teacher-')) {\n const classId = channel_name.replace('private-teacher-', '');\n const isTeacher = await prisma.class.findFirst({\n where: { id: classId, teachers: { some: { id: user.id } } },\n });\n if (!isTeacher) {\n return res.status(403).json({ error: 'Not a teacher of this class' });\n }\n }\n\n // Verify channel access for private-class-* channels\n // if (channel_name.startsWith('private-class-')) {\n // const classId = channel_name.replace('private-class-', '');\n // const isMember = await prisma.class.findFirst({\n // where: {\n // id: classId,\n // OR: [\n // { students: { some: { id: user.id } } },\n // { teachers: { some: { id: user.id } } },\n // ],\n // },\n // });\n // if (!isMember) {\n // return res.status(403).json({ error: 'Not a member of this class' });\n // }\n // }\n\n if (channel_name.startsWith('presence-')) {\n const authResponse = pusher.authorizeChannel(socket_id, channel_name, {\n user_id: user.id,\n user_info: { username: user.username },\n });\n return res.json(authResponse);\n }\n\n const authResponse = pusher.authorizeChannel(socket_id, channel_name);\n return res.json(authResponse);\n } catch (error) {\n logger.error('Pusher auth error', { error });\n return res.status(500).json({ error: 'Authentication failed' });\n }\n});\n\n// Setup Socket.IO\nconst io = new Server(httpServer, {\n cors: {\n origin: (origin, callback) => {\n callback(null, !origin || isAllowedOrigin(origin));\n },\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n credentials: true,\n allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Access-Control-Allow-Origin', 'x-user']\n },\n transports: ['websocket', 'polling'],\n pingTimeout: 60000,\n pingInterval: 25000,\n connectTimeout: 45000,\n path: '/socket.io/',\n allowEIO3: true\n});\n\n// Add server-level logging\nio.engine.on('connection_error', (err: Error) => {\n logger.error('Socket connection error', { error: err.message });\n});\n\n// Setup socket handlers\nsetupSocketHandlers(io);\n\n// File serving endpoint for secure file access\napp.get('/api/files/:fileId', async (req, res) => {\n try {\n const fileId = decodeURIComponent(req.params.fileId);\n // console.log('File request:', { fileId, originalPath: req.params.fileId });\n \n // Get user from request headers\n const userHeader = req.headers['x-user'];\n if (!userHeader) {\n return res.status(401).json({ error: 'Authentication required' });\n }\n\n const token = typeof userHeader === 'string' ? userHeader : userHeader[0];\n\n // Find user by session token\n const user = await prisma.user.findFirst({\n where: {\n sessions: {\n some: {\n id: token\n }\n }\n },\n select: {\n id: true,\n username: true,\n }\n });\n\n if (!user) {\n return res.status(401).json({ error: 'Invalid or expired session' });\n }\n\n // Find file in database by path\n const fileRecord = await prisma.file.findFirst({\n where: { id: fileId },\n include: {\n user: true,\n assignment: {\n include: {\n class: {\n include: {\n students: true,\n teachers: true\n }\n }\n }\n },\n submission: {\n include: {\n student: true,\n assignment: {\n include: {\n class: {\n include: {\n teachers: true\n }\n }\n }\n }\n }\n },\n annotations: {\n include: {\n student: true,\n assignment: {\n include: {\n class: {\n include: {\n teachers: true\n }\n }\n }\n }\n }\n },\n folder: {\n include: {\n class: {\n include: {\n students: true,\n teachers: true\n }\n }\n }\n },\n classDraft: {\n include: {\n students: true,\n teachers: true\n }\n }\n }\n });\n\n if (!fileRecord) {\n return res.status(404).json({ error: 'File not found in database' });\n }\n\n // Check if user has permission to access this file\n let hasPermission = false;\n\n // Check if user created the file\n if (fileRecord.userId === user.id) {\n hasPermission = true;\n }\n\n // Check if file is related to a class where user is a member\n if (!hasPermission) {\n // Check assignment files\n if (fileRecord.assignment?.class) {\n const classData = fileRecord.assignment.class;\n const isStudent = classData.students.some(student => student.id === user.id);\n const isTeacher = classData.teachers.some(teacher => teacher.id === user.id);\n if (isStudent || isTeacher) {\n hasPermission = true;\n }\n }\n\n if (!hasPermission && fileRecord.annotations) {\n const annotation = fileRecord.annotations;\n if (annotation.studentId === user.id) {\n hasPermission = true;\n } else if (annotation.assignment?.class?.teachers.some(teacher => teacher.id === user.id)) {\n hasPermission = true;\n }\n }\n\n // Check submission files (student can access their own submissions, teachers can access all submissions in their class)\n if (!hasPermission && fileRecord.submission) {\n const submission = fileRecord.submission;\n if (submission.studentId === user.id) {\n hasPermission = true; // Student accessing their own submission\n } else if (submission.assignment?.class?.teachers.some(teacher => teacher.id === user.id)) {\n hasPermission = true; // Teacher accessing submission in their class\n }\n }\n\n // Check folder files\n if (!hasPermission && fileRecord.folder?.class) {\n const classData = fileRecord.folder.class;\n const isStudent = classData.students.some(student => student.id === user.id);\n const isTeacher = classData.teachers.some(teacher => teacher.id === user.id);\n if (isStudent || isTeacher) {\n hasPermission = true;\n }\n }\n\n // Check class draft files\n if (!hasPermission && fileRecord.classDraft) {\n const classData = fileRecord.classDraft;\n const isStudent = classData.students.some(student => student.id === user.id);\n const isTeacher = classData.teachers.some(teacher => teacher.id === user.id);\n if (isStudent || isTeacher) {\n hasPermission = true;\n }\n }\n }\n\n if (!hasPermission) {\n return res.status(403).json({ error: 'Access denied - insufficient permissions' });\n }\n \n const filePath = fileRecord.path;\n \n // Get file from Google Cloud Storage\n const file = bucket.file(filePath);\n const [exists] = await file.exists();\n \n if (!exists) {\n return res.status(404).json({ error: 'File not found in storage', filePath });\n }\n \n // Get file metadata\n const [metadata] = await file.getMetadata();\n \n // Set appropriate headers\n res.set({\n 'Content-Type': metadata.contentType || 'application/octet-stream',\n 'Content-Length': metadata.size,\n 'Cache-Control': 'public, max-age=31536000', // 1 year cache\n 'ETag': metadata.etag,\n });\n \n // Stream file to response\n const stream = file.createReadStream();\n stream.pipe(res);\n \n stream.on('error', (error) => {\n logger.error('Error streaming file:', {error});\n if (!res.headersSent) {\n res.status(500).json({ error: 'Error streaming file' });\n }\n });\n \n } catch (error) {\n logger.error('Error serving file:', {error});\n res.status(500).json({ error: 'Internal server error' });\n }\n});\n\napp.use('/trpc/auth.login', authLimiter);\napp.use('/trpc/auth.register', authLimiter);\n\n// File upload endpoint for secure file uploads (supports both POST and PUT)\napp.post('/api/upload/:filePath', uploadLimiter, async (req, res) => {\n handleFileUpload(req, res);\n});\n\napp.put('/api/upload/:filePath', uploadLimiter, async (req, res) => {\n handleFileUpload(req, res);\n});\n\nfunction handleFileUpload(req: any, res: any) {\n try {\n const filePath = decodeURIComponent(req.params.filePath);\n \n // Set CORS headers for upload endpoint\n const origin = req.headers.origin;\n \n if (origin && !isAllowedOrigin(origin)) {\n return res.status(403).json({ error: 'Origin not allowed' });\n }\n\n if (origin) {\n res.header('Access-Control-Allow-Origin', origin);\n }\n \n res.header('Access-Control-Allow-Credentials', 'true');\n \n // Get content type from headers\n const contentType = req.headers['content-type'] || 'application/octet-stream';\n \n // Create a new file in the bucket\n const file = bucket.file(filePath);\n \n // Create a write stream to Google Cloud Storage\n const writeStream = file.createWriteStream({\n metadata: {\n contentType,\n },\n });\n \n // Handle stream events\n writeStream.on('error', (error) => {\n logger.error('Error uploading file:', {error});\n if (!res.headersSent) {\n res.status(500).json({ error: 'Error uploading file' });\n }\n });\n \n writeStream.on('finish', () => {\n res.status(200).json({ \n success: true, \n filePath,\n message: 'File uploaded successfully' \n });\n });\n \n // Pipe the request body to the write stream\n req.pipe(writeStream);\n \n } catch (error) {\n logger.error('Error handling file upload:', {error});\n res.status(500).json({ error: 'Internal server error' });\n }\n}\n\n// Create caller\nconst createCaller = createCallerFactory(appRouter);\n\n// Fallback OPTIONS handler — prevents tRPC from rejecting preflight requests\n// when the cors middleware passes through without ending the response.\napp.options('/trpc/*', (req, res) => {\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader('Access-Control-Allow-Origin', origin);\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, x-user');\n }\n res.sendStatus(204);\n});\n\n// Setup tRPC middleware\napp.use(\n '/trpc',\n createExpressMiddleware({\n router: appRouter,\n createContext: async ({ req, res }: { req: Request; res: Response }) => {\n return createTRPCContext({ req, res });\n },\n })\n);\n\n// IMPORTANT: Sentry error handler must be added AFTER all other middleware and routes\n// but BEFORE any other error handlers\nSentry.setupExpressErrorHandler(app);\n\n// app.use(function onError(err, req, res, next) {\n// // The error id is attached to `res.sentry` to be returned\n// // and optionally displayed to the user for support.\n// res.statusCode = 500;\n// res.end(res.sentry + \"\\n\");\n// });\n\n\nconst PORT = env.PORT || 3001;\n\nconnectRedis().then(() => {\n httpServer.listen(PORT, () => {\n logger.info(`Server running on port ${PORT}`, {\n port: PORT,\n services: ['tRPC', 'Socket.IO', env.REDIS_URL ? 'Redis' : null].filter(Boolean),\n });\n });\n}); \n\n// log all env variables\nlogger.info('Configurations', {\n NODE_ENV: env.NODE_ENV,\n PORT: env.PORT,\n NEXT_PUBLIC_APP_URL: env.NEXT_PUBLIC_APP_URL,\n LOG_MODE: env.LOG_MODE,\n});\n\n// Log CORS configuration\nlogger.info('CORS Configuration', {\n allowedOrigins,\n allowedOriginPatterns: allowedOriginPatterns.map((pattern) => pattern.source),\n});\n\nconst gracefulShutdown = (signal: string) => {\n logger.info(`Received ${signal}, shutting down gracefully`);\n \n httpServer.close(() => {\n logger.info('HTTP server closed');\n \n io.close(() => {\n logger.info('Socket.IO server closed');\n\n disconnectRedis().then(() =>\n prisma.$disconnect().then(() => {\n logger.info('Database connections closed');\n process.exit(0);\n }).catch((err) => {\n logger.error('Error disconnecting from database', { error: err });\n process.exit(1);\n })\n );\n });\n });\n \n // Force shutdown after 10 seconds\n setTimeout(() => {\n logger.error('Forced shutdown after timeout');\n process.exit(1);\n }, 10000);\n};\n\nprocess.on('SIGTERM', () => gracefulShutdown('SIGTERM'));\nprocess.on('SIGINT', () => gracefulShutdown('SIGINT'));\n"],"names":[],"mappings":";;AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEpG,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,WAAW,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAGpC,OAAO,iBAAiB,CAAC;AAGzB,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;AAE1B,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AACtB,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;AACvB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAEhD,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACzB,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;IAC3B,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACzC,IAAI,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEH,MAAM,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;AAEhG,kBAAkB;AAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;IACX,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE;QAC3B,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;QAChE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;IACD,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC;IACpD,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,EAAE,kBAAkB,EAAE,QAAQ,CAAC;IAC/E,iBAAiB,EAAE,KAAK;IACxB,oBAAoB,EAAE,GAAG;CAC1B,CAAC,CAAC,CAAC;AAEJ,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAExB,4BAA4B;AAC5B,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACzB,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE;YAC1B,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM;YAC1B,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC;SACrC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEH,mCAAmC;AACnC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;YAC/B,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ,EAAE,GAAG,QAAQ,IAAI;SAC1B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,IAAI,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEH,wCAAwC;AACxC,0CAA0C;AAC1C,gDAAgD;AAChD,MAAM;AAEN,8DAA8D;AAC9D,yDAAyD;AAEzD,qBAAqB;AACrB,mCAAmC;AACnC,sDAAsD;AACtD,gBAAgB;AAChB,qCAAqC;AACrC,uBAAuB;AACvB,iEAAiE;AACjE,WAAW;AACX,SAAS;AACT,OAAO;AACP,MAAM;AAGN,qBAAqB;AACrB,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;AAErC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAEpC,IAAI,CAAC;QACH,8BAA8B;QAC9B,MAAM,MAAM,CAAC,SAAS,CAAA,UAAU,CAAC;QAEjC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;YACxB,QAAQ,EAAE,WAAW;SACtB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,MAAM,EAAE,OAAO;YACf,QAAQ,EAAE,cAAc;YACxB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;SAChE,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,8DAA8D;AAC9D,yFAAyF;AACzF,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC9C,IAAI,CAAC;QACH,IAAI,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAuB,CAAC;QACxD,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACjC,MAAM,UAAU,GAAG,GAAG,CAAC,uBAAuB,IAAI,OAAO,CAAC;YAC1D,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,UAAU,UAAU,CAAC,CAAC,CAAC;YAC5E,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QACD,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,IAAqD,CAAC;QAE9F,IAAI,CAAC,SAAS,IAAI,CAAC,YAAY,EAAE,CAAC;YAChC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qCAAqC,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;YACvC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YAC5C,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;SACrC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,4DAA4D;QAC5D,IAAI,YAAY,CAAC,UAAU,CAAC,uBAAuB,CAAC,EAAE,CAAC;YACrD,MAAM,cAAc,GAAG,YAAY,CAAC,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;YACzE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC;gBACvD,KAAK,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;aAC3C,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mCAAmC,EAAE,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QAED,IAAI,YAAY,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,CAAC;YAClD,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;YACnE,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,SAAS,CAAC;gBAChE,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;wBAC5B,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE;wBACtB,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;qBACnF,EAAE;aACJ,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAED,IAAI,YAAY,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAChD,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;YAC7D,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;gBAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE;aAC5D,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAED,qDAAqD;QACrD,mDAAmD;QACnD,gEAAgE;QAChE,oDAAoD;QACpD,eAAe;QACf,qBAAqB;QACrB,cAAc;QACd,mDAAmD;QACnD,mDAAmD;QACnD,WAAW;QACX,SAAS;QACT,QAAQ;QACR,qBAAqB;QACrB,4EAA4E;QAC5E,MAAM;QACN,IAAI;QAEJ,IAAI,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YACzC,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,YAAY,EAAE;gBACpE,OAAO,EAAE,IAAI,CAAC,EAAE;gBAChB,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;aACvC,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAChC,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QACtE,OAAO,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7C,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAkB;AAClB,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE;IAChC,IAAI,EAAE;QACJ,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE;YAC3B,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC;QACpD,WAAW,EAAE,IAAI;QACjB,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,EAAE,kBAAkB,EAAE,6BAA6B,EAAE,QAAQ,CAAC;KAC/G;IACD,UAAU,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC;IACpC,WAAW,EAAE,KAAK;IAClB,YAAY,EAAE,KAAK;IACnB,cAAc,EAAE,KAAK;IACrB,IAAI,EAAE,aAAa;IACnB,SAAS,EAAE,IAAI;CAChB,CAAC,CAAC;AAEH,2BAA2B;AAC3B,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,kBAAkB,EAAE,CAAC,GAAU,EAAE,EAAE;IAC9C,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,wBAAwB;AACxB,mBAAmB,CAAC,EAAE,CAAC,CAAC;AAExB,+CAA+C;AAC/C,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACrD,6EAA6E;QAE7E,gCAAgC;QAChC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAE1E,6BAA6B;QAC7B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;YACvC,KAAK,EAAE;gBACL,QAAQ,EAAE;oBACR,IAAI,EAAE;wBACJ,EAAE,EAAE,KAAK;qBACV;iBACF;aACF;YACD,MAAM,EAAE;gBACN,EAAE,EAAE,IAAI;gBACR,QAAQ,EAAE,IAAI;aACf;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,gCAAgC;QAChC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;YAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;YACrB,OAAO,EAAE;gBACP,IAAI,EAAE,IAAI;gBACV,UAAU,EAAE;oBACV,OAAO,EAAE;wBACP,KAAK,EAAE;4BACL,OAAO,EAAE;gCACP,QAAQ,EAAE,IAAI;gCACd,QAAQ,EAAE,IAAI;6BACf;yBACF;qBACF;iBACF;gBACD,UAAU,EAAE;oBACV,OAAO,EAAE;wBACP,OAAO,EAAE,IAAI;wBACb,UAAU,EAAE;4BACV,OAAO,EAAE;gCACP,KAAK,EAAE;oCACL,OAAO,EAAE;wCACP,QAAQ,EAAE,IAAI;qCACf;iCACF;6BACF;yBACF;qBACF;iBACF;gBACD,WAAW,EAAE;oBACX,OAAO,EAAE;wBACP,OAAO,EAAE,IAAI;wBACb,UAAU,EAAE;4BACV,OAAO,EAAE;gCACP,KAAK,EAAE;oCACL,OAAO,EAAE;wCACP,QAAQ,EAAE,IAAI;qCACf;iCACF;6BACF;yBACF;qBACF;iBACF;gBACD,MAAM,EAAE;oBACN,OAAO,EAAE;wBACP,KAAK,EAAE;4BACL,OAAO,EAAE;gCACP,QAAQ,EAAE,IAAI;gCACd,QAAQ,EAAE,IAAI;6BACf;yBACF;qBACF;iBACF;gBACD,UAAU,EAAE;oBACV,OAAO,EAAE;wBACP,QAAQ,EAAE,IAAI;wBACd,QAAQ,EAAE,IAAI;qBACf;iBACF;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,mDAAmD;QACnD,IAAI,aAAa,GAAG,KAAK,CAAC;QAE1B,iCAAiC;QACjC,IAAI,UAAU,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;YAClC,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QAED,6DAA6D;QAC7D,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,yBAAyB;YACzB,IAAI,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC;gBACjC,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC;gBAC9C,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC7E,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC7E,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;oBAC3B,aAAa,GAAG,IAAI,CAAC;gBACvB,CAAC;YACH,CAAC;YAED,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;gBAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,CAAC;gBAC1C,IAAI,UAAU,CAAC,SAAS,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;oBACrC,aAAa,GAAG,IAAI,CAAC;gBACvB,CAAC;qBAAM,IAAI,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC1F,aAAa,GAAG,IAAI,CAAC;gBACvB,CAAC;YACH,CAAC;YAED,wHAAwH;YACxH,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC5C,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC;gBACzC,IAAI,UAAU,CAAC,SAAS,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;oBACrC,aAAa,GAAG,IAAI,CAAC,CAAC,yCAAyC;gBACjE,CAAC;qBAAM,IAAI,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC1F,aAAa,GAAG,IAAI,CAAC,CAAC,8CAA8C;gBACtE,CAAC;YACH,CAAC;YAED,qBAAqB;YACrB,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;gBAC/C,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC1C,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC7E,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC7E,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;oBAC3B,aAAa,GAAG,IAAI,CAAC;gBACvB,CAAC;YACH,CAAC;YAED,0BAA0B;YAC1B,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC5C,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC;gBACxC,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC7E,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC7E,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;oBAC3B,aAAa,GAAG,IAAI,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0CAA0C,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;QAEjC,qCAAqC;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAErC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,2BAA2B,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,oBAAoB;QACpB,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAE5C,0BAA0B;QAC1B,GAAG,CAAC,GAAG,CAAC;YACN,cAAc,EAAE,QAAQ,CAAC,WAAW,IAAI,0BAA0B;YAClE,gBAAgB,EAAE,QAAQ,CAAC,IAAI;YAC/B,eAAe,EAAE,0BAA0B,EAAE,eAAe;YAC5D,MAAM,EAAE,QAAQ,CAAC,IAAI;SACtB,CAAC,CAAC;QAEH,0BAA0B;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEjB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC;YAC/C,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC,CAAC,CAAC;IAEL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC;QAC7C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;AACzC,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;AAE5C,4EAA4E;AAC5E,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAClE,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,uBAAuB,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACjE,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,SAAS,gBAAgB,CAAC,GAAQ,EAAE,GAAQ;IAC1C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAEzD,uCAAuC;QACvC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;QAElC,IAAI,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC;QACpD,CAAC;QAED,GAAG,CAAC,MAAM,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;QAEvD,gCAAgC;QAChC,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,0BAA0B,CAAC;QAE9E,kCAAkC;QAClC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEnC,gDAAgD;QAChD,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC;YACzC,QAAQ,EAAE;gBACR,WAAW;aACZ;SACF,CAAC,CAAC;QAEH,uBAAuB;QACvB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAChC,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC;YAC/C,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC5B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,IAAI;gBACb,QAAQ;gBACR,OAAO,EAAE,4BAA4B;aACtC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,4CAA4C;QAC5C,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAExB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC;QACrD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,gBAAgB;AAChB,MAAM,YAAY,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;AAEpD,6EAA6E;AAC7E,uEAAuE;AACvE,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAClC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IAClC,IAAI,MAAM,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC;QACrD,GAAG,CAAC,SAAS,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;QAC1D,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,iCAAiC,CAAC,CAAC;QACjF,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,uDAAuD,CAAC,CAAC;IACzG,CAAC;IACD,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AACtB,CAAC,CAAC,CAAC;AAEH,wBAAwB;AACxB,GAAG,CAAC,GAAG,CACL,OAAO,EACP,uBAAuB,CAAC;IACtB,MAAM,EAAE,SAAS;IACjB,aAAa,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,EAAmC,EAAE,EAAE;QACrE,OAAO,iBAAiB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAC,CACH,CAAC;AAEF,sFAAsF;AACtF,sCAAsC;AACtC,MAAM,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC;AAErC,kDAAkD;AAClD,+DAA+D;AAC/D,yDAAyD;AACzD,0BAA0B;AAC1B,gCAAgC;AAChC,MAAM;AAGN,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAE9B,YAAY,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;IACvB,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,IAAI,CAAC,0BAA0B,IAAI,EAAE,EAAE;YAC5C,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;SAChF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,wBAAwB;AACxB,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE;IAC5B,QAAQ,EAAE,GAAG,CAAC,QAAQ;IACtB,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,mBAAmB,EAAE,GAAG,CAAC,mBAAmB;IAC5C,QAAQ,EAAE,GAAG,CAAC,QAAQ;CACvB,CAAC,CAAC;AAEH,yBAAyB;AACzB,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE;IAChC,cAAc;IACd,qBAAqB,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;CAC9E,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,CAAC,MAAc,EAAE,EAAE;IAC1C,MAAM,CAAC,IAAI,CAAC,YAAY,MAAM,4BAA4B,CAAC,CAAC;IAE5D,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE;QACpB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAElC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YACZ,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;YAEvC,eAAe,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAC1B,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC7B,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;gBAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACf,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;gBAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,kCAAkC;IAClC,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,EAAE,KAAK,CAAC,CAAC;AACZ,CAAC,CAAC;AAEF,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AACzD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC","debug_id":"0a09f7dd-016d-5f2f-bdf3-5a5a294b50cb"}
1
+ {"version":3,"file":"index.js","sources":["index.ts"],"sourceRoot":"/","sourcesContent":["import express from 'express';\nimport type { Request, Response } from 'express';\nimport { createServer } from 'http';\nimport { Server } from 'socket.io';\nimport cors from 'cors';\nimport dotenv from 'dotenv';\nimport { createExpressMiddleware } from '@trpc/server/adapters/express';\nimport { appRouter } from './routers/_app.js';\nimport { createTRPCContext, createCallerFactory } from './trpc.js';\nimport { findUserBySessionToken } from './models/auth.js';\nimport { logger } from './utils/logger.js';\nimport { setupSocketHandlers } from './socket/handlers.js';\nimport { bucket } from './lib/googleCloudStorage.js';\nimport { validateDirectUploadIntent } from './lib/fileUpload.js';\nimport { prisma } from './lib/prisma.js';\nimport { pusher } from './lib/pusher.js';\nimport { connectRedis, disconnectRedis } from './lib/redis.js';\nimport { createCorsOriginMatcher } from './lib/config/cors.js';\n\nimport { authLimiter, generalLimiter, helmetConfig, uploadLimiter } from './middleware/security.js';\n\nimport * as Sentry from \"@sentry/node\";\nimport { env } from './lib/config/env.js';\nimport compression from 'compression';\nimport { v4 as uuidv4 } from 'uuid';\n\n\nimport \"./instrument.js\";\nimport { openAIClient } from './utils/inference.js';\n\nconst app = express();\n\napp.set('trust proxy', 1);\n\napp.use(helmetConfig);\napp.use(compression());\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\napp.use((req, res, next) => {\n const requestId = uuidv4();\n res.setHeader('X-Request-ID', requestId);\n next();\n});\n\nconst { allowedOrigins, allowedOriginPatterns, isAllowedOrigin } = createCorsOriginMatcher(env);\n\n// CORS middleware\napp.use(cors({\n origin: (origin, callback) => {\n if (!origin || isAllowedOrigin(origin)) {\n callback(null, true);\n return;\n }\n\n logger.warn('CORS origin rejected', { origin, allowedOrigins });\n callback(null, false);\n },\n credentials: true,\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'x-user'],\n preflightContinue: false,\n optionsSuccessStatus: 204,\n}));\n\napp.use(generalLimiter);\n\n// CORS debugging middleware\napp.use((req, res, next) => {\n if (req.method === 'OPTIONS' || req.path.includes('trpc')) {\n logger.info('CORS Request', {\n method: req.method,\n path: req.path,\n origin: req.headers.origin,\n userAgent: req.headers['user-agent']\n });\n }\n next();\n});\n\n// Response time logging middleware\napp.use((req, res, next) => {\n const start = Date.now();\n res.on('finish', () => {\n const duration = Date.now() - start;\n logger.info('Request completed', {\n method: req.method,\n path: req.path,\n statusCode: res.statusCode,\n duration: `${duration}ms`\n });\n });\n next();\n});\n\n// app.use(\"/panel\", async (_, res) => {\n// if (env.NODE_ENV !== \"development\") {\n// return res.status(404).send(\"Not Found\");\n// }\n\n// // Dynamically import renderTrpcPanel only in development\n// const { renderTrpcPanel } = await import(\"trpc-ui\");\n\n// return res.send(\n// renderTrpcPanel(appRouter, {\n// url: \"/trpc\", // Base url of your trpc server\n// meta: {\n// title: \"Studious Backend\",\n// description:\n// \"This is the backend for the Studious application.\",\n// },\n// })\n// );\n// });\n\n\n// Create HTTP server\nconst httpServer = createServer(app);\n\napp.get('/', async (req, res) => {\n res.status(200).json({ \n status: 'OK',\n timestamp: new Date().toISOString(),\n uptime: process.uptime(),\n version: process.env.npm_package_version,\n environment: process.env.NODE_ENV,\n appUrl: process.env.NEXT_PUBLIC_APP_URL,\n });\n});\n\napp.get('/health', async (req, res) => {\n\n try {\n // Check database connectivity\n await prisma.$queryRaw`SELECT 1`;\n \n res.status(200).json({ \n status: 'OK',\n timestamp: new Date().toISOString(),\n uptime: process.uptime(),\n database: 'connected'\n });\n } catch (error) {\n res.status(503).json({ \n status: 'ERROR',\n database: 'disconnected',\n error: error instanceof Error ? error.message : 'Unknown error'\n });\n }\n});\n\n// Pusher channel auth (for private-* and presence-* channels)\n// Token from: x-user header, or cookie (same-origin requests send cookies automatically)\napp.post('/api/pusher/auth', async (req, res) => {\n try {\n let token = req.headers['x-user'] as string | undefined;\n if (!token && req.headers.cookie) {\n const cookieName = env.PUSHER_AUTH_COOKIE_NAME || 'token';\n const match = req.headers.cookie.match(new RegExp(`${cookieName}=([^;]+)`));\n token = match?.[1]?.trim();\n }\n const { socket_id, channel_name } = req.body as { socket_id?: string; channel_name?: string };\n\n if (!socket_id || !channel_name) {\n return res.status(400).json({ error: 'socket_id and channel_name required' });\n }\n\n if (!token) {\n return res.status(401).json({ error: 'Authentication required' });\n }\n\n const user = await findUserBySessionToken(token);\n\n if (!user) {\n return res.status(401).json({ error: 'Invalid or expired session' });\n }\n\n // Verify channel access for private-conversation-* channels\n if (channel_name.startsWith('private-conversation-')) {\n const conversationId = channel_name.replace('private-conversation-', '');\n const member = await prisma.conversationMember.findFirst({\n where: { conversationId, userId: user.id },\n });\n \n if (!member) {\n return res.status(403).json({ error: 'Not a member of this conversation' });\n }\n }\n\n if (channel_name.startsWith('private-worksheet-')) {\n const worksheetId = channel_name.replace('private-worksheet-', '');\n const worksheet = await prisma.studentWorksheetResponse.findFirst({\n where: { id: worksheetId, OR: [\n { studentId: user.id },\n { submission: { assignment: { class: { teachers: { some: { id: user.id } } } } } },\n ] },\n });\n if (!worksheet) {\n return res.status(403).json({ error: 'No access to this worksheet' });\n }\n }\n\n if (channel_name.startsWith('private-teacher-')) {\n const classId = channel_name.replace('private-teacher-', '');\n const isTeacher = await prisma.class.findFirst({\n where: { id: classId, teachers: { some: { id: user.id } } },\n });\n if (!isTeacher) {\n return res.status(403).json({ error: 'Not a teacher of this class' });\n }\n }\n\n // Verify channel access for private-class-* channels\n // if (channel_name.startsWith('private-class-')) {\n // const classId = channel_name.replace('private-class-', '');\n // const isMember = await prisma.class.findFirst({\n // where: {\n // id: classId,\n // OR: [\n // { students: { some: { id: user.id } } },\n // { teachers: { some: { id: user.id } } },\n // ],\n // },\n // });\n // if (!isMember) {\n // return res.status(403).json({ error: 'Not a member of this class' });\n // }\n // }\n\n if (channel_name.startsWith('presence-')) {\n const authResponse = pusher.authorizeChannel(socket_id, channel_name, {\n user_id: user.id,\n user_info: { username: user.username },\n });\n return res.json(authResponse);\n }\n\n const authResponse = pusher.authorizeChannel(socket_id, channel_name);\n return res.json(authResponse);\n } catch (error) {\n logger.error('Pusher auth error', { error });\n return res.status(500).json({ error: 'Authentication failed' });\n }\n});\n\n// Setup Socket.IO\nconst io = new Server(httpServer, {\n cors: {\n origin: (origin, callback) => {\n callback(null, !origin || isAllowedOrigin(origin));\n },\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n credentials: true,\n allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Access-Control-Allow-Origin', 'x-user']\n },\n transports: ['websocket', 'polling'],\n pingTimeout: 60000,\n pingInterval: 25000,\n connectTimeout: 45000,\n path: '/socket.io/',\n allowEIO3: true\n});\n\n// Add server-level logging\nio.engine.on('connection_error', (err: Error) => {\n logger.error('Socket connection error', { error: err.message });\n});\n\n// Setup socket handlers\nsetupSocketHandlers(io);\n\n// File serving endpoint for secure file access\napp.get('/api/files/:fileId', async (req, res) => {\n try {\n const fileId = decodeURIComponent(req.params.fileId);\n // console.log('File request:', { fileId, originalPath: req.params.fileId });\n \n // Get user from request headers\n const userHeader = req.headers['x-user'];\n if (!userHeader) {\n return res.status(401).json({ error: 'Authentication required' });\n }\n\n const token = typeof userHeader === 'string' ? userHeader : userHeader[0];\n\n // Find user by session token\n const user = await findUserBySessionToken(token);\n\n if (!user) {\n return res.status(401).json({ error: 'Invalid or expired session' });\n }\n\n // Find file in database by path\n const fileRecord = await prisma.file.findFirst({\n where: { id: fileId },\n include: {\n user: true,\n assignment: {\n include: {\n class: {\n include: {\n students: true,\n teachers: true\n }\n }\n }\n },\n submission: {\n include: {\n student: true,\n assignment: {\n include: {\n class: {\n include: {\n teachers: true\n }\n }\n }\n }\n }\n },\n annotations: {\n include: {\n student: true,\n assignment: {\n include: {\n class: {\n include: {\n teachers: true\n }\n }\n }\n }\n }\n },\n folder: {\n include: {\n class: {\n include: {\n students: true,\n teachers: true\n }\n }\n }\n },\n classDraft: {\n include: {\n students: true,\n teachers: true\n }\n }\n }\n });\n\n if (!fileRecord) {\n return res.status(404).json({ error: 'File not found in database' });\n }\n\n // Check if user has permission to access this file\n let hasPermission = false;\n\n // Check if user created the file\n if (fileRecord.userId === user.id) {\n hasPermission = true;\n }\n\n // Check if file is related to a class where user is a member\n if (!hasPermission) {\n // Check assignment files\n if (fileRecord.assignment?.class) {\n const classData = fileRecord.assignment.class;\n const isStudent = classData.students.some(student => student.id === user.id);\n const isTeacher = classData.teachers.some(teacher => teacher.id === user.id);\n if (isStudent || isTeacher) {\n hasPermission = true;\n }\n }\n\n if (!hasPermission && fileRecord.annotations) {\n const annotation = fileRecord.annotations;\n if (annotation.studentId === user.id) {\n hasPermission = true;\n } else if (annotation.assignment?.class?.teachers.some(teacher => teacher.id === user.id)) {\n hasPermission = true;\n }\n }\n\n // Check submission files (student can access their own submissions, teachers can access all submissions in their class)\n if (!hasPermission && fileRecord.submission) {\n const submission = fileRecord.submission;\n if (submission.studentId === user.id) {\n hasPermission = true; // Student accessing their own submission\n } else if (submission.assignment?.class?.teachers.some(teacher => teacher.id === user.id)) {\n hasPermission = true; // Teacher accessing submission in their class\n }\n }\n\n // Check folder files\n if (!hasPermission && fileRecord.folder?.class) {\n const classData = fileRecord.folder.class;\n const isStudent = classData.students.some(student => student.id === user.id);\n const isTeacher = classData.teachers.some(teacher => teacher.id === user.id);\n if (isStudent || isTeacher) {\n hasPermission = true;\n }\n }\n\n // Check class draft files\n if (!hasPermission && fileRecord.classDraft) {\n const classData = fileRecord.classDraft;\n const isStudent = classData.students.some(student => student.id === user.id);\n const isTeacher = classData.teachers.some(teacher => teacher.id === user.id);\n if (isStudent || isTeacher) {\n hasPermission = true;\n }\n }\n }\n\n if (!hasPermission) {\n return res.status(403).json({ error: 'Access denied - insufficient permissions' });\n }\n \n const filePath = fileRecord.path;\n \n // Get file from Google Cloud Storage\n const file = bucket.file(filePath);\n const [exists] = await file.exists();\n \n if (!exists) {\n return res.status(404).json({ error: 'File not found in storage', filePath });\n }\n \n // Get file metadata\n const [metadata] = await file.getMetadata();\n \n // Set appropriate headers\n res.set({\n 'Content-Type': metadata.contentType || 'application/octet-stream',\n 'Content-Length': metadata.size,\n 'Cache-Control': 'public, max-age=31536000', // 1 year cache\n 'ETag': metadata.etag,\n });\n \n // Stream file to response\n const stream = file.createReadStream();\n stream.pipe(res);\n \n stream.on('error', (error) => {\n logger.error('Error streaming file:', {error});\n if (!res.headersSent) {\n res.status(500).json({ error: 'Error streaming file' });\n }\n });\n \n } catch (error) {\n logger.error('Error serving file:', {error});\n res.status(500).json({ error: 'Internal server error' });\n }\n});\n\napp.use('/trpc/auth.login', authLimiter);\napp.use('/trpc/auth.register', authLimiter);\n\nasync function getAuthenticatedRequestUser(req: Request) {\n const userHeader = req.headers['x-user'];\n const tokenFromUserHeader = typeof userHeader === 'string' ? userHeader : userHeader?.[0];\n const authorizationHeader = req.headers.authorization;\n const tokenFromAuthorization =\n typeof authorizationHeader === 'string' && authorizationHeader.startsWith('Bearer ')\n ? authorizationHeader.slice(7)\n : null;\n\n let token = tokenFromUserHeader ?? tokenFromAuthorization;\n\n if (!token && req.headers.cookie) {\n const cookieName = env.PUSHER_AUTH_COOKIE_NAME || 'token';\n const match = req.headers.cookie.match(new RegExp(cookieName + '=([^;]+)'));\n token = match?.[1]?.trim() ?? null;\n }\n\n if (!token) {\n return null;\n }\n\n return findUserBySessionToken(token);\n}\n\nfunction getUploadSessionId(req: Request) {\n const queryValue = req.query.uploadSessionId;\n if (typeof queryValue === 'string' && queryValue.trim()) {\n return queryValue;\n }\n\n const headerValue = req.headers['x-upload-session-id'];\n return typeof headerValue === 'string' ? headerValue : headerValue?.[0];\n}\n\n// File upload endpoint for secure file uploads (supports both POST and PUT)\napp.post('/api/upload/:filePath', uploadLimiter, async (req, res) => {\n await handleFileUpload(req, res);\n});\n\napp.put('/api/upload/:filePath', uploadLimiter, async (req, res) => {\n await handleFileUpload(req, res);\n});\n\nasync function handleFileUpload(req: Request, res: Response) {\n try {\n const filePath = decodeURIComponent(req.params.filePath);\n \n // Set CORS headers for upload endpoint\n const origin = req.headers.origin;\n \n if (origin && !isAllowedOrigin(origin)) {\n return res.status(403).json({ error: 'Origin not allowed' });\n }\n\n if (origin) {\n res.header('Access-Control-Allow-Origin', origin);\n }\n \n res.header('Access-Control-Allow-Credentials', 'true');\n\n const user = await getAuthenticatedRequestUser(req);\n if (!user) {\n return res.status(401).json({ error: 'Authentication required' });\n }\n\n const uploadSessionId = getUploadSessionId(req);\n if (!uploadSessionId) {\n return res.status(401).json({ error: 'Upload session is required' });\n }\n\n await validateDirectUploadIntent({\n filePath,\n uploadSessionId,\n userId: user.id,\n });\n \n const contentType = req.headers['content-type'] || 'application/octet-stream';\n const file = bucket.file(filePath);\n const writeStream = file.createWriteStream({\n metadata: {\n contentType,\n },\n });\n \n writeStream.on('error', (error) => {\n logger.error('Error uploading file:', {error, filePath, userId: user.id});\n if (!res.headersSent) {\n res.status(500).json({ error: 'Error uploading file' });\n }\n });\n \n writeStream.on('finish', () => {\n res.status(200).json({ \n success: true, \n filePath,\n message: 'File uploaded successfully' \n });\n });\n \n req.pipe(writeStream);\n \n } catch (error) {\n logger.error('Error handling file upload:', {error});\n const code = typeof error === 'object' && error && 'code' in error ? error.code : null;\n const message = error instanceof Error ? error.message : 'Internal server error';\n\n if (code === 'FORBIDDEN') {\n return res.status(403).json({ error: message });\n }\n\n if (code === 'BAD_REQUEST') {\n return res.status(400).json({ error: message });\n }\n\n res.status(500).json({ error: 'Internal server error' });\n }\n}\n\n// Create caller\nconst createCaller = createCallerFactory(appRouter);\n\n// Fallback OPTIONS handler — prevents tRPC from rejecting preflight requests\n// when the cors middleware passes through without ending the response.\napp.options('/trpc/*', (req, res) => {\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader('Access-Control-Allow-Origin', origin);\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, x-user');\n }\n res.sendStatus(204);\n});\n\n// Setup tRPC middleware\napp.use(\n '/trpc',\n createExpressMiddleware({\n router: appRouter,\n createContext: async ({ req, res }: { req: Request; res: Response }) => {\n return createTRPCContext({ req, res });\n },\n })\n);\n\n// IMPORTANT: Sentry error handler must be added AFTER all other middleware and routes\n// but BEFORE any other error handlers\nSentry.setupExpressErrorHandler(app);\n\n// app.use(function onError(err, req, res, next) {\n// // The error id is attached to `res.sentry` to be returned\n// // and optionally displayed to the user for support.\n// res.statusCode = 500;\n// res.end(res.sentry + \"\\n\");\n// });\n\n\nconst PORT = env.PORT || 3001;\n\nconnectRedis().then(() => {\n httpServer.listen(PORT, () => {\n logger.info(`Server running on port ${PORT}`, {\n port: PORT,\n services: ['tRPC', 'Socket.IO', env.REDIS_URL ? 'Redis' : null].filter(Boolean),\n });\n });\n}); \n\n// log all env variables\nlogger.info('Configurations', {\n NODE_ENV: env.NODE_ENV,\n PORT: env.PORT,\n NEXT_PUBLIC_APP_URL: env.NEXT_PUBLIC_APP_URL,\n LOG_MODE: env.LOG_MODE,\n});\n\n// Log CORS configuration\nlogger.info('CORS Configuration', {\n allowedOrigins,\n allowedOriginPatterns: allowedOriginPatterns.map((pattern) => pattern.source),\n});\n\nconst gracefulShutdown = (signal: string) => {\n logger.info(`Received ${signal}, shutting down gracefully`);\n \n httpServer.close(() => {\n logger.info('HTTP server closed');\n \n io.close(() => {\n logger.info('Socket.IO server closed');\n\n disconnectRedis().then(() =>\n prisma.$disconnect().then(() => {\n logger.info('Database connections closed');\n process.exit(0);\n }).catch((err) => {\n logger.error('Error disconnecting from database', { error: err });\n process.exit(1);\n })\n );\n });\n });\n \n // Force shutdown after 10 seconds\n setTimeout(() => {\n logger.error('Forced shutdown after timeout');\n process.exit(1);\n }, 10000);\n};\n\nprocess.on('SIGTERM', () => gracefulShutdown('SIGTERM'));\nprocess.on('SIGINT', () => gracefulShutdown('SIGINT'));\n"],"names":[],"mappings":"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","debug_id":"3cfa0c9c-5588-5fbd-a283-4495b71df718"}
@@ -26,6 +26,14 @@ export interface DirectUploadFile {
26
26
  uploadExpiresAt: Date;
27
27
  uploadSessionId: string;
28
28
  }
29
+ export declare function validateDirectUploadIntent(input: {
30
+ filePath: string;
31
+ uploadSessionId: string;
32
+ userId: string;
33
+ }): Promise<{
34
+ id: string;
35
+ uploadStatus: import(".prisma/client").$Enums.UploadStatus;
36
+ }>;
29
37
  /**
30
38
  * @deprecated Use createDirectUploadFile instead
31
39
  */
@@ -49,7 +57,7 @@ export declare function getFileUrl(filePath: string): Promise<string>;
49
57
  * @param submissionId Optional submission ID to associate the file with
50
58
  * @returns The direct upload file information with signed URL
51
59
  */
52
- export declare function createDirectUploadFile(file: DirectFileData, userId: string, directory?: string, assignmentId?: string, submissionId?: string, announcementId?: string): Promise<DirectUploadFile>;
60
+ export declare function createDirectUploadFile(file: DirectFileData, userId: string, directory?: string, assignmentId?: string, submissionId?: string, announcementId?: string, conversationId?: string): Promise<DirectUploadFile>;
53
61
  /**
54
62
  * Confirms a direct upload was successful
55
63
  * @param fileId The ID of the file record
@@ -72,5 +80,5 @@ export declare function updateUploadProgress(fileId: string, progress: number):
72
80
  * @param submissionId Optional submission ID to associate files with
73
81
  * @returns Array of direct upload file information
74
82
  */
75
- export declare function createDirectUploadFiles(files: DirectFileData[], userId: string, directory?: string, assignmentId?: string, submissionId?: string, announcementId?: string): Promise<DirectUploadFile[]>;
83
+ export declare function createDirectUploadFiles(files: DirectFileData[], userId: string, directory?: string, assignmentId?: string, submissionId?: string, announcementId?: string, conversationId?: string): Promise<DirectUploadFile[]>;
76
84
  //# sourceMappingURL=fileUpload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fileUpload.d.ts","sourceRoot":"/","sources":["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
+ {"version":3,"file":"fileUpload.d.ts","sourceRoot":"/","sources":["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;AAED,wBAAsB,0BAA0B,CAAC,KAAK,EAAE;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;;;GA+BA;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,EACvB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAiG3B;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,EACvB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAyB7B"}
@@ -1,11 +1,40 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4ff4c0f2-8784-50fe-a248-6adcac3fa659")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="01a3ee28-2d14-5542-9301-e6f24e5c7a0d")}catch(e){}}();
3
3
  import { TRPCError } from "@trpc/server";
4
4
  import { v4 as uuidv4 } from "uuid";
5
5
  import { getSignedUrl, objectExists } from "./googleCloudStorage.js";
6
6
  import { prisma } from "./prisma.js";
7
7
  import { logger } from "../utils/logger.js";
8
8
  import { env } from "./config/env.js";
9
+ export async function validateDirectUploadIntent(input) {
10
+ const fileRecord = await prisma.file.findFirst({
11
+ where: {
12
+ path: input.filePath,
13
+ uploadSessionId: input.uploadSessionId,
14
+ userId: input.userId,
15
+ uploadExpiresAt: { gt: new Date() },
16
+ isOrphaned: false,
17
+ messageId: null,
18
+ },
19
+ select: {
20
+ id: true,
21
+ uploadStatus: true,
22
+ },
23
+ });
24
+ if (!fileRecord) {
25
+ throw new TRPCError({
26
+ code: 'FORBIDDEN',
27
+ message: 'Invalid or expired upload intent',
28
+ });
29
+ }
30
+ if (!['PENDING', 'UPLOADING'].includes(fileRecord.uploadStatus)) {
31
+ throw new TRPCError({
32
+ code: 'BAD_REQUEST',
33
+ message: 'Upload is no longer accepting data',
34
+ });
35
+ }
36
+ return fileRecord;
37
+ }
9
38
  // DEPRECATED: These functions are no longer used - files are uploaded directly to GCS
10
39
  // Use createDirectUploadFile() and createDirectUploadFiles() instead
11
40
  /**
@@ -52,7 +81,7 @@ export async function getFileUrl(filePath) {
52
81
  * @param submissionId Optional submission ID to associate the file with
53
82
  * @returns The direct upload file information with signed URL
54
83
  */
55
- export async function createDirectUploadFile(file, userId, directory, assignmentId, submissionId, announcementId) {
84
+ export async function createDirectUploadFile(file, userId, directory, assignmentId, submissionId, announcementId, conversationId) {
56
85
  try {
57
86
  // Validate file extension matches MIME type
58
87
  const fileExtension = file.name.split('.').pop()?.toLowerCase();
@@ -79,7 +108,7 @@ export async function createDirectUploadFile(file, userId, directory, assignment
79
108
  const uploadSessionId = uuidv4();
80
109
  // Generate backend proxy upload URL (not direct GCS)
81
110
  const baseUrl = env.BACKEND_URL || 'http://localhost:3001';
82
- const uploadUrl = `${baseUrl}/api/upload/${encodeURIComponent(filePath)}`;
111
+ const uploadUrl = `${baseUrl}/api/upload/${encodeURIComponent(filePath)}?uploadSessionId=${encodeURIComponent(uploadSessionId)}`;
83
112
  const uploadExpiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes from now
84
113
  // Create file record in database with PENDING status
85
114
  const fileRecord = await prisma.file.create({
@@ -114,6 +143,9 @@ export async function createDirectUploadFile(file, userId, directory, assignment
114
143
  announcement: {
115
144
  connect: { id: announcementId }
116
145
  }
146
+ }),
147
+ ...(conversationId && {
148
+ conversationId,
117
149
  })
118
150
  },
119
151
  });
@@ -252,9 +284,9 @@ export async function updateUploadProgress(fileId, progress) {
252
284
  * @param submissionId Optional submission ID to associate files with
253
285
  * @returns Array of direct upload file information
254
286
  */
255
- export async function createDirectUploadFiles(files, userId, directory, assignmentId, submissionId, announcementId) {
287
+ export async function createDirectUploadFiles(files, userId, directory, assignmentId, submissionId, announcementId, conversationId) {
256
288
  try {
257
- const uploadPromises = files.map(file => createDirectUploadFile(file, userId, directory, assignmentId, submissionId, announcementId));
289
+ const uploadPromises = files.map(file => createDirectUploadFile(file, userId, directory, assignmentId, submissionId, announcementId, conversationId));
258
290
  return await Promise.all(uploadPromises);
259
291
  }
260
292
  catch (error) {
@@ -270,4 +302,4 @@ export async function createDirectUploadFiles(files, userId, directory, assignme
270
302
  }
271
303
  }
272
304
  //# sourceMappingURL=fileUpload.js.map
273
- //# debugId=4ff4c0f2-8784-50fe-a248-6adcac3fa659
305
+ //# debugId=01a3ee28-2d14-5542-9301-e6f24e5c7a0d
@@ -1 +1 @@
1
- {"version":3,"file":"fileUpload.js","sources":["lib/fileUpload.ts"],"sourceRoot":"/","sourcesContent":["import { TRPCError } from \"@trpc/server\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { getSignedUrl, objectExists } from \"./googleCloudStorage.js\";\nimport { prisma } from \"./prisma.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { env } from \"./config/env.js\";\n\nexport interface FileData {\n name: string;\n type: string;\n size: number;\n // No data field - for direct file uploads\n}\n\nexport interface DirectFileData {\n name: string;\n type: string;\n size: number;\n // No data field - for direct file uploads\n}\n\nexport interface UploadedFile {\n id: string;\n name: string;\n type: string;\n size: number;\n path: string;\n thumbnailId?: string;\n}\n\nexport interface DirectUploadFile {\n id: string;\n name: string;\n type: string;\n size: number;\n path: string;\n uploadUrl: string;\n uploadExpiresAt: Date;\n uploadSessionId: string;\n}\n\n// DEPRECATED: These functions are no longer used - files are uploaded directly to GCS\n// Use createDirectUploadFile() and createDirectUploadFiles() instead\n\n/**\n * @deprecated Use createDirectUploadFile instead\n */\nexport async function uploadFile(\n file: FileData,\n userId: string,\n directory?: string,\n assignmentId?: string\n): Promise<UploadedFile> {\n throw new TRPCError({\n code: 'NOT_IMPLEMENTED',\n message: 'uploadFile is deprecated. Use createDirectUploadFile instead.',\n });\n}\n\n/**\n * @deprecated Use createDirectUploadFiles instead\n */\nexport async function uploadFiles(\n files: FileData[], \n userId: string,\n directory?: string\n): Promise<UploadedFile[]> {\n throw new TRPCError({\n code: 'NOT_IMPLEMENTED',\n message: 'uploadFiles is deprecated. Use createDirectUploadFiles instead.',\n });\n}\n\n/**\n * Gets a signed URL for a file\n * @param filePath The path of the file in Google Cloud Storage\n * @returns The signed URL\n */\nexport async function getFileUrl(filePath: string): Promise<string> {\n try {\n return await getSignedUrl(filePath);\n } catch (error) {\n console.error('Error getting signed URL:', error);\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to get file URL',\n });\n }\n}\n\n/**\n * Creates a file record for direct upload and generates signed URL\n * @param file The file metadata (no base64 data)\n * @param userId The ID of the user uploading the file\n * @param directory Optional directory to store the file in\n * @param assignmentId Optional assignment ID to associate the file with\n * @param submissionId Optional submission ID to associate the file with\n * @returns The direct upload file information with signed URL\n */\nexport async function createDirectUploadFile(\n file: DirectFileData,\n userId: string,\n directory?: string,\n assignmentId?: string,\n submissionId?: string,\n announcementId?: string\n): Promise<DirectUploadFile> {\n try {\n // Validate file extension matches MIME type\n const fileExtension = file.name.split('.').pop()?.toLowerCase();\n const mimeType = file.type.toLowerCase();\n \n const extensionMimeMap: Record<string, string[]> = {\n 'jpg': ['image/jpeg'],\n 'jpeg': ['image/jpeg'],\n 'png': ['image/png'],\n 'gif': ['image/gif'],\n 'webp': ['image/webp']\n };\n \n if (fileExtension && extensionMimeMap[fileExtension]) {\n if (!extensionMimeMap[fileExtension].includes(mimeType)) {\n throw new Error(`File extension .${fileExtension} does not match MIME type ${mimeType}`);\n }\n }\n \n // Create a unique filename\n const uniqueFilename = `${uuidv4()}.${fileExtension}`;\n \n // Construct the full path\n const filePath = directory \n ? `${directory}/${uniqueFilename}`\n : uniqueFilename;\n \n // Generate upload session ID\n const uploadSessionId = uuidv4();\n \n // Generate backend proxy upload URL (not direct GCS)\n const baseUrl = env.BACKEND_URL || 'http://localhost:3001';\n const uploadUrl = `${baseUrl}/api/upload/${encodeURIComponent(filePath)}`;\n const uploadExpiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes from now\n \n // Create file record in database with PENDING status\n const fileRecord = await prisma.file.create({\n data: {\n name: file.name,\n type: file.type,\n size: file.size,\n path: filePath,\n uploadStatus: 'PENDING',\n uploadUrl,\n uploadExpiresAt,\n uploadSessionId,\n user: {\n connect: { id: userId }\n },\n ...(directory && {\n folder: {\n connect: {id: directory},\n },\n }),\n ...(assignmentId && {\n assignment: {\n connect: { id: assignmentId }\n }\n }),\n ...(submissionId && {\n submission: {\n connect: { id: submissionId }\n }\n }),\n ...(announcementId && {\n announcement: {\n connect: { id: announcementId }\n }\n })\n },\n });\n \n return {\n id: fileRecord.id,\n name: file.name,\n type: file.type,\n size: file.size,\n path: filePath,\n uploadUrl,\n uploadExpiresAt,\n uploadSessionId\n };\n } catch (error) {\n logger.error('Error creating direct upload file:', {error: error instanceof Error ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n } : error});\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to create direct upload file',\n });\n }\n}\n\n/**\n * Confirms a direct upload was successful\n * @param fileId The ID of the file record\n * @param uploadSuccess Whether the upload was successful\n * @param errorMessage Optional error message if upload failed\n */\nexport async function confirmDirectUpload(\n fileId: string,\n uploadSuccess: boolean,\n errorMessage?: string\n): Promise<void> {\n try {\n // First fetch the file record to get the object path\n const fileRecord = await prisma.file.findUnique({\n where: { id: fileId },\n select: { path: true }\n });\n\n if (!fileRecord) {\n throw new TRPCError({\n code: 'NOT_FOUND',\n message: 'File record not found',\n });\n }\n\n let actualUploadSuccess = uploadSuccess;\n let actualErrorMessage = errorMessage;\n\n // If uploadSuccess is true, verify the object actually exists in GCS\n if (uploadSuccess) {\n try {\n const exists = await objectExists(env.GOOGLE_CLOUD_BUCKET_NAME!, fileRecord.path);\n if (!exists) {\n actualUploadSuccess = false;\n actualErrorMessage = 'File upload reported as successful but object not found in Google Cloud Storage';\n logger.error(`File upload verification failed for ${fileId}: object ${fileRecord.path} not found in GCS`);\n }\n } catch (error) {\n logger.error(`Error verifying file existence in GCS for ${fileId}:`, {error: error instanceof Error ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n } : error});\n actualUploadSuccess = false;\n actualErrorMessage = 'Failed to verify file existence in Google Cloud Storage';\n }\n }\n\n const updateData: any = {\n uploadStatus: actualUploadSuccess ? 'COMPLETED' : 'FAILED',\n uploadProgress: actualUploadSuccess ? 100 : 0,\n };\n \n if (!actualUploadSuccess && actualErrorMessage) {\n updateData.uploadError = actualErrorMessage;\n updateData.uploadRetryCount = { increment: 1 };\n }\n \n if (actualUploadSuccess) {\n updateData.uploadedAt = new Date();\n }\n \n await prisma.file.update({\n where: { id: fileId },\n data: updateData\n });\n } catch (error) {\n logger.error('Error confirming direct upload:', {error});\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to confirm upload',\n });\n }\n}\n\n/**\n * Updates upload progress for a direct upload\n * @param fileId The ID of the file record\n * @param progress Progress percentage (0-100)\n */\nexport async function updateUploadProgress(\n fileId: string,\n progress: number\n): Promise<void> {\n try {\n // await prisma.file.update({\n // where: { id: fileId },\n // data: {\n // uploadStatus: 'UPLOADING',\n // uploadProgress: Math.min(100, Math.max(0, progress))\n // }\n // });\n const current = await prisma.file.findUnique({ where: { id: fileId }, select: { uploadStatus: true } });\n if (!current || ['COMPLETED','FAILED','CANCELLED'].includes(current.uploadStatus as string)) return;\n const clamped = Math.min(100, Math.max(0, progress));\n await prisma.file.update({\n where: { id: fileId },\n data: {\n uploadStatus: 'UPLOADING',\n uploadProgress: clamped\n }\n });\n } catch (error) {\n logger.error('Error updating upload progress:', {error: error instanceof Error ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n } : error}); \n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to update upload progress',\n });\n }\n}\n\n/**\n * Creates multiple direct upload files\n * @param files Array of file metadata\n * @param userId The ID of the user uploading the files\n * @param directory Optional subdirectory to store the files in\n * @param assignmentId Optional assignment ID to associate files with\n * @param submissionId Optional submission ID to associate files with\n * @returns Array of direct upload file information\n */\nexport async function createDirectUploadFiles(\n files: DirectFileData[], \n userId: string,\n directory?: string,\n assignmentId?: string,\n submissionId?: string,\n announcementId?: string\n): Promise<DirectUploadFile[]> {\n try {\n const uploadPromises = files.map(file => \n createDirectUploadFile(file, userId, directory, assignmentId, submissionId, announcementId)\n );\n return await Promise.all(uploadPromises);\n } catch (error) {\n logger.error('Error creating direct upload files:', {error: error instanceof Error ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n } : error});\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to create direct upload files',\n });\n }\n}"],"names":[],"mappings":"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","debug_id":"4ff4c0f2-8784-50fe-a248-6adcac3fa659"}
1
+ {"version":3,"file":"fileUpload.js","sources":["lib/fileUpload.ts"],"sourceRoot":"/","sourcesContent":["import { TRPCError } from \"@trpc/server\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { getSignedUrl, objectExists } from \"./googleCloudStorage.js\";\nimport { prisma } from \"./prisma.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { env } from \"./config/env.js\";\n\nexport interface FileData {\n name: string;\n type: string;\n size: number;\n // No data field - for direct file uploads\n}\n\nexport interface DirectFileData {\n name: string;\n type: string;\n size: number;\n // No data field - for direct file uploads\n}\n\nexport interface UploadedFile {\n id: string;\n name: string;\n type: string;\n size: number;\n path: string;\n thumbnailId?: string;\n}\n\nexport interface DirectUploadFile {\n id: string;\n name: string;\n type: string;\n size: number;\n path: string;\n uploadUrl: string;\n uploadExpiresAt: Date;\n uploadSessionId: string;\n}\n\nexport async function validateDirectUploadIntent(input: {\n filePath: string;\n uploadSessionId: string;\n userId: string;\n}) {\n const fileRecord = await prisma.file.findFirst({\n where: {\n path: input.filePath,\n uploadSessionId: input.uploadSessionId,\n userId: input.userId,\n uploadExpiresAt: { gt: new Date() },\n isOrphaned: false,\n messageId: null,\n },\n select: {\n id: true,\n uploadStatus: true,\n },\n });\n\n if (!fileRecord) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Invalid or expired upload intent',\n });\n }\n\n if (!['PENDING', 'UPLOADING'].includes(fileRecord.uploadStatus)) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: 'Upload is no longer accepting data',\n });\n }\n\n return fileRecord;\n}\n\n// DEPRECATED: These functions are no longer used - files are uploaded directly to GCS\n// Use createDirectUploadFile() and createDirectUploadFiles() instead\n\n/**\n * @deprecated Use createDirectUploadFile instead\n */\nexport async function uploadFile(\n file: FileData,\n userId: string,\n directory?: string,\n assignmentId?: string\n): Promise<UploadedFile> {\n throw new TRPCError({\n code: 'NOT_IMPLEMENTED',\n message: 'uploadFile is deprecated. Use createDirectUploadFile instead.',\n });\n}\n\n/**\n * @deprecated Use createDirectUploadFiles instead\n */\nexport async function uploadFiles(\n files: FileData[], \n userId: string,\n directory?: string\n): Promise<UploadedFile[]> {\n throw new TRPCError({\n code: 'NOT_IMPLEMENTED',\n message: 'uploadFiles is deprecated. Use createDirectUploadFiles instead.',\n });\n}\n\n/**\n * Gets a signed URL for a file\n * @param filePath The path of the file in Google Cloud Storage\n * @returns The signed URL\n */\nexport async function getFileUrl(filePath: string): Promise<string> {\n try {\n return await getSignedUrl(filePath);\n } catch (error) {\n console.error('Error getting signed URL:', error);\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to get file URL',\n });\n }\n}\n\n/**\n * Creates a file record for direct upload and generates signed URL\n * @param file The file metadata (no base64 data)\n * @param userId The ID of the user uploading the file\n * @param directory Optional directory to store the file in\n * @param assignmentId Optional assignment ID to associate the file with\n * @param submissionId Optional submission ID to associate the file with\n * @returns The direct upload file information with signed URL\n */\nexport async function createDirectUploadFile(\n file: DirectFileData,\n userId: string,\n directory?: string,\n assignmentId?: string,\n submissionId?: string,\n announcementId?: string,\n conversationId?: string\n): Promise<DirectUploadFile> {\n try {\n // Validate file extension matches MIME type\n const fileExtension = file.name.split('.').pop()?.toLowerCase();\n const mimeType = file.type.toLowerCase();\n \n const extensionMimeMap: Record<string, string[]> = {\n 'jpg': ['image/jpeg'],\n 'jpeg': ['image/jpeg'],\n 'png': ['image/png'],\n 'gif': ['image/gif'],\n 'webp': ['image/webp']\n };\n \n if (fileExtension && extensionMimeMap[fileExtension]) {\n if (!extensionMimeMap[fileExtension].includes(mimeType)) {\n throw new Error(`File extension .${fileExtension} does not match MIME type ${mimeType}`);\n }\n }\n \n // Create a unique filename\n const uniqueFilename = `${uuidv4()}.${fileExtension}`;\n \n // Construct the full path\n const filePath = directory \n ? `${directory}/${uniqueFilename}`\n : uniqueFilename;\n \n // Generate upload session ID\n const uploadSessionId = uuidv4();\n \n // Generate backend proxy upload URL (not direct GCS)\n const baseUrl = env.BACKEND_URL || 'http://localhost:3001';\n const uploadUrl = `${baseUrl}/api/upload/${encodeURIComponent(filePath)}?uploadSessionId=${encodeURIComponent(uploadSessionId)}`;\n const uploadExpiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes from now\n \n // Create file record in database with PENDING status\n const fileRecord = await prisma.file.create({\n data: {\n name: file.name,\n type: file.type,\n size: file.size,\n path: filePath,\n uploadStatus: 'PENDING',\n uploadUrl,\n uploadExpiresAt,\n uploadSessionId,\n user: {\n connect: { id: userId }\n },\n ...(directory && {\n folder: {\n connect: {id: directory},\n },\n }),\n ...(assignmentId && {\n assignment: {\n connect: { id: assignmentId }\n }\n }),\n ...(submissionId && {\n submission: {\n connect: { id: submissionId }\n }\n }),\n ...(announcementId && {\n announcement: {\n connect: { id: announcementId }\n }\n }),\n ...(conversationId && {\n conversationId,\n })\n },\n });\n \n return {\n id: fileRecord.id,\n name: file.name,\n type: file.type,\n size: file.size,\n path: filePath,\n uploadUrl,\n uploadExpiresAt,\n uploadSessionId\n };\n } catch (error) {\n logger.error('Error creating direct upload file:', {error: error instanceof Error ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n } : error});\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to create direct upload file',\n });\n }\n}\n\n/**\n * Confirms a direct upload was successful\n * @param fileId The ID of the file record\n * @param uploadSuccess Whether the upload was successful\n * @param errorMessage Optional error message if upload failed\n */\nexport async function confirmDirectUpload(\n fileId: string,\n uploadSuccess: boolean,\n errorMessage?: string\n): Promise<void> {\n try {\n // First fetch the file record to get the object path\n const fileRecord = await prisma.file.findUnique({\n where: { id: fileId },\n select: { path: true }\n });\n\n if (!fileRecord) {\n throw new TRPCError({\n code: 'NOT_FOUND',\n message: 'File record not found',\n });\n }\n\n let actualUploadSuccess = uploadSuccess;\n let actualErrorMessage = errorMessage;\n\n // If uploadSuccess is true, verify the object actually exists in GCS\n if (uploadSuccess) {\n try {\n const exists = await objectExists(env.GOOGLE_CLOUD_BUCKET_NAME!, fileRecord.path);\n if (!exists) {\n actualUploadSuccess = false;\n actualErrorMessage = 'File upload reported as successful but object not found in Google Cloud Storage';\n logger.error(`File upload verification failed for ${fileId}: object ${fileRecord.path} not found in GCS`);\n }\n } catch (error) {\n logger.error(`Error verifying file existence in GCS for ${fileId}:`, {error: error instanceof Error ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n } : error});\n actualUploadSuccess = false;\n actualErrorMessage = 'Failed to verify file existence in Google Cloud Storage';\n }\n }\n\n const updateData: any = {\n uploadStatus: actualUploadSuccess ? 'COMPLETED' : 'FAILED',\n uploadProgress: actualUploadSuccess ? 100 : 0,\n };\n \n if (!actualUploadSuccess && actualErrorMessage) {\n updateData.uploadError = actualErrorMessage;\n updateData.uploadRetryCount = { increment: 1 };\n }\n \n if (actualUploadSuccess) {\n updateData.uploadedAt = new Date();\n }\n \n await prisma.file.update({\n where: { id: fileId },\n data: updateData\n });\n } catch (error) {\n logger.error('Error confirming direct upload:', {error});\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to confirm upload',\n });\n }\n}\n\n/**\n * Updates upload progress for a direct upload\n * @param fileId The ID of the file record\n * @param progress Progress percentage (0-100)\n */\nexport async function updateUploadProgress(\n fileId: string,\n progress: number\n): Promise<void> {\n try {\n // await prisma.file.update({\n // where: { id: fileId },\n // data: {\n // uploadStatus: 'UPLOADING',\n // uploadProgress: Math.min(100, Math.max(0, progress))\n // }\n // });\n const current = await prisma.file.findUnique({ where: { id: fileId }, select: { uploadStatus: true } });\n if (!current || ['COMPLETED','FAILED','CANCELLED'].includes(current.uploadStatus as string)) return;\n const clamped = Math.min(100, Math.max(0, progress));\n await prisma.file.update({\n where: { id: fileId },\n data: {\n uploadStatus: 'UPLOADING',\n uploadProgress: clamped\n }\n });\n } catch (error) {\n logger.error('Error updating upload progress:', {error: error instanceof Error ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n } : error}); \n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to update upload progress',\n });\n }\n}\n\n/**\n * Creates multiple direct upload files\n * @param files Array of file metadata\n * @param userId The ID of the user uploading the files\n * @param directory Optional subdirectory to store the files in\n * @param assignmentId Optional assignment ID to associate files with\n * @param submissionId Optional submission ID to associate files with\n * @returns Array of direct upload file information\n */\nexport async function createDirectUploadFiles(\n files: DirectFileData[], \n userId: string,\n directory?: string,\n assignmentId?: string,\n submissionId?: string,\n announcementId?: string,\n conversationId?: string\n): Promise<DirectUploadFile[]> {\n try {\n const uploadPromises = files.map(file => \n createDirectUploadFile(\n file,\n userId,\n directory,\n assignmentId,\n submissionId,\n announcementId,\n conversationId\n )\n );\n return await Promise.all(uploadPromises);\n } catch (error) {\n logger.error('Error creating direct upload files:', {error: error instanceof Error ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n } : error});\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to create direct upload files',\n });\n }\n}\n"],"names":[],"mappings":"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","debug_id":"01a3ee28-2d14-5542-9301-e6f24e5c7a0d"}
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"/","sources":["middleware/auth.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,oBAAoB,GAAI,GAAG,GAAG;;;;;CA+H1C,CAAC"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"/","sources":["middleware/auth.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,oBAAoB,GAAI,GAAG,GAAG;;;;;CA6H1C,CAAC"}