@equilateral_ai/mindmeld 3.5.2 → 4.0.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 (140) hide show
  1. package/hooks/session-end.js +25 -0
  2. package/hooks/session-start.js +363 -83
  3. package/hooks/session-watcher.js +585 -0
  4. package/package.json +19 -13
  5. package/scripts/init-project.js +9 -23
  6. package/src/client/dbShim.js +16 -0
  7. package/src/core/AuthManager.js +3 -2
  8. package/src/handlers/helpers/dbOperations.js +9 -46
  9. package/src/index.js +2 -217
  10. package/src/utils/piiMask.js +16 -0
  11. package/scripts/harvest.js +0 -601
  12. package/scripts/inject.js +0 -409
  13. package/scripts/mcp-bridge.js +0 -220
  14. package/scripts/repo-analyzer.js +0 -870
  15. package/src/collaboration/CollaborationPrompt.js +0 -460
  16. package/src/core/AlertEngine.js +0 -813
  17. package/src/core/AlertNotifier.js +0 -363
  18. package/src/core/CorrelationAnalyzer.js +0 -931
  19. package/src/core/CrossReferenceEngine.js +0 -624
  20. package/src/core/CurationEngine.js +0 -688
  21. package/src/core/DeprecationScheduler.js +0 -183
  22. package/src/core/LoadBearingDetector.js +0 -242
  23. package/src/core/NotificationService.js +0 -1032
  24. package/src/core/RapportOrchestrator.js +0 -632
  25. package/src/core/RelevanceDetector.js +0 -694
  26. package/src/core/StandardLifecycle.js +0 -244
  27. package/src/core/StandardsIngestion.js +0 -991
  28. package/src/core/TeamLoadBearingDetector.js +0 -431
  29. package/src/core/parsers/adrParser.js +0 -479
  30. package/src/core/parsers/cursorRulesParser.js +0 -564
  31. package/src/core/parsers/eslintParser.js +0 -439
  32. package/src/database/dbOperations.js +0 -105
  33. package/src/handlers/activity/activityGetMe.js +0 -98
  34. package/src/handlers/activity/activityGetTeam.js +0 -175
  35. package/src/handlers/admin/adminSetup.js +0 -216
  36. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  37. package/src/handlers/alerts/alertsGet.js +0 -250
  38. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  39. package/src/handlers/analytics/coachingGet.js +0 -361
  40. package/src/handlers/analytics/convergenceGet.js +0 -236
  41. package/src/handlers/analytics/developerScoreGet.js +0 -137
  42. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  43. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  44. package/src/handlers/collaborators/collaboratorList.js +0 -82
  45. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  46. package/src/handlers/collaborators/inviteAccept.js +0 -122
  47. package/src/handlers/company/companyUsersDelete.js +0 -141
  48. package/src/handlers/company/companyUsersGet.js +0 -90
  49. package/src/handlers/company/companyUsersPost.js +0 -267
  50. package/src/handlers/company/companyUsersPut.js +0 -76
  51. package/src/handlers/context/contextGet.js +0 -57
  52. package/src/handlers/context/invariantsGet.js +0 -74
  53. package/src/handlers/context/loopsGet.js +0 -82
  54. package/src/handlers/context/notesCreate.js +0 -74
  55. package/src/handlers/context/purposeGet.js +0 -78
  56. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  57. package/src/handlers/correlations/correlationsGet.js +0 -93
  58. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  59. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  60. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  61. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  62. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  63. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  64. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  65. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  66. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  67. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  68. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  69. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  70. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  71. package/src/handlers/github/githubConnectionStatus.js +0 -49
  72. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  73. package/src/handlers/github/githubOAuthCallback.js +0 -178
  74. package/src/handlers/github/githubOAuthStart.js +0 -59
  75. package/src/handlers/github/githubPatternsReview.js +0 -76
  76. package/src/handlers/github/githubReposList.js +0 -105
  77. package/src/handlers/health/healthGet.js +0 -55
  78. package/src/handlers/helpers/auditLogger.js +0 -201
  79. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  80. package/src/handlers/helpers/decisionFrames.js +0 -29
  81. package/src/handlers/helpers/errorHandler.js +0 -49
  82. package/src/handlers/helpers/index.js +0 -138
  83. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  84. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  85. package/src/handlers/helpers/predictiveCache.js +0 -51
  86. package/src/handlers/helpers/projectAccess.js +0 -88
  87. package/src/handlers/helpers/responseUtil.js +0 -55
  88. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  89. package/src/handlers/mcp/mcpHandler.js +0 -569
  90. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  91. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  92. package/src/handlers/notifications/getPreferences.js +0 -84
  93. package/src/handlers/notifications/sendNotification.js +0 -170
  94. package/src/handlers/notifications/updatePreferences.js +0 -316
  95. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  96. package/src/handlers/patterns/patternUsagePost.js +0 -182
  97. package/src/handlers/patterns/patternViolationPost.js +0 -185
  98. package/src/handlers/projects/projectCreate.js +0 -248
  99. package/src/handlers/projects/projectDelete.js +0 -82
  100. package/src/handlers/projects/projectGet.js +0 -95
  101. package/src/handlers/projects/projectUpdate.js +0 -117
  102. package/src/handlers/reports/aiLeverage.js +0 -210
  103. package/src/handlers/reports/engineeringInvestment.js +0 -132
  104. package/src/handlers/reports/riskForecast.js +0 -206
  105. package/src/handlers/reports/standardsRoi.js +0 -254
  106. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  107. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  108. package/src/handlers/scheduled/generateAlerts.js +0 -135
  109. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  110. package/src/handlers/scheduled/refreshActivity.js +0 -21
  111. package/src/handlers/scheduled/scanCompliance.js +0 -334
  112. package/src/handlers/sessions/sessionEndPost.js +0 -180
  113. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  114. package/src/handlers/standards/catalogGet.js +0 -185
  115. package/src/handlers/standards/catalogSync.js +0 -120
  116. package/src/handlers/standards/discoveriesGet.js +0 -89
  117. package/src/handlers/standards/projectStandardsGet.js +0 -129
  118. package/src/handlers/standards/projectStandardsPut.js +0 -151
  119. package/src/handlers/standards/standardsAuditGet.js +0 -65
  120. package/src/handlers/standards/standardsParseUpload.js +0 -149
  121. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  122. package/src/handlers/standards/standardsTransition.js +0 -161
  123. package/src/handlers/stripe/addonManagePost.js +0 -240
  124. package/src/handlers/stripe/billingPortalPost.js +0 -93
  125. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  126. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  127. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  128. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  129. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  130. package/src/handlers/stripe/webhookPost.js +0 -482
  131. package/src/handlers/user/apiTokenCreate.js +0 -71
  132. package/src/handlers/user/apiTokenList.js +0 -64
  133. package/src/handlers/user/userSplashAck.js +0 -91
  134. package/src/handlers/user/userSplashGet.js +0 -211
  135. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  136. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  137. package/src/handlers/users/userEntitlementsGet.js +0 -89
  138. package/src/handlers/users/userGet.js +0 -118
  139. package/src/handlers/users/userProfilePut.js +0 -77
  140. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,128 +0,0 @@
1
- /**
2
- * Collaborator Remove Handler
3
- * Removes a collaborator from a project
4
- *
5
- * DELETE /api/collaborators?project_id=xxx&email=xxx
6
- * Auth: Cognito JWT required
7
- */
8
-
9
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
10
-
11
- /**
12
- * Remove collaborator from project
13
- * Requires owner or admin role on project
14
- * Owners cannot be removed (must transfer ownership first)
15
- */
16
- async function removeCollaborator({ queryStringParameters: queryParams = {}, requestContext }) {
17
- try {
18
- const Request_ID = requestContext.requestId;
19
- const removerEmail = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
20
- const projectId = queryParams.project_id;
21
- const collaboratorEmail = queryParams.email;
22
-
23
- if (!removerEmail) {
24
- return createErrorResponse(401, 'Authentication required');
25
- }
26
-
27
- if (!projectId) {
28
- return createErrorResponse(400, 'projectId is required');
29
- }
30
-
31
- if (!collaboratorEmail) {
32
- return createErrorResponse(400, 'email is required');
33
- }
34
-
35
- // Decode email from URL (may be URL encoded)
36
- const targetEmail = decodeURIComponent(collaboratorEmail);
37
-
38
- // Check remover has permission and get billing info
39
- const accessQuery = `
40
- SELECT
41
- pc.role,
42
- c.client_id,
43
- c.billing_type
44
- FROM rapport.project_collaborators pc
45
- JOIN rapport.projects p ON pc.project_id = p.project_id
46
- JOIN rapport.clients c ON p.company_id = c.client_id
47
- WHERE pc.project_id = $1 AND pc.email_address = $2
48
- `;
49
- const accessCheck = await executeQuery(accessQuery, [projectId, removerEmail]);
50
-
51
- if (accessCheck.rowCount === 0) {
52
- return createErrorResponse(403, 'You do not have access to this project');
53
- }
54
-
55
- const { role: removerRole, client_id, billing_type } = accessCheck.rows[0];
56
- const isSelfRemoval = removerEmail.toLowerCase() === targetEmail.toLowerCase();
57
-
58
- // Allow self-removal for anyone except owner
59
- // Only owner/admin can remove others
60
- if (!isSelfRemoval && removerRole !== 'owner' && removerRole !== 'admin') {
61
- return createErrorResponse(403, 'Only project owners and admins can remove collaborators');
62
- }
63
-
64
- // Check target collaborator exists and get their role
65
- const targetQuery = `
66
- SELECT role FROM rapport.project_collaborators
67
- WHERE project_id = $1 AND email_address = $2
68
- `;
69
- const targetCheck = await executeQuery(targetQuery, [projectId, targetEmail]);
70
-
71
- if (targetCheck.rowCount === 0) {
72
- return createErrorResponse(404, 'Collaborator not found on this project');
73
- }
74
-
75
- const targetRole = targetCheck.rows[0].role;
76
-
77
- // Prevent removing owner
78
- if (targetRole === 'owner') {
79
- return createErrorResponse(400, 'Cannot remove project owner. Transfer ownership first.');
80
- }
81
-
82
- // Prevent admin from removing another admin (only owner can)
83
- if (targetRole === 'admin' && removerRole !== 'owner') {
84
- return createErrorResponse(403, 'Only project owner can remove admins');
85
- }
86
-
87
- // Remove collaborator
88
- const deleteQuery = `
89
- DELETE FROM rapport.project_collaborators
90
- WHERE project_id = $1 AND email_address = $2
91
- RETURNING project_id, email_address, role
92
- `;
93
-
94
- const result = await executeQuery(deleteQuery, [projectId, targetEmail]);
95
-
96
- // For enterprise invoice billing, decrement billable_users count
97
- if (billing_type === 'invoice' && client_id) {
98
- await executeQuery(`
99
- UPDATE rapport.clients
100
- SET billable_users = GREATEST(COALESCE(billable_users, 0) - 1, 0),
101
- last_updated = NOW()
102
- WHERE client_id = $1
103
- `, [client_id]);
104
- }
105
-
106
- return createSuccessResponse(
107
- {
108
- Records: [{
109
- ...result.rows[0],
110
- removed_by: removerEmail,
111
- removed_at: new Date().toISOString()
112
- }]
113
- },
114
- isSelfRemoval ? 'You have left the project' : 'Collaborator removed',
115
- {
116
- Total_Records: 1,
117
- Request_ID,
118
- Timestamp: new Date().toISOString()
119
- }
120
- );
121
-
122
- } catch (error) {
123
- console.error('Handler Error:', error);
124
- return handleError(error);
125
- }
126
- }
127
-
128
- exports.handler = wrapHandler(removeCollaborator);
@@ -1,122 +0,0 @@
1
- /**
2
- * Invite Accept Handler
3
- * Accepts a collaboration invitation via token
4
- *
5
- * POST /api/invites/accept
6
- * Body: { token }
7
- * Auth: Cognito JWT required (user must be logged in)
8
- */
9
-
10
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
11
-
12
- /**
13
- * Accept collaboration invitation
14
- * User must be authenticated and email must match invite
15
- */
16
- async function acceptInvite({ body: requestBody = {}, requestContext }) {
17
- try {
18
- const Request_ID = requestContext.requestId;
19
- const userEmail = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
20
- const { token } = requestBody;
21
-
22
- if (!userEmail) {
23
- return createErrorResponse(401, 'Authentication required. Please sign in first.');
24
- }
25
-
26
- if (!token) {
27
- return createErrorResponse(400, 'Invite token is required');
28
- }
29
-
30
- // Find invite by token
31
- const inviteQuery = `
32
- SELECT
33
- pc.project_id,
34
- pc.email_address,
35
- pc.role,
36
- pc.invited_by,
37
- pc.invited_at,
38
- pc.accepted_at,
39
- p.project_name,
40
- p.description,
41
- p.company_id
42
- FROM rapport.project_collaborators pc
43
- JOIN rapport.projects p ON p.project_id = pc.project_id
44
- WHERE pc.invite_token = $1
45
- `;
46
- const inviteCheck = await executeQuery(inviteQuery, [token]);
47
-
48
- if (inviteCheck.rowCount === 0) {
49
- return createErrorResponse(404, 'Invalid or expired invite token');
50
- }
51
-
52
- const invite = inviteCheck.rows[0];
53
-
54
- // Check if already accepted
55
- if (invite.accepted_at) {
56
- return createErrorResponse(400, 'This invitation has already been accepted');
57
- }
58
-
59
- // Verify email matches (case insensitive)
60
- if (invite.email_address.toLowerCase() !== userEmail.toLowerCase()) {
61
- return createErrorResponse(403,
62
- 'This invitation was sent to a different email address. ' +
63
- 'Please sign in with the email the invite was sent to.'
64
- );
65
- }
66
-
67
- // Check if invite is expired (7 days)
68
- const invitedAt = new Date(invite.invited_at);
69
- const now = new Date();
70
- const daysSinceInvite = (now - invitedAt) / (1000 * 60 * 60 * 24);
71
-
72
- if (daysSinceInvite > 7) {
73
- return createErrorResponse(410, 'This invitation has expired. Please ask for a new invite.');
74
- }
75
-
76
- // Accept the invite
77
- const acceptQuery = `
78
- UPDATE rapport.project_collaborators
79
- SET
80
- accepted_at = NOW(),
81
- invite_token = NULL,
82
- is_external = false
83
- WHERE project_id = $1 AND email_address = $2
84
- RETURNING project_id, email_address, role, accepted_at
85
- `;
86
- const acceptResult = await executeQuery(acceptQuery, [invite.project_id, invite.email_address]);
87
-
88
- // Ensure user exists in users table
89
- await executeQuery(`
90
- INSERT INTO rapport.users (email_address, client_id, active, created_at)
91
- VALUES ($1, $2, true, NOW())
92
- ON CONFLICT (email_address) DO UPDATE SET
93
- active = true,
94
- updated_at = NOW()
95
- `, [userEmail, invite.company_id]);
96
-
97
- return createSuccessResponse(
98
- {
99
- Records: [{
100
- project_id: invite.project_id,
101
- project_name: invite.project_name,
102
- description: invite.description,
103
- role: invite.role,
104
- accepted_at: acceptResult.rows[0].accepted_at,
105
- invited_by: invite.invited_by
106
- }]
107
- },
108
- `Welcome to ${invite.project_name}!`,
109
- {
110
- Total_Records: 1,
111
- Request_ID,
112
- Timestamp: new Date().toISOString()
113
- }
114
- );
115
-
116
- } catch (error) {
117
- console.error('Handler Error:', error);
118
- return handleError(error);
119
- }
120
- }
121
-
122
- exports.handler = wrapHandler(acceptInvite);
@@ -1,141 +0,0 @@
1
- /**
2
- * Company Users Remove Handler
3
- * Removes a user's entitlement from a company
4
- * If the user was admin-paid, decrements license count and updates Stripe subscription quantity
5
- *
6
- * DELETE /api/company/users?company_id=xxx&email=xxx
7
- * Auth: Cognito JWT required
8
- */
9
-
10
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
11
- const Stripe = require('stripe');
12
-
13
- const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
14
-
15
- /**
16
- * Remove user from company
17
- * Requires admin access to the company
18
- */
19
- async function removeCompanyUser({ queryStringParameters: queryParams = {}, requestContext }) {
20
- try {
21
- const Request_ID = requestContext.requestId;
22
- const callerEmail = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
23
- const companyId = queryParams.company_id;
24
- const targetEmail = queryParams.email;
25
-
26
- if (!callerEmail) {
27
- return createErrorResponse(401, 'Authentication required');
28
- }
29
-
30
- if (!companyId || !targetEmail) {
31
- return createErrorResponse(400, 'company_id and email query parameters are required');
32
- }
33
-
34
- // Prevent self-removal
35
- if (targetEmail === callerEmail) {
36
- return createErrorResponse(400, 'Cannot remove yourself from the company');
37
- }
38
-
39
- // Verify caller is admin of this company
40
- const adminQuery = `
41
- SELECT admin, client_id FROM rapport.user_entitlements
42
- WHERE email_address = $1 AND company_id = $2
43
- `;
44
- const adminCheck = await executeQuery(adminQuery, [callerEmail, companyId]);
45
-
46
- if (adminCheck.rowCount === 0 || !adminCheck.rows[0].admin) {
47
- return createErrorResponse(403, 'Admin access required to remove users');
48
- }
49
-
50
- const clientId = adminCheck.rows[0].client_id;
51
-
52
- // Check the target user's billing_type before deleting
53
- const targetQuery = `
54
- SELECT billing_type FROM rapport.user_entitlements
55
- WHERE email_address = $1 AND company_id = $2
56
- `;
57
- const targetResult = await executeQuery(targetQuery, [targetEmail, companyId]);
58
-
59
- if (targetResult.rowCount === 0) {
60
- return createErrorResponse(404, `User ${targetEmail} not found in this company`);
61
- }
62
-
63
- const billingType = targetResult.rows[0].billing_type;
64
-
65
- // If admin-paid, decrement license count and update Stripe
66
- let stripeUpdated = false;
67
- if (billingType === 'admin_paid') {
68
- // Get client's Stripe subscription
69
- const clientQuery = `
70
- SELECT stripe_subscription_id, license_count
71
- FROM rapport.clients WHERE client_id = $1
72
- `;
73
- const clientResult = await executeQuery(clientQuery, [clientId]);
74
- const clientRecord = clientResult.rows[0];
75
-
76
- if (clientRecord?.stripe_subscription_id) {
77
- // Decrement license_count (minimum 1 — the admin's own license)
78
- const updateResult = await executeQuery(`
79
- UPDATE rapport.clients
80
- SET license_count = GREATEST(COALESCE(license_count, 1) - 1, 1), last_updated = CURRENT_TIMESTAMP
81
- WHERE client_id = $1
82
- RETURNING license_count
83
- `, [clientId]);
84
- const newLicenseCount = updateResult.rows[0].license_count;
85
-
86
- // Update Stripe subscription quantity
87
- try {
88
- const subscription = await stripe.subscriptions.retrieve(clientRecord.stripe_subscription_id);
89
- const item = subscription.items.data[0];
90
- if (item) {
91
- await stripe.subscriptions.update(clientRecord.stripe_subscription_id, {
92
- items: [{ id: item.id, quantity: newLicenseCount }],
93
- proration_behavior: 'none'
94
- });
95
- stripeUpdated = true;
96
- console.log(`[License] Decreased Stripe quantity to ${newLicenseCount} for ${clientId}`);
97
- }
98
- } catch (stripeError) {
99
- console.error('[License] Stripe update failed on removal:', stripeError.message);
100
- // Revert license_count since Stripe failed
101
- await executeQuery(`
102
- UPDATE rapport.clients
103
- SET license_count = COALESCE(license_count, 0) + 1, last_updated = CURRENT_TIMESTAMP
104
- WHERE client_id = $1
105
- `, [clientId]);
106
- return createErrorResponse(500, 'Failed to update subscription. Please try again or contact support.');
107
- }
108
- }
109
- }
110
-
111
- // Delete the entitlement
112
- const deleteQuery = `
113
- DELETE FROM rapport.user_entitlements
114
- WHERE email_address = $1 AND company_id = $2
115
- RETURNING email_address
116
- `;
117
- const result = await executeQuery(deleteQuery, [targetEmail, companyId]);
118
-
119
- const message = billingType === 'admin_paid'
120
- ? `User ${targetEmail} removed and license released${stripeUpdated ? '' : ' (Stripe update pending)'}`
121
- : `User ${targetEmail} removed from company`;
122
-
123
- return createSuccessResponse(
124
- { Records: result.rows },
125
- message,
126
- {
127
- Total_Records: result.rowCount,
128
- Request_ID,
129
- Timestamp: new Date().toISOString(),
130
- license_released: billingType === 'admin_paid',
131
- stripe_updated: stripeUpdated
132
- }
133
- );
134
-
135
- } catch (error) {
136
- console.error('Handler Error:', error);
137
- return handleError(error);
138
- }
139
- }
140
-
141
- exports.handler = wrapHandler(removeCompanyUser);
@@ -1,90 +0,0 @@
1
- /**
2
- * Company Users List Handler
3
- * Lists all users (entitlements) for a company
4
- *
5
- * GET /api/company/users?company_id=xxx
6
- * Auth: Cognito JWT required
7
- */
8
-
9
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
10
-
11
- /**
12
- * List company users
13
- * Requires member access to the company
14
- */
15
- async function listCompanyUsers({ queryStringParameters: queryParams = {}, requestContext }) {
16
- try {
17
- const Request_ID = requestContext.requestId;
18
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
19
- const companyId = queryParams.company_id;
20
-
21
- if (!email) {
22
- return createErrorResponse(401, 'Authentication required');
23
- }
24
-
25
- if (!companyId) {
26
- return createErrorResponse(400, 'company_id query parameter is required');
27
- }
28
-
29
- // Verify caller has access to this company
30
- const accessQuery = `
31
- SELECT admin, member
32
- FROM rapport.user_entitlements
33
- WHERE email_address = $1 AND company_id = $2
34
- `;
35
- const accessCheck = await executeQuery(accessQuery, [email, companyId]);
36
-
37
- if (accessCheck.rowCount === 0) {
38
- return createErrorResponse(403, 'You do not have access to this company');
39
- }
40
-
41
- // Get all users for the company
42
- const query = `
43
- SELECT
44
- ue.email_address,
45
- ue.admin,
46
- ue.member,
47
- ue.client_id,
48
- ue.company_id,
49
- ue.billing_type,
50
- u.first_name,
51
- u.last_name,
52
- u.user_status,
53
- u.create_date,
54
- u.last_updated
55
- FROM rapport.user_entitlements ue
56
- LEFT JOIN rapport.users u ON u.email_address = ue.email_address
57
- WHERE ue.company_id = $1
58
- ORDER BY ue.admin DESC, u.create_date ASC
59
- `;
60
- const result = await executeQuery(query, [companyId]);
61
-
62
- const users = result.rows.map(row => ({
63
- email: row.email_address,
64
- display_name: [row.first_name, row.last_name].filter(Boolean).join(' ') || null,
65
- role: row.admin ? 'admin' : 'member',
66
- status: row.user_status || 'active',
67
- billing_type: row.billing_type || 'self_paid',
68
- created_at: row.create_date,
69
- last_active: row.last_updated,
70
- client_id: row.client_id,
71
- company_id: row.company_id,
72
- }));
73
-
74
- return createSuccessResponse(
75
- { Records: users },
76
- `Found ${users.length} user(s)`,
77
- {
78
- Total_Records: users.length,
79
- Request_ID,
80
- Timestamp: new Date().toISOString()
81
- }
82
- );
83
-
84
- } catch (error) {
85
- console.error('Handler Error:', error);
86
- return handleError(error);
87
- }
88
- }
89
-
90
- exports.handler = wrapHandler(listCompanyUsers);