@equilateral_ai/mindmeld 3.5.3 → 4.0.2

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