@goscribe/server 1.3.0 → 1.3.1

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 (79) hide show
  1. package/dist/context.d.ts +5 -1
  2. package/dist/lib/activity_human_description.d.ts +13 -0
  3. package/dist/lib/activity_human_description.js +221 -0
  4. package/dist/lib/activity_human_description.test.d.ts +1 -0
  5. package/dist/lib/activity_human_description.test.js +16 -0
  6. package/dist/lib/activity_log_service.d.ts +87 -0
  7. package/dist/lib/activity_log_service.js +276 -0
  8. package/dist/lib/activity_log_service.test.d.ts +1 -0
  9. package/dist/lib/activity_log_service.test.js +27 -0
  10. package/dist/lib/ai-session.d.ts +15 -2
  11. package/dist/lib/ai-session.js +147 -85
  12. package/dist/lib/constants.d.ts +13 -0
  13. package/dist/lib/constants.js +12 -0
  14. package/dist/lib/email.d.ts +11 -0
  15. package/dist/lib/email.js +193 -0
  16. package/dist/lib/env.d.ts +13 -0
  17. package/dist/lib/env.js +16 -0
  18. package/dist/lib/inference.d.ts +4 -1
  19. package/dist/lib/inference.js +3 -3
  20. package/dist/lib/logger.d.ts +4 -4
  21. package/dist/lib/logger.js +30 -8
  22. package/dist/lib/notification-service.d.ts +152 -0
  23. package/dist/lib/notification-service.js +473 -0
  24. package/dist/lib/notification-service.test.d.ts +1 -0
  25. package/dist/lib/notification-service.test.js +87 -0
  26. package/dist/lib/prisma.d.ts +2 -1
  27. package/dist/lib/prisma.js +5 -1
  28. package/dist/lib/pusher.d.ts +23 -0
  29. package/dist/lib/pusher.js +69 -5
  30. package/dist/lib/retry.d.ts +15 -0
  31. package/dist/lib/retry.js +37 -0
  32. package/dist/lib/storage.js +2 -2
  33. package/dist/lib/stripe.d.ts +9 -0
  34. package/dist/lib/stripe.js +36 -0
  35. package/dist/lib/subscription_service.d.ts +37 -0
  36. package/dist/lib/subscription_service.js +654 -0
  37. package/dist/lib/usage_service.d.ts +26 -0
  38. package/dist/lib/usage_service.js +59 -0
  39. package/dist/lib/worksheet-generation.d.ts +91 -0
  40. package/dist/lib/worksheet-generation.js +95 -0
  41. package/dist/lib/worksheet-generation.test.d.ts +1 -0
  42. package/dist/lib/worksheet-generation.test.js +20 -0
  43. package/dist/lib/workspace-access.d.ts +18 -0
  44. package/dist/lib/workspace-access.js +13 -0
  45. package/dist/routers/_app.d.ts +1349 -253
  46. package/dist/routers/_app.js +10 -0
  47. package/dist/routers/admin.d.ts +361 -0
  48. package/dist/routers/admin.js +633 -0
  49. package/dist/routers/annotations.d.ts +219 -0
  50. package/dist/routers/annotations.js +187 -0
  51. package/dist/routers/auth.d.ts +88 -7
  52. package/dist/routers/auth.js +339 -19
  53. package/dist/routers/chat.d.ts +6 -12
  54. package/dist/routers/copilot.d.ts +199 -0
  55. package/dist/routers/copilot.js +571 -0
  56. package/dist/routers/flashcards.d.ts +47 -81
  57. package/dist/routers/flashcards.js +143 -27
  58. package/dist/routers/members.d.ts +36 -7
  59. package/dist/routers/members.js +200 -19
  60. package/dist/routers/notifications.d.ts +99 -0
  61. package/dist/routers/notifications.js +127 -0
  62. package/dist/routers/payment.d.ts +89 -0
  63. package/dist/routers/payment.js +403 -0
  64. package/dist/routers/podcast.d.ts +8 -13
  65. package/dist/routers/podcast.js +54 -31
  66. package/dist/routers/studyguide.d.ts +1 -29
  67. package/dist/routers/studyguide.js +80 -71
  68. package/dist/routers/worksheets.d.ts +105 -38
  69. package/dist/routers/worksheets.js +258 -68
  70. package/dist/routers/workspace.d.ts +139 -60
  71. package/dist/routers/workspace.js +455 -315
  72. package/dist/scripts/purge-deleted-users.d.ts +1 -0
  73. package/dist/scripts/purge-deleted-users.js +149 -0
  74. package/dist/server.js +130 -10
  75. package/dist/services/flashcard-progress.service.d.ts +18 -66
  76. package/dist/services/flashcard-progress.service.js +51 -42
  77. package/dist/trpc.d.ts +20 -21
  78. package/dist/trpc.js +150 -1
  79. package/package.json +1 -1
@@ -1,15 +1,33 @@
1
1
  import { z } from 'zod';
2
- import { router, publicProcedure } from '../trpc.js';
2
+ import { router, publicProcedure, authedProcedure } from '../trpc.js';
3
+ import PusherService from '../lib/pusher.js';
4
+ import { logger } from '../lib/logger.js';
3
5
  import bcrypt from 'bcryptjs';
4
6
  import { serialize } from 'cookie';
5
7
  import crypto from 'node:crypto';
6
8
  import { TRPCError } from '@trpc/server';
7
9
  import { supabaseClient } from '../lib/storage.js';
10
+ import { sendVerificationEmail, sendAccountDeletionScheduledEmail, sendAccountRestoredEmail, sendPasswordResetEmail, } from '../lib/email.js';
11
+ import { createStripeCustomer } from '../lib/stripe.js';
12
+ import { notifyAdminsAccountDeletionScheduled, notifyAdminsOnSignup, } from '../lib/notification-service.js';
8
13
  // Helper to create custom auth token
14
+ const passwordFieldSchema = z
15
+ .string()
16
+ .min(8, 'Password must be at least 8 characters')
17
+ .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
18
+ .regex(/[0-9]/, 'Password must contain at least one number')
19
+ .regex(/[^a-zA-Z0-9]/, 'Password must contain at least one special character');
20
+ function hashPasswordResetToken(rawToken) {
21
+ return crypto.createHash('sha256').update(rawToken, 'utf8').digest('hex');
22
+ }
23
+ /** Use until `npx prisma generate` runs after adding PasswordResetToken to the schema. */
24
+ function passwordResetDb(ctx) {
25
+ return ctx.db.passwordResetToken;
26
+ }
9
27
  function createCustomAuthToken(userId) {
10
28
  const secret = process.env.AUTH_SECRET;
11
29
  if (!secret) {
12
- throw new Error("AUTH_SECRET is not set");
30
+ throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: "AUTH_SECRET is not set" });
13
31
  }
14
32
  const base64UserId = Buffer.from(userId, 'utf8').toString('base64url');
15
33
  const hmac = crypto.createHmac('sha256', secret);
@@ -37,18 +55,58 @@ export const auth = router({
37
55
  message: 'Profile updated successfully',
38
56
  };
39
57
  }),
40
- uploadProfilePicture: publicProcedure
58
+ changePassword: authedProcedure
59
+ .input(z.object({
60
+ currentPassword: z.string().min(1, "Current password is required"),
61
+ newPassword: passwordFieldSchema,
62
+ }))
41
63
  .mutation(async ({ ctx, input }) => {
42
- const objectKey = `profile_picture_${ctx.session.user.id}`;
64
+ const user = await ctx.db.user.findUnique({
65
+ where: { id: ctx.session.user.id },
66
+ select: { id: true, passwordHash: true },
67
+ });
68
+ if (!user) {
69
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
70
+ }
71
+ if (!user.passwordHash) {
72
+ throw new TRPCError({
73
+ code: 'BAD_REQUEST',
74
+ message: 'Password change is unavailable for this account.',
75
+ });
76
+ }
77
+ const validCurrentPassword = await bcrypt.compare(input.currentPassword, user.passwordHash);
78
+ if (!validCurrentPassword) {
79
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Current password is incorrect' });
80
+ }
81
+ const isSamePassword = await bcrypt.compare(input.newPassword, user.passwordHash);
82
+ if (isSamePassword) {
83
+ throw new TRPCError({
84
+ code: 'BAD_REQUEST',
85
+ message: 'New password must be different from current password',
86
+ });
87
+ }
88
+ const newHash = await bcrypt.hash(input.newPassword, 10);
89
+ await ctx.db.user.update({
90
+ where: { id: user.id },
91
+ data: { passwordHash: newHash },
92
+ });
93
+ return { success: true, message: 'Password changed successfully' };
94
+ }),
95
+ uploadProfilePicture: authedProcedure
96
+ .mutation(async ({ ctx }) => {
97
+ const userId = ctx.session.user.id;
98
+ logger.info(`Generating upload URL for user ${userId}`, 'AUTH');
99
+ const objectKey = `profile_picture_${userId}`;
43
100
  const { data: signedUrlData, error: signedUrlError } = await supabaseClient.storage
44
101
  .from('media')
45
- .createSignedUploadUrl(objectKey, { upsert: true }); // 5 minutes
102
+ .createSignedUploadUrl(objectKey, { upsert: true });
46
103
  if (signedUrlError) {
104
+ logger.error(`Failed to generate upload URL: ${signedUrlError.message}`, 'AUTH');
47
105
  throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: `Failed to generate upload URL: ${signedUrlError.message}` });
48
106
  }
49
- await ctx.db.fileAsset.create({
107
+ const fileAsset = await ctx.db.fileAsset.create({
50
108
  data: {
51
- userId: ctx.session.user.id,
109
+ userId: userId,
52
110
  name: 'Profile Picture',
53
111
  mimeType: 'image/jpeg',
54
112
  size: 0,
@@ -56,36 +114,139 @@ export const auth = router({
56
114
  objectKey: objectKey,
57
115
  },
58
116
  });
117
+ await ctx.db.user.update({
118
+ where: { id: userId },
119
+ data: {
120
+ fileAssetId: fileAsset.id,
121
+ },
122
+ });
123
+ logger.info(`Profile picture asset created and linked for user ${userId}`, 'AUTH');
59
124
  return {
60
125
  success: true,
61
126
  message: 'Profile picture uploaded successfully',
62
127
  signedUrl: signedUrlData.signedUrl,
63
128
  };
64
129
  }),
130
+ confirmProfileUpdate: authedProcedure
131
+ .mutation(async ({ ctx }) => {
132
+ logger.info(`Confirming profile update for user ${ctx.session.user.id}`, 'AUTH');
133
+ await PusherService.emitProfileUpdate(ctx.session.user.id);
134
+ return { success: true };
135
+ }),
65
136
  signup: publicProcedure
66
137
  .input(z.object({
67
138
  name: z.string().min(1),
68
139
  email: z.string().email(),
69
- password: z.string().min(6),
140
+ password: passwordFieldSchema,
70
141
  }))
71
142
  .mutation(async ({ ctx, input }) => {
72
143
  const existing = await ctx.db.user.findUnique({
73
144
  where: { email: input.email },
74
145
  });
75
146
  if (existing) {
76
- throw new Error("Email already registered");
147
+ throw new TRPCError({ code: 'CONFLICT', message: "Email already registered" });
77
148
  }
78
149
  const hash = await bcrypt.hash(input.password, 10);
150
+ // Get default "User" role
151
+ const userRole = await ctx.db.role.findUnique({
152
+ where: { name: 'User' },
153
+ });
79
154
  const user = await ctx.db.user.create({
80
155
  data: {
81
156
  name: input.name,
82
157
  email: input.email,
83
158
  passwordHash: hash,
84
- emailVerified: new Date(), // skip verification for demo
159
+ roleId: userRole?.id,
160
+ // emailVerified is null -- user must verify
85
161
  },
86
162
  });
163
+ await notifyAdminsOnSignup(ctx.db, {
164
+ id: user.id,
165
+ name: user.name,
166
+ email: user.email,
167
+ });
168
+ // Create verification token (24h expiry)
169
+ const token = crypto.randomUUID();
170
+ await ctx.db.verificationToken.create({
171
+ data: {
172
+ identifier: input.email,
173
+ token,
174
+ expires: new Date(Date.now() + 24 * 60 * 60 * 1000),
175
+ },
176
+ });
177
+ // Send verification email (non-blocking)
178
+ sendVerificationEmail(input.email, token, input.name).catch(() => { });
179
+ // Create Stripe Customer (non-blocking for registration, but we want it)
180
+ createStripeCustomer(input.email, input.name).then(async (stripeCustomerId) => {
181
+ if (stripeCustomerId) {
182
+ await ctx.db.user.update({
183
+ where: { id: user.id },
184
+ data: { stripe_customer_id: stripeCustomerId }
185
+ }).catch((err) => logger.error(`Failed to update user with stripe_customer_id: ${err.message}`, 'AUTH'));
186
+ }
187
+ });
87
188
  return { id: user.id, email: user.email, name: user.name };
88
189
  }),
190
+ // Verify email with token from the email link
191
+ verifyEmail: publicProcedure
192
+ .input(z.object({
193
+ token: z.string(),
194
+ }))
195
+ .mutation(async ({ ctx, input }) => {
196
+ const record = await ctx.db.verificationToken.findUnique({
197
+ where: { token: input.token },
198
+ });
199
+ if (!record) {
200
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'Invalid or expired verification link' });
201
+ }
202
+ if (record.expires < new Date()) {
203
+ // Clean up expired token
204
+ await ctx.db.verificationToken.deleteMany({ where: { token: input.token } });
205
+ throw new TRPCError({ code: 'BAD_REQUEST', message: 'Verification link has expired. Please request a new one.' });
206
+ }
207
+ // Mark user as verified
208
+ await ctx.db.user.update({
209
+ where: { email: record.identifier },
210
+ data: { emailVerified: new Date() },
211
+ });
212
+ // Delete used token
213
+ await ctx.db.verificationToken.deleteMany({ where: { token: input.token } });
214
+ return { success: true, message: 'Email verified successfully' };
215
+ }),
216
+ // Resend verification email (for logged-in users who haven't verified)
217
+ resendVerification: publicProcedure
218
+ .mutation(async ({ ctx }) => {
219
+ if (!ctx.session?.user?.id) {
220
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Not logged in' });
221
+ }
222
+ const user = await ctx.db.user.findUnique({
223
+ where: { id: ctx.session.user.id },
224
+ });
225
+ if (!user || !user.email) {
226
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
227
+ }
228
+ if (user.emailVerified) {
229
+ return { success: true, message: 'Email is already verified' };
230
+ }
231
+ // Delete any existing tokens for this email
232
+ await ctx.db.verificationToken.deleteMany({
233
+ where: { identifier: user.email },
234
+ });
235
+ // Create new token
236
+ const token = crypto.randomUUID();
237
+ await ctx.db.verificationToken.create({
238
+ data: {
239
+ identifier: user.email,
240
+ token,
241
+ expires: new Date(Date.now() + 24 * 60 * 60 * 1000),
242
+ },
243
+ });
244
+ const sent = await sendVerificationEmail(user.email, token, user.name);
245
+ if (!sent) {
246
+ throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to send email. Please try again.' });
247
+ }
248
+ return { success: true, message: 'Verification email sent' };
249
+ }),
89
250
  login: publicProcedure
90
251
  .input(z.object({
91
252
  email: z.string().email(),
@@ -96,11 +257,14 @@ export const auth = router({
96
257
  where: { email: input.email },
97
258
  });
98
259
  if (!user) {
99
- throw new Error("Invalid credentials");
260
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: "Invalid credentials" });
261
+ }
262
+ if (user.deletedAt) {
263
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: "Account scheduled for deletion. Please check your email for a restore link." });
100
264
  }
101
265
  const valid = await bcrypt.compare(input.password, user.passwordHash);
102
266
  if (!valid) {
103
- throw new Error("Invalid credentials");
267
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: "Invalid credentials" });
104
268
  }
105
269
  // Create custom auth token
106
270
  const authToken = createCustomAuthToken(user.id);
@@ -121,33 +285,189 @@ export const auth = router({
121
285
  token: authToken
122
286
  };
123
287
  }),
288
+ /**
289
+ * Request a password reset email. Always returns the same message (no email enumeration).
290
+ */
291
+ requestPasswordReset: publicProcedure
292
+ .input(z.object({ email: z.string().email() }))
293
+ .mutation(async ({ ctx, input }) => {
294
+ const email = input.email.trim().toLowerCase();
295
+ const generic = {
296
+ success: true,
297
+ message: 'If an account exists for this email, we sent password reset instructions.',
298
+ };
299
+ const user = await ctx.db.user.findUnique({
300
+ where: { email },
301
+ select: {
302
+ id: true,
303
+ email: true,
304
+ name: true,
305
+ passwordHash: true,
306
+ deletedAt: true,
307
+ },
308
+ });
309
+ if (!user?.passwordHash || user.deletedAt || !user.email) {
310
+ return generic;
311
+ }
312
+ await passwordResetDb(ctx).deleteMany({
313
+ where: { userId: user.id, usedAt: null },
314
+ });
315
+ const rawToken = crypto.randomBytes(32).toString('hex');
316
+ const tokenHash = hashPasswordResetToken(rawToken);
317
+ const expiresAt = new Date(Date.now() + 60 * 60 * 1000);
318
+ await passwordResetDb(ctx).create({
319
+ data: {
320
+ userId: user.id,
321
+ tokenHash,
322
+ expiresAt,
323
+ },
324
+ });
325
+ sendPasswordResetEmail(user.email, rawToken, user.name).catch(() => { });
326
+ return generic;
327
+ }),
328
+ /**
329
+ * Complete password reset using the token from the email link.
330
+ */
331
+ resetPassword: publicProcedure
332
+ .input(z.object({
333
+ token: z.string().min(1),
334
+ newPassword: passwordFieldSchema,
335
+ }))
336
+ .mutation(async ({ ctx, input }) => {
337
+ const tokenHash = hashPasswordResetToken(input.token);
338
+ const record = await passwordResetDb(ctx).findUnique({
339
+ where: { tokenHash },
340
+ });
341
+ if (!record || record.usedAt || record.expiresAt < new Date()) {
342
+ throw new TRPCError({
343
+ code: 'BAD_REQUEST',
344
+ message: 'Invalid or expired reset link. Please request a new one.',
345
+ });
346
+ }
347
+ const newHash = await bcrypt.hash(input.newPassword, 10);
348
+ await ctx.db.user.update({
349
+ where: { id: record.userId },
350
+ data: { passwordHash: newHash },
351
+ });
352
+ await passwordResetDb(ctx).update({
353
+ where: { id: record.id },
354
+ data: { usedAt: new Date() },
355
+ });
356
+ await passwordResetDb(ctx).deleteMany({
357
+ where: { userId: record.userId, id: { not: record.id } },
358
+ });
359
+ return { success: true, message: 'Password updated. You can sign in now.' };
360
+ }),
124
361
  getSession: publicProcedure.query(async ({ ctx }) => {
125
362
  // Just return the current session from context
126
363
  if (!ctx.session) {
127
- throw new Error("No session found");
364
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: "No session found" });
128
365
  }
366
+ const userId = ctx.session.user.id;
129
367
  const user = await ctx.db.user.findUnique({
130
- where: { id: ctx.session.user.id },
368
+ where: { id: userId },
369
+ include: {
370
+ profilePicture: true,
371
+ role: true,
372
+ }
131
373
  });
132
374
  if (!user) {
133
- throw new Error("User not found");
375
+ throw new TRPCError({ code: 'NOT_FOUND', message: "User not found" });
134
376
  }
377
+ const profilePictureUrl = user.profilePicture?.objectKey
378
+ ? `/profile-picture/${user.profilePicture.objectKey}?t=${new Date(user.updatedAt).getTime()}`
379
+ : null;
380
+ logger.info(`Session fetched for user ${userId}, profilePicture: ${profilePictureUrl}`, 'AUTH');
135
381
  return {
136
382
  user: {
137
383
  id: user.id,
138
384
  email: user.email,
139
385
  name: user.name,
386
+ emailVerified: !!user.emailVerified,
387
+ profilePicture: profilePictureUrl,
388
+ role: user.role,
140
389
  }
141
390
  };
142
391
  }),
392
+ requestAccountDeletion: authedProcedure
393
+ .mutation(async ({ ctx }) => {
394
+ const user = await ctx.db.user.findUnique({
395
+ where: { id: ctx.session.user.id },
396
+ });
397
+ if (!user) {
398
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
399
+ }
400
+ // Mark user as deleted
401
+ await ctx.db.user.update({
402
+ where: { id: user.id },
403
+ data: { deletedAt: new Date() },
404
+ });
405
+ await notifyAdminsAccountDeletionScheduled(ctx.db, {
406
+ id: user.id,
407
+ name: user.name,
408
+ email: user.email,
409
+ }).catch(() => { });
410
+ // Clear existing restore tokens
411
+ await ctx.db.verificationToken.deleteMany({
412
+ where: { identifier: `restore-${user.email}` },
413
+ });
414
+ // Create restore token (30 days expiry)
415
+ const token = crypto.randomUUID();
416
+ await ctx.db.verificationToken.create({
417
+ data: {
418
+ identifier: `restore-${user.email}`,
419
+ token,
420
+ expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
421
+ },
422
+ });
423
+ // Send email
424
+ if (user.email) {
425
+ sendAccountDeletionScheduledEmail(user.email, token).catch(() => { });
426
+ }
427
+ // Log out user by clearing cookie
428
+ ctx.res.setHeader("Set-Cookie", serialize("auth_token", "", {
429
+ httpOnly: true,
430
+ secure: process.env.NODE_ENV === "production",
431
+ sameSite: "lax",
432
+ path: "/",
433
+ maxAge: 0,
434
+ }));
435
+ return { success: true, message: 'Account scheduled for deletion' };
436
+ }),
437
+ restoreAccount: publicProcedure
438
+ .input(z.object({
439
+ token: z.string(),
440
+ }))
441
+ .mutation(async ({ ctx, input }) => {
442
+ const record = await ctx.db.verificationToken.findUnique({
443
+ where: { token: input.token },
444
+ });
445
+ if (!record || !record.identifier.startsWith('restore-')) {
446
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'Invalid or expired restore link' });
447
+ }
448
+ if (record.expires < new Date()) {
449
+ await ctx.db.verificationToken.deleteMany({ where: { token: input.token } });
450
+ throw new TRPCError({ code: 'BAD_REQUEST', message: 'Restore link has expired.' });
451
+ }
452
+ const email = record.identifier.replace('restore-', '');
453
+ // Mark user as restored
454
+ await ctx.db.user.update({
455
+ where: { email },
456
+ data: { deletedAt: null },
457
+ });
458
+ // Delete used token
459
+ await ctx.db.verificationToken.deleteMany({ where: { token: input.token } });
460
+ // Send confirmation email
461
+ sendAccountRestoredEmail(email).catch(() => { });
462
+ return { success: true, message: 'Account restored successfully' };
463
+ }),
143
464
  logout: publicProcedure.mutation(async ({ ctx }) => {
144
465
  const token = ctx.cookies["auth_token"];
145
466
  if (!token) {
146
- throw new Error("No token found");
467
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: "No token found" });
147
468
  }
148
- await ctx.db.session.delete({
149
- where: { id: token },
150
- });
469
+ // We don't need to delete from db.session because we use a stateless
470
+ // custom HMAC auth system (auth_token cookie).
151
471
  ctx.res.setHeader("Set-Cookie", serialize("auth_token", "", {
152
472
  httpOnly: true,
153
473
  secure: process.env.NODE_ENV === "production",
@@ -1,11 +1,5 @@
1
1
  export declare const chat: import("@trpc/server").TRPCBuiltRouter<{
2
- ctx: {
3
- db: import("@prisma/client").PrismaClient<import("@prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
4
- session: any;
5
- req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
6
- res: import("express").Response<any, Record<string, any>>;
7
- cookies: Record<string, string | undefined>;
8
- };
2
+ ctx: import("../context.js").Context;
9
3
  meta: object;
10
4
  errorShape: {
11
5
  data: {
@@ -47,8 +41,8 @@ export declare const chat: import("@trpc/server").TRPCBuiltRouter<{
47
41
  id: string;
48
42
  createdAt: Date;
49
43
  updatedAt: Date;
50
- userId: string | null;
51
44
  channelId: string;
45
+ userId: string | null;
52
46
  message: string;
53
47
  })[];
54
48
  } & {
@@ -85,8 +79,8 @@ export declare const chat: import("@trpc/server").TRPCBuiltRouter<{
85
79
  id: string;
86
80
  createdAt: Date;
87
81
  updatedAt: Date;
88
- userId: string | null;
89
82
  channelId: string;
83
+ userId: string | null;
90
84
  message: string;
91
85
  })[];
92
86
  } & {
@@ -112,8 +106,8 @@ export declare const chat: import("@trpc/server").TRPCBuiltRouter<{
112
106
  id: string;
113
107
  createdAt: Date;
114
108
  updatedAt: Date;
115
- userId: string | null;
116
109
  channelId: string;
110
+ userId: string | null;
117
111
  message: string;
118
112
  })[];
119
113
  } & {
@@ -138,8 +132,8 @@ export declare const chat: import("@trpc/server").TRPCBuiltRouter<{
138
132
  id: string;
139
133
  createdAt: Date;
140
134
  updatedAt: Date;
141
- userId: string | null;
142
135
  channelId: string;
136
+ userId: string | null;
143
137
  message: string;
144
138
  };
145
139
  meta: object;
@@ -158,8 +152,8 @@ export declare const chat: import("@trpc/server").TRPCBuiltRouter<{
158
152
  id: string;
159
153
  createdAt: Date;
160
154
  updatedAt: Date;
161
- userId: string | null;
162
155
  channelId: string;
156
+ userId: string | null;
163
157
  message: string;
164
158
  };
165
159
  meta: object;