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,492 @@
1
+ "use strict";
2
+ /**
3
+ * =============================================================================
4
+ * INTRODUCTION REQUEST OPERATIONS
5
+ * =============================================================================
6
+ *
7
+ * Database operations for managing introduction requests between users
8
+ *
9
+ * @author Eggi.ai Team
10
+ * @version 1.0.0 - Initial implementation
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.createIntroductionRequest = createIntroductionRequest;
14
+ exports.getIntroductionRequestsByUser = getIntroductionRequestsByUser;
15
+ exports.getIntroductionRequestById = getIntroductionRequestById;
16
+ exports.hasExistingIntroductionRequest = hasExistingIntroductionRequest;
17
+ exports.updateIntroductionRequestStatus = updateIntroductionRequestStatus;
18
+ exports.markIntroductionRequestAsSeen = markIntroductionRequestAsSeen;
19
+ exports.getReceivedIntroductionRequests = getReceivedIntroductionRequests;
20
+ exports.getSentIntroductionRequests = getSentIntroductionRequests;
21
+ exports.markReceivedIntroductionRequestsAsSeen = markReceivedIntroductionRequestsAsSeen;
22
+ const pg_client_1 = require("../lib/pg-client");
23
+ /**
24
+ * Create a new introduction request
25
+ *
26
+ * @param db - Database instance
27
+ * @param params - Introduction request parameters
28
+ * @returns Created introduction request
29
+ * @throws Error if relationship ID or requester user ID doesn't exist
30
+ * @throws Error if unique constraint is violated (duplicate request)
31
+ */
32
+ async function createIntroductionRequest(db, params) {
33
+ try {
34
+ const sql = `
35
+ INSERT INTO public.introduction_requests (by_user_id, to_user_id, to_relationship_id, contact_info_id, message, status, was_seen_by_receiver, created_at)
36
+ VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
37
+ RETURNING id, by_user_id, to_user_id, to_relationship_id, contact_info_id, message, status, was_seen_by_receiver, created_at
38
+ `;
39
+ const result = await (0, pg_client_1.queryOne)(db, sql, [
40
+ params.byUserId,
41
+ params.toUserId,
42
+ params.toRelationshipId,
43
+ params.contactInfoId || null,
44
+ params.message || null,
45
+ params.status || "pending",
46
+ false,
47
+ ]);
48
+ if (!result) {
49
+ throw new Error("Failed to create introduction request");
50
+ }
51
+ return {
52
+ id: result.id,
53
+ createdAt: result.created_at,
54
+ byUserId: result.by_user_id,
55
+ toRelationshipId: result.to_relationship_id,
56
+ contactInfoId: result.contact_info_id,
57
+ message: result.message,
58
+ status: result.status,
59
+ wasSeenByReceiver: result.was_seen_by_receiver,
60
+ toUserId: result.to_user_id,
61
+ };
62
+ }
63
+ catch (error) {
64
+ // Handle unique constraint violation
65
+ if (error.code === "23505") {
66
+ throw new Error("An introduction request for this relationship already exists");
67
+ }
68
+ // Handle foreign key violation
69
+ if (error.code === "23503") {
70
+ throw new Error("Invalid user ID or relationship ID - referenced record not found");
71
+ }
72
+ throw error;
73
+ }
74
+ }
75
+ /**
76
+ * Get introduction requests sent by a user
77
+ *
78
+ * @param db - Database instance
79
+ * @param userId - User ID who sent the requests
80
+ * @param limit - Maximum number of requests to return (default: 50)
81
+ * @returns Array of introduction requests
82
+ */
83
+ async function getIntroductionRequestsByUser(db, userId, limit = 50) {
84
+ const sql = `
85
+ SELECT id, by_user_id, to_user_id, to_relationship_id, contact_info_id, message, status, was_seen_by_receiver, created_at
86
+ FROM public.introduction_requests
87
+ WHERE by_user_id = $1
88
+ ORDER BY created_at DESC
89
+ LIMIT $2
90
+ `;
91
+ const results = await (0, pg_client_1.query)(db, sql, [userId, limit]);
92
+ return results.map(r => ({
93
+ id: r.id,
94
+ createdAt: r.created_at,
95
+ byUserId: r.by_user_id,
96
+ toRelationshipId: r.to_relationship_id,
97
+ contactInfoId: r.contact_info_id,
98
+ message: r.message,
99
+ status: r.status,
100
+ wasSeenByReceiver: r.was_seen_by_receiver,
101
+ toUserId: r.to_user_id,
102
+ }));
103
+ }
104
+ /**
105
+ * Get a specific introduction request by ID
106
+ *
107
+ * @param db - Database instance
108
+ * @param requestId - Introduction request ID
109
+ * @returns Introduction request or null if not found
110
+ */
111
+ async function getIntroductionRequestById(db, requestId) {
112
+ const sql = `
113
+ SELECT id, by_user_id, to_user_id, to_relationship_id, contact_info_id, message, status, was_seen_by_receiver, created_at
114
+ FROM public.introduction_requests
115
+ WHERE id = $1
116
+ LIMIT 1
117
+ `;
118
+ const result = await (0, pg_client_1.queryOne)(db, sql, [requestId]);
119
+ if (!result)
120
+ return null;
121
+ return {
122
+ id: result.id,
123
+ createdAt: result.created_at,
124
+ byUserId: result.by_user_id,
125
+ toRelationshipId: result.to_relationship_id,
126
+ contactInfoId: result.contact_info_id,
127
+ message: result.message,
128
+ status: result.status,
129
+ wasSeenByReceiver: result.was_seen_by_receiver,
130
+ toUserId: result.to_user_id,
131
+ };
132
+ }
133
+ /**
134
+ * Check if an introduction request already exists for a relationship
135
+ *
136
+ * @param db - Database instance
137
+ * @param byUserId - User ID making the request
138
+ * @param toRelationshipId - Relationship ID
139
+ * @returns True if request exists, false otherwise
140
+ */
141
+ async function hasExistingIntroductionRequest(db, byUserId, toRelationshipId) {
142
+ const sql = `
143
+ SELECT id
144
+ FROM public.introduction_requests
145
+ WHERE by_user_id = $1 AND to_relationship_id = $2
146
+ LIMIT 1
147
+ `;
148
+ const result = await (0, pg_client_1.queryOne)(db, sql, [
149
+ byUserId,
150
+ toRelationshipId,
151
+ ]);
152
+ return !!result;
153
+ }
154
+ /**
155
+ * Update introduction request status
156
+ *
157
+ * @param db - Database instance
158
+ * @param requestId - Introduction request ID
159
+ * @param status - New status
160
+ * @returns Updated introduction request
161
+ */
162
+ async function updateIntroductionRequestStatus(db, requestId, status) {
163
+ const sql = `
164
+ UPDATE public.introduction_requests
165
+ SET status = $1
166
+ WHERE id = $2
167
+ RETURNING id, by_user_id, to_user_id, to_relationship_id, contact_info_id, message, status, was_seen_by_receiver, created_at
168
+ `;
169
+ const result = await (0, pg_client_1.queryOne)(db, sql, [status, requestId]);
170
+ if (!result) {
171
+ throw new Error(`Introduction request ${requestId} not found`);
172
+ }
173
+ return {
174
+ id: result.id,
175
+ createdAt: result.created_at,
176
+ byUserId: result.by_user_id,
177
+ toRelationshipId: result.to_relationship_id,
178
+ contactInfoId: result.contact_info_id,
179
+ message: result.message,
180
+ status: result.status,
181
+ wasSeenByReceiver: result.was_seen_by_receiver,
182
+ toUserId: result.to_user_id,
183
+ };
184
+ }
185
+ /**
186
+ * Mark introduction request as seen
187
+ *
188
+ * @param db - Database instance
189
+ * @param requestId - Introduction request ID
190
+ * @returns Updated introduction request
191
+ */
192
+ async function markIntroductionRequestAsSeen(db, requestId) {
193
+ const sql = `
194
+ UPDATE public.introduction_requests
195
+ SET was_seen_by_receiver = true
196
+ WHERE id = $1
197
+ RETURNING id, by_user_id, to_user_id, to_relationship_id, contact_info_id, message, status, was_seen_by_receiver, created_at
198
+ `;
199
+ const result = await (0, pg_client_1.queryOne)(db, sql, [requestId]);
200
+ if (!result) {
201
+ throw new Error(`Introduction request ${requestId} not found`);
202
+ }
203
+ return {
204
+ id: result.id,
205
+ createdAt: result.created_at,
206
+ byUserId: result.by_user_id,
207
+ toRelationshipId: result.to_relationship_id,
208
+ contactInfoId: result.contact_info_id,
209
+ message: result.message,
210
+ status: result.status,
211
+ wasSeenByReceiver: result.was_seen_by_receiver,
212
+ toUserId: result.to_user_id,
213
+ };
214
+ }
215
+ /**
216
+ * Get introduction requests received by a user (where user is the contributor)
217
+ * Filtered by organization - only returns requests for relationships assigned to the organization
218
+ *
219
+ * @param db - Database instance
220
+ * @param userId - User ID who is the contributor
221
+ * @param organizationId - Organization ID to filter by
222
+ * @returns Array of detailed introduction requests
223
+ */
224
+ async function getReceivedIntroductionRequests(db, userId, organizationId) {
225
+ // First, get the user's LinkedIn account
226
+ const linkedinAccountSql = `SELECT id FROM linkedin.accounts WHERE user_id = $1 LIMIT 1`;
227
+ const userLinkedInAccount = await (0, pg_client_1.queryOne)(db, linkedinAccountSql, [userId]);
228
+ if (!userLinkedInAccount) {
229
+ return [];
230
+ }
231
+ // Query for introduction requests where user is the contributor
232
+ // Filter by organization through organization_user_relationships_assignments
233
+ const sql = `
234
+ SELECT
235
+ ir.id,
236
+ ir.created_at,
237
+ ir.to_relationship_id,
238
+ oura.id as organization_user_relationship_assignment_id,
239
+ ir.message,
240
+ ir.status,
241
+ ir.was_seen_by_receiver,
242
+ requester_user.id as requester_id,
243
+ requester_user.given_name as requester_given_name,
244
+ requester_user.family_name as requester_family_name,
245
+ COALESCE(requester_account.profile_image_cloudfront_url, requester_account.profile_picture_url) as requester_profile_picture_url,
246
+ target_user.id as target_id,
247
+ target_user.given_name as target_given_name,
248
+ target_user.family_name as target_family_name,
249
+ target_account.headline as target_headline,
250
+ COALESCE(target_account.profile_image_cloudfront_url, target_account.profile_picture_url) as target_profile_picture_url,
251
+ target_account.id as target_account_id
252
+ FROM public.introduction_requests ir
253
+ INNER JOIN linkedin.relationships lr ON ir.to_relationship_id = lr.id
254
+ INNER JOIN public.organization_user_relationships_assignments oura
255
+ ON oura.linkedin_relationship_id = lr.id AND oura.organization_id = $3
256
+ INNER JOIN public.users target_user ON ir.to_user_id = target_user.id
257
+ LEFT JOIN linkedin.accounts target_account ON target_user.id = target_account.user_id
258
+ INNER JOIN public.users requester_user ON ir.by_user_id = requester_user.id
259
+ LEFT JOIN linkedin.accounts requester_account ON requester_user.id = requester_account.user_id
260
+ WHERE (
261
+ lr.linkedin_account_id_a = $1 OR lr.linkedin_account_id_b = $1
262
+ )
263
+ AND ir.by_user_id != $2
264
+ ORDER BY ir.created_at DESC
265
+ `;
266
+ const results = await (0, pg_client_1.query)(db, sql, [userLinkedInAccount.id, userId, organizationId]);
267
+ // Fetch work experience for each target separately
268
+ const enrichedResults = await Promise.all(results.map(async (row) => {
269
+ let currentPosition = null;
270
+ let currentCompany = null;
271
+ if (row.target_account_id) {
272
+ const workExpSql = `
273
+ SELECT position, company
274
+ FROM linkedin.work_experience
275
+ WHERE linkedin_account_id = $1
276
+ ORDER BY
277
+ CASE WHEN end_date IS NULL THEN 0 ELSE 1 END,
278
+ start_date DESC
279
+ LIMIT 1
280
+ `;
281
+ const workExp = await (0, pg_client_1.queryOne)(db, workExpSql, [row.target_account_id]);
282
+ if (workExp) {
283
+ currentPosition = workExp.position;
284
+ currentCompany = workExp.company;
285
+ }
286
+ }
287
+ return {
288
+ id: row.id,
289
+ createdAt: row.created_at,
290
+ toRelationshipId: row.to_relationship_id,
291
+ organizationUserRelationshipAssignmentId: row.organization_user_relationship_assignment_id,
292
+ message: row.message,
293
+ status: row.status,
294
+ wasSeenByReceiver: row.was_seen_by_receiver,
295
+ requester: {
296
+ id: row.requester_id,
297
+ givenName: row.requester_given_name,
298
+ familyName: row.requester_family_name,
299
+ profilePictureUrl: row.requester_profile_picture_url,
300
+ },
301
+ target: {
302
+ id: row.target_id,
303
+ givenName: row.target_given_name,
304
+ familyName: row.target_family_name,
305
+ headline: row.target_headline,
306
+ currentPosition,
307
+ currentCompany,
308
+ profilePictureUrl: row.target_profile_picture_url,
309
+ },
310
+ };
311
+ }));
312
+ return enrichedResults;
313
+ }
314
+ /**
315
+ * Get introduction requests sent by a user (where user is the requester)
316
+ * Filtered by organization - only returns requests for relationships assigned to the organization
317
+ *
318
+ * @param db - Database instance
319
+ * @param userId - User ID who sent the requests
320
+ * @param organizationId - Organization ID to filter by
321
+ * @returns Array of detailed introduction requests with contributor info
322
+ */
323
+ async function getSentIntroductionRequests(db, userId, organizationId) {
324
+ const sql = `
325
+ SELECT
326
+ ir.id,
327
+ ir.created_at,
328
+ ir.to_relationship_id,
329
+ oura.id as organization_user_relationship_assignment_id,
330
+ ir.message,
331
+ ir.status,
332
+ ir.was_seen_by_receiver,
333
+ requester_user.id as requester_id,
334
+ requester_user.given_name as requester_given_name,
335
+ requester_user.family_name as requester_family_name,
336
+ COALESCE(requester_account.profile_image_cloudfront_url, requester_account.profile_picture_url) as requester_profile_picture_url,
337
+ target_user.id as target_id,
338
+ target_user.given_name as target_given_name,
339
+ target_user.family_name as target_family_name,
340
+ target_account.headline as target_headline,
341
+ COALESCE(target_account.profile_image_cloudfront_url, target_account.profile_picture_url) as target_profile_picture_url,
342
+ target_account.id as target_account_id,
343
+ account_a.id as account_a_id,
344
+ account_a.user_id as account_a_user_id,
345
+ account_b.id as account_b_id,
346
+ account_b.user_id as account_b_user_id
347
+ FROM public.introduction_requests ir
348
+ INNER JOIN linkedin.relationships lr ON ir.to_relationship_id = lr.id
349
+ INNER JOIN public.organization_user_relationships_assignments oura
350
+ ON oura.linkedin_relationship_id = lr.id AND oura.organization_id = $2
351
+ INNER JOIN public.users requester_user ON ir.by_user_id = requester_user.id
352
+ LEFT JOIN linkedin.accounts requester_account ON requester_user.id = requester_account.user_id
353
+ INNER JOIN public.users target_user ON ir.to_user_id = target_user.id
354
+ LEFT JOIN linkedin.accounts target_account ON target_user.id = target_account.user_id
355
+ INNER JOIN linkedin.accounts account_a ON lr.linkedin_account_id_a = account_a.id
356
+ INNER JOIN linkedin.accounts account_b ON lr.linkedin_account_id_b = account_b.id
357
+ WHERE ir.by_user_id = $1
358
+ ORDER BY ir.created_at DESC
359
+ `;
360
+ const results = await (0, pg_client_1.query)(db, sql, [userId, organizationId]);
361
+ // Enrich results with contributor and work experience
362
+ const enrichedResults = await Promise.all(results.map(async (row) => {
363
+ // Determine contributor ID (the person in the relationship who is NOT the target)
364
+ let contributorUserId = null;
365
+ if (row.target_account_id === row.account_a_id) {
366
+ contributorUserId = row.account_b_user_id;
367
+ }
368
+ else if (row.target_account_id === row.account_b_id) {
369
+ contributorUserId = row.account_a_user_id;
370
+ }
371
+ // Fetch contributor user info
372
+ let contributor = null;
373
+ if (contributorUserId) {
374
+ const contributorSql = `
375
+ SELECT
376
+ u.id,
377
+ u.given_name,
378
+ u.family_name,
379
+ COALESCE(la.profile_image_cloudfront_url, la.profile_picture_url) as profile_picture_url
380
+ FROM public.users u
381
+ LEFT JOIN linkedin.accounts la ON u.id = la.user_id
382
+ WHERE u.id = $1
383
+ LIMIT 1
384
+ `;
385
+ const contributorData = await (0, pg_client_1.queryOne)(db, contributorSql, [contributorUserId]);
386
+ if (contributorData) {
387
+ contributor = {
388
+ id: contributorData.id,
389
+ givenName: contributorData.given_name,
390
+ familyName: contributorData.family_name,
391
+ profilePictureUrl: contributorData.profile_picture_url,
392
+ };
393
+ }
394
+ }
395
+ // Fetch target work experience
396
+ let currentPosition = null;
397
+ let currentCompany = null;
398
+ if (row.target_account_id) {
399
+ const workExpSql = `
400
+ SELECT position, company
401
+ FROM linkedin.work_experience
402
+ WHERE linkedin_account_id = $1
403
+ ORDER BY
404
+ CASE WHEN end_date IS NULL THEN 0 ELSE 1 END,
405
+ start_date DESC
406
+ LIMIT 1
407
+ `;
408
+ const workExp = await (0, pg_client_1.queryOne)(db, workExpSql, [row.target_account_id]);
409
+ if (workExp) {
410
+ currentPosition = workExp.position;
411
+ currentCompany = workExp.company;
412
+ }
413
+ }
414
+ return {
415
+ id: row.id,
416
+ createdAt: row.created_at,
417
+ toRelationshipId: row.to_relationship_id,
418
+ organizationUserRelationshipAssignmentId: row.organization_user_relationship_assignment_id,
419
+ message: row.message,
420
+ status: row.status,
421
+ wasSeenByReceiver: row.was_seen_by_receiver,
422
+ requester: {
423
+ id: row.requester_id,
424
+ givenName: row.requester_given_name,
425
+ familyName: row.requester_family_name,
426
+ profilePictureUrl: row.requester_profile_picture_url,
427
+ },
428
+ target: {
429
+ id: row.target_id,
430
+ givenName: row.target_given_name,
431
+ familyName: row.target_family_name,
432
+ headline: row.target_headline,
433
+ currentPosition,
434
+ currentCompany,
435
+ profilePictureUrl: row.target_profile_picture_url,
436
+ },
437
+ contributor,
438
+ };
439
+ }));
440
+ return enrichedResults;
441
+ }
442
+ /**
443
+ * Mark all received introduction requests as seen for a user
444
+ * Filtered by organization - only marks requests for relationships assigned to the organization
445
+ *
446
+ * Updates was_seen_by_receiver = true for all introduction requests where:
447
+ * - The user is the contributor (one of the two people in the relationship)
448
+ * - The user is NOT the requester
449
+ * - was_seen_by_receiver is currently false
450
+ * - The relationship is assigned to the specified organization
451
+ *
452
+ * @param db - Database instance
453
+ * @param userId - The user ID marking requests as seen
454
+ * @param organizationId - Organization ID to filter by
455
+ * @returns Number of requests marked as seen
456
+ */
457
+ async function markReceivedIntroductionRequestsAsSeen(db, userId, organizationId) {
458
+ // First, get the user's LinkedIn account
459
+ const linkedinAccountSql = `SELECT id FROM linkedin.accounts WHERE user_id = $1 LIMIT 1`;
460
+ const userLinkedInAccount = await (0, pg_client_1.queryOne)(db, linkedinAccountSql, [userId]);
461
+ if (!userLinkedInAccount) {
462
+ return 0;
463
+ }
464
+ // Update introduction requests - filter by organization through organization_user_relationships_assignments
465
+ const sql = `
466
+ UPDATE public.introduction_requests
467
+ SET was_seen_by_receiver = true
468
+ WHERE
469
+ EXISTS (
470
+ SELECT 1
471
+ FROM linkedin.relationships lr
472
+ INNER JOIN public.organization_user_relationships_assignments oura
473
+ ON oura.linkedin_relationship_id = lr.id AND oura.organization_id = $3
474
+ WHERE lr.id = introduction_requests.to_relationship_id
475
+ AND (
476
+ lr.linkedin_account_id_a = $1
477
+ OR lr.linkedin_account_id_b = $1
478
+ )
479
+ )
480
+ AND introduction_requests.by_user_id != $2
481
+ AND introduction_requests.was_seen_by_receiver = false
482
+ RETURNING id
483
+ `;
484
+ const result = await (0, pg_client_1.query)(db, sql, [
485
+ userLinkedInAccount.id,
486
+ userId,
487
+ organizationId,
488
+ ]);
489
+ // Return the number of rows affected
490
+ return result.length;
491
+ }
492
+ //# sourceMappingURL=introduction-request-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introduction-request-operations.js","sourceRoot":"","sources":["../../src/utils/introduction-request-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * =============================================================================
3
+ * INVITATION OPERATIONS - Database Operations for User Invitations
4
+ * =============================================================================
5
+ * Helper functions for managing user invitations to organizations
6
+ */
7
+ import { Pool, PoolClient } from 'pg';
8
+ type DbType = Pool | PoolClient | any;
9
+ export interface CreateInvitationInput {
10
+ email: string;
11
+ invitedByUserId: number;
12
+ shadowUserId: number;
13
+ organizationId: number;
14
+ invitationMessage?: string;
15
+ invitedUserPermissionsRole?: "ADMIN" | "REGULAR";
16
+ expiresInDays?: number;
17
+ }
18
+ export interface InvitationDetails {
19
+ id: number;
20
+ email: string;
21
+ token: string;
22
+ invitationMessage: string | null;
23
+ invitedUserPermissionsRole: "ADMIN" | "REGULAR";
24
+ invitedByUserId: number;
25
+ shadowUserId: number;
26
+ organizationId: number;
27
+ createdAt: Date;
28
+ expiresAt: Date;
29
+ usedAt: Date | null;
30
+ revokedAt: Date | null;
31
+ }
32
+ export interface InvitationWithDetails extends InvitationDetails {
33
+ invitedByUser: {
34
+ id: number;
35
+ givenName: string | null;
36
+ familyName: string | null;
37
+ };
38
+ shadowUser: {
39
+ id: number;
40
+ givenName: string | null;
41
+ familyName: string | null;
42
+ profilePictureUrl: string | null;
43
+ };
44
+ organization: {
45
+ id: number;
46
+ name: string;
47
+ };
48
+ }
49
+ export interface ValidateTokenResult {
50
+ valid: boolean;
51
+ invitation?: InvitationWithDetails;
52
+ reason?: "not_found" | "expired" | "already_used" | "revoked";
53
+ }
54
+ /**
55
+ * Comprehensive validation for invitation eligibility
56
+ * Checks all three scenarios:
57
+ * a) No active invitation exists for this email
58
+ * b) Email is not already used by an authenticated user
59
+ * c) Email is not reserved by contact_info
60
+ */
61
+ export declare function validateInvitationEligibility(db: DbType, email: string): Promise<{
62
+ eligible: boolean;
63
+ reason?: "active_invitation" | "email_registered" | "email_reserved";
64
+ }>;
65
+ /**
66
+ * Returns true if there is a still-active (pending) invitation for this email anywhere
67
+ * Active means: not used, not revoked, and not expired
68
+ */
69
+ export declare function hasActiveInvitationByEmail(db: DbType, email: string): Promise<boolean>;
70
+ /**
71
+ * Returns true if there is a still-active (pending) invitation for this email in the given organization
72
+ */
73
+ export declare function hasActiveInvitationByEmailForOrg(db: DbType, email: string, organizationId: number): Promise<boolean>;
74
+ /**
75
+ * Email is already used by an authenticated user if there exists authentication.users → contact_infos value match (case-insensitive)
76
+ */
77
+ export declare function isEmailUsedByAuthenticatedUser(db: DbType, email: string): Promise<boolean>;
78
+ /**
79
+ * Returns true if any contact_info indicates the email is reserved/owned and should not be invited again.
80
+ * Criteria: ci.type = 'EMAIL' OR ci.source IN ('MANUAL', 'INVITATION') for the same email value.
81
+ */
82
+ export declare function isEmailReservedByContactInfo(db: DbType, email: string): Promise<boolean>;
83
+ /**
84
+ * Generate a cryptographically secure invitation token
85
+ * @returns 32-character hex string
86
+ */
87
+ export declare function generateInvitationToken(): string;
88
+ /**
89
+ * Create a new invitation
90
+ */
91
+ export declare function createInvitation(db: DbType, input: CreateInvitationInput): Promise<InvitationDetails>;
92
+ /**
93
+ * Validate an invitation token and return invitation details
94
+ */
95
+ export declare function validateInvitationToken(db: DbType, token: string): Promise<ValidateTokenResult>;
96
+ /**
97
+ * Mark an invitation as used
98
+ */
99
+ export declare function markInvitationAsUsed(db: DbType, token: string): Promise<InvitationDetails | null>;
100
+ /**
101
+ * Revoke an invitation
102
+ */
103
+ export declare function revokeInvitation(db: DbType, invitationId: number): Promise<InvitationDetails | null>;
104
+ /**
105
+ * Get invitation by ID
106
+ */
107
+ export declare function getInvitationById(db: DbType, invitationId: number): Promise<InvitationWithDetails | null>;
108
+ /**
109
+ * Get invitation by token
110
+ */
111
+ export declare function getInvitationByToken(db: DbType, token: string): Promise<InvitationWithDetails | null>;
112
+ /**
113
+ * Get all pending invitations for an organization
114
+ */
115
+ export declare function getPendingInvitationsByOrganization(db: DbType, organizationId: number): Promise<InvitationWithDetails[]>;
116
+ /**
117
+ * Get all invitations (pending, used, expired, revoked) for an organization
118
+ */
119
+ export declare function getAllInvitationsByOrganization(db: DbType, organizationId: number): Promise<InvitationWithDetails[]>;
120
+ /**
121
+ * Check if a pending invitation exists for an email in an organization
122
+ */
123
+ export declare function hasPendingInvitation(db: DbType, email: string, organizationId: number): Promise<boolean>;
124
+ /**
125
+ * Check if a shadow user has an active invitation for an organization
126
+ */
127
+ export declare function hasActiveInvitationForShadowUser(db: DbType, shadowUserId: number, organizationId: number): Promise<boolean>;
128
+ /**
129
+ * Get all invitations sent by a specific user
130
+ */
131
+ export declare function getInvitationsSentByUser(db: DbType, invitedByUserId: number): Promise<InvitationWithDetails[]>;
132
+ /**
133
+ * Get all pending invitations sent by a specific user
134
+ */
135
+ export declare function getPendingInvitationsSentByUser(db: DbType, invitedByUserId: number): Promise<InvitationWithDetails[]>;
136
+ /**
137
+ * Delete expired invitations older than specified days
138
+ */
139
+ export declare function cleanupExpiredInvitations(db: DbType, olderThanDays?: number): Promise<number>;
140
+ export {};
141
+ //# sourceMappingURL=invitation-operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invitation-operations.d.ts","sourceRoot":"","sources":["../../src/utils/invitation-operations.ts"],"names":[],"mappings":"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"}