@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
@@ -10,6 +10,16 @@ export const pusher = new Pusher({
10
10
  });
11
11
  // Pusher service for managing notifications
12
12
  export class PusherService {
13
+ static async emitUserEvent(userId, eventName, data) {
14
+ try {
15
+ const channel = `user_${userId}`;
16
+ await pusher.trigger(channel, eventName, data);
17
+ logger.info(`📡 Pusher user event sent: ${eventName} to ${channel}`);
18
+ }
19
+ catch (error) {
20
+ logger.error('Pusher user event error:', 'PUSHER', { error, userId, eventName });
21
+ }
22
+ }
13
23
  // Emit task completion notification
14
24
  static async emitTaskComplete(workspaceId, event, data) {
15
25
  try {
@@ -19,7 +29,7 @@ export class PusherService {
19
29
  logger.info(`📡 Pusher notification sent: ${eventName} to ${channel}`);
20
30
  }
21
31
  catch (error) {
22
- console.error('Pusher notification error:', error);
32
+ logger.error('Pusher notification error:', 'PUSHER', { error });
23
33
  }
24
34
  }
25
35
  // Emit AI analysis completion
@@ -32,7 +42,7 @@ export class PusherService {
32
42
  }
33
43
  // Emit study guide completion
34
44
  static async emitStudyGuideComplete(workspaceId, artifact) {
35
- await this.emitAnalysisComplete(workspaceId, 'studyguide', {
45
+ await this.emitTaskComplete(workspaceId, 'study_guide_generation_complete', {
36
46
  artifactId: artifact.id,
37
47
  title: artifact.title,
38
48
  status: 'completed'
@@ -54,6 +64,18 @@ export class PusherService {
54
64
  status: 'completed'
55
65
  });
56
66
  }
67
+ // Emit worksheet generation start notification
68
+ static async emitWorksheetGenerationStart(workspaceId) {
69
+ await this.emitTaskComplete(workspaceId, 'worksheet_generation_start', {});
70
+ }
71
+ // Emit new worksheet notification
72
+ static async emitWorksheetNew(workspaceId, worksheet) {
73
+ await this.emitTaskComplete(workspaceId, 'worksheet_new', { worksheet });
74
+ }
75
+ // Emit worksheet generation completion notification
76
+ static async emitWorksheetGenerationComplete(workspaceId, worksheet) {
77
+ await this.emitTaskComplete(workspaceId, 'worksheet_generation_complete', { worksheet });
78
+ }
57
79
  // Emit podcast completion
58
80
  static async emitPodcastComplete(workspaceId, artifact) {
59
81
  await this.emitAnalysisComplete(workspaceId, 'podcast', {
@@ -87,7 +109,7 @@ export class PusherService {
87
109
  logger.info(`📡 Analysis progress sent to ${channel}: ${progress.status}`);
88
110
  }
89
111
  catch (error) {
90
- console.error('Pusher progress notification error:', error);
112
+ logger.error('Pusher progress notification error:', 'PUSHER', { error });
91
113
  }
92
114
  }
93
115
  // Emit channel-specific events (for chat messages)
@@ -96,10 +118,52 @@ export class PusherService {
96
118
  const channel = channelId; // Use channelId directly as channel name
97
119
  const eventName = event;
98
120
  await pusher.trigger(channel, eventName, data);
99
- console.log(`📡 Pusher notification sent: ${eventName} to ${channel}`);
121
+ logger.info(`Pusher notification sent: ${eventName} to ${channel}`);
122
+ }
123
+ catch (error) {
124
+ logger.error('Pusher channel notification error:', 'PUSHER', { error });
125
+ }
126
+ }
127
+ // Emit member joined notification
128
+ static async emitMemberJoined(workspaceId, member) {
129
+ await this.emitTaskComplete(workspaceId, 'member_joined', { member });
130
+ }
131
+ // Emit profile update notification
132
+ static async emitProfileUpdate(userId) {
133
+ await this.emitUserEvent(userId, 'profile_updated', {
134
+ userId,
135
+ timestamp: new Date().toISOString(),
136
+ });
137
+ }
138
+ // Emit library (folders/workspaces) update notification
139
+ static async emitLibraryUpdate(userId) {
140
+ await this.emitUserEvent(userId, 'library_updated', {
141
+ userId,
142
+ timestamp: new Date().toISOString(),
143
+ });
144
+ }
145
+ static async emitNotificationNew(userId, data) {
146
+ await this.emitUserEvent(userId, 'notification_new', data);
147
+ }
148
+ static async emitNotificationReadState(userId, data) {
149
+ await this.emitUserEvent(userId, 'notification_read_state_changed', {
150
+ ...data,
151
+ timestamp: data.timestamp ?? new Date().toISOString(),
152
+ });
153
+ }
154
+ // Emit payment success (top-up or subscription)
155
+ static async emitPaymentSuccess(userId, data) {
156
+ try {
157
+ const channel = `user_${userId}`;
158
+ const eventName = 'payment_success';
159
+ await pusher.trigger(channel, eventName, {
160
+ ...data,
161
+ timestamp: new Date().toISOString(),
162
+ });
163
+ logger.info(`📡 Pusher payment success sent: ${eventName} to ${channel}`);
100
164
  }
101
165
  catch (error) {
102
- console.error('Pusher notification error:', error);
166
+ logger.error('Pusher payment success error:', 'PUSHER', { error });
103
167
  }
104
168
  }
105
169
  }
@@ -0,0 +1,15 @@
1
+ export interface RetryOptions {
2
+ /** Maximum number of attempts (default: 3) */
3
+ maxRetries?: number;
4
+ /** Base delay in ms for exponential backoff (default: 2000) */
5
+ baseDelayMs?: number;
6
+ /** Timeout per attempt in ms (default: 300000 = 5 minutes) */
7
+ timeoutMs?: number;
8
+ /** Label for logging (optional) */
9
+ label?: string;
10
+ }
11
+ /**
12
+ * Wraps an async function with retry logic and exponential backoff.
13
+ * Throws the last error if all attempts fail.
14
+ */
15
+ export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
@@ -0,0 +1,37 @@
1
+ import { logger } from './logger.js';
2
+ /**
3
+ * Wraps an async function with retry logic and exponential backoff.
4
+ * Throws the last error if all attempts fail.
5
+ */
6
+ export async function withRetry(fn, options = {}) {
7
+ const { maxRetries = 3, baseDelayMs = 2000, timeoutMs = 300000, label = 'operation', } = options;
8
+ let lastError = null;
9
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
10
+ try {
11
+ logger.info(`[retry] ${label} attempt ${attempt}/${maxRetries}`);
12
+ // Create an AbortController for per-attempt timeout
13
+ const controller = new AbortController();
14
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
15
+ try {
16
+ const result = await fn();
17
+ clearTimeout(timeoutId);
18
+ return result;
19
+ }
20
+ catch (error) {
21
+ clearTimeout(timeoutId);
22
+ throw error;
23
+ }
24
+ }
25
+ catch (error) {
26
+ lastError = error instanceof Error ? error : new Error(String(error));
27
+ logger.error(`[retry] ${label} attempt ${attempt} failed: ${lastError.message}`);
28
+ if (attempt < maxRetries) {
29
+ const delay = Math.pow(2, attempt) * baseDelayMs;
30
+ logger.info(`[retry] ${label} retrying in ${delay}ms...`);
31
+ await new Promise((resolve) => setTimeout(resolve, delay));
32
+ }
33
+ }
34
+ }
35
+ logger.error(`[retry] ${label} all ${maxRetries} attempts failed`);
36
+ throw lastError;
37
+ }
@@ -60,12 +60,12 @@ export async function makeFilePublic(objectKey) {
60
60
  // In Supabase, files are public by default when uploaded to public buckets
61
61
  // For private buckets, you would need to update the bucket policy
62
62
  // This function is kept for compatibility but may not be needed
63
- console.log(`File ${objectKey} is already public in Supabase Storage`);
63
+ // File is already public in Supabase Storage
64
64
  }
65
65
  export async function makeFilePrivate(objectKey) {
66
66
  // In Supabase, you would need to update the bucket policy to make files private
67
67
  // This function is kept for compatibility but may not be needed
68
- console.log(`File ${objectKey} privacy is controlled by bucket policy in Supabase Storage`);
68
+ // File privacy is controlled by bucket policy in Supabase Storage
69
69
  }
70
70
  // Export supabase client for direct access if needed
71
71
  export const supabaseClient = supabase;
@@ -0,0 +1,9 @@
1
+ export declare const stripe: any;
2
+ /**
3
+ * Creates a Stripe customer for a user.
4
+ *
5
+ * @param email - User's email
6
+ * @param name - User's name
7
+ * @returns The Stripe customer ID
8
+ */
9
+ export declare function createStripeCustomer(email: string, name?: string): Promise<string | null>;
@@ -0,0 +1,36 @@
1
+ import Stripe from 'stripe';
2
+ import { env } from './env.js';
3
+ import { logger } from './logger.js';
4
+ if (!env.STRIPE_SECRET_KEY) {
5
+ logger.warn('STRIPE_SECRET_KEY is not set. Stripe functionality will be disabled.', 'STRIPE');
6
+ }
7
+ export const stripe = env.STRIPE_SECRET_KEY
8
+ ? new Stripe(env.STRIPE_SECRET_KEY, {
9
+ apiVersion: '2026-02-25.clover',
10
+ })
11
+ : null;
12
+ /**
13
+ * Creates a Stripe customer for a user.
14
+ *
15
+ * @param email - User's email
16
+ * @param name - User's name
17
+ * @returns The Stripe customer ID
18
+ */
19
+ export async function createStripeCustomer(email, name) {
20
+ if (!stripe) {
21
+ logger.error('Stripe is not initialized. Cannot create customer.', 'STRIPE');
22
+ return null;
23
+ }
24
+ try {
25
+ const customer = await stripe.customers.create({
26
+ email,
27
+ name,
28
+ });
29
+ logger.info(`Stripe customer created for ${email}: ${customer.id}`, 'STRIPE');
30
+ return customer.id;
31
+ }
32
+ catch (error) {
33
+ logger.error(`Failed to create Stripe customer for ${email}: ${error.message}`, 'STRIPE');
34
+ return null;
35
+ }
36
+ }
@@ -0,0 +1,37 @@
1
+ import Stripe from 'stripe';
2
+ /**
3
+ * Handle checkout.session.completed event
4
+ */
5
+ export declare function handleCheckoutCompleted(event: Stripe.Event): Promise<void>;
6
+ /**
7
+ * Handle customer.subscription.created event
8
+ */
9
+ export declare function handleSubscriptionCreated(event: Stripe.Event): Promise<void>;
10
+ /**
11
+ * Handle customer.subscription.updated event
12
+ */
13
+ export declare function handleSubscriptionUpdated(event: Stripe.Event): Promise<void>;
14
+ /**
15
+ * Handle customer.subscription.deleted event
16
+ */
17
+ export declare function handleSubscriptionDeleted(event: Stripe.Event): Promise<void>;
18
+ /**
19
+ * Handle invoice.paid event
20
+ */
21
+ export declare function handleInvoicePaid(event: Stripe.Event): Promise<void>;
22
+ /**
23
+ * Handle invoice.payment_failed event
24
+ */
25
+ export declare function handlePaymentFailed(event: Stripe.Event): Promise<void>;
26
+ /**
27
+ * Handle payment_intent.payment_failed event (One-time payments)
28
+ */
29
+ export declare function handlePaymentIntentFailed(event: Stripe.Event): Promise<void>;
30
+ /**
31
+ * Get the storage limit for a user based on their active subscription
32
+ */
33
+ export declare function getUserStorageLimit(userId: string): Promise<number>;
34
+ /**
35
+ * Core logic to sync Stripe subscription state with Prisma database
36
+ */
37
+ export declare function upsertSubscriptionFromStripe(subscriptionIdOrObject: Stripe.Subscription | string): Promise<void>;