eggi-ai-db-schema-2 0.1.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 (188) hide show
  1. package/CHANGELOG.md +750 -0
  2. package/README.md +660 -0
  3. package/dist/config/database.d.ts +28 -0
  4. package/dist/config/database.d.ts.map +1 -0
  5. package/dist/config/database.js +72 -0
  6. package/dist/config/database.js.map +1 -0
  7. package/dist/index.d.ts +28 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +199 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/lib/database-service.d.ts +714 -0
  12. package/dist/lib/database-service.d.ts.map +1 -0
  13. package/dist/lib/database-service.js +1394 -0
  14. package/dist/lib/database-service.js.map +1 -0
  15. package/dist/lib/db-types.d.ts +167 -0
  16. package/dist/lib/db-types.d.ts.map +1 -0
  17. package/dist/lib/db-types.js +28 -0
  18. package/dist/lib/db-types.js.map +1 -0
  19. package/dist/lib/db.d.ts +58 -0
  20. package/dist/lib/db.d.ts.map +1 -0
  21. package/dist/lib/db.js +292 -0
  22. package/dist/lib/db.js.map +1 -0
  23. package/dist/lib/index.d.ts +11 -0
  24. package/dist/lib/index.d.ts.map +1 -0
  25. package/dist/lib/index.js +26 -0
  26. package/dist/lib/index.js.map +1 -0
  27. package/dist/lib/pg-client.d.ts +50 -0
  28. package/dist/lib/pg-client.d.ts.map +1 -0
  29. package/dist/lib/pg-client.js +106 -0
  30. package/dist/lib/pg-client.js.map +1 -0
  31. package/dist/lib/schema.d.ts +298 -0
  32. package/dist/lib/schema.d.ts.map +1 -0
  33. package/dist/lib/schema.js +12 -0
  34. package/dist/lib/schema.js.map +1 -0
  35. package/dist/migration-manager.d.ts +49 -0
  36. package/dist/migration-manager.d.ts.map +1 -0
  37. package/dist/migration-manager.js +282 -0
  38. package/dist/migration-manager.js.map +1 -0
  39. package/dist/queries/minimal-connections.d.ts +31 -0
  40. package/dist/queries/minimal-connections.d.ts.map +1 -0
  41. package/dist/queries/minimal-connections.js +143 -0
  42. package/dist/queries/minimal-connections.js.map +1 -0
  43. package/dist/schema.ts +340 -0
  44. package/dist/seed.d.ts +8 -0
  45. package/dist/seed.d.ts.map +1 -0
  46. package/dist/seed.js +40 -0
  47. package/dist/seed.js.map +1 -0
  48. package/dist/types/index.d.ts +7 -0
  49. package/dist/types/index.d.ts.map +1 -0
  50. package/dist/types/index.js +23 -0
  51. package/dist/types/index.js.map +1 -0
  52. package/dist/types/types.d.ts +77 -0
  53. package/dist/types/types.d.ts.map +1 -0
  54. package/dist/types/types.js +3 -0
  55. package/dist/types/types.js.map +1 -0
  56. package/dist/utils/authenticated-user-operations.d.ts +110 -0
  57. package/dist/utils/authenticated-user-operations.d.ts.map +1 -0
  58. package/dist/utils/authenticated-user-operations.js +292 -0
  59. package/dist/utils/authenticated-user-operations.js.map +1 -0
  60. package/dist/utils/authentication-operations.d.ts +48 -0
  61. package/dist/utils/authentication-operations.d.ts.map +1 -0
  62. package/dist/utils/authentication-operations.js +172 -0
  63. package/dist/utils/authentication-operations.js.map +1 -0
  64. package/dist/utils/company-mapping-job-operations.d.ts +103 -0
  65. package/dist/utils/company-mapping-job-operations.d.ts.map +1 -0
  66. package/dist/utils/company-mapping-job-operations.js +413 -0
  67. package/dist/utils/company-mapping-job-operations.js.map +1 -0
  68. package/dist/utils/company-sheet-upload-operations.d.ts +53 -0
  69. package/dist/utils/company-sheet-upload-operations.d.ts.map +1 -0
  70. package/dist/utils/company-sheet-upload-operations.js +135 -0
  71. package/dist/utils/company-sheet-upload-operations.js.map +1 -0
  72. package/dist/utils/contact-operations.d.ts +70 -0
  73. package/dist/utils/contact-operations.d.ts.map +1 -0
  74. package/dist/utils/contact-operations.js +294 -0
  75. package/dist/utils/contact-operations.js.map +1 -0
  76. package/dist/utils/forager-linkedin-operations.d.ts +74 -0
  77. package/dist/utils/forager-linkedin-operations.d.ts.map +1 -0
  78. package/dist/utils/forager-linkedin-operations.js +778 -0
  79. package/dist/utils/forager-linkedin-operations.js.map +1 -0
  80. package/dist/utils/ghost-genius-linkedin-operations.d.ts +23 -0
  81. package/dist/utils/ghost-genius-linkedin-operations.d.ts.map +1 -0
  82. package/dist/utils/ghost-genius-linkedin-operations.js +282 -0
  83. package/dist/utils/ghost-genius-linkedin-operations.js.map +1 -0
  84. package/dist/utils/index.d.ts +29 -0
  85. package/dist/utils/index.d.ts.map +1 -0
  86. package/dist/utils/index.js +77 -0
  87. package/dist/utils/index.js.map +1 -0
  88. package/dist/utils/introduction-request-operations.d.ts +160 -0
  89. package/dist/utils/introduction-request-operations.d.ts.map +1 -0
  90. package/dist/utils/introduction-request-operations.js +492 -0
  91. package/dist/utils/introduction-request-operations.js.map +1 -0
  92. package/dist/utils/invitation-operations.d.ts +141 -0
  93. package/dist/utils/invitation-operations.d.ts.map +1 -0
  94. package/dist/utils/invitation-operations.js +749 -0
  95. package/dist/utils/invitation-operations.js.map +1 -0
  96. package/dist/utils/linkedin-account-operations.d.ts +45 -0
  97. package/dist/utils/linkedin-account-operations.d.ts.map +1 -0
  98. package/dist/utils/linkedin-account-operations.js +279 -0
  99. package/dist/utils/linkedin-account-operations.js.map +1 -0
  100. package/dist/utils/linkedin-account-relationship-operations.d.ts +77 -0
  101. package/dist/utils/linkedin-account-relationship-operations.d.ts.map +1 -0
  102. package/dist/utils/linkedin-account-relationship-operations.js +274 -0
  103. package/dist/utils/linkedin-account-relationship-operations.js.map +1 -0
  104. package/dist/utils/linkedin-data-operations.d.ts +102 -0
  105. package/dist/utils/linkedin-data-operations.d.ts.map +1 -0
  106. package/dist/utils/linkedin-data-operations.js +613 -0
  107. package/dist/utils/linkedin-data-operations.js.map +1 -0
  108. package/dist/utils/linkedin-identifier-utils.d.ts +31 -0
  109. package/dist/utils/linkedin-identifier-utils.d.ts.map +1 -0
  110. package/dist/utils/linkedin-identifier-utils.js +63 -0
  111. package/dist/utils/linkedin-identifier-utils.js.map +1 -0
  112. package/dist/utils/linkedin-profile-cache.d.ts +131 -0
  113. package/dist/utils/linkedin-profile-cache.d.ts.map +1 -0
  114. package/dist/utils/linkedin-profile-cache.js +418 -0
  115. package/dist/utils/linkedin-profile-cache.js.map +1 -0
  116. package/dist/utils/llm-inference-job-operations.d.ts +116 -0
  117. package/dist/utils/llm-inference-job-operations.d.ts.map +1 -0
  118. package/dist/utils/llm-inference-job-operations.js +267 -0
  119. package/dist/utils/llm-inference-job-operations.js.map +1 -0
  120. package/dist/utils/mapping-job-operations.d.ts +272 -0
  121. package/dist/utils/mapping-job-operations.d.ts.map +1 -0
  122. package/dist/utils/mapping-job-operations.js +833 -0
  123. package/dist/utils/mapping-job-operations.js.map +1 -0
  124. package/dist/utils/mapping-operations.d.ts +80 -0
  125. package/dist/utils/mapping-operations.d.ts.map +1 -0
  126. package/dist/utils/mapping-operations.js +318 -0
  127. package/dist/utils/mapping-operations.js.map +1 -0
  128. package/dist/utils/on-demand-mapping-operations.d.ts +199 -0
  129. package/dist/utils/on-demand-mapping-operations.d.ts.map +1 -0
  130. package/dist/utils/on-demand-mapping-operations.js +728 -0
  131. package/dist/utils/on-demand-mapping-operations.js.map +1 -0
  132. package/dist/utils/onboarding-operations.d.ts +53 -0
  133. package/dist/utils/onboarding-operations.d.ts.map +1 -0
  134. package/dist/utils/onboarding-operations.js +223 -0
  135. package/dist/utils/onboarding-operations.js.map +1 -0
  136. package/dist/utils/organization-assignment-job-operations.d.ts +258 -0
  137. package/dist/utils/organization-assignment-job-operations.d.ts.map +1 -0
  138. package/dist/utils/organization-assignment-job-operations.js +881 -0
  139. package/dist/utils/organization-assignment-job-operations.js.map +1 -0
  140. package/dist/utils/organization-assignment-operations.d.ts +59 -0
  141. package/dist/utils/organization-assignment-operations.d.ts.map +1 -0
  142. package/dist/utils/organization-assignment-operations.js +130 -0
  143. package/dist/utils/organization-assignment-operations.js.map +1 -0
  144. package/dist/utils/organization-operations.d.ts +284 -0
  145. package/dist/utils/organization-operations.d.ts.map +1 -0
  146. package/dist/utils/organization-operations.js +1030 -0
  147. package/dist/utils/organization-operations.js.map +1 -0
  148. package/dist/utils/organization-relationship-operations.d.ts +79 -0
  149. package/dist/utils/organization-relationship-operations.d.ts.map +1 -0
  150. package/dist/utils/organization-relationship-operations.js +294 -0
  151. package/dist/utils/organization-relationship-operations.js.map +1 -0
  152. package/dist/utils/quota-operations.d.ts +107 -0
  153. package/dist/utils/quota-operations.d.ts.map +1 -0
  154. package/dist/utils/quota-operations.js +692 -0
  155. package/dist/utils/quota-operations.js.map +1 -0
  156. package/dist/utils/recursive-mapping-job-operations.d.ts +42 -0
  157. package/dist/utils/recursive-mapping-job-operations.d.ts.map +1 -0
  158. package/dist/utils/recursive-mapping-job-operations.js +169 -0
  159. package/dist/utils/recursive-mapping-job-operations.js.map +1 -0
  160. package/dist/utils/relationship-operations.d.ts +130 -0
  161. package/dist/utils/relationship-operations.d.ts.map +1 -0
  162. package/dist/utils/relationship-operations.js +329 -0
  163. package/dist/utils/relationship-operations.js.map +1 -0
  164. package/dist/utils/sales-pipeline-operations.d.ts +163 -0
  165. package/dist/utils/sales-pipeline-operations.d.ts.map +1 -0
  166. package/dist/utils/sales-pipeline-operations.js +725 -0
  167. package/dist/utils/sales-pipeline-operations.js.map +1 -0
  168. package/dist/utils/skills-operations.d.ts +117 -0
  169. package/dist/utils/skills-operations.d.ts.map +1 -0
  170. package/dist/utils/skills-operations.js +487 -0
  171. package/dist/utils/skills-operations.js.map +1 -0
  172. package/dist/utils/subscription-operations.d.ts +123 -0
  173. package/dist/utils/subscription-operations.d.ts.map +1 -0
  174. package/dist/utils/subscription-operations.js +391 -0
  175. package/dist/utils/subscription-operations.js.map +1 -0
  176. package/dist/utils/unipile-account-operations.d.ts +96 -0
  177. package/dist/utils/unipile-account-operations.d.ts.map +1 -0
  178. package/dist/utils/unipile-account-operations.js +255 -0
  179. package/dist/utils/unipile-account-operations.js.map +1 -0
  180. package/dist/utils/user-industry-operations.d.ts +80 -0
  181. package/dist/utils/user-industry-operations.d.ts.map +1 -0
  182. package/dist/utils/user-industry-operations.js +237 -0
  183. package/dist/utils/user-industry-operations.js.map +1 -0
  184. package/dist/utils/user-operations.d.ts +87 -0
  185. package/dist/utils/user-operations.d.ts.map +1 -0
  186. package/dist/utils/user-operations.js +212 -0
  187. package/dist/utils/user-operations.js.map +1 -0
  188. package/package.json +98 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * =============================================================================
3
+ * SUBSCRIPTION OPERATIONS
4
+ * =============================================================================
5
+ * Database operations for managing Stripe subscriptions
6
+ *
7
+ * This module provides reusable functions for subscription management,
8
+ * ensuring all subscription-related database operations are centralized
9
+ * and reusable across Lambda functions.
10
+ */
11
+ import { Pool, PoolClient } from "pg";
12
+ type Database = Pool | PoolClient | any;
13
+ /**
14
+ * Subscription record interface
15
+ */
16
+ export interface Subscription {
17
+ id: number;
18
+ organizationId: number;
19
+ stripeCustomerId: string;
20
+ stripeSubscriptionId: string;
21
+ stripePriceId: string;
22
+ status: string;
23
+ currentPeriodStart: Date;
24
+ currentPeriodEnd: Date;
25
+ trialStart: Date | null;
26
+ trialEnd: Date | null;
27
+ canceledAt: Date | null;
28
+ metadata: Record<string, any>;
29
+ createdAt: Date;
30
+ updatedAt: Date;
31
+ }
32
+ /**
33
+ * Create or update a subscription record
34
+ *
35
+ * This function ensures one subscription per organization by using ON CONFLICT (organization_id).
36
+ * When a new subscription is created for an organization, it replaces the existing subscription record.
37
+ *
38
+ * @param db - Database instance
39
+ * @param subscriptionData - Subscription data
40
+ * @returns Created or updated subscription
41
+ */
42
+ export declare function upsertSubscription(db: Database, subscriptionData: {
43
+ organizationId: number;
44
+ stripeCustomerId: string;
45
+ stripeSubscriptionId: string;
46
+ stripePriceId: string;
47
+ status: string;
48
+ currentPeriodStart: Date;
49
+ currentPeriodEnd: Date;
50
+ trialStart?: Date | null;
51
+ trialEnd?: Date | null;
52
+ canceledAt?: Date | null;
53
+ metadata?: Record<string, any>;
54
+ }): Promise<Subscription>;
55
+ /**
56
+ * Get subscription by Stripe subscription ID
57
+ *
58
+ * @param db - Database instance
59
+ * @param stripeSubscriptionId - Stripe subscription ID
60
+ * @returns Subscription or null if not found
61
+ */
62
+ export declare function getSubscriptionByStripeId(db: Database, stripeSubscriptionId: string): Promise<Subscription | null>;
63
+ /**
64
+ * Get subscription by organization ID
65
+ *
66
+ * @param db - Database instance
67
+ * @param organizationId - Organization ID
68
+ * @returns Subscription or null if not found
69
+ */
70
+ export declare function getSubscriptionByOrganizationId(db: Database, organizationId: number): Promise<Subscription | null>;
71
+ /**
72
+ * Check if a Stripe event has already been processed (idempotency check)
73
+ *
74
+ * @param db - Database instance
75
+ * @param stripeEventId - Stripe event ID
76
+ * @returns true if event has been processed, false otherwise
77
+ */
78
+ export declare function isStripeEventProcessed(db: Database, stripeEventId: string): Promise<boolean>;
79
+ /**
80
+ * Update subscription status and dates
81
+ *
82
+ * @param db - Database instance
83
+ * @param stripeSubscriptionId - Stripe subscription ID
84
+ * @param updateData - Data to update
85
+ * @returns Updated subscription or null if not found
86
+ */
87
+ export declare function updateSubscription(db: Database, stripeSubscriptionId: string, updateData: {
88
+ status?: string;
89
+ currentPeriodStart?: Date;
90
+ currentPeriodEnd?: Date;
91
+ trialStart?: Date | null;
92
+ trialEnd?: Date | null;
93
+ canceledAt?: Date | null;
94
+ metadata?: Record<string, any>;
95
+ }): Promise<Subscription | null>;
96
+ /**
97
+ * Check if an organization has ever had a subscription (regardless of trial or status)
98
+ * This is used to prevent free trial abuse (users canceling and resubscribing to get another free trial)
99
+ *
100
+ * Business Logic: If an organization has ANY previous subscription (even if canceled, even if no trial),
101
+ * they should not get a free trial on resubscription. The free trial is only for first-time subscribers.
102
+ *
103
+ * IMPORTANT: Since subscriptions use ON CONFLICT (organization_id), new subscriptions overwrite old ones.
104
+ * However, the existence of ANY subscription record (regardless of status) indicates the organization
105
+ * has already used their "first subscription" opportunity.
106
+ *
107
+ * @param db - Database instance
108
+ * @param organizationId - Organization ID
109
+ * @returns true if organization has ever had any subscription, false otherwise
110
+ */
111
+ export declare function hasOrganizationUsedFreeTrial(db: Database, organizationId: number): Promise<boolean>;
112
+ /**
113
+ * Check if a user has ever had a subscription with a free trial period (across all organizations)
114
+ * This is used to prevent free trial abuse (users canceling during trial and resubscribing)
115
+ * The check is per user, not per organization - once a user has used a free trial, they can't get another one
116
+ *
117
+ * @param db - Database instance
118
+ * @param userId - User ID
119
+ * @returns true if user has ever had a subscription with a trial period, false otherwise
120
+ */
121
+ export declare function hasUserUsedFreeTrial(db: Database, userId: number): Promise<boolean>;
122
+ export {};
123
+ //# sourceMappingURL=subscription-operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription-operations.d.ts","sourceRoot":"","sources":["../../src/utils/subscription-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,391 @@
1
+ "use strict";
2
+ /**
3
+ * =============================================================================
4
+ * SUBSCRIPTION OPERATIONS
5
+ * =============================================================================
6
+ * Database operations for managing Stripe subscriptions
7
+ *
8
+ * This module provides reusable functions for subscription management,
9
+ * ensuring all subscription-related database operations are centralized
10
+ * and reusable across Lambda functions.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.upsertSubscription = upsertSubscription;
14
+ exports.getSubscriptionByStripeId = getSubscriptionByStripeId;
15
+ exports.getSubscriptionByOrganizationId = getSubscriptionByOrganizationId;
16
+ exports.isStripeEventProcessed = isStripeEventProcessed;
17
+ exports.updateSubscription = updateSubscription;
18
+ exports.hasOrganizationUsedFreeTrial = hasOrganizationUsedFreeTrial;
19
+ exports.hasUserUsedFreeTrial = hasUserUsedFreeTrial;
20
+ const pg_client_1 = require("../lib/pg-client");
21
+ const db_1 = require("../lib/db");
22
+ /**
23
+ * Create or update a subscription record
24
+ *
25
+ * This function ensures one subscription per organization by using ON CONFLICT (organization_id).
26
+ * When a new subscription is created for an organization, it replaces the existing subscription record.
27
+ *
28
+ * @param db - Database instance
29
+ * @param subscriptionData - Subscription data
30
+ * @returns Created or updated subscription
31
+ */
32
+ async function upsertSubscription(db, subscriptionData) {
33
+ (0, db_1.debugLogDbOperation)("upsert", "subscriptions", { subscriptionData });
34
+ // Note: We no longer need to preserve trial history in metadata because
35
+ // hasOrganizationUsedFreeTrial now simply checks if ANY subscription exists for the organization.
36
+ // The existence of a subscription record (regardless of status or trial) indicates
37
+ // the organization has already used their "first subscription" opportunity.
38
+ const sql = `
39
+ INSERT INTO public.subscriptions (
40
+ organization_id,
41
+ stripe_customer_id,
42
+ stripe_subscription_id,
43
+ stripe_price_id,
44
+ status,
45
+ current_period_start,
46
+ current_period_end,
47
+ trial_start,
48
+ trial_end,
49
+ canceled_at,
50
+ metadata,
51
+ created_at,
52
+ updated_at
53
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, NOW(), NOW())
54
+ ON CONFLICT (organization_id) DO UPDATE SET
55
+ stripe_customer_id = EXCLUDED.stripe_customer_id,
56
+ stripe_subscription_id = EXCLUDED.stripe_subscription_id,
57
+ stripe_price_id = EXCLUDED.stripe_price_id,
58
+ status = EXCLUDED.status,
59
+ current_period_start = EXCLUDED.current_period_start,
60
+ current_period_end = EXCLUDED.current_period_end,
61
+ trial_start = EXCLUDED.trial_start,
62
+ trial_end = EXCLUDED.trial_end,
63
+ canceled_at = EXCLUDED.canceled_at,
64
+ metadata = EXCLUDED.metadata,
65
+ updated_at = NOW()
66
+ RETURNING
67
+ id,
68
+ organization_id,
69
+ stripe_customer_id,
70
+ stripe_subscription_id,
71
+ stripe_price_id,
72
+ status,
73
+ current_period_start,
74
+ current_period_end,
75
+ trial_start,
76
+ trial_end,
77
+ canceled_at,
78
+ metadata,
79
+ created_at,
80
+ updated_at
81
+ `;
82
+ const result = await (0, pg_client_1.queryOne)(db, sql, [
83
+ subscriptionData.organizationId,
84
+ subscriptionData.stripeCustomerId,
85
+ subscriptionData.stripeSubscriptionId,
86
+ subscriptionData.stripePriceId,
87
+ subscriptionData.status,
88
+ subscriptionData.currentPeriodStart,
89
+ subscriptionData.currentPeriodEnd,
90
+ subscriptionData.trialStart || null,
91
+ subscriptionData.trialEnd || null,
92
+ subscriptionData.canceledAt || null,
93
+ JSON.stringify(subscriptionData.metadata || {}),
94
+ ]);
95
+ if (!result) {
96
+ throw new Error("Failed to upsert subscription");
97
+ }
98
+ return {
99
+ id: result.id,
100
+ organizationId: result.organization_id,
101
+ stripeCustomerId: result.stripe_customer_id,
102
+ stripeSubscriptionId: result.stripe_subscription_id,
103
+ stripePriceId: result.stripe_price_id,
104
+ status: result.status,
105
+ currentPeriodStart: result.current_period_start,
106
+ currentPeriodEnd: result.current_period_end,
107
+ trialStart: result.trial_start,
108
+ trialEnd: result.trial_end,
109
+ canceledAt: result.canceled_at,
110
+ metadata: result.metadata,
111
+ createdAt: result.created_at,
112
+ updatedAt: result.updated_at,
113
+ };
114
+ }
115
+ /**
116
+ * Get subscription by Stripe subscription ID
117
+ *
118
+ * @param db - Database instance
119
+ * @param stripeSubscriptionId - Stripe subscription ID
120
+ * @returns Subscription or null if not found
121
+ */
122
+ async function getSubscriptionByStripeId(db, stripeSubscriptionId) {
123
+ (0, db_1.debugLogDbOperation)("select", "subscriptions", { stripeSubscriptionId });
124
+ const sql = `
125
+ SELECT
126
+ id,
127
+ organization_id,
128
+ stripe_customer_id,
129
+ stripe_subscription_id,
130
+ stripe_price_id,
131
+ status,
132
+ current_period_start,
133
+ current_period_end,
134
+ trial_start,
135
+ trial_end,
136
+ canceled_at,
137
+ metadata,
138
+ created_at,
139
+ updated_at
140
+ FROM public.subscriptions
141
+ WHERE stripe_subscription_id = $1
142
+ LIMIT 1
143
+ `;
144
+ const result = await (0, pg_client_1.queryOne)(db, sql, [stripeSubscriptionId]);
145
+ if (!result) {
146
+ return null;
147
+ }
148
+ return {
149
+ id: result.id,
150
+ organizationId: result.organization_id,
151
+ stripeCustomerId: result.stripe_customer_id,
152
+ stripeSubscriptionId: result.stripe_subscription_id,
153
+ stripePriceId: result.stripe_price_id,
154
+ status: result.status,
155
+ currentPeriodStart: result.current_period_start,
156
+ currentPeriodEnd: result.current_period_end,
157
+ trialStart: result.trial_start,
158
+ trialEnd: result.trial_end,
159
+ canceledAt: result.canceled_at,
160
+ metadata: result.metadata,
161
+ createdAt: result.created_at,
162
+ updatedAt: result.updated_at,
163
+ };
164
+ }
165
+ /**
166
+ * Get subscription by organization ID
167
+ *
168
+ * @param db - Database instance
169
+ * @param organizationId - Organization ID
170
+ * @returns Subscription or null if not found
171
+ */
172
+ async function getSubscriptionByOrganizationId(db, organizationId) {
173
+ (0, db_1.debugLogDbOperation)("select", "subscriptions", { organizationId });
174
+ const sql = `
175
+ SELECT
176
+ id,
177
+ organization_id,
178
+ stripe_customer_id,
179
+ stripe_subscription_id,
180
+ stripe_price_id,
181
+ status,
182
+ current_period_start,
183
+ current_period_end,
184
+ trial_start,
185
+ trial_end,
186
+ canceled_at,
187
+ metadata,
188
+ created_at,
189
+ updated_at
190
+ FROM public.subscriptions
191
+ WHERE organization_id = $1
192
+ ORDER BY created_at DESC
193
+ LIMIT 1
194
+ `;
195
+ const result = await (0, pg_client_1.queryOne)(db, sql, [organizationId]);
196
+ if (!result) {
197
+ return null;
198
+ }
199
+ return {
200
+ id: result.id,
201
+ organizationId: result.organization_id,
202
+ stripeCustomerId: result.stripe_customer_id,
203
+ stripeSubscriptionId: result.stripe_subscription_id,
204
+ stripePriceId: result.stripe_price_id,
205
+ status: result.status,
206
+ currentPeriodStart: result.current_period_start,
207
+ currentPeriodEnd: result.current_period_end,
208
+ trialStart: result.trial_start,
209
+ trialEnd: result.trial_end,
210
+ canceledAt: result.canceled_at,
211
+ metadata: result.metadata,
212
+ createdAt: result.created_at,
213
+ updatedAt: result.updated_at,
214
+ };
215
+ }
216
+ /**
217
+ * Check if a Stripe event has already been processed (idempotency check)
218
+ *
219
+ * @param db - Database instance
220
+ * @param stripeEventId - Stripe event ID
221
+ * @returns true if event has been processed, false otherwise
222
+ */
223
+ async function isStripeEventProcessed(db, stripeEventId) {
224
+ (0, db_1.debugLogDbOperation)("select", "subscriptions", { stripeEventId }, undefined, {
225
+ operation: "isStripeEventProcessed",
226
+ });
227
+ const sql = `
228
+ SELECT id
229
+ FROM public.subscriptions
230
+ WHERE metadata->>'stripe_event_id' = $1
231
+ LIMIT 1
232
+ `;
233
+ const result = await (0, pg_client_1.query)(db, sql, [stripeEventId]);
234
+ return result && result.length > 0;
235
+ }
236
+ /**
237
+ * Update subscription status and dates
238
+ *
239
+ * @param db - Database instance
240
+ * @param stripeSubscriptionId - Stripe subscription ID
241
+ * @param updateData - Data to update
242
+ * @returns Updated subscription or null if not found
243
+ */
244
+ async function updateSubscription(db, stripeSubscriptionId, updateData) {
245
+ (0, db_1.debugLogDbOperation)("update", "subscriptions", {
246
+ stripeSubscriptionId,
247
+ updateData,
248
+ });
249
+ const updates = [];
250
+ const params = [];
251
+ let paramIndex = 1;
252
+ if (updateData.status !== undefined) {
253
+ updates.push(`status = $${paramIndex}`);
254
+ params.push(updateData.status);
255
+ paramIndex++;
256
+ }
257
+ if (updateData.currentPeriodStart !== undefined) {
258
+ updates.push(`current_period_start = $${paramIndex}`);
259
+ params.push(updateData.currentPeriodStart);
260
+ paramIndex++;
261
+ }
262
+ if (updateData.currentPeriodEnd !== undefined) {
263
+ updates.push(`current_period_end = $${paramIndex}`);
264
+ params.push(updateData.currentPeriodEnd);
265
+ paramIndex++;
266
+ }
267
+ if (updateData.trialStart !== undefined) {
268
+ updates.push(`trial_start = $${paramIndex}`);
269
+ params.push(updateData.trialStart);
270
+ paramIndex++;
271
+ }
272
+ if (updateData.trialEnd !== undefined) {
273
+ updates.push(`trial_end = $${paramIndex}`);
274
+ params.push(updateData.trialEnd);
275
+ paramIndex++;
276
+ }
277
+ if (updateData.canceledAt !== undefined) {
278
+ updates.push(`canceled_at = $${paramIndex}`);
279
+ params.push(updateData.canceledAt);
280
+ paramIndex++;
281
+ }
282
+ if (updateData.metadata !== undefined) {
283
+ updates.push(`metadata = $${paramIndex}`);
284
+ params.push(JSON.stringify(updateData.metadata));
285
+ paramIndex++;
286
+ }
287
+ if (updates.length === 0) {
288
+ // Nothing to update, return existing subscription
289
+ return getSubscriptionByStripeId(db, stripeSubscriptionId);
290
+ }
291
+ updates.push(`updated_at = NOW()`);
292
+ params.push(stripeSubscriptionId);
293
+ const sql = `
294
+ UPDATE public.subscriptions
295
+ SET ${updates.join(", ")}
296
+ WHERE stripe_subscription_id = $${paramIndex}
297
+ RETURNING
298
+ id,
299
+ organization_id,
300
+ stripe_customer_id,
301
+ stripe_subscription_id,
302
+ stripe_price_id,
303
+ status,
304
+ current_period_start,
305
+ current_period_end,
306
+ trial_start,
307
+ trial_end,
308
+ canceled_at,
309
+ metadata,
310
+ created_at,
311
+ updated_at
312
+ `;
313
+ const result = await (0, pg_client_1.queryOne)(db, sql, params);
314
+ if (!result) {
315
+ return null;
316
+ }
317
+ return {
318
+ id: result.id,
319
+ organizationId: result.organization_id,
320
+ stripeCustomerId: result.stripe_customer_id,
321
+ stripeSubscriptionId: result.stripe_subscription_id,
322
+ stripePriceId: result.stripe_price_id,
323
+ status: result.status,
324
+ currentPeriodStart: result.current_period_start,
325
+ currentPeriodEnd: result.current_period_end,
326
+ trialStart: result.trial_start,
327
+ trialEnd: result.trial_end,
328
+ canceledAt: result.canceled_at,
329
+ metadata: result.metadata,
330
+ createdAt: result.created_at,
331
+ updatedAt: result.updated_at,
332
+ };
333
+ }
334
+ /**
335
+ * Check if an organization has ever had a subscription (regardless of trial or status)
336
+ * This is used to prevent free trial abuse (users canceling and resubscribing to get another free trial)
337
+ *
338
+ * Business Logic: If an organization has ANY previous subscription (even if canceled, even if no trial),
339
+ * they should not get a free trial on resubscription. The free trial is only for first-time subscribers.
340
+ *
341
+ * IMPORTANT: Since subscriptions use ON CONFLICT (organization_id), new subscriptions overwrite old ones.
342
+ * However, the existence of ANY subscription record (regardless of status) indicates the organization
343
+ * has already used their "first subscription" opportunity.
344
+ *
345
+ * @param db - Database instance
346
+ * @param organizationId - Organization ID
347
+ * @returns true if organization has ever had any subscription, false otherwise
348
+ */
349
+ async function hasOrganizationUsedFreeTrial(db, organizationId) {
350
+ (0, db_1.debugLogDbOperation)("select", "subscriptions", { organizationId }, undefined, {
351
+ operation: "hasOrganizationUsedFreeTrial",
352
+ });
353
+ const sql = `
354
+ SELECT id
355
+ FROM public.subscriptions
356
+ WHERE organization_id = $1
357
+ LIMIT 1
358
+ `;
359
+ const result = await (0, pg_client_1.query)(db, sql, [organizationId]);
360
+ return result && result.length > 0;
361
+ }
362
+ /**
363
+ * Check if a user has ever had a subscription with a free trial period (across all organizations)
364
+ * This is used to prevent free trial abuse (users canceling during trial and resubscribing)
365
+ * The check is per user, not per organization - once a user has used a free trial, they can't get another one
366
+ *
367
+ * @param db - Database instance
368
+ * @param userId - User ID
369
+ * @returns true if user has ever had a subscription with a trial period, false otherwise
370
+ */
371
+ async function hasUserUsedFreeTrial(db, userId) {
372
+ (0, db_1.debugLogDbOperation)("select", "subscriptions", { userId }, undefined, {
373
+ operation: "hasUserUsedFreeTrial",
374
+ });
375
+ // Query for user_id as integer in JSONB metadata
376
+ // user_id should always be stored as an integer, but we handle both for safety
377
+ const sql = `
378
+ SELECT id
379
+ FROM public.subscriptions
380
+ WHERE (
381
+ (metadata->'user_id')::numeric = $1::numeric
382
+ OR metadata->>'user_id' = $1::text
383
+ )
384
+ AND trial_start IS NOT NULL
385
+ AND trial_end IS NOT NULL
386
+ LIMIT 1
387
+ `;
388
+ const result = await (0, pg_client_1.query)(db, sql, [userId]);
389
+ return result && result.length > 0;
390
+ }
391
+ //# sourceMappingURL=subscription-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription-operations.js","sourceRoot":"","sources":["../../src/utils/subscription-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * =============================================================================
3
+ * UNIPILE ACCOUNT OPERATIONS UTILITIES
4
+ * =============================================================================
5
+ * Utility functions for creating and managing Unipile social media accounts
6
+ */
7
+ import { Pool, PoolClient } from 'pg';
8
+ import type { Database } from "../lib/db";
9
+ import { type UnipileAccount, type SocialPlatformType, type User } from "../lib/schema";
10
+ export interface UnipileAccountWithUser extends UnipileAccount {
11
+ user: User;
12
+ }
13
+ export interface CreateUnipileAccountData {
14
+ linkedinAccountId: number;
15
+ unipileAccountId: string;
16
+ isActive?: boolean;
17
+ connectedAt?: Date;
18
+ lastSyncedAt?: Date;
19
+ metadata?: Record<string, any>;
20
+ }
21
+ export interface UpdateUnipileAccountData {
22
+ isActive?: boolean;
23
+ lastSyncedAt?: Date;
24
+ metadata?: Record<string, any>;
25
+ }
26
+ export interface UserWithUnipileAccounts extends User {
27
+ unipileAccounts: UnipileAccount[];
28
+ }
29
+ export interface UnipileAccountSearchCriteria {
30
+ platformTypes?: SocialPlatformType[];
31
+ isActive?: boolean;
32
+ userIds?: number[];
33
+ unipileAccountIds?: string[];
34
+ lastSyncedBefore?: Date;
35
+ lastSyncedAfter?: Date;
36
+ }
37
+ export interface PlatformSummary {
38
+ platform: SocialPlatformType;
39
+ totalAccounts: number;
40
+ activeAccounts: number;
41
+ inactiveAccounts: number;
42
+ recentlyConnected: number;
43
+ needsSync: number;
44
+ }
45
+ /**
46
+ * Creates a new Unipile account record
47
+ *
48
+ * @param data - Unipile account creation data
49
+ * @returns Promise resolving to the created account
50
+ */
51
+ export declare function createUnipileAccount(db: Database | Pool | PoolClient, data: CreateUnipileAccountData): Promise<UnipileAccount>;
52
+ /**
53
+ * Creates or updates a Unipile account (upsert)
54
+ *
55
+ * @param data - Unipile account data
56
+ * @returns Promise resolving to the created or updated account
57
+ */
58
+ export declare function upsertUnipileAccount(db: Database | Pool | PoolClient, data: CreateUnipileAccountData): Promise<UnipileAccount>;
59
+ /**
60
+ * Simple social account status map for quick UI updates
61
+ */
62
+ export interface LinkedAccountStatusMap {
63
+ linkedin: boolean;
64
+ whatsapp: boolean;
65
+ twitter: boolean;
66
+ instagram: boolean;
67
+ facebook: boolean;
68
+ }
69
+ /**
70
+ * Gets a simple boolean map of which social accounts are linked for a user
71
+ * Optimized for frontend UI to show connection status without sensitive data
72
+ *
73
+ * @param cognitoUserId - The Cognito user ID
74
+ * @param activeOnly - Whether to only consider active accounts (default: true)
75
+ * @returns Promise resolving to a simple boolean map of linked platforms
76
+ */
77
+ export declare function getUserLinkedAccountStatus(db: Database | Pool | PoolClient, cognitoUserId: string, activeOnly?: boolean): Promise<LinkedAccountStatusMap>;
78
+ /**
79
+ * Checks if a LinkedIn account with the given ACoA identifier is already connected
80
+ * to a different user via an active Unipile account
81
+ *
82
+ * @param db - Database connection
83
+ * @param linkedinIdentifierAcoa - The ACoA identifier to check
84
+ * @param currentUserId - The current user ID (to exclude from the check)
85
+ * @returns Promise resolving to the existing account's user ID if found, null otherwise
86
+ */
87
+ export declare function checkAcoaAlreadyConnectedToDifferentUser(db: Database | Pool | PoolClient, linkedinIdentifierAcoa: string, currentUserId: number): Promise<number | null>;
88
+ /**
89
+ * Batch updates last synced time for multiple accounts
90
+ *
91
+ * @param accountIds - Array of account IDs to update
92
+ * @param syncTime - Optional: specific sync time (defaults to now)
93
+ * @returns Promise resolving to number of updated accounts
94
+ */
95
+ export declare function batchMarkAccountsAsSynced(db: Database | Pool | PoolClient, accountIds: number[], syncTime?: Date): Promise<number>;
96
+ //# sourceMappingURL=unipile-account-operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unipile-account-operations.d.ts","sourceRoot":"","sources":["../../src/utils/unipile-account-operations.ts"],"names":[],"mappings":"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"}