@equilateral_ai/mindmeld 3.5.2 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/hooks/session-end.js +25 -0
  2. package/hooks/session-start.js +363 -83
  3. package/hooks/session-watcher.js +585 -0
  4. package/package.json +19 -13
  5. package/scripts/init-project.js +9 -23
  6. package/src/client/dbShim.js +16 -0
  7. package/src/core/AuthManager.js +3 -2
  8. package/src/handlers/helpers/dbOperations.js +9 -46
  9. package/src/index.js +2 -217
  10. package/src/utils/piiMask.js +16 -0
  11. package/scripts/harvest.js +0 -601
  12. package/scripts/inject.js +0 -409
  13. package/scripts/mcp-bridge.js +0 -220
  14. package/scripts/repo-analyzer.js +0 -870
  15. package/src/collaboration/CollaborationPrompt.js +0 -460
  16. package/src/core/AlertEngine.js +0 -813
  17. package/src/core/AlertNotifier.js +0 -363
  18. package/src/core/CorrelationAnalyzer.js +0 -931
  19. package/src/core/CrossReferenceEngine.js +0 -624
  20. package/src/core/CurationEngine.js +0 -688
  21. package/src/core/DeprecationScheduler.js +0 -183
  22. package/src/core/LoadBearingDetector.js +0 -242
  23. package/src/core/NotificationService.js +0 -1032
  24. package/src/core/RapportOrchestrator.js +0 -632
  25. package/src/core/RelevanceDetector.js +0 -694
  26. package/src/core/StandardLifecycle.js +0 -244
  27. package/src/core/StandardsIngestion.js +0 -991
  28. package/src/core/TeamLoadBearingDetector.js +0 -431
  29. package/src/core/parsers/adrParser.js +0 -479
  30. package/src/core/parsers/cursorRulesParser.js +0 -564
  31. package/src/core/parsers/eslintParser.js +0 -439
  32. package/src/database/dbOperations.js +0 -105
  33. package/src/handlers/activity/activityGetMe.js +0 -98
  34. package/src/handlers/activity/activityGetTeam.js +0 -175
  35. package/src/handlers/admin/adminSetup.js +0 -216
  36. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  37. package/src/handlers/alerts/alertsGet.js +0 -250
  38. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  39. package/src/handlers/analytics/coachingGet.js +0 -361
  40. package/src/handlers/analytics/convergenceGet.js +0 -236
  41. package/src/handlers/analytics/developerScoreGet.js +0 -137
  42. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  43. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  44. package/src/handlers/collaborators/collaboratorList.js +0 -82
  45. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  46. package/src/handlers/collaborators/inviteAccept.js +0 -122
  47. package/src/handlers/company/companyUsersDelete.js +0 -141
  48. package/src/handlers/company/companyUsersGet.js +0 -90
  49. package/src/handlers/company/companyUsersPost.js +0 -267
  50. package/src/handlers/company/companyUsersPut.js +0 -76
  51. package/src/handlers/context/contextGet.js +0 -57
  52. package/src/handlers/context/invariantsGet.js +0 -74
  53. package/src/handlers/context/loopsGet.js +0 -82
  54. package/src/handlers/context/notesCreate.js +0 -74
  55. package/src/handlers/context/purposeGet.js +0 -78
  56. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  57. package/src/handlers/correlations/correlationsGet.js +0 -93
  58. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  59. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  60. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  61. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  62. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  63. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  64. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  65. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  66. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  67. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  68. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  69. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  70. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  71. package/src/handlers/github/githubConnectionStatus.js +0 -49
  72. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  73. package/src/handlers/github/githubOAuthCallback.js +0 -178
  74. package/src/handlers/github/githubOAuthStart.js +0 -59
  75. package/src/handlers/github/githubPatternsReview.js +0 -76
  76. package/src/handlers/github/githubReposList.js +0 -105
  77. package/src/handlers/health/healthGet.js +0 -55
  78. package/src/handlers/helpers/auditLogger.js +0 -201
  79. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  80. package/src/handlers/helpers/decisionFrames.js +0 -29
  81. package/src/handlers/helpers/errorHandler.js +0 -49
  82. package/src/handlers/helpers/index.js +0 -138
  83. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  84. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  85. package/src/handlers/helpers/predictiveCache.js +0 -51
  86. package/src/handlers/helpers/projectAccess.js +0 -88
  87. package/src/handlers/helpers/responseUtil.js +0 -55
  88. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  89. package/src/handlers/mcp/mcpHandler.js +0 -569
  90. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  91. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  92. package/src/handlers/notifications/getPreferences.js +0 -84
  93. package/src/handlers/notifications/sendNotification.js +0 -170
  94. package/src/handlers/notifications/updatePreferences.js +0 -316
  95. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  96. package/src/handlers/patterns/patternUsagePost.js +0 -182
  97. package/src/handlers/patterns/patternViolationPost.js +0 -185
  98. package/src/handlers/projects/projectCreate.js +0 -248
  99. package/src/handlers/projects/projectDelete.js +0 -82
  100. package/src/handlers/projects/projectGet.js +0 -95
  101. package/src/handlers/projects/projectUpdate.js +0 -117
  102. package/src/handlers/reports/aiLeverage.js +0 -210
  103. package/src/handlers/reports/engineeringInvestment.js +0 -132
  104. package/src/handlers/reports/riskForecast.js +0 -206
  105. package/src/handlers/reports/standardsRoi.js +0 -254
  106. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  107. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  108. package/src/handlers/scheduled/generateAlerts.js +0 -135
  109. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  110. package/src/handlers/scheduled/refreshActivity.js +0 -21
  111. package/src/handlers/scheduled/scanCompliance.js +0 -334
  112. package/src/handlers/sessions/sessionEndPost.js +0 -180
  113. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  114. package/src/handlers/standards/catalogGet.js +0 -185
  115. package/src/handlers/standards/catalogSync.js +0 -120
  116. package/src/handlers/standards/discoveriesGet.js +0 -89
  117. package/src/handlers/standards/projectStandardsGet.js +0 -129
  118. package/src/handlers/standards/projectStandardsPut.js +0 -151
  119. package/src/handlers/standards/standardsAuditGet.js +0 -65
  120. package/src/handlers/standards/standardsParseUpload.js +0 -149
  121. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  122. package/src/handlers/standards/standardsTransition.js +0 -161
  123. package/src/handlers/stripe/addonManagePost.js +0 -240
  124. package/src/handlers/stripe/billingPortalPost.js +0 -93
  125. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  126. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  127. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  128. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  129. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  130. package/src/handlers/stripe/webhookPost.js +0 -482
  131. package/src/handlers/user/apiTokenCreate.js +0 -71
  132. package/src/handlers/user/apiTokenList.js +0 -64
  133. package/src/handlers/user/userSplashAck.js +0 -91
  134. package/src/handlers/user/userSplashGet.js +0 -211
  135. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  136. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  137. package/src/handlers/users/userEntitlementsGet.js +0 -89
  138. package/src/handlers/users/userGet.js +0 -118
  139. package/src/handlers/users/userProfilePut.js +0 -77
  140. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,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
- });