@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,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Activity Get Team Handler
|
|
3
|
-
* Retrieves team activity summary (managers only)
|
|
4
|
-
*
|
|
5
|
-
* GET /api/activity/team
|
|
6
|
-
* Auth: Cognito JWT required, Manager or Admin role
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
exports.handler = wrapHandler(async ({ requestContext, queryStringParameters }) => {
|
|
12
|
-
const Request_ID = requestContext.requestId;
|
|
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
|
-
// Check if user is a manager or admin
|
|
20
|
-
const managerCheck = await executeQuery(`
|
|
21
|
-
SELECT ue.company_id, ue.manager, ue.admin, u.super_admin
|
|
22
|
-
FROM rapport.user_entitlements ue
|
|
23
|
-
JOIN rapport.users u ON ue.email_address = u.email_address
|
|
24
|
-
WHERE ue.email_address = $1
|
|
25
|
-
`, [email]);
|
|
26
|
-
|
|
27
|
-
if (managerCheck.rowCount === 0) {
|
|
28
|
-
return createErrorResponse(403, 'Access denied');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const userRole = managerCheck.rows[0];
|
|
32
|
-
const isAuthorized = userRole.manager || userRole.admin || userRole.super_admin;
|
|
33
|
-
|
|
34
|
-
if (!isAuthorized) {
|
|
35
|
-
return createErrorResponse(403, 'Manager or Admin access required');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const companyId = queryStringParameters?.company_id || userRole.company_id;
|
|
39
|
-
|
|
40
|
-
// Get team summary directly from base tables
|
|
41
|
-
const summaryResult = await executeQuery(`
|
|
42
|
-
SELECT
|
|
43
|
-
COUNT(DISTINCT ue.email_address) as total_developers,
|
|
44
|
-
COUNT(DISTINCT ue.email_address) FILTER (
|
|
45
|
-
WHERE EXISTS (
|
|
46
|
-
SELECT 1 FROM rapport.sessions s
|
|
47
|
-
WHERE s.email_address = ue.email_address
|
|
48
|
-
AND s.started_at > NOW() - INTERVAL '30 days'
|
|
49
|
-
)
|
|
50
|
-
) as active_developers
|
|
51
|
-
FROM rapport.user_entitlements ue
|
|
52
|
-
JOIN rapport.users u ON ue.email_address = u.email_address
|
|
53
|
-
WHERE ue.company_id = $1
|
|
54
|
-
AND u.active = true
|
|
55
|
-
`, [companyId]);
|
|
56
|
-
|
|
57
|
-
// Get individual developer activity
|
|
58
|
-
const developersResult = await executeQuery(`
|
|
59
|
-
SELECT
|
|
60
|
-
ue.email_address,
|
|
61
|
-
u.first_name || ' ' || u.last_name as display_name,
|
|
62
|
-
COALESCE(sess.sessions_30d, 0) as sessions_30d,
|
|
63
|
-
COALESCE(sess.sessions_7d, 0) as sessions_7d,
|
|
64
|
-
sess.last_session,
|
|
65
|
-
COALESCE(comm.commits_30d, 0) as commits_30d,
|
|
66
|
-
COALESCE(comm.commits_7d, 0) as commits_7d,
|
|
67
|
-
comm.last_commit,
|
|
68
|
-
CASE WHEN comm.last_commit IS NOT NULL
|
|
69
|
-
THEN EXTRACT(DAY FROM NOW() - comm.last_commit)::INTEGER
|
|
70
|
-
ELSE NULL
|
|
71
|
-
END as days_since_commit,
|
|
72
|
-
COALESCE(pr_data.prs_merged_30d, 0) as prs_merged_30d
|
|
73
|
-
FROM rapport.user_entitlements ue
|
|
74
|
-
JOIN rapport.users u ON ue.email_address = u.email_address
|
|
75
|
-
LEFT JOIN (
|
|
76
|
-
SELECT email_address,
|
|
77
|
-
COUNT(*) FILTER (WHERE started_at > NOW() - INTERVAL '30 days') as sessions_30d,
|
|
78
|
-
COUNT(*) FILTER (WHERE started_at > NOW() - INTERVAL '7 days') as sessions_7d,
|
|
79
|
-
MAX(started_at) as last_session
|
|
80
|
-
FROM rapport.sessions
|
|
81
|
-
GROUP BY email_address
|
|
82
|
-
) sess ON sess.email_address = ue.email_address
|
|
83
|
-
LEFT JOIN (
|
|
84
|
-
SELECT author_email,
|
|
85
|
-
COUNT(*) FILTER (WHERE commit_timestamp > NOW() - INTERVAL '30 days') as commits_30d,
|
|
86
|
-
COUNT(*) FILTER (WHERE commit_timestamp > NOW() - INTERVAL '7 days') as commits_7d,
|
|
87
|
-
MAX(commit_timestamp) as last_commit
|
|
88
|
-
FROM rapport.commits
|
|
89
|
-
GROUP BY author_email
|
|
90
|
-
) comm ON comm.author_email = ue.email_address
|
|
91
|
-
LEFT JOIN (
|
|
92
|
-
SELECT pr.author_email,
|
|
93
|
-
COUNT(*) as prs_merged_30d
|
|
94
|
-
FROM rapport.pull_requests pr
|
|
95
|
-
WHERE pr.merged_at > NOW() - INTERVAL '30 days'
|
|
96
|
-
GROUP BY pr.author_email
|
|
97
|
-
) pr_data ON pr_data.author_email = ue.email_address
|
|
98
|
-
WHERE ue.company_id = $1
|
|
99
|
-
AND u.active = true
|
|
100
|
-
ORDER BY
|
|
101
|
-
CASE WHEN comm.last_commit IS NULL THEN 999
|
|
102
|
-
ELSE EXTRACT(DAY FROM NOW() - comm.last_commit)
|
|
103
|
-
END DESC,
|
|
104
|
-
COALESCE(sess.sessions_30d, 0) DESC
|
|
105
|
-
`, [companyId]);
|
|
106
|
-
|
|
107
|
-
const summary = summaryResult.rows[0] || {
|
|
108
|
-
total_developers: 0,
|
|
109
|
-
active_developers: 0
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const developers = developersResult.rows.map(dev => {
|
|
113
|
-
const sessions30d = parseInt(dev.sessions_30d) || 0;
|
|
114
|
-
const commits30d = parseInt(dev.commits_30d) || 0;
|
|
115
|
-
const conversionPct = sessions30d > 0 ? Math.round((commits30d / sessions30d) * 100) : 0;
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
email_address: dev.email_address,
|
|
119
|
-
display_name: dev.display_name,
|
|
120
|
-
sessions: {
|
|
121
|
-
last_30_days: sessions30d,
|
|
122
|
-
last_7_days: parseInt(dev.sessions_7d) || 0,
|
|
123
|
-
last_session: dev.last_session
|
|
124
|
-
},
|
|
125
|
-
commits: {
|
|
126
|
-
last_30_days: commits30d,
|
|
127
|
-
last_7_days: parseInt(dev.commits_7d) || 0,
|
|
128
|
-
last_commit: dev.last_commit,
|
|
129
|
-
days_since_commit: parseInt(dev.days_since_commit) || null
|
|
130
|
-
},
|
|
131
|
-
prs_merged_30d: parseInt(dev.prs_merged_30d) || 0,
|
|
132
|
-
session_to_commit_conversion_pct: conversionPct,
|
|
133
|
-
status: getDevStatus(dev)
|
|
134
|
-
};
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
const totalSessions = developers.reduce((sum, d) => sum + d.sessions.last_30_days, 0);
|
|
138
|
-
const totalCommits = developers.reduce((sum, d) => sum + d.commits.last_30_days, 0);
|
|
139
|
-
const devCount = developers.length || 1;
|
|
140
|
-
const avgConversion = developers.length > 0
|
|
141
|
-
? developers.reduce((sum, d) => sum + d.session_to_commit_conversion_pct, 0) / developers.length
|
|
142
|
-
: 0;
|
|
143
|
-
|
|
144
|
-
return createSuccessResponse(
|
|
145
|
-
{
|
|
146
|
-
Records: [{
|
|
147
|
-
company_id: companyId,
|
|
148
|
-
summary: {
|
|
149
|
-
total_developers: parseInt(summary.total_developers) || 0,
|
|
150
|
-
active_developers: parseInt(summary.active_developers) || 0,
|
|
151
|
-
stale_developers: developers.filter(d => d.status === 'stale').length,
|
|
152
|
-
very_stale_developers: developers.filter(d => d.status === 'very_stale').length,
|
|
153
|
-
avg_sessions_30d: parseFloat((totalSessions / devCount).toFixed(1)),
|
|
154
|
-
avg_commits_30d: parseFloat((totalCommits / devCount).toFixed(1)),
|
|
155
|
-
avg_conversion_pct: parseFloat(avgConversion.toFixed(1))
|
|
156
|
-
},
|
|
157
|
-
developers
|
|
158
|
-
}]
|
|
159
|
-
},
|
|
160
|
-
'Team activity retrieved',
|
|
161
|
-
{ Total_Records: developers.length, Request_ID, Timestamp: new Date().toISOString() }
|
|
162
|
-
);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
function getDevStatus(dev) {
|
|
166
|
-
const daysSinceCommit = parseInt(dev.days_since_commit);
|
|
167
|
-
const sessions30d = parseInt(dev.sessions_30d) || 0;
|
|
168
|
-
const commits30d = parseInt(dev.commits_30d) || 0;
|
|
169
|
-
|
|
170
|
-
if (daysSinceCommit > 14) return 'very_stale';
|
|
171
|
-
if (daysSinceCommit > 7) return 'stale';
|
|
172
|
-
if (sessions30d === 0 && commits30d > 0) return 'no_ai_usage';
|
|
173
|
-
if (sessions30d > 0 && commits30d === 0) return 'low_output';
|
|
174
|
-
return 'active';
|
|
175
|
-
}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Admin Setup Handler
|
|
3
|
-
* One-time setup for enterprise clients via Lambda invocation
|
|
4
|
-
*
|
|
5
|
-
* Invoke via AWS CLI:
|
|
6
|
-
* aws lambda invoke --function-name rapport-api-prod-AdminSetup \
|
|
7
|
-
* --payload '{"action":"setup-equilateral"}' \
|
|
8
|
-
* --profile production-sso response.json
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('../helpers');
|
|
12
|
-
|
|
13
|
-
const SETUP_CONFIGS = {
|
|
14
|
-
'setup-equilateral': {
|
|
15
|
-
client: {
|
|
16
|
-
client_id: 'EQUILATERAL_MAIN',
|
|
17
|
-
client_name: 'Equilateral AI',
|
|
18
|
-
client_type: 'ENTERPRISE',
|
|
19
|
-
subscription_tier: 'enterprise',
|
|
20
|
-
seat_count: 25,
|
|
21
|
-
billing_type: 'internal'
|
|
22
|
-
},
|
|
23
|
-
company: {
|
|
24
|
-
company_id: 'EQUILATERAL_MAIN',
|
|
25
|
-
company_name: 'Equilateral AI'
|
|
26
|
-
},
|
|
27
|
-
admin_email: 'james.ford@happyhippo.ai',
|
|
28
|
-
invitations: [
|
|
29
|
-
{ email: 'adeel@equilateral.ai', name: 'Adeel', role: 'member' },
|
|
30
|
-
{ email: 'satish@equilateral.ai', name: 'Satish', role: 'member' }
|
|
31
|
-
]
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
async function adminSetup({ body }) {
|
|
36
|
-
try {
|
|
37
|
-
const action = body?.action;
|
|
38
|
-
|
|
39
|
-
// Special action: list-tables
|
|
40
|
-
if (action === 'list-tables') {
|
|
41
|
-
const result = await executeQuery(`
|
|
42
|
-
SELECT table_name
|
|
43
|
-
FROM information_schema.tables
|
|
44
|
-
WHERE table_schema = 'rapport'
|
|
45
|
-
ORDER BY table_name
|
|
46
|
-
`);
|
|
47
|
-
return createSuccessResponse({
|
|
48
|
-
tables: result.rows.map(r => r.table_name)
|
|
49
|
-
}, `Found ${result.rows.length} tables`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Special action: run-sql (for migrations)
|
|
53
|
-
if (action === 'run-sql') {
|
|
54
|
-
const sql = body?.sql;
|
|
55
|
-
if (!sql) {
|
|
56
|
-
return createErrorResponse(400, 'sql parameter required');
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
await executeQuery(sql);
|
|
60
|
-
return createSuccessResponse({ executed: true }, 'SQL executed successfully');
|
|
61
|
-
} catch (err) {
|
|
62
|
-
return createErrorResponse(500, `SQL error: ${err.message}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Special action: query (returns results)
|
|
67
|
-
if (action === 'query') {
|
|
68
|
-
const sql = body?.sql;
|
|
69
|
-
if (!sql) {
|
|
70
|
-
return createErrorResponse(400, 'sql parameter required');
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
const result = await executeQuery(sql);
|
|
74
|
-
return createSuccessResponse({ rows: result.rows, rowCount: result.rowCount }, `Query returned ${result.rowCount} rows`);
|
|
75
|
-
} catch (err) {
|
|
76
|
-
return createErrorResponse(500, `Query error: ${err.message}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (!action || !SETUP_CONFIGS[action]) {
|
|
81
|
-
return createErrorResponse(400, `Invalid action. Available: ${Object.keys(SETUP_CONFIGS).join(', ')}, list-tables, run-sql`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const config = SETUP_CONFIGS[action];
|
|
85
|
-
const results = [];
|
|
86
|
-
|
|
87
|
-
// 1. Create client
|
|
88
|
-
const existingClient = await executeQuery(
|
|
89
|
-
'SELECT client_id FROM rapport.clients WHERE client_id = $1',
|
|
90
|
-
[config.client.client_id]
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
if (existingClient.rows.length > 0) {
|
|
94
|
-
results.push({ step: 'client', status: 'exists', client_id: config.client.client_id });
|
|
95
|
-
} else {
|
|
96
|
-
await executeQuery(`
|
|
97
|
-
INSERT INTO rapport.clients (
|
|
98
|
-
client_id, client_name, client_type, client_status, active,
|
|
99
|
-
subscription_tier, subscription_status, billing_type, seat_count
|
|
100
|
-
)
|
|
101
|
-
VALUES ($1, $2, $3, 'Active', true, $4, 'active', $5, $6)
|
|
102
|
-
`, [
|
|
103
|
-
config.client.client_id,
|
|
104
|
-
config.client.client_name,
|
|
105
|
-
config.client.client_type,
|
|
106
|
-
config.client.subscription_tier,
|
|
107
|
-
config.client.billing_type,
|
|
108
|
-
config.client.seat_count
|
|
109
|
-
]);
|
|
110
|
-
results.push({ step: 'client', status: 'created', client_id: config.client.client_id });
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// 2. Create company
|
|
114
|
-
const existingCompany = await executeQuery(
|
|
115
|
-
'SELECT company_id FROM rapport.companies WHERE company_id = $1',
|
|
116
|
-
[config.company.company_id]
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
if (existingCompany.rows.length > 0) {
|
|
120
|
-
results.push({ step: 'company', status: 'exists', company_id: config.company.company_id });
|
|
121
|
-
} else {
|
|
122
|
-
await executeQuery(`
|
|
123
|
-
INSERT INTO rapport.companies (
|
|
124
|
-
company_id, client_id, company_name, company_status
|
|
125
|
-
)
|
|
126
|
-
VALUES ($1, $2, $3, 'Active')
|
|
127
|
-
`, [config.company.company_id, config.client.client_id, config.company.company_name]);
|
|
128
|
-
results.push({ step: 'company', status: 'created', company_id: config.company.company_id });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// 3. Create admin entitlement
|
|
132
|
-
const existingEntitlement = await executeQuery(
|
|
133
|
-
'SELECT * FROM rapport.user_entitlements WHERE email_address = $1 AND company_id = $2',
|
|
134
|
-
[config.admin_email, config.company.company_id]
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
if (existingEntitlement.rows.length > 0) {
|
|
138
|
-
results.push({ step: 'admin_entitlement', status: 'exists', email: config.admin_email });
|
|
139
|
-
} else {
|
|
140
|
-
await executeQuery(`
|
|
141
|
-
INSERT INTO rapport.user_entitlements (
|
|
142
|
-
email_address, client_id, company_id, admin, member
|
|
143
|
-
)
|
|
144
|
-
VALUES ($1, $2, $3, true, true)
|
|
145
|
-
`, [config.admin_email, config.client.client_id, config.company.company_id]);
|
|
146
|
-
results.push({ step: 'admin_entitlement', status: 'created', email: config.admin_email });
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// 4. Create invitations (skip if table doesn't exist)
|
|
150
|
-
let invitationsSkipped = false;
|
|
151
|
-
for (const inv of config.invitations) {
|
|
152
|
-
if (invitationsSkipped) {
|
|
153
|
-
results.push({ step: 'invitation', status: 'skipped', email: inv.email, reason: 'table not found' });
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
try {
|
|
157
|
-
await executeQuery(`
|
|
158
|
-
INSERT INTO rapport.enterprise_invitations
|
|
159
|
-
(client_id, company_id, email, role, invited_by, status, created_at)
|
|
160
|
-
VALUES ($1, $2, $3, $4, $5, 'pending', NOW())
|
|
161
|
-
ON CONFLICT (company_id, email) DO UPDATE SET
|
|
162
|
-
role = EXCLUDED.role,
|
|
163
|
-
invited_by = EXCLUDED.invited_by,
|
|
164
|
-
status = 'pending',
|
|
165
|
-
created_at = NOW()
|
|
166
|
-
`, [config.client.client_id, config.company.company_id, inv.email.toLowerCase(), inv.role, config.admin_email]);
|
|
167
|
-
results.push({ step: 'invitation', status: 'created', email: inv.email, role: inv.role });
|
|
168
|
-
} catch (err) {
|
|
169
|
-
if (err.code === '42P01') { // relation does not exist
|
|
170
|
-
invitationsSkipped = true;
|
|
171
|
-
results.push({ step: 'invitation', status: 'skipped', email: inv.email, reason: 'enterprise_invitations table not found' });
|
|
172
|
-
} else {
|
|
173
|
-
results.push({ step: 'invitation', status: 'error', email: inv.email, error: err.message });
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// 5. Get final state
|
|
179
|
-
const clientInfo = await executeQuery(
|
|
180
|
-
'SELECT client_id, client_name, subscription_tier, seat_count FROM rapport.clients WHERE client_id = $1',
|
|
181
|
-
[config.client.client_id]
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
const entitlements = await executeQuery(
|
|
185
|
-
'SELECT email_address, admin, member FROM rapport.user_entitlements WHERE company_id = $1',
|
|
186
|
-
[config.company.company_id]
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
let invitationsList = [];
|
|
190
|
-
try {
|
|
191
|
-
const invitations = await executeQuery(
|
|
192
|
-
'SELECT email, role, status FROM rapport.enterprise_invitations WHERE company_id = $1',
|
|
193
|
-
[config.company.company_id]
|
|
194
|
-
);
|
|
195
|
-
invitationsList = invitations.rows;
|
|
196
|
-
} catch (err) {
|
|
197
|
-
if (err.code !== '42P01') throw err; // rethrow if not "relation does not exist"
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return createSuccessResponse({
|
|
201
|
-
action,
|
|
202
|
-
results,
|
|
203
|
-
final_state: {
|
|
204
|
-
client: clientInfo.rows[0],
|
|
205
|
-
entitlements: entitlements.rows,
|
|
206
|
-
invitations: invitationsList
|
|
207
|
-
}
|
|
208
|
-
}, 'Setup complete');
|
|
209
|
-
|
|
210
|
-
} catch (error) {
|
|
211
|
-
console.error('Admin setup error:', error);
|
|
212
|
-
return handleError(error);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
exports.handler = wrapHandler(adminSetup);
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Alerts Acknowledge Handler
|
|
3
|
-
* Acknowledges or resolves an attention alert
|
|
4
|
-
*
|
|
5
|
-
* POST /api/alerts/acknowledge
|
|
6
|
-
* Body: { alert_id, action }
|
|
7
|
-
* Auth: Cognito JWT required, Manager or Admin role
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
|
|
11
|
-
|
|
12
|
-
exports.handler = wrapHandler(async ({ requestContext, body }) => {
|
|
13
|
-
const Request_ID = requestContext.requestId;
|
|
14
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
15
|
-
|
|
16
|
-
if (!email) {
|
|
17
|
-
return createErrorResponse(401, 'Authentication required');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const alertId = body?.alert_id;
|
|
21
|
-
if (!alertId) {
|
|
22
|
-
return createErrorResponse(400, 'Alert ID required');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Check user role
|
|
26
|
-
const userCheck = await executeQuery(`
|
|
27
|
-
SELECT ue.company_id, ue.manager, ue.admin, u.super_admin
|
|
28
|
-
FROM rapport.user_entitlements ue
|
|
29
|
-
JOIN rapport.users u ON ue.email_address = u.email_address
|
|
30
|
-
WHERE ue.email_address = $1
|
|
31
|
-
`, [email]);
|
|
32
|
-
|
|
33
|
-
if (userCheck.rowCount === 0) {
|
|
34
|
-
return createErrorResponse(403, 'Access denied');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const userRole = userCheck.rows[0];
|
|
38
|
-
const isManager = userRole.manager || userRole.admin || userRole.super_admin;
|
|
39
|
-
|
|
40
|
-
if (!isManager) {
|
|
41
|
-
return createErrorResponse(403, 'Manager or Admin access required');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const action = body?.action || 'acknowledge'; // acknowledge or resolve
|
|
45
|
-
|
|
46
|
-
// Verify alert exists and belongs to user's company
|
|
47
|
-
const alertCheck = await executeQuery(`
|
|
48
|
-
SELECT alert_id, company_id, status
|
|
49
|
-
FROM rapport.attention_alerts
|
|
50
|
-
WHERE alert_id = $1
|
|
51
|
-
`, [alertId]);
|
|
52
|
-
|
|
53
|
-
if (alertCheck.rowCount === 0) {
|
|
54
|
-
return createErrorResponse(404, 'Alert not found');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const alert = alertCheck.rows[0];
|
|
58
|
-
if (alert.company_id !== userRole.company_id && !userRole.super_admin) {
|
|
59
|
-
return createErrorResponse(403, 'Access denied to this alert');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Update alert
|
|
63
|
-
let result;
|
|
64
|
-
if (action === 'resolve') {
|
|
65
|
-
result = await executeQuery(`
|
|
66
|
-
UPDATE rapport.attention_alerts
|
|
67
|
-
SET
|
|
68
|
-
status = 'resolved',
|
|
69
|
-
acknowledged_by = $1,
|
|
70
|
-
acknowledged_at = COALESCE(acknowledged_at, NOW()),
|
|
71
|
-
resolved_at = NOW()
|
|
72
|
-
WHERE alert_id = $2
|
|
73
|
-
RETURNING *
|
|
74
|
-
`, [email, alertId]);
|
|
75
|
-
} else {
|
|
76
|
-
result = await executeQuery(`
|
|
77
|
-
UPDATE rapport.attention_alerts
|
|
78
|
-
SET
|
|
79
|
-
status = 'acknowledged',
|
|
80
|
-
acknowledged_by = $1,
|
|
81
|
-
acknowledged_at = NOW()
|
|
82
|
-
WHERE alert_id = $2
|
|
83
|
-
RETURNING *
|
|
84
|
-
`, [email, alertId]);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return createSuccessResponse(
|
|
88
|
-
{ Records: result.rows },
|
|
89
|
-
`Alert ${action}d`,
|
|
90
|
-
{ Total_Records: 1, Request_ID, Timestamp: new Date().toISOString() }
|
|
91
|
-
);
|
|
92
|
-
});
|