@equilateral_ai/mindmeld 3.5.3 → 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.
- package/hooks/session-start.js +312 -85
- package/package.json +20 -14
- package/scripts/init-project.js +9 -23
- package/src/client/dbShim.js +16 -0
- package/src/core/AuthManager.js +3 -2
- package/src/handlers/helpers/dbOperations.js +9 -46
- package/src/index.js +2 -217
- package/src/utils/piiMask.js +16 -0
- package/scripts/harvest.js +0 -601
- package/scripts/inject.js +0 -409
- package/scripts/mcp-bridge.js +0 -220
- package/scripts/repo-analyzer.js +0 -870
- package/scripts/standards.js +0 -285
- package/src/collaboration/CollaborationPrompt.js +0 -460
- package/src/core/AlertEngine.js +0 -813
- package/src/core/AlertNotifier.js +0 -363
- package/src/core/CorrelationAnalyzer.js +0 -931
- package/src/core/CrossReferenceEngine.js +0 -624
- package/src/core/CurationEngine.js +0 -688
- package/src/core/DeprecationScheduler.js +0 -183
- package/src/core/LoadBearingDetector.js +0 -242
- package/src/core/NotificationService.js +0 -1032
- package/src/core/RapportOrchestrator.js +0 -632
- package/src/core/RelevanceDetector.js +0 -694
- package/src/core/StandardLifecycle.js +0 -244
- package/src/core/StandardsIngestion.js +0 -991
- package/src/core/TeamLoadBearingDetector.js +0 -431
- package/src/core/parsers/adrParser.js +0 -479
- package/src/core/parsers/cursorRulesParser.js +0 -564
- package/src/core/parsers/eslintParser.js +0 -439
- package/src/database/dbOperations.js +0 -105
- package/src/handlers/activity/activityGetMe.js +0 -98
- package/src/handlers/activity/activityGetTeam.js +0 -175
- package/src/handlers/admin/adminSetup.js +0 -216
- package/src/handlers/alerts/alertsAcknowledge.js +0 -92
- package/src/handlers/alerts/alertsGet.js +0 -250
- package/src/handlers/analytics/activitySummaryGet.js +0 -234
- package/src/handlers/analytics/coachingGet.js +0 -361
- package/src/handlers/analytics/convergenceGet.js +0 -236
- package/src/handlers/analytics/developerScoreGet.js +0 -137
- package/src/handlers/collaborators/collaboratorAdd.js +0 -200
- package/src/handlers/collaborators/collaboratorInvite.js +0 -219
- package/src/handlers/collaborators/collaboratorList.js +0 -82
- package/src/handlers/collaborators/collaboratorRemove.js +0 -128
- package/src/handlers/collaborators/inviteAccept.js +0 -122
- package/src/handlers/company/companyUsersDelete.js +0 -141
- package/src/handlers/company/companyUsersGet.js +0 -90
- package/src/handlers/company/companyUsersPost.js +0 -267
- package/src/handlers/company/companyUsersPut.js +0 -76
- package/src/handlers/context/contextGet.js +0 -57
- package/src/handlers/context/invariantsGet.js +0 -74
- package/src/handlers/context/loopsGet.js +0 -82
- package/src/handlers/context/notesCreate.js +0 -74
- package/src/handlers/context/purposeGet.js +0 -78
- package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
- package/src/handlers/correlations/correlationsGet.js +0 -93
- package/src/handlers/correlations/correlationsProjectGet.js +0 -153
- package/src/handlers/enterprise/controlTowerGet.js +0 -224
- package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
- package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
- package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
- package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
- package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
- package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
- package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
- package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
- package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
- package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
- package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
- package/src/handlers/github/githubConnectionStatus.js +0 -49
- package/src/handlers/github/githubDiscoverPatterns.js +0 -621
- package/src/handlers/github/githubOAuthCallback.js +0 -178
- package/src/handlers/github/githubOAuthStart.js +0 -59
- package/src/handlers/github/githubPatternsReview.js +0 -76
- package/src/handlers/github/githubReposList.js +0 -105
- package/src/handlers/health/healthGet.js +0 -55
- package/src/handlers/helpers/auditLogger.js +0 -201
- package/src/handlers/helpers/checkSuperAdmin.js +0 -84
- package/src/handlers/helpers/decisionFrames.js +0 -29
- package/src/handlers/helpers/errorHandler.js +0 -49
- package/src/handlers/helpers/index.js +0 -138
- package/src/handlers/helpers/lambdaWrapper.js +0 -60
- package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
- package/src/handlers/helpers/predictiveCache.js +0 -51
- package/src/handlers/helpers/projectAccess.js +0 -88
- package/src/handlers/helpers/responseUtil.js +0 -55
- package/src/handlers/helpers/subscriptionTiers.js +0 -1168
- package/src/handlers/mcp/mcpHandler.js +0 -569
- package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
- package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
- package/src/handlers/notifications/getPreferences.js +0 -84
- package/src/handlers/notifications/sendNotification.js +0 -170
- package/src/handlers/notifications/updatePreferences.js +0 -316
- package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
- package/src/handlers/patterns/patternUsagePost.js +0 -182
- package/src/handlers/patterns/patternViolationPost.js +0 -185
- package/src/handlers/projects/projectCreate.js +0 -248
- package/src/handlers/projects/projectDelete.js +0 -82
- package/src/handlers/projects/projectGet.js +0 -95
- package/src/handlers/projects/projectUpdate.js +0 -117
- package/src/handlers/reports/aiLeverage.js +0 -210
- package/src/handlers/reports/engineeringInvestment.js +0 -132
- package/src/handlers/reports/riskForecast.js +0 -206
- package/src/handlers/reports/standardsRoi.js +0 -254
- package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
- package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
- package/src/handlers/scheduled/generateAlerts.js +0 -135
- package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
- package/src/handlers/scheduled/refreshActivity.js +0 -21
- package/src/handlers/scheduled/scanCompliance.js +0 -334
- package/src/handlers/sessions/sessionEndPost.js +0 -180
- package/src/handlers/sessions/sessionStandardsPost.js +0 -171
- package/src/handlers/standards/catalogGet.js +0 -185
- package/src/handlers/standards/catalogSync.js +0 -120
- package/src/handlers/standards/discoveriesGet.js +0 -89
- package/src/handlers/standards/projectStandardsGet.js +0 -129
- package/src/handlers/standards/projectStandardsPut.js +0 -151
- package/src/handlers/standards/standardsAuditGet.js +0 -65
- package/src/handlers/standards/standardsParseUpload.js +0 -149
- package/src/handlers/standards/standardsRelevantPost.js +0 -405
- package/src/handlers/standards/standardsTransition.js +0 -161
- package/src/handlers/stripe/addonManagePost.js +0 -240
- package/src/handlers/stripe/billingPortalPost.js +0 -93
- package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
- package/src/handlers/stripe/seatsUpdatePost.js +0 -185
- package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
- package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
- package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
- package/src/handlers/stripe/webhookPost.js +0 -482
- package/src/handlers/user/apiTokenCreate.js +0 -71
- package/src/handlers/user/apiTokenList.js +0 -64
- package/src/handlers/user/userSplashAck.js +0 -91
- package/src/handlers/user/userSplashGet.js +0 -211
- package/src/handlers/users/cognitoPostConfirmation.js +0 -186
- package/src/handlers/users/cognitoPreSignUp.js +0 -114
- package/src/handlers/users/userEntitlementsGet.js +0 -89
- package/src/handlers/users/userGet.js +0 -118
- package/src/handlers/users/userProfilePut.js +0 -77
- 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);
|