@equilateral_ai/mindmeld 3.5.3 → 4.0.2
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 +21 -13
- package/scripts/init-project.js +9 -23
- package/scripts/repo-analyzer.js +118 -2
- 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/inject.js +0 -409
- package/scripts/mcp-bridge.js +0 -220
- 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,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Knowledge Get Handler
|
|
3
|
-
*
|
|
4
|
-
* GET /api/enterprise/knowledge?page=&pageSize=&status=&category=
|
|
5
|
-
* Returns: { Records: CuratedKnowledge[] } + meta { Total_Records, Page, Page_Size, Request_ID, Timestamp }
|
|
6
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
async function getEnterpriseKnowledge({ queryStringParameters, requestContext }) {
|
|
12
|
-
try {
|
|
13
|
-
const Request_ID = requestContext.requestId;
|
|
14
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
15
|
-
if (!email) {
|
|
16
|
-
return createErrorResponse(401, 'Authentication required');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const userResult = await executeQuery(
|
|
20
|
-
`SELECT u.client_id, c.subscription_tier
|
|
21
|
-
FROM rapport.users u
|
|
22
|
-
JOIN rapport.clients c ON c.client_id = u.client_id
|
|
23
|
-
WHERE u.email_address = $1 LIMIT 1`,
|
|
24
|
-
[email]
|
|
25
|
-
);
|
|
26
|
-
if (!userResult.rows.length) {
|
|
27
|
-
return createErrorResponse(403, 'User not found');
|
|
28
|
-
}
|
|
29
|
-
if (userResult.rows[0].subscription_tier !== 'enterprise') {
|
|
30
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
31
|
-
}
|
|
32
|
-
const clientId = userResult.rows[0].client_id;
|
|
33
|
-
|
|
34
|
-
const params = queryStringParameters || {};
|
|
35
|
-
const page = Math.max(1, parseInt(params.page) || 1);
|
|
36
|
-
const pageSize = Math.min(100, Math.max(1, parseInt(params.pageSize) || 20));
|
|
37
|
-
const offset = (page - 1) * pageSize;
|
|
38
|
-
|
|
39
|
-
const conditions = ['client_id = $1'];
|
|
40
|
-
const values = [clientId];
|
|
41
|
-
let paramIndex = 2;
|
|
42
|
-
|
|
43
|
-
if (params.status) {
|
|
44
|
-
conditions.push(`status = $${paramIndex}`);
|
|
45
|
-
values.push(params.status);
|
|
46
|
-
paramIndex++;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (params.category) {
|
|
50
|
-
conditions.push(`category = $${paramIndex}`);
|
|
51
|
-
values.push(params.category);
|
|
52
|
-
paramIndex++;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const whereClause = conditions.join(' AND ');
|
|
56
|
-
|
|
57
|
-
const countResult = await executeQuery(
|
|
58
|
-
`SELECT COUNT(*) as total FROM rapport.curated_knowledge WHERE ${whereClause}`,
|
|
59
|
-
values
|
|
60
|
-
);
|
|
61
|
-
const total = parseInt(countResult.rows[0].total);
|
|
62
|
-
|
|
63
|
-
const dataResult = await executeQuery(
|
|
64
|
-
`SELECT
|
|
65
|
-
knowledge_id, client_id, invariant_id,
|
|
66
|
-
title, content, category, tags,
|
|
67
|
-
curator_email, status,
|
|
68
|
-
created_at, published_at
|
|
69
|
-
FROM rapport.curated_knowledge
|
|
70
|
-
WHERE ${whereClause}
|
|
71
|
-
ORDER BY created_at DESC
|
|
72
|
-
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
|
|
73
|
-
[...values, pageSize, offset]
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
return createSuccessResponse(
|
|
77
|
-
{ Records: dataResult.rows },
|
|
78
|
-
'Knowledge items retrieved',
|
|
79
|
-
{ Total_Records: total, Page: page, Page_Size: pageSize, Request_ID, Timestamp: new Date().toISOString() }
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
} catch (error) {
|
|
83
|
-
return handleError(error);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
exports.handler = wrapHandler(getEnterpriseKnowledge);
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Knowledge Update Handler
|
|
3
|
-
*
|
|
4
|
-
* PUT /api/enterprise/knowledge
|
|
5
|
-
* Body: { knowledge_id, title?, content?, category?, tags?, status? }
|
|
6
|
-
* Returns: { Records: [CuratedKnowledge] } + meta
|
|
7
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, logModificationEvent, AuditEventType, EntityType } = require('./helpers');
|
|
11
|
-
|
|
12
|
-
async function updateEnterpriseKnowledge({ body, requestContext }) {
|
|
13
|
-
try {
|
|
14
|
-
const Request_ID = requestContext.requestId;
|
|
15
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
16
|
-
if (!email) {
|
|
17
|
-
return createErrorResponse(401, 'Authentication required');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const userResult = await executeQuery(
|
|
21
|
-
`SELECT u.client_id, c.subscription_tier
|
|
22
|
-
FROM rapport.users u
|
|
23
|
-
JOIN rapport.clients c ON c.client_id = u.client_id
|
|
24
|
-
WHERE u.email_address = $1 LIMIT 1`,
|
|
25
|
-
[email]
|
|
26
|
-
);
|
|
27
|
-
if (!userResult.rows.length) {
|
|
28
|
-
return createErrorResponse(403, 'User not found');
|
|
29
|
-
}
|
|
30
|
-
if (userResult.rows[0].subscription_tier !== 'enterprise') {
|
|
31
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
32
|
-
}
|
|
33
|
-
const clientId = userResult.rows[0].client_id;
|
|
34
|
-
|
|
35
|
-
const requestBody = body || {};
|
|
36
|
-
const { knowledge_id, title, content, category, tags, status } = requestBody;
|
|
37
|
-
|
|
38
|
-
if (!knowledge_id) {
|
|
39
|
-
return createErrorResponse(400, 'knowledge_id is required');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Fetch current state for audit trail
|
|
43
|
-
const current = await executeQuery(
|
|
44
|
-
'SELECT * FROM rapport.curated_knowledge WHERE knowledge_id = $1 AND client_id = $2',
|
|
45
|
-
[knowledge_id, clientId]
|
|
46
|
-
);
|
|
47
|
-
if (!current.rows.length) {
|
|
48
|
-
return createErrorResponse(404, 'Knowledge item not found');
|
|
49
|
-
}
|
|
50
|
-
const beforeState = current.rows[0];
|
|
51
|
-
|
|
52
|
-
// Build dynamic update
|
|
53
|
-
const updates = [];
|
|
54
|
-
const values = [];
|
|
55
|
-
let paramIndex = 1;
|
|
56
|
-
|
|
57
|
-
if (title !== undefined) {
|
|
58
|
-
updates.push(`title = $${paramIndex}`);
|
|
59
|
-
values.push(title);
|
|
60
|
-
paramIndex++;
|
|
61
|
-
}
|
|
62
|
-
if (content !== undefined) {
|
|
63
|
-
updates.push(`content = $${paramIndex}`);
|
|
64
|
-
values.push(content);
|
|
65
|
-
paramIndex++;
|
|
66
|
-
}
|
|
67
|
-
if (category !== undefined) {
|
|
68
|
-
updates.push(`category = $${paramIndex}`);
|
|
69
|
-
values.push(category || null);
|
|
70
|
-
paramIndex++;
|
|
71
|
-
}
|
|
72
|
-
if (tags !== undefined) {
|
|
73
|
-
updates.push(`tags = $${paramIndex}`);
|
|
74
|
-
values.push(tags);
|
|
75
|
-
paramIndex++;
|
|
76
|
-
}
|
|
77
|
-
if (status !== undefined) {
|
|
78
|
-
updates.push(`status = $${paramIndex}`);
|
|
79
|
-
values.push(status);
|
|
80
|
-
paramIndex++;
|
|
81
|
-
|
|
82
|
-
// Set published_at when transitioning to published
|
|
83
|
-
if (status === 'published' && beforeState.status !== 'published') {
|
|
84
|
-
updates.push('published_at = NOW()');
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (updates.length === 0) {
|
|
89
|
-
return createErrorResponse(400, 'No fields to update');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
updates.push('updated_at = NOW()');
|
|
93
|
-
|
|
94
|
-
const result = await executeQuery(
|
|
95
|
-
`UPDATE rapport.curated_knowledge
|
|
96
|
-
SET ${updates.join(', ')}
|
|
97
|
-
WHERE knowledge_id = $${paramIndex} AND client_id = $${paramIndex + 1}
|
|
98
|
-
RETURNING *`,
|
|
99
|
-
[...values, knowledge_id, clientId]
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const updated = result.rows[0];
|
|
103
|
-
|
|
104
|
-
await logModificationEvent(AuditEventType.DATA_UPDATE, EntityType.KNOWLEDGE, String(knowledge_id), {
|
|
105
|
-
requestContext,
|
|
106
|
-
client_id: clientId,
|
|
107
|
-
before_state: { title: beforeState.title, status: beforeState.status, category: beforeState.category },
|
|
108
|
-
after_state: { title: updated.title, status: updated.status, category: updated.category },
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
return createSuccessResponse(
|
|
112
|
-
{ Records: [updated] },
|
|
113
|
-
'Knowledge item updated',
|
|
114
|
-
{ Total_Records: 1, Request_ID, Timestamp: new Date().toISOString() }
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
} catch (error) {
|
|
118
|
-
return handleError(error);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
exports.handler = wrapHandler(updateEnterpriseKnowledge);
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Onboarding Complete Handler
|
|
3
|
-
*
|
|
4
|
-
* POST /api/enterprise/onboarding/complete
|
|
5
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
6
|
-
*
|
|
7
|
-
* Body: { enabled_categories?: Record<string, boolean> }
|
|
8
|
-
* Marks onboarding as completed and optionally saves standards preferences.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
12
|
-
|
|
13
|
-
async function enterpriseOnboardingComplete({ requestContext, body }) {
|
|
14
|
-
try {
|
|
15
|
-
const Request_ID = requestContext.requestId;
|
|
16
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
17
|
-
if (!email) {
|
|
18
|
-
return createErrorResponse(401, 'Authentication required');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Look up client_id and company_id
|
|
22
|
-
const userResult = await executeQuery(
|
|
23
|
-
`SELECT u.client_id, ue.company_id
|
|
24
|
-
FROM rapport.users u
|
|
25
|
-
JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
|
|
26
|
-
WHERE u.email_address = $1
|
|
27
|
-
LIMIT 1`,
|
|
28
|
-
[email]
|
|
29
|
-
);
|
|
30
|
-
if (!userResult.rows.length) {
|
|
31
|
-
return createErrorResponse(403, 'User not found or no company assigned');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const { client_id, company_id } = userResult.rows[0];
|
|
35
|
-
if (!company_id) {
|
|
36
|
-
return createErrorResponse(400, 'No company associated with this account');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Verify enterprise tier
|
|
40
|
-
const clientResult = await executeQuery(
|
|
41
|
-
'SELECT subscription_tier FROM rapport.clients WHERE client_id = $1',
|
|
42
|
-
[client_id]
|
|
43
|
-
);
|
|
44
|
-
if (!clientResult.rows.length || clientResult.rows[0].subscription_tier !== 'enterprise') {
|
|
45
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Save standards preferences if provided
|
|
49
|
-
const enabledCategories = body?.enabled_categories;
|
|
50
|
-
const standardsConfigured = enabledCategories && Object.keys(enabledCategories).length > 0;
|
|
51
|
-
|
|
52
|
-
// Mark onboarding as completed
|
|
53
|
-
await executeQuery(
|
|
54
|
-
`INSERT INTO rapport.enterprise_onboarding_status
|
|
55
|
-
(company_id, step_completed, standards_configured, completed, completed_at, completed_by)
|
|
56
|
-
VALUES ($1, 5, $2, true, NOW(), $3)
|
|
57
|
-
ON CONFLICT (company_id) DO UPDATE SET
|
|
58
|
-
step_completed = 5,
|
|
59
|
-
standards_configured = COALESCE($2, rapport.enterprise_onboarding_status.standards_configured),
|
|
60
|
-
completed = true,
|
|
61
|
-
completed_at = NOW(),
|
|
62
|
-
completed_by = $3`,
|
|
63
|
-
[company_id, standardsConfigured || false, email]
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
return createSuccessResponse(
|
|
67
|
-
{ Records: [{ company_id, completed: true }] },
|
|
68
|
-
'Enterprise onboarding completed',
|
|
69
|
-
{ Request_ID, Timestamp: new Date().toISOString() }
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
} catch (error) {
|
|
73
|
-
return handleError(error);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
exports.handler = wrapHandler(enterpriseOnboardingComplete);
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Onboarding Invite Handler
|
|
3
|
-
*
|
|
4
|
-
* POST /api/enterprise/onboarding/invite
|
|
5
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
6
|
-
*
|
|
7
|
-
* Body: { invitations: [{ email, role }] }
|
|
8
|
-
* Creates invitation records for team members. Max 10 per request.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
12
|
-
|
|
13
|
-
async function enterpriseOnboardingInvite({ requestContext, body }) {
|
|
14
|
-
try {
|
|
15
|
-
const Request_ID = requestContext.requestId;
|
|
16
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
17
|
-
if (!email) {
|
|
18
|
-
return createErrorResponse(401, 'Authentication required');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Look up client_id and company_id
|
|
22
|
-
const userResult = await executeQuery(
|
|
23
|
-
`SELECT u.client_id, ue.company_id
|
|
24
|
-
FROM rapport.users u
|
|
25
|
-
JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
|
|
26
|
-
WHERE u.email_address = $1
|
|
27
|
-
LIMIT 1`,
|
|
28
|
-
[email]
|
|
29
|
-
);
|
|
30
|
-
if (!userResult.rows.length) {
|
|
31
|
-
return createErrorResponse(403, 'User not found or no company assigned');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const { client_id, company_id } = userResult.rows[0];
|
|
35
|
-
if (!company_id) {
|
|
36
|
-
return createErrorResponse(400, 'No company associated with this account');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Verify enterprise tier
|
|
40
|
-
const clientResult = await executeQuery(
|
|
41
|
-
'SELECT subscription_tier, seat_count FROM rapport.clients WHERE client_id = $1',
|
|
42
|
-
[client_id]
|
|
43
|
-
);
|
|
44
|
-
if (!clientResult.rows.length || clientResult.rows[0].subscription_tier !== 'enterprise') {
|
|
45
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const seatCount = clientResult.rows[0].seat_count || 25;
|
|
49
|
-
|
|
50
|
-
// Validate invitations
|
|
51
|
-
const invitations = body?.invitations;
|
|
52
|
-
if (!Array.isArray(invitations) || invitations.length === 0) {
|
|
53
|
-
return createErrorResponse(400, 'invitations array is required');
|
|
54
|
-
}
|
|
55
|
-
if (invitations.length > 10) {
|
|
56
|
-
return createErrorResponse(400, 'Maximum 10 invitations per request');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
60
|
-
const validRoles = ['admin', 'member'];
|
|
61
|
-
|
|
62
|
-
for (const inv of invitations) {
|
|
63
|
-
if (!inv.email || !emailRegex.test(inv.email)) {
|
|
64
|
-
return createErrorResponse(400, `Invalid email: ${inv.email || '(empty)'}`);
|
|
65
|
-
}
|
|
66
|
-
if (!inv.role || !validRoles.includes(inv.role)) {
|
|
67
|
-
return createErrorResponse(400, `Invalid role for ${inv.email}: must be admin or member`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Check seat capacity
|
|
72
|
-
const existingResult = await executeQuery(
|
|
73
|
-
`SELECT COUNT(*) as count FROM rapport.user_entitlements WHERE company_id = $1`,
|
|
74
|
-
[company_id]
|
|
75
|
-
);
|
|
76
|
-
const existingUsers = parseInt(existingResult.rows[0].count, 10);
|
|
77
|
-
|
|
78
|
-
const pendingResult = await executeQuery(
|
|
79
|
-
`SELECT COUNT(*) as count FROM rapport.enterprise_invitations
|
|
80
|
-
WHERE company_id = $1 AND status = 'pending'`,
|
|
81
|
-
[company_id]
|
|
82
|
-
);
|
|
83
|
-
const pendingInvites = parseInt(pendingResult.rows[0].count, 10);
|
|
84
|
-
|
|
85
|
-
const totalAfterInvite = existingUsers + pendingInvites + invitations.length;
|
|
86
|
-
if (totalAfterInvite > seatCount) {
|
|
87
|
-
return createErrorResponse(400,
|
|
88
|
-
`Seat limit exceeded. You have ${seatCount} seats, ${existingUsers} users, ${pendingInvites} pending invites. Cannot add ${invitations.length} more.`
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Insert invitations (skip duplicates)
|
|
93
|
-
const inserted = [];
|
|
94
|
-
for (const inv of invitations) {
|
|
95
|
-
try {
|
|
96
|
-
const result = await executeQuery(
|
|
97
|
-
`INSERT INTO rapport.enterprise_invitations
|
|
98
|
-
(client_id, company_id, email, role, invited_by, status, created_at)
|
|
99
|
-
VALUES ($1, $2, $3, $4, $5, 'pending', NOW())
|
|
100
|
-
ON CONFLICT (company_id, email) DO UPDATE SET
|
|
101
|
-
role = EXCLUDED.role,
|
|
102
|
-
invited_by = EXCLUDED.invited_by,
|
|
103
|
-
status = 'pending',
|
|
104
|
-
created_at = NOW()
|
|
105
|
-
RETURNING invitation_id, email, role, status`,
|
|
106
|
-
[client_id, company_id, inv.email.toLowerCase(), inv.role, email]
|
|
107
|
-
);
|
|
108
|
-
if (result.rows.length) {
|
|
109
|
-
inserted.push(result.rows[0]);
|
|
110
|
-
}
|
|
111
|
-
} catch (insertErr) {
|
|
112
|
-
console.log(`[EnterpriseInvite] Failed to insert invite for ${inv.email}:`, insertErr.message);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Update onboarding status
|
|
117
|
-
await executeQuery(
|
|
118
|
-
`INSERT INTO rapport.enterprise_onboarding_status
|
|
119
|
-
(company_id, step_completed, invites_sent, completed_by)
|
|
120
|
-
VALUES ($1, 2, $2, $3)
|
|
121
|
-
ON CONFLICT (company_id) DO UPDATE SET
|
|
122
|
-
step_completed = GREATEST(rapport.enterprise_onboarding_status.step_completed, 2),
|
|
123
|
-
invites_sent = $2`,
|
|
124
|
-
[company_id, inserted.length, email]
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
return createSuccessResponse(
|
|
128
|
-
{ Records: inserted, sent: inserted.length },
|
|
129
|
-
`${inserted.length} invitation(s) created`,
|
|
130
|
-
{ Request_ID, Timestamp: new Date().toISOString() }
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
} catch (error) {
|
|
134
|
-
return handleError(error);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
exports.handler = wrapHandler(enterpriseOnboardingInvite);
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Onboarding Setup Handler
|
|
3
|
-
*
|
|
4
|
-
* POST /api/enterprise/onboarding/setup
|
|
5
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
6
|
-
*
|
|
7
|
-
* Body: { company_name, domain, industry?, company_size? }
|
|
8
|
-
* Updates the company profile and marks onboarding step 1 complete.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
12
|
-
|
|
13
|
-
async function enterpriseOnboardingSetup({ requestContext, body }) {
|
|
14
|
-
try {
|
|
15
|
-
const Request_ID = requestContext.requestId;
|
|
16
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
17
|
-
if (!email) {
|
|
18
|
-
return createErrorResponse(401, 'Authentication required');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Look up client_id and company_id for the authenticated user
|
|
22
|
-
const userResult = await executeQuery(
|
|
23
|
-
`SELECT u.client_id, ue.company_id
|
|
24
|
-
FROM rapport.users u
|
|
25
|
-
JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
|
|
26
|
-
WHERE u.email_address = $1
|
|
27
|
-
LIMIT 1`,
|
|
28
|
-
[email]
|
|
29
|
-
);
|
|
30
|
-
if (!userResult.rows.length) {
|
|
31
|
-
return createErrorResponse(403, 'User not found or no company assigned');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const { client_id, company_id: currentCompanyId } = userResult.rows[0];
|
|
35
|
-
if (!currentCompanyId) {
|
|
36
|
-
return createErrorResponse(400, 'No company associated with this account');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Verify enterprise tier
|
|
40
|
-
const clientResult = await executeQuery(
|
|
41
|
-
'SELECT subscription_tier FROM rapport.clients WHERE client_id = $1',
|
|
42
|
-
[client_id]
|
|
43
|
-
);
|
|
44
|
-
if (!clientResult.rows.length || clientResult.rows[0].subscription_tier !== 'enterprise') {
|
|
45
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Validate required fields
|
|
49
|
-
const companyName = body?.company_name;
|
|
50
|
-
const domain = body?.domain;
|
|
51
|
-
if (!companyName || !domain) {
|
|
52
|
-
return createErrorResponse(400, 'company_name and domain are required');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const industry = body?.industry || null;
|
|
56
|
-
const companySize = body?.company_size || null;
|
|
57
|
-
|
|
58
|
-
// Generate a proper enterprise company_id from company name
|
|
59
|
-
const enterpriseCompanyId = `${companyName.replace(/[^a-zA-Z0-9]/g, '_').toUpperCase()}_MAIN`;
|
|
60
|
-
let company_id = currentCompanyId;
|
|
61
|
-
|
|
62
|
-
if (enterpriseCompanyId !== currentCompanyId) {
|
|
63
|
-
// Create the enterprise company (or update if it already exists)
|
|
64
|
-
await executeQuery(
|
|
65
|
-
`INSERT INTO rapport.companies (company_id, client_id, company_name, company_status, domain, industry, company_size)
|
|
66
|
-
VALUES ($1, $2, $3, 'Active', $4, $5, $6)
|
|
67
|
-
ON CONFLICT (company_id) DO UPDATE SET
|
|
68
|
-
company_name = $3, domain = $4, industry = $5, company_size = $6, last_updated = NOW()`,
|
|
69
|
-
[enterpriseCompanyId, client_id, companyName, domain, industry, companySize]
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
// Migrate user entitlement from personal to enterprise company
|
|
73
|
-
await executeQuery(
|
|
74
|
-
`UPDATE rapport.user_entitlements
|
|
75
|
-
SET company_id = $1
|
|
76
|
-
WHERE email_address = $2 AND company_id = $3`,
|
|
77
|
-
[enterpriseCompanyId, email, currentCompanyId]
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
// Clean up orphaned personal company if nothing else references it
|
|
81
|
-
await executeQuery(
|
|
82
|
-
`DELETE FROM rapport.companies
|
|
83
|
-
WHERE company_id = $1
|
|
84
|
-
AND NOT EXISTS (
|
|
85
|
-
SELECT 1 FROM rapport.user_entitlements WHERE company_id = $1
|
|
86
|
-
)
|
|
87
|
-
AND NOT EXISTS (
|
|
88
|
-
SELECT 1 FROM rapport.projects WHERE company_id = $1
|
|
89
|
-
)`,
|
|
90
|
-
[currentCompanyId]
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
company_id = enterpriseCompanyId;
|
|
94
|
-
console.log(`Migrated company: ${currentCompanyId} -> ${enterpriseCompanyId}`);
|
|
95
|
-
} else {
|
|
96
|
-
// Same company_id, just update profile
|
|
97
|
-
await executeQuery(
|
|
98
|
-
`UPDATE rapport.companies
|
|
99
|
-
SET company_name = $1, domain = $2, industry = $3, company_size = $4, last_updated = NOW()
|
|
100
|
-
WHERE company_id = $5`,
|
|
101
|
-
[companyName, domain, industry, companySize, company_id]
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Upsert onboarding status
|
|
106
|
-
await executeQuery(
|
|
107
|
-
`INSERT INTO rapport.enterprise_onboarding_status
|
|
108
|
-
(company_id, step_completed, org_configured, started_at, completed_by)
|
|
109
|
-
VALUES ($1, 1, true, NOW(), $2)
|
|
110
|
-
ON CONFLICT (company_id) DO UPDATE SET
|
|
111
|
-
step_completed = GREATEST(rapport.enterprise_onboarding_status.step_completed, 1),
|
|
112
|
-
org_configured = true,
|
|
113
|
-
completed_by = $2`,
|
|
114
|
-
[company_id, email]
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
return createSuccessResponse(
|
|
118
|
-
{ Records: [{ company_id, company_name: companyName, domain, step_completed: 1 }] },
|
|
119
|
-
'Organization profile saved',
|
|
120
|
-
{ Request_ID, Timestamp: new Date().toISOString() }
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
} catch (error) {
|
|
124
|
-
return handleError(error);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
exports.handler = wrapHandler(enterpriseOnboardingSetup);
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Onboarding Status Handler
|
|
3
|
-
*
|
|
4
|
-
* GET /api/enterprise/onboarding/status
|
|
5
|
-
* Auth: Cognito JWT required
|
|
6
|
-
*
|
|
7
|
-
* Returns the enterprise onboarding completion state for the user's company.
|
|
8
|
-
* Used by Dashboard to decide whether to redirect to enterprise onboarding.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
12
|
-
|
|
13
|
-
async function getEnterpriseOnboardingStatus({ requestContext }) {
|
|
14
|
-
try {
|
|
15
|
-
const Request_ID = requestContext.requestId;
|
|
16
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
17
|
-
if (!email) {
|
|
18
|
-
return createErrorResponse(401, 'Authentication required');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Look up client_id and company_id
|
|
22
|
-
const userResult = await executeQuery(
|
|
23
|
-
`SELECT u.client_id, ue.company_id
|
|
24
|
-
FROM rapport.users u
|
|
25
|
-
JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
|
|
26
|
-
WHERE u.email_address = $1
|
|
27
|
-
LIMIT 1`,
|
|
28
|
-
[email]
|
|
29
|
-
);
|
|
30
|
-
if (!userResult.rows.length) {
|
|
31
|
-
return createSuccessResponse(
|
|
32
|
-
{ Records: [{ completed: false, step_completed: 0 }] },
|
|
33
|
-
'No company found',
|
|
34
|
-
{ Request_ID, Timestamp: new Date().toISOString() }
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const { client_id, company_id } = userResult.rows[0];
|
|
39
|
-
if (!company_id) {
|
|
40
|
-
return createSuccessResponse(
|
|
41
|
-
{ Records: [{ completed: false, step_completed: 0 }] },
|
|
42
|
-
'No company assigned',
|
|
43
|
-
{ Request_ID, Timestamp: new Date().toISOString() }
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Check if enterprise tier
|
|
48
|
-
const clientResult = await executeQuery(
|
|
49
|
-
'SELECT subscription_tier FROM rapport.clients WHERE client_id = $1',
|
|
50
|
-
[client_id]
|
|
51
|
-
);
|
|
52
|
-
if (!clientResult.rows.length || clientResult.rows[0].subscription_tier !== 'enterprise') {
|
|
53
|
-
return createSuccessResponse(
|
|
54
|
-
{ Records: [{ completed: true, step_completed: 5, not_enterprise: true }] },
|
|
55
|
-
'Not an enterprise account',
|
|
56
|
-
{ Request_ID, Timestamp: new Date().toISOString() }
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Get onboarding status
|
|
61
|
-
const statusResult = await executeQuery(
|
|
62
|
-
`SELECT *
|
|
63
|
-
FROM rapport.enterprise_onboarding_status
|
|
64
|
-
WHERE company_id = $1`,
|
|
65
|
-
[company_id]
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
if (!statusResult.rows.length) {
|
|
69
|
-
return createSuccessResponse(
|
|
70
|
-
{ Records: [{ completed: false, step_completed: 0, company_id }] },
|
|
71
|
-
'Onboarding not started',
|
|
72
|
-
{ Request_ID, Timestamp: new Date().toISOString() }
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const status = statusResult.rows[0];
|
|
77
|
-
return createSuccessResponse(
|
|
78
|
-
{ Records: [{ ...status, company_id }] },
|
|
79
|
-
'Onboarding status retrieved',
|
|
80
|
-
{ Request_ID, Timestamp: new Date().toISOString() }
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
} catch (error) {
|
|
84
|
-
return handleError(error);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
exports.handler = wrapHandler(getEnterpriseOnboardingStatus);
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub Connection Status Handler
|
|
3
|
-
* Returns current GitHub connection status
|
|
4
|
-
*
|
|
5
|
-
* GET /api/github/status (CognitoAuthorizer)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
|
|
9
|
-
|
|
10
|
-
async function githubConnectionStatus({ requestContext }) {
|
|
11
|
-
try {
|
|
12
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
13
|
-
|
|
14
|
-
if (!email) {
|
|
15
|
-
return createErrorResponse(401, 'Authentication required');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Check for active connection
|
|
19
|
-
const connResult = await executeQuery(`
|
|
20
|
-
SELECT github_username, connected_at, last_used_at
|
|
21
|
-
FROM rapport.github_connections
|
|
22
|
-
WHERE email_address = $1 AND revoked = FALSE
|
|
23
|
-
`, [email]);
|
|
24
|
-
|
|
25
|
-
const connected = connResult.rowCount > 0;
|
|
26
|
-
const github_username = connected ? connResult.rows[0].github_username : null;
|
|
27
|
-
|
|
28
|
-
// Count projects with GitHub connected
|
|
29
|
-
const projectsResult = await executeQuery(`
|
|
30
|
-
SELECT COUNT(*) as count FROM rapport.projects p
|
|
31
|
-
JOIN rapport.project_collaborators pc ON p.project_id = pc.project_id
|
|
32
|
-
WHERE pc.email_address = $1
|
|
33
|
-
AND p.repo_url IS NOT NULL
|
|
34
|
-
AND p.archived = FALSE
|
|
35
|
-
`, [email]);
|
|
36
|
-
|
|
37
|
-
const projects_connected = parseInt(projectsResult.rows[0].count);
|
|
38
|
-
|
|
39
|
-
return createSuccessResponse(
|
|
40
|
-
{ connected, github_username, projects_connected },
|
|
41
|
-
'Status retrieved'
|
|
42
|
-
);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
console.error('GitHub Status Error:', error);
|
|
45
|
-
return createErrorResponse(500, 'Failed to get connection status');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
exports.handler = wrapHandler(githubConnectionStatus);
|