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