@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,178 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub OAuth Callback Handler
|
|
3
|
-
* Receives GET redirect from GitHub, exchanges code for token, redirects to frontend
|
|
4
|
-
*
|
|
5
|
-
* GET /api/github/oauth/callback (No auth - GitHub redirect)
|
|
6
|
-
* Query: ?code=...&state=...
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery } = require('./helpers');
|
|
10
|
-
const crypto = require('crypto');
|
|
11
|
-
const https = require('https');
|
|
12
|
-
|
|
13
|
-
const FRONTEND_URL = process.env.FRONTEND_URL || 'https://app.mindmeld.dev';
|
|
14
|
-
|
|
15
|
-
function redirect(url) {
|
|
16
|
-
return {
|
|
17
|
-
statusCode: 302,
|
|
18
|
-
headers: {
|
|
19
|
-
'Location': url,
|
|
20
|
-
'Access-Control-Allow-Origin': '*'
|
|
21
|
-
},
|
|
22
|
-
body: ''
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function httpsRequest(options, postData) {
|
|
27
|
-
return new Promise((resolve, reject) => {
|
|
28
|
-
const req = https.request(options, (res) => {
|
|
29
|
-
let data = '';
|
|
30
|
-
res.on('data', chunk => data += chunk);
|
|
31
|
-
res.on('end', () => {
|
|
32
|
-
try {
|
|
33
|
-
resolve({ statusCode: res.statusCode, body: JSON.parse(data) });
|
|
34
|
-
} catch (e) {
|
|
35
|
-
// GitHub token endpoint may return form-encoded
|
|
36
|
-
const parsed = {};
|
|
37
|
-
data.split('&').forEach(pair => {
|
|
38
|
-
const [key, val] = pair.split('=');
|
|
39
|
-
if (key) parsed[decodeURIComponent(key)] = decodeURIComponent(val || '');
|
|
40
|
-
});
|
|
41
|
-
resolve({ statusCode: res.statusCode, body: parsed });
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
req.on('error', reject);
|
|
46
|
-
if (postData) req.write(postData);
|
|
47
|
-
req.end();
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function encryptToken(token, key) {
|
|
52
|
-
const iv = crypto.randomBytes(16);
|
|
53
|
-
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key, 'hex'), iv);
|
|
54
|
-
let encrypted = cipher.update(token, 'utf8', 'hex');
|
|
55
|
-
encrypted += cipher.final('hex');
|
|
56
|
-
return iv.toString('hex') + ':' + encrypted;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function githubOAuthCallback({ queryStringParameters }) {
|
|
60
|
-
try {
|
|
61
|
-
const code = queryStringParameters.code;
|
|
62
|
-
const state = queryStringParameters.state;
|
|
63
|
-
|
|
64
|
-
if (!code || !state) {
|
|
65
|
-
console.error('Missing code or state in callback');
|
|
66
|
-
return redirect(`${FRONTEND_URL}/onboarding?error=missing_params`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Look up state token in DB to identify user
|
|
70
|
-
const stateResult = await executeQuery(`
|
|
71
|
-
SELECT email_address FROM rapport.github_oauth_states
|
|
72
|
-
WHERE state_token = $1 AND used = FALSE
|
|
73
|
-
AND created_at > NOW() - INTERVAL '15 minutes'
|
|
74
|
-
`, [state]);
|
|
75
|
-
|
|
76
|
-
if (stateResult.rowCount === 0) {
|
|
77
|
-
console.error('Invalid or expired state token');
|
|
78
|
-
return redirect(`${FRONTEND_URL}/onboarding?error=invalid_state`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const email = stateResult.rows[0].email_address;
|
|
82
|
-
|
|
83
|
-
// Mark state as used
|
|
84
|
-
await executeQuery(`
|
|
85
|
-
UPDATE rapport.github_oauth_states SET used = TRUE
|
|
86
|
-
WHERE state_token = $1
|
|
87
|
-
`, [state]);
|
|
88
|
-
|
|
89
|
-
// Exchange code for access token
|
|
90
|
-
const tokenPayload = JSON.stringify({
|
|
91
|
-
client_id: process.env.GITHUB_OAUTH_CLIENT_ID,
|
|
92
|
-
client_secret: process.env.GITHUB_OAUTH_CLIENT_SECRET,
|
|
93
|
-
code: code
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const tokenResponse = await httpsRequest({
|
|
97
|
-
hostname: 'github.com',
|
|
98
|
-
path: '/login/oauth/access_token',
|
|
99
|
-
method: 'POST',
|
|
100
|
-
headers: {
|
|
101
|
-
'Content-Type': 'application/json',
|
|
102
|
-
'Accept': 'application/json',
|
|
103
|
-
'Content-Length': Buffer.byteLength(tokenPayload)
|
|
104
|
-
}
|
|
105
|
-
}, tokenPayload);
|
|
106
|
-
|
|
107
|
-
const accessToken = tokenResponse.body.access_token;
|
|
108
|
-
if (!accessToken) {
|
|
109
|
-
console.error('GitHub token exchange failed:', tokenResponse.body);
|
|
110
|
-
return redirect(`${FRONTEND_URL}/onboarding?error=token_exchange_failed`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const tokenScope = tokenResponse.body.scope || '';
|
|
114
|
-
|
|
115
|
-
// Get GitHub user info
|
|
116
|
-
const userResponse = await httpsRequest({
|
|
117
|
-
hostname: 'api.github.com',
|
|
118
|
-
path: '/user',
|
|
119
|
-
method: 'GET',
|
|
120
|
-
headers: {
|
|
121
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
122
|
-
'User-Agent': 'MindMeld-App',
|
|
123
|
-
'Accept': 'application/json'
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (userResponse.statusCode !== 200) {
|
|
128
|
-
console.error('GitHub user fetch failed:', userResponse.statusCode);
|
|
129
|
-
return redirect(`${FRONTEND_URL}/onboarding?error=github_user_failed`);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const githubUser = userResponse.body;
|
|
133
|
-
|
|
134
|
-
// Encrypt the access token
|
|
135
|
-
const encryptionKey = process.env.GITHUB_TOKEN_ENCRYPTION_KEY;
|
|
136
|
-
if (!encryptionKey) {
|
|
137
|
-
console.error('GITHUB_TOKEN_ENCRYPTION_KEY not configured');
|
|
138
|
-
return redirect(`${FRONTEND_URL}/onboarding?error=config_error`);
|
|
139
|
-
}
|
|
140
|
-
const encryptedToken = encryptToken(accessToken, encryptionKey);
|
|
141
|
-
|
|
142
|
-
// Upsert GitHub connection
|
|
143
|
-
await executeQuery(`
|
|
144
|
-
INSERT INTO rapport.github_connections (
|
|
145
|
-
email_address, github_username, github_user_id,
|
|
146
|
-
access_token_encrypted, token_scope, connected_at
|
|
147
|
-
) VALUES ($1, $2, $3, $4, $5, NOW())
|
|
148
|
-
ON CONFLICT (email_address) DO UPDATE SET
|
|
149
|
-
github_username = EXCLUDED.github_username,
|
|
150
|
-
github_user_id = EXCLUDED.github_user_id,
|
|
151
|
-
access_token_encrypted = EXCLUDED.access_token_encrypted,
|
|
152
|
-
token_scope = EXCLUDED.token_scope,
|
|
153
|
-
connected_at = NOW(),
|
|
154
|
-
revoked = FALSE
|
|
155
|
-
`, [email, githubUser.login, githubUser.id, encryptedToken, tokenScope]);
|
|
156
|
-
|
|
157
|
-
// Determine redirect: enterprise users go to enterprise onboarding
|
|
158
|
-
const tierResult = await executeQuery(`
|
|
159
|
-
SELECT c.subscription_tier FROM rapport.users u
|
|
160
|
-
JOIN rapport.clients c ON c.client_id = u.client_id
|
|
161
|
-
WHERE u.email_address = $1 LIMIT 1
|
|
162
|
-
`, [email]);
|
|
163
|
-
|
|
164
|
-
const tier = tierResult.rows[0]?.subscription_tier;
|
|
165
|
-
if (tier === 'enterprise') {
|
|
166
|
-
return redirect(`${FRONTEND_URL}/enterprise/onboarding?step=4&github=connected`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Standard users - redirect to onboarding step 2
|
|
170
|
-
return redirect(`${FRONTEND_URL}/onboarding?step=2`);
|
|
171
|
-
|
|
172
|
-
} catch (error) {
|
|
173
|
-
console.error('GitHub OAuth Callback Error:', error);
|
|
174
|
-
return redirect(`${FRONTEND_URL}/onboarding?error=server_error`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
exports.handler = wrapHandler(githubOAuthCallback);
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub OAuth Start Handler
|
|
3
|
-
* Generates GitHub OAuth authorization URL
|
|
4
|
-
*
|
|
5
|
-
* GET /api/github/oauth/start (CognitoAuthorizer)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
|
|
9
|
-
const crypto = require('crypto');
|
|
10
|
-
|
|
11
|
-
async function githubOAuthStart({ requestContext }) {
|
|
12
|
-
try {
|
|
13
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
14
|
-
|
|
15
|
-
if (!email) {
|
|
16
|
-
return createErrorResponse(401, 'Authentication required');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const clientId = process.env.GITHUB_OAUTH_CLIENT_ID;
|
|
20
|
-
if (!clientId) {
|
|
21
|
-
return createErrorResponse(500, 'GitHub OAuth not configured');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Generate unique state token and store in DB for CSRF verification
|
|
25
|
-
const stateToken = crypto.randomBytes(32).toString('hex');
|
|
26
|
-
|
|
27
|
-
// Clean up old unused states for this user, then insert new one
|
|
28
|
-
await executeQuery(`
|
|
29
|
-
DELETE FROM rapport.github_oauth_states
|
|
30
|
-
WHERE email_address = $1 OR created_at < NOW() - INTERVAL '15 minutes'
|
|
31
|
-
`, [email]);
|
|
32
|
-
|
|
33
|
-
await executeQuery(`
|
|
34
|
-
INSERT INTO rapport.github_oauth_states (state_token, email_address)
|
|
35
|
-
VALUES ($1, $2)
|
|
36
|
-
`, [stateToken, email]);
|
|
37
|
-
|
|
38
|
-
const callbackUrl = process.env.GITHUB_OAUTH_CALLBACK_URL || 'https://api.mindmeld.dev/api/github/oauth/callback';
|
|
39
|
-
|
|
40
|
-
const params = new URLSearchParams({
|
|
41
|
-
client_id: clientId,
|
|
42
|
-
redirect_uri: callbackUrl,
|
|
43
|
-
scope: 'repo',
|
|
44
|
-
state: stateToken
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const authorization_url = `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
48
|
-
|
|
49
|
-
return createSuccessResponse(
|
|
50
|
-
{ authorization_url },
|
|
51
|
-
'OAuth URL generated'
|
|
52
|
-
);
|
|
53
|
-
} catch (error) {
|
|
54
|
-
console.error('GitHub OAuth Start Error:', error);
|
|
55
|
-
return createErrorResponse(500, 'Failed to generate OAuth URL');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
exports.handler = wrapHandler(githubOAuthStart);
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub Patterns Review Handler
|
|
3
|
-
* Processes user's approve/reject decisions on discovered patterns
|
|
4
|
-
*
|
|
5
|
-
* PUT /api/github/patterns/review (CognitoAuthorizer)
|
|
6
|
-
* Body: { project_id, approvals: [{ discovery_id, approved }] }
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, verifyProjectAccess } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
async function githubPatternsReview({ body, requestContext }) {
|
|
12
|
-
try {
|
|
13
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
14
|
-
|
|
15
|
-
if (!email) {
|
|
16
|
-
return createErrorResponse(401, 'Authentication required');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const { project_id, approvals } = body;
|
|
20
|
-
if (!project_id || !approvals || !Array.isArray(approvals)) {
|
|
21
|
-
return createErrorResponse(400, 'project_id and approvals array are required');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Verify user has access to project (collaborator or company member)
|
|
25
|
-
const projectAccess = await verifyProjectAccess(project_id, email);
|
|
26
|
-
if (!projectAccess) {
|
|
27
|
-
return createErrorResponse(403, 'Access denied to project');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
let patterns_created = 0;
|
|
31
|
-
let patterns_rejected = 0;
|
|
32
|
-
|
|
33
|
-
for (const { discovery_id, approved } of approvals) {
|
|
34
|
-
if (!discovery_id) continue;
|
|
35
|
-
// Skip frontend-generated placeholder IDs (disc_0, disc_1, etc.)
|
|
36
|
-
// These occur when the discover endpoint didn't return DB-generated UUIDs
|
|
37
|
-
if (discovery_id.startsWith('disc_')) continue;
|
|
38
|
-
|
|
39
|
-
if (approved) {
|
|
40
|
-
// Update discovery status to approved
|
|
41
|
-
await executeQuery(`
|
|
42
|
-
UPDATE rapport.onboarding_discoveries
|
|
43
|
-
SET status = 'approved', reviewed_at = NOW()
|
|
44
|
-
WHERE discovery_id = $1 AND project_id = $2
|
|
45
|
-
`, [discovery_id, project_id]);
|
|
46
|
-
|
|
47
|
-
patterns_created++;
|
|
48
|
-
} else {
|
|
49
|
-
// Reject
|
|
50
|
-
await executeQuery(`
|
|
51
|
-
UPDATE rapport.onboarding_discoveries
|
|
52
|
-
SET status = 'rejected', reviewed_at = NOW()
|
|
53
|
-
WHERE discovery_id = $1 AND project_id = $2
|
|
54
|
-
`, [discovery_id, project_id]);
|
|
55
|
-
|
|
56
|
-
patterns_rejected++;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Mark onboarding as completed
|
|
61
|
-
await executeQuery(`
|
|
62
|
-
UPDATE rapport.projects SET onboarding_completed = TRUE
|
|
63
|
-
WHERE project_id = $1
|
|
64
|
-
`, [project_id]);
|
|
65
|
-
|
|
66
|
-
return createSuccessResponse(
|
|
67
|
-
{ patterns_created, patterns_rejected },
|
|
68
|
-
'Patterns reviewed successfully'
|
|
69
|
-
);
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.error('GitHub Patterns Review Error:', error);
|
|
72
|
-
return createErrorResponse(500, 'Failed to process pattern reviews');
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
exports.handler = wrapHandler(githubPatternsReview);
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub Repos List Handler
|
|
3
|
-
* Lists user's GitHub repositories
|
|
4
|
-
*
|
|
5
|
-
* GET /api/github/repos (CognitoAuthorizer)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
|
|
9
|
-
const crypto = require('crypto');
|
|
10
|
-
const https = require('https');
|
|
11
|
-
|
|
12
|
-
function httpsGet(options) {
|
|
13
|
-
return new Promise((resolve, reject) => {
|
|
14
|
-
const req = https.request(options, (res) => {
|
|
15
|
-
let data = '';
|
|
16
|
-
res.on('data', chunk => data += chunk);
|
|
17
|
-
res.on('end', () => {
|
|
18
|
-
try {
|
|
19
|
-
resolve({ statusCode: res.statusCode, body: JSON.parse(data) });
|
|
20
|
-
} catch (e) {
|
|
21
|
-
resolve({ statusCode: res.statusCode, body: data });
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
req.on('error', reject);
|
|
26
|
-
req.end();
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function decryptToken(encryptedData, key) {
|
|
31
|
-
const [ivHex, encrypted] = encryptedData.split(':');
|
|
32
|
-
const iv = Buffer.from(ivHex, 'hex');
|
|
33
|
-
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key, 'hex'), iv);
|
|
34
|
-
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
35
|
-
decrypted += decipher.final('utf8');
|
|
36
|
-
return decrypted;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function githubReposList({ requestContext }) {
|
|
40
|
-
try {
|
|
41
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
42
|
-
|
|
43
|
-
if (!email) {
|
|
44
|
-
return createErrorResponse(401, 'Authentication required');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Get stored connection
|
|
48
|
-
const connResult = await executeQuery(`
|
|
49
|
-
SELECT access_token_encrypted FROM rapport.github_connections
|
|
50
|
-
WHERE email_address = $1 AND revoked = FALSE
|
|
51
|
-
`, [email]);
|
|
52
|
-
|
|
53
|
-
if (connResult.rowCount === 0) {
|
|
54
|
-
return createErrorResponse(404, 'No GitHub connection found. Please connect GitHub first.');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Decrypt token
|
|
58
|
-
const encryptionKey = process.env.GITHUB_TOKEN_ENCRYPTION_KEY;
|
|
59
|
-
const accessToken = decryptToken(connResult.rows[0].access_token_encrypted, encryptionKey);
|
|
60
|
-
|
|
61
|
-
// Fetch repos from GitHub
|
|
62
|
-
const reposResponse = await httpsGet({
|
|
63
|
-
hostname: 'api.github.com',
|
|
64
|
-
path: '/user/repos?sort=updated&per_page=30&type=owner',
|
|
65
|
-
method: 'GET',
|
|
66
|
-
headers: {
|
|
67
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
68
|
-
'User-Agent': 'MindMeld-App',
|
|
69
|
-
'Accept': 'application/json'
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (reposResponse.statusCode !== 200) {
|
|
74
|
-
console.error('GitHub repos fetch failed:', reposResponse.statusCode);
|
|
75
|
-
return createErrorResponse(502, 'Failed to fetch repositories from GitHub');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Update last_used_at
|
|
79
|
-
await executeQuery(`
|
|
80
|
-
UPDATE rapport.github_connections SET last_used_at = NOW()
|
|
81
|
-
WHERE email_address = $1
|
|
82
|
-
`, [email]);
|
|
83
|
-
|
|
84
|
-
// Map to minimal repo info
|
|
85
|
-
const repos = reposResponse.body.map(repo => ({
|
|
86
|
-
name: repo.name,
|
|
87
|
-
full_name: repo.full_name,
|
|
88
|
-
language: repo.language,
|
|
89
|
-
default_branch: repo.default_branch,
|
|
90
|
-
private: repo.private,
|
|
91
|
-
html_url: repo.html_url,
|
|
92
|
-
updated_at: repo.updated_at
|
|
93
|
-
}));
|
|
94
|
-
|
|
95
|
-
return createSuccessResponse(
|
|
96
|
-
{ repos },
|
|
97
|
-
'Repositories fetched successfully'
|
|
98
|
-
);
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error('GitHub Repos List Error:', error);
|
|
101
|
-
return createErrorResponse(500, 'Failed to list repositories');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
exports.handler = wrapHandler(githubReposList);
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Health Check Handler
|
|
3
|
-
* Returns system health status for monitoring
|
|
4
|
-
*
|
|
5
|
-
* GET /api/health
|
|
6
|
-
* Auth: None (public endpoint for external monitors)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { executeQuery } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
exports.handler = async (event) => {
|
|
12
|
-
const headers = {
|
|
13
|
-
'Content-Type': 'application/json',
|
|
14
|
-
'Access-Control-Allow-Origin': '*',
|
|
15
|
-
'Access-Control-Allow-Methods': 'GET,OPTIONS',
|
|
16
|
-
'Access-Control-Allow-Headers': 'Content-Type'
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const result = {
|
|
20
|
-
status: 'ok',
|
|
21
|
-
timestamp: new Date().toISOString(),
|
|
22
|
-
version: process.env.STACK_VERSION || '1.0.0',
|
|
23
|
-
checks: {}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Database connectivity
|
|
27
|
-
try {
|
|
28
|
-
const start = Date.now();
|
|
29
|
-
await executeQuery('SELECT 1 AS ok');
|
|
30
|
-
result.checks.database = {
|
|
31
|
-
status: 'ok',
|
|
32
|
-
latency_ms: Date.now() - start
|
|
33
|
-
};
|
|
34
|
-
} catch (err) {
|
|
35
|
-
result.status = 'degraded';
|
|
36
|
-
result.checks.database = {
|
|
37
|
-
status: 'error',
|
|
38
|
-
message: 'Database connection failed'
|
|
39
|
-
};
|
|
40
|
-
console.error('[Health] Database check failed:', err.message);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Stripe configuration
|
|
44
|
-
result.checks.stripe = {
|
|
45
|
-
status: process.env.STRIPE_SECRET_KEY ? 'configured' : 'not_configured'
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const statusCode = result.status === 'ok' ? 200 : 503;
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
statusCode,
|
|
52
|
-
headers,
|
|
53
|
-
body: JSON.stringify(result)
|
|
54
|
-
};
|
|
55
|
-
};
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified Audit Logger
|
|
3
|
-
*
|
|
4
|
-
* Provides consistent audit logging across all handlers.
|
|
5
|
-
* Simplified from EquilateralAgents pattern — no agent chains or impersonation.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* const { logModificationEvent, AuditEventType, EntityType } = require('./helpers');
|
|
9
|
-
* await logModificationEvent(AuditEventType.DATA_CREATE, EntityType.PROJECT, projectId, {
|
|
10
|
-
* requestContext, client_id, after_state: { project_name }
|
|
11
|
-
* });
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const { executeQuery } = require('./dbOperations');
|
|
15
|
-
const crypto = require('crypto');
|
|
16
|
-
|
|
17
|
-
const AuditEventType = {
|
|
18
|
-
// Authentication
|
|
19
|
-
AUTH_LOGIN: 'AUTH_LOGIN',
|
|
20
|
-
AUTH_LOGOUT: 'AUTH_LOGOUT',
|
|
21
|
-
AUTH_FAILED: 'AUTH_FAILED',
|
|
22
|
-
|
|
23
|
-
// Access
|
|
24
|
-
ACCESS_VIEW: 'ACCESS_VIEW',
|
|
25
|
-
ACCESS_DENIED: 'ACCESS_DENIED',
|
|
26
|
-
|
|
27
|
-
// Modification
|
|
28
|
-
DATA_CREATE: 'DATA_CREATE',
|
|
29
|
-
DATA_UPDATE: 'DATA_UPDATE',
|
|
30
|
-
DATA_DELETE: 'DATA_DELETE',
|
|
31
|
-
DATA_ARCHIVE: 'DATA_ARCHIVE',
|
|
32
|
-
|
|
33
|
-
// Admin
|
|
34
|
-
ADMIN_USER_UPDATE: 'ADMIN_USER_UPDATE',
|
|
35
|
-
ADMIN_PERMISSION_CHANGE: 'ADMIN_PERMISSION_CHANGE',
|
|
36
|
-
|
|
37
|
-
// System
|
|
38
|
-
SYSTEM_ERROR: 'SYSTEM_ERROR',
|
|
39
|
-
SYSTEM_CONFIG_CHANGE: 'SYSTEM_CONFIG_CHANGE',
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const EntityType = {
|
|
43
|
-
USER: 'USER',
|
|
44
|
-
CLIENT: 'CLIENT',
|
|
45
|
-
PROJECT: 'PROJECT',
|
|
46
|
-
STANDARD: 'STANDARD',
|
|
47
|
-
KNOWLEDGE: 'KNOWLEDGE',
|
|
48
|
-
INVARIANT: 'INVARIANT',
|
|
49
|
-
SESSION: 'SESSION',
|
|
50
|
-
SYSTEM: 'SYSTEM',
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Generate a correlation ID for request tracing
|
|
55
|
-
*/
|
|
56
|
-
function generateCorrelationId() {
|
|
57
|
-
const timestamp = Date.now().toString(36);
|
|
58
|
-
const random = crypto.randomBytes(4).toString('hex');
|
|
59
|
-
return `COR_${timestamp}_${random}`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Extract request context from lambdaWrapper's requestContext
|
|
64
|
-
*/
|
|
65
|
-
function extractRequestContext(requestContext) {
|
|
66
|
-
return {
|
|
67
|
-
request_id: requestContext?.requestId || null,
|
|
68
|
-
ip_address: requestContext?.identity?.sourceIp || null,
|
|
69
|
-
user_agent: requestContext?.identity?.userAgent || null,
|
|
70
|
-
user_email: requestContext?.authorizer?.claims?.email
|
|
71
|
-
|| requestContext?.authorizer?.jwt?.claims?.email
|
|
72
|
-
|| null,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Core audit event logger — INSERT into rapport.unified_audit_log
|
|
78
|
-
* Non-throwing: logs errors but never breaks business logic.
|
|
79
|
-
*/
|
|
80
|
-
async function logAuditEvent(params) {
|
|
81
|
-
const {
|
|
82
|
-
event_type,
|
|
83
|
-
entity_type,
|
|
84
|
-
entity_id = null,
|
|
85
|
-
actor_email = null,
|
|
86
|
-
actor_type = 'USER',
|
|
87
|
-
client_id = null,
|
|
88
|
-
company_id = null,
|
|
89
|
-
details = {},
|
|
90
|
-
before_state = null,
|
|
91
|
-
after_state = null,
|
|
92
|
-
correlation_id = null,
|
|
93
|
-
request_id = null,
|
|
94
|
-
ip_address = null,
|
|
95
|
-
user_agent = null,
|
|
96
|
-
outcome = 'SUCCESS',
|
|
97
|
-
error_message = null,
|
|
98
|
-
} = params;
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const query = `
|
|
102
|
-
INSERT INTO rapport.unified_audit_log (
|
|
103
|
-
event_type, entity_type, entity_id,
|
|
104
|
-
actor_email, actor_type,
|
|
105
|
-
client_id, company_id,
|
|
106
|
-
details, before_state, after_state,
|
|
107
|
-
correlation_id, request_id,
|
|
108
|
-
ip_address, user_agent,
|
|
109
|
-
outcome, error_message,
|
|
110
|
-
created_at
|
|
111
|
-
) VALUES (
|
|
112
|
-
$1, $2, $3, $4, $5, $6, $7,
|
|
113
|
-
$8, $9, $10, $11, $12, $13, $14,
|
|
114
|
-
$15, $16, NOW()
|
|
115
|
-
)
|
|
116
|
-
RETURNING audit_id
|
|
117
|
-
`;
|
|
118
|
-
|
|
119
|
-
const result = await executeQuery(query, [
|
|
120
|
-
event_type,
|
|
121
|
-
entity_type,
|
|
122
|
-
entity_id,
|
|
123
|
-
actor_email,
|
|
124
|
-
actor_type,
|
|
125
|
-
client_id,
|
|
126
|
-
company_id,
|
|
127
|
-
JSON.stringify(details),
|
|
128
|
-
before_state ? JSON.stringify(before_state) : null,
|
|
129
|
-
after_state ? JSON.stringify(after_state) : null,
|
|
130
|
-
correlation_id || generateCorrelationId(),
|
|
131
|
-
request_id,
|
|
132
|
-
ip_address,
|
|
133
|
-
user_agent,
|
|
134
|
-
outcome,
|
|
135
|
-
error_message,
|
|
136
|
-
]);
|
|
137
|
-
|
|
138
|
-
return result.rows[0]?.audit_id;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.error('[AuditLogger] Failed to log audit event:', error.message);
|
|
141
|
-
console.error('[AuditLogger] Event:', JSON.stringify({ event_type, entity_type, entity_id }));
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Log a data modification event (create, update, delete, archive)
|
|
148
|
-
*/
|
|
149
|
-
async function logModificationEvent(event_type, entity_type, entity_id, opts = {}) {
|
|
150
|
-
const { requestContext, client_id = null, company_id = null, before_state = null, after_state = null, details = {} } = opts;
|
|
151
|
-
const ctx = extractRequestContext(requestContext);
|
|
152
|
-
|
|
153
|
-
return logAuditEvent({
|
|
154
|
-
event_type,
|
|
155
|
-
entity_type,
|
|
156
|
-
entity_id,
|
|
157
|
-
actor_email: ctx.user_email,
|
|
158
|
-
actor_type: 'USER',
|
|
159
|
-
client_id,
|
|
160
|
-
company_id,
|
|
161
|
-
before_state,
|
|
162
|
-
after_state,
|
|
163
|
-
details,
|
|
164
|
-
request_id: ctx.request_id,
|
|
165
|
-
ip_address: ctx.ip_address,
|
|
166
|
-
user_agent: ctx.user_agent,
|
|
167
|
-
outcome: 'SUCCESS',
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Log a data access event (read operations)
|
|
173
|
-
*/
|
|
174
|
-
async function logAccessEvent(entity_type, entity_id, opts = {}) {
|
|
175
|
-
const { requestContext, client_id = null, details = {} } = opts;
|
|
176
|
-
const ctx = extractRequestContext(requestContext);
|
|
177
|
-
|
|
178
|
-
return logAuditEvent({
|
|
179
|
-
event_type: AuditEventType.ACCESS_VIEW,
|
|
180
|
-
entity_type,
|
|
181
|
-
entity_id,
|
|
182
|
-
actor_email: ctx.user_email,
|
|
183
|
-
actor_type: 'USER',
|
|
184
|
-
client_id,
|
|
185
|
-
details,
|
|
186
|
-
request_id: ctx.request_id,
|
|
187
|
-
ip_address: ctx.ip_address,
|
|
188
|
-
user_agent: ctx.user_agent,
|
|
189
|
-
outcome: 'SUCCESS',
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
module.exports = {
|
|
194
|
-
AuditEventType,
|
|
195
|
-
EntityType,
|
|
196
|
-
logAuditEvent,
|
|
197
|
-
logModificationEvent,
|
|
198
|
-
logAccessEvent,
|
|
199
|
-
extractRequestContext,
|
|
200
|
-
generateCorrelationId,
|
|
201
|
-
};
|