@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.
- package/hooks/session-start.js +312 -85
- package/package.json +21 -13
- package/scripts/init-project.js +9 -23
- package/scripts/repo-analyzer.js +118 -2
- 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/inject.js +0 -409
- package/scripts/mcp-bridge.js +0 -220
- 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,224 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Control Tower Handler
|
|
3
|
-
* Unified org health dashboard for enterprise tier
|
|
4
|
-
*
|
|
5
|
-
* GET /api/enterprise/control-tower
|
|
6
|
-
* Auth: Cognito JWT required, Enterprise tier
|
|
7
|
-
*
|
|
8
|
-
* Aggregates cross-project health metrics:
|
|
9
|
-
* - Organization health score
|
|
10
|
-
* - Standards compliance across projects
|
|
11
|
-
* - Risk indicators (stale devs, violations, coverage gaps)
|
|
12
|
-
* - Governance activity (audit events, knowledge items)
|
|
13
|
-
* - Project-level breakdown
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
|
|
17
|
-
|
|
18
|
-
async function getControlTower({ requestContext, queryStringParameters }) {
|
|
19
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
20
|
-
|
|
21
|
-
if (!email) {
|
|
22
|
-
return createErrorResponse(401, 'Authentication required');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Verify enterprise admin/manager access
|
|
26
|
-
const accessCheck = await executeQuery(`
|
|
27
|
-
SELECT ue.company_id, ue.admin, ue.manager, c.subscription_tier, co.company_name
|
|
28
|
-
FROM rapport.user_entitlements ue
|
|
29
|
-
JOIN rapport.clients c ON ue.client_id = c.client_id
|
|
30
|
-
JOIN rapport.companies co ON ue.company_id = co.company_id
|
|
31
|
-
WHERE ue.email_address = $1
|
|
32
|
-
AND (ue.admin = true OR ue.manager = true)
|
|
33
|
-
LIMIT 1
|
|
34
|
-
`, [email]);
|
|
35
|
-
|
|
36
|
-
if (accessCheck.rows.length === 0) {
|
|
37
|
-
return createErrorResponse(403, 'Admin or Manager access required for Control Tower');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const { company_id: companyId, company_name: companyName } = accessCheck.rows[0];
|
|
41
|
-
const periodDays = 30;
|
|
42
|
-
const periodStart = new Date();
|
|
43
|
-
periodStart.setDate(periodStart.getDate() - periodDays);
|
|
44
|
-
|
|
45
|
-
// 1. Team health metrics
|
|
46
|
-
let teamHealth = { total: 0, active: 0, stale: 0, very_stale: 0, no_sessions: 0 };
|
|
47
|
-
try {
|
|
48
|
-
const teamResult = await executeQuery(`
|
|
49
|
-
SELECT
|
|
50
|
-
COUNT(DISTINCT ue.email_address) as total,
|
|
51
|
-
COUNT(DISTINCT CASE WHEN mda.sessions_last_30d > 0 AND mda.commits_last_30d > 0 THEN ue.email_address END) as active,
|
|
52
|
-
COUNT(DISTINCT CASE WHEN mda.days_since_last_commit BETWEEN 8 AND 14 THEN ue.email_address END) as stale,
|
|
53
|
-
COUNT(DISTINCT CASE WHEN mda.days_since_last_commit > 14 THEN ue.email_address END) as very_stale,
|
|
54
|
-
COUNT(DISTINCT CASE WHEN mda.sessions_last_30d = 0 OR mda.sessions_last_30d IS NULL THEN ue.email_address END) as no_sessions
|
|
55
|
-
FROM rapport.user_entitlements ue
|
|
56
|
-
LEFT JOIN rapport.mv_developer_activity mda ON ue.email_address = mda.email_address
|
|
57
|
-
WHERE ue.company_id = $1
|
|
58
|
-
`, [companyId]);
|
|
59
|
-
if (teamResult.rows[0]) {
|
|
60
|
-
teamHealth = {
|
|
61
|
-
total: parseInt(teamResult.rows[0].total) || 0,
|
|
62
|
-
active: parseInt(teamResult.rows[0].active) || 0,
|
|
63
|
-
stale: parseInt(teamResult.rows[0].stale) || 0,
|
|
64
|
-
very_stale: parseInt(teamResult.rows[0].very_stale) || 0,
|
|
65
|
-
no_sessions: parseInt(teamResult.rows[0].no_sessions) || 0
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
} catch (e) {
|
|
69
|
-
console.log('[ControlTower] Team health query failed:', e.message);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// 2. Project coverage
|
|
73
|
-
let projects = [];
|
|
74
|
-
try {
|
|
75
|
-
const projectResult = await executeQuery(`
|
|
76
|
-
SELECT
|
|
77
|
-
p.project_id,
|
|
78
|
-
p.project_name,
|
|
79
|
-
p.repo_url,
|
|
80
|
-
COUNT(DISTINCT s.session_id) as sessions_30d,
|
|
81
|
-
COUNT(DISTINCT ss.standard_id) as standards_used,
|
|
82
|
-
MAX(s.started_at) as last_session
|
|
83
|
-
FROM rapport.projects p
|
|
84
|
-
LEFT JOIN rapport.sessions s ON p.project_id = s.project_id AND s.started_at >= $2
|
|
85
|
-
LEFT JOIN rapport.session_standards ss ON s.session_id = ss.session_id AND ss.created_at >= $2
|
|
86
|
-
WHERE p.company_id = $1
|
|
87
|
-
GROUP BY p.project_id, p.project_name, p.repo_url
|
|
88
|
-
ORDER BY sessions_30d DESC
|
|
89
|
-
`, [companyId, periodStart]);
|
|
90
|
-
projects = projectResult.rows.map(row => ({
|
|
91
|
-
project_id: row.project_id,
|
|
92
|
-
project_name: row.project_name,
|
|
93
|
-
repo_connected: !!row.repo_url,
|
|
94
|
-
sessions_30d: parseInt(row.sessions_30d) || 0,
|
|
95
|
-
standards_used: parseInt(row.standards_used) || 0,
|
|
96
|
-
last_session: row.last_session,
|
|
97
|
-
status: parseInt(row.sessions_30d) > 0 ? 'active' : 'inactive'
|
|
98
|
-
}));
|
|
99
|
-
} catch (e) {
|
|
100
|
-
console.log('[ControlTower] Projects query failed:', e.message);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// 3. Standards coverage (how many unique standards used across org)
|
|
104
|
-
let standardsCoverage = { total_injections: 0, unique_standards: 0, sessions_with_standards: 0 };
|
|
105
|
-
try {
|
|
106
|
-
const stdResult = await executeQuery(`
|
|
107
|
-
SELECT
|
|
108
|
-
COUNT(*) as total_injections,
|
|
109
|
-
COUNT(DISTINCT ss.standard_id) as unique_standards,
|
|
110
|
-
COUNT(DISTINCT ss.session_id) as sessions_with_standards
|
|
111
|
-
FROM rapport.session_standards ss
|
|
112
|
-
JOIN rapport.sessions s ON ss.session_id = s.session_id
|
|
113
|
-
JOIN rapport.projects p ON s.project_id = p.project_id
|
|
114
|
-
WHERE p.company_id = $1
|
|
115
|
-
AND ss.created_at >= $2
|
|
116
|
-
`, [companyId, periodStart]);
|
|
117
|
-
if (stdResult.rows[0]) {
|
|
118
|
-
standardsCoverage = {
|
|
119
|
-
total_injections: parseInt(stdResult.rows[0].total_injections) || 0,
|
|
120
|
-
unique_standards: parseInt(stdResult.rows[0].unique_standards) || 0,
|
|
121
|
-
sessions_with_standards: parseInt(stdResult.rows[0].sessions_with_standards) || 0
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
} catch (e) {
|
|
125
|
-
console.log('[ControlTower] Standards coverage query failed:', e.message);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 4. Governance activity (audit events, knowledge items)
|
|
129
|
-
let governance = { audit_events_30d: 0, knowledge_items: 0, published_knowledge: 0 };
|
|
130
|
-
try {
|
|
131
|
-
const auditResult = await executeQuery(`
|
|
132
|
-
SELECT COUNT(*) as count
|
|
133
|
-
FROM rapport.unified_audit_log
|
|
134
|
-
WHERE client_id = (
|
|
135
|
-
SELECT client_id FROM rapport.user_entitlements WHERE company_id = $1 LIMIT 1
|
|
136
|
-
)
|
|
137
|
-
AND created_at >= $2
|
|
138
|
-
`, [companyId, periodStart]);
|
|
139
|
-
governance.audit_events_30d = parseInt(auditResult.rows[0]?.count) || 0;
|
|
140
|
-
} catch (e) {
|
|
141
|
-
console.log('[ControlTower] Audit events query failed:', e.message);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
const knowledgeResult = await executeQuery(`
|
|
146
|
-
SELECT
|
|
147
|
-
COUNT(*) as total,
|
|
148
|
-
COUNT(CASE WHEN status = 'published' THEN 1 END) as published
|
|
149
|
-
FROM rapport.curated_knowledge
|
|
150
|
-
WHERE client_id = (
|
|
151
|
-
SELECT client_id FROM rapport.user_entitlements WHERE company_id = $1 LIMIT 1
|
|
152
|
-
)
|
|
153
|
-
`, [companyId]);
|
|
154
|
-
governance.knowledge_items = parseInt(knowledgeResult.rows[0]?.total) || 0;
|
|
155
|
-
governance.published_knowledge = parseInt(knowledgeResult.rows[0]?.published) || 0;
|
|
156
|
-
} catch (e) {
|
|
157
|
-
console.log('[ControlTower] Knowledge query failed:', e.message);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 5. Risk indicators
|
|
161
|
-
const risks = [];
|
|
162
|
-
if (teamHealth.very_stale > 0) {
|
|
163
|
-
risks.push({
|
|
164
|
-
severity: 'high',
|
|
165
|
-
category: 'team',
|
|
166
|
-
message: `${teamHealth.very_stale} developer${teamHealth.very_stale > 1 ? 's' : ''} inactive for 14+ days`
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
if (teamHealth.no_sessions > teamHealth.total * 0.3 && teamHealth.total > 1) {
|
|
170
|
-
risks.push({
|
|
171
|
-
severity: 'medium',
|
|
172
|
-
category: 'adoption',
|
|
173
|
-
message: `${teamHealth.no_sessions} of ${teamHealth.total} developers have no AI sessions in 30 days`
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
const projectsWithoutStandards = projects.filter(p => p.sessions_30d > 0 && p.standards_used === 0);
|
|
177
|
-
if (projectsWithoutStandards.length > 0) {
|
|
178
|
-
risks.push({
|
|
179
|
-
severity: 'medium',
|
|
180
|
-
category: 'coverage',
|
|
181
|
-
message: `${projectsWithoutStandards.length} active project${projectsWithoutStandards.length > 1 ? 's' : ''} with no standards injection`
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
const inactiveProjects = projects.filter(p => p.status === 'inactive');
|
|
185
|
-
if (inactiveProjects.length > projects.length * 0.5 && projects.length > 1) {
|
|
186
|
-
risks.push({
|
|
187
|
-
severity: 'low',
|
|
188
|
-
category: 'projects',
|
|
189
|
-
message: `${inactiveProjects.length} of ${projects.length} projects had no sessions in 30 days`
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// 6. Calculate org health score (0-100)
|
|
194
|
-
let healthScore = 50; // baseline
|
|
195
|
-
if (teamHealth.total > 0) {
|
|
196
|
-
const activeRate = teamHealth.active / teamHealth.total;
|
|
197
|
-
healthScore = Math.round(activeRate * 40); // up to 40 points for team activity
|
|
198
|
-
}
|
|
199
|
-
if (projects.length > 0) {
|
|
200
|
-
const coveredProjects = projects.filter(p => p.sessions_30d > 0).length;
|
|
201
|
-
healthScore += Math.round((coveredProjects / projects.length) * 30); // up to 30 for project coverage
|
|
202
|
-
}
|
|
203
|
-
if (standardsCoverage.unique_standards > 0) {
|
|
204
|
-
healthScore += Math.min(standardsCoverage.unique_standards * 2, 20); // up to 20 for standards breadth
|
|
205
|
-
}
|
|
206
|
-
if (governance.published_knowledge > 0) {
|
|
207
|
-
healthScore += Math.min(governance.published_knowledge, 10); // up to 10 for knowledge curation
|
|
208
|
-
}
|
|
209
|
-
healthScore = Math.min(healthScore, 100);
|
|
210
|
-
|
|
211
|
-
return createSuccessResponse({
|
|
212
|
-
company_name: companyName,
|
|
213
|
-
health_score: healthScore,
|
|
214
|
-
team: teamHealth,
|
|
215
|
-
projects,
|
|
216
|
-
standards_coverage: standardsCoverage,
|
|
217
|
-
governance,
|
|
218
|
-
risks,
|
|
219
|
-
period_days: periodDays,
|
|
220
|
-
period_start: periodStart.toISOString()
|
|
221
|
-
}, 'Control Tower data retrieved');
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
exports.handler = wrapHandler(getControlTower);
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Audit Log Get Handler
|
|
3
|
-
*
|
|
4
|
-
* GET /api/enterprise/audit?page=&pageSize=&action=&resourceType=&startDate=&endDate=
|
|
5
|
-
* Returns: { Records: AuditLogEntry[] } + meta { Total_Records, Page, Page_Size, Request_ID, Timestamp }
|
|
6
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
async function getEnterpriseAudit({ queryStringParameters, requestContext }) {
|
|
12
|
-
try {
|
|
13
|
-
const Request_ID = requestContext.requestId;
|
|
14
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
15
|
-
if (!email) {
|
|
16
|
-
return createErrorResponse(401, 'Authentication required');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Look up client_id and verify enterprise tier
|
|
20
|
-
const userResult = await executeQuery(
|
|
21
|
-
`SELECT u.client_id, c.subscription_tier
|
|
22
|
-
FROM rapport.users u
|
|
23
|
-
JOIN rapport.clients c ON c.client_id = u.client_id
|
|
24
|
-
WHERE u.email_address = $1 LIMIT 1`,
|
|
25
|
-
[email]
|
|
26
|
-
);
|
|
27
|
-
if (!userResult.rows.length) {
|
|
28
|
-
return createErrorResponse(403, 'User not found');
|
|
29
|
-
}
|
|
30
|
-
if (userResult.rows[0].subscription_tier !== 'enterprise') {
|
|
31
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
32
|
-
}
|
|
33
|
-
const clientId = userResult.rows[0].client_id;
|
|
34
|
-
|
|
35
|
-
const params = queryStringParameters || {};
|
|
36
|
-
const page = Math.max(1, parseInt(params.page) || 1);
|
|
37
|
-
const pageSize = Math.min(100, Math.max(1, parseInt(params.pageSize) || 20));
|
|
38
|
-
const offset = (page - 1) * pageSize;
|
|
39
|
-
|
|
40
|
-
// Build WHERE clause
|
|
41
|
-
const conditions = ['client_id = $1'];
|
|
42
|
-
const values = [clientId];
|
|
43
|
-
let paramIndex = 2;
|
|
44
|
-
|
|
45
|
-
if (params.action) {
|
|
46
|
-
conditions.push(`event_type = $${paramIndex}`);
|
|
47
|
-
values.push(params.action);
|
|
48
|
-
paramIndex++;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (params.resourceType) {
|
|
52
|
-
conditions.push(`entity_type = $${paramIndex}`);
|
|
53
|
-
values.push(params.resourceType);
|
|
54
|
-
paramIndex++;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (params.startDate) {
|
|
58
|
-
conditions.push(`created_at >= $${paramIndex}`);
|
|
59
|
-
values.push(params.startDate);
|
|
60
|
-
paramIndex++;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (params.endDate) {
|
|
64
|
-
conditions.push(`created_at <= $${paramIndex}`);
|
|
65
|
-
values.push(params.endDate);
|
|
66
|
-
paramIndex++;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const whereClause = conditions.join(' AND ');
|
|
70
|
-
|
|
71
|
-
// Get total count
|
|
72
|
-
const countResult = await executeQuery(
|
|
73
|
-
`SELECT COUNT(*) as total FROM rapport.unified_audit_log WHERE ${whereClause}`,
|
|
74
|
-
values
|
|
75
|
-
);
|
|
76
|
-
const total = parseInt(countResult.rows[0].total);
|
|
77
|
-
|
|
78
|
-
// Get paginated records, mapping columns to frontend AuditLogEntry type
|
|
79
|
-
const dataResult = await executeQuery(
|
|
80
|
-
`SELECT
|
|
81
|
-
audit_id,
|
|
82
|
-
client_id,
|
|
83
|
-
actor_email,
|
|
84
|
-
event_type as action,
|
|
85
|
-
entity_type as resource_type,
|
|
86
|
-
entity_id as resource_id,
|
|
87
|
-
before_state as old_value,
|
|
88
|
-
after_state as new_value,
|
|
89
|
-
created_at
|
|
90
|
-
FROM rapport.unified_audit_log
|
|
91
|
-
WHERE ${whereClause}
|
|
92
|
-
ORDER BY created_at DESC
|
|
93
|
-
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
|
|
94
|
-
[...values, pageSize, offset]
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
return createSuccessResponse(
|
|
98
|
-
{ Records: dataResult.rows },
|
|
99
|
-
'Audit log retrieved',
|
|
100
|
-
{ Total_Records: total, Page: page, Page_Size: pageSize, Request_ID, Timestamp: new Date().toISOString() }
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
} catch (error) {
|
|
104
|
-
return handleError(error);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
exports.handler = wrapHandler(getEnterpriseAudit);
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Contributors Get Handler
|
|
3
|
-
*
|
|
4
|
-
* GET /api/enterprise/contributors?period=
|
|
5
|
-
* Returns: { Records: [contributor stats] } + meta
|
|
6
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
async function getEnterpriseContributors({ queryStringParameters, requestContext }) {
|
|
12
|
-
try {
|
|
13
|
-
const Request_ID = requestContext.requestId;
|
|
14
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
15
|
-
if (!email) {
|
|
16
|
-
return createErrorResponse(401, 'Authentication required');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const userResult = await executeQuery(
|
|
20
|
-
`SELECT u.client_id, c.subscription_tier
|
|
21
|
-
FROM rapport.users u
|
|
22
|
-
JOIN rapport.clients c ON c.client_id = u.client_id
|
|
23
|
-
WHERE u.email_address = $1 LIMIT 1`,
|
|
24
|
-
[email]
|
|
25
|
-
);
|
|
26
|
-
if (!userResult.rows.length) {
|
|
27
|
-
return createErrorResponse(403, 'User not found');
|
|
28
|
-
}
|
|
29
|
-
if (userResult.rows[0].subscription_tier !== 'enterprise') {
|
|
30
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
31
|
-
}
|
|
32
|
-
const clientId = userResult.rows[0].client_id;
|
|
33
|
-
|
|
34
|
-
const params = queryStringParameters || {};
|
|
35
|
-
const period = params.period || '30d';
|
|
36
|
-
|
|
37
|
-
// Calculate date filter
|
|
38
|
-
let intervalDays;
|
|
39
|
-
switch (period) {
|
|
40
|
-
case '7d': intervalDays = 7; break;
|
|
41
|
-
case '90d': intervalDays = 90; break;
|
|
42
|
-
case '30d':
|
|
43
|
-
default: intervalDays = 30; break;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Aggregate contributions across audit trail and knowledge curation
|
|
47
|
-
const result = await executeQuery(
|
|
48
|
-
`WITH audit_activity AS (
|
|
49
|
-
SELECT
|
|
50
|
-
actor_email as contributor_email,
|
|
51
|
-
COUNT(*) FILTER (WHERE event_type = 'DATA_CREATE' AND entity_type = 'STANDARD') as discoveries,
|
|
52
|
-
COUNT(*) FILTER (WHERE event_type = 'DATA_UPDATE' AND entity_type = 'STANDARD') as refinements,
|
|
53
|
-
COUNT(*) FILTER (WHERE event_type = 'DATA_UPDATE' AND entity_type = 'STANDARD'
|
|
54
|
-
AND (after_state->>'status') = 'approved') as verifications,
|
|
55
|
-
COUNT(*) FILTER (WHERE event_type = 'DATA_CREATE' AND entity_type = 'KNOWLEDGE') as curations
|
|
56
|
-
FROM rapport.unified_audit_log
|
|
57
|
-
WHERE client_id = $1
|
|
58
|
-
AND created_at >= NOW() - ($2 || ' days')::interval
|
|
59
|
-
AND actor_email IS NOT NULL
|
|
60
|
-
GROUP BY actor_email
|
|
61
|
-
)
|
|
62
|
-
SELECT
|
|
63
|
-
contributor_email,
|
|
64
|
-
COALESCE(discoveries, 0) as discoveries,
|
|
65
|
-
COALESCE(refinements, 0) as refinements,
|
|
66
|
-
COALESCE(verifications, 0) as verifications,
|
|
67
|
-
COALESCE(curations, 0) as curations,
|
|
68
|
-
COALESCE(discoveries * 3 + refinements * 2 + verifications * 2 + curations * 4, 0) as total_weight
|
|
69
|
-
FROM audit_activity
|
|
70
|
-
ORDER BY total_weight DESC`,
|
|
71
|
-
[clientId, String(intervalDays)]
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
return createSuccessResponse(
|
|
75
|
-
{ Records: result.rows },
|
|
76
|
-
'Contributors retrieved',
|
|
77
|
-
{ Total_Records: result.rowCount, Request_ID, Timestamp: new Date().toISOString() }
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
} catch (error) {
|
|
81
|
-
return handleError(error);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
exports.handler = wrapHandler(getEnterpriseContributors);
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Knowledge Categories Get Handler
|
|
3
|
-
*
|
|
4
|
-
* GET /api/enterprise/knowledge/categories
|
|
5
|
-
* Returns: { Records: [{ category_id, name, description }] } + meta
|
|
6
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
async function getEnterpriseKnowledgeCategories({ requestContext }) {
|
|
12
|
-
try {
|
|
13
|
-
const Request_ID = requestContext.requestId;
|
|
14
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
15
|
-
if (!email) {
|
|
16
|
-
return createErrorResponse(401, 'Authentication required');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const userResult = await executeQuery(
|
|
20
|
-
`SELECT u.client_id, c.subscription_tier
|
|
21
|
-
FROM rapport.users u
|
|
22
|
-
JOIN rapport.clients c ON c.client_id = u.client_id
|
|
23
|
-
WHERE u.email_address = $1 LIMIT 1`,
|
|
24
|
-
[email]
|
|
25
|
-
);
|
|
26
|
-
if (!userResult.rows.length) {
|
|
27
|
-
return createErrorResponse(403, 'User not found');
|
|
28
|
-
}
|
|
29
|
-
if (userResult.rows[0].subscription_tier !== 'enterprise') {
|
|
30
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
31
|
-
}
|
|
32
|
-
const clientId = userResult.rows[0].client_id;
|
|
33
|
-
|
|
34
|
-
const result = await executeQuery(
|
|
35
|
-
`SELECT category_id, name, description
|
|
36
|
-
FROM rapport.knowledge_categories
|
|
37
|
-
WHERE client_id = $1
|
|
38
|
-
ORDER BY name`,
|
|
39
|
-
[clientId]
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
return createSuccessResponse(
|
|
43
|
-
{ Records: result.rows },
|
|
44
|
-
'Categories retrieved',
|
|
45
|
-
{ Total_Records: result.rowCount, Request_ID, Timestamp: new Date().toISOString() }
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
} catch (error) {
|
|
49
|
-
return handleError(error);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
exports.handler = wrapHandler(getEnterpriseKnowledgeCategories);
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Knowledge Create Handler
|
|
3
|
-
*
|
|
4
|
-
* POST /api/enterprise/knowledge
|
|
5
|
-
* Body: { title, content, category?, tags?, invariant_id? }
|
|
6
|
-
* Returns: { Records: [CuratedKnowledge] } + meta
|
|
7
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, logModificationEvent, AuditEventType, EntityType } = require('./helpers');
|
|
11
|
-
|
|
12
|
-
async function createEnterpriseKnowledge({ body, requestContext }) {
|
|
13
|
-
try {
|
|
14
|
-
const Request_ID = requestContext.requestId;
|
|
15
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
16
|
-
if (!email) {
|
|
17
|
-
return createErrorResponse(401, 'Authentication required');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const userResult = await executeQuery(
|
|
21
|
-
`SELECT u.client_id, c.subscription_tier
|
|
22
|
-
FROM rapport.users u
|
|
23
|
-
JOIN rapport.clients c ON c.client_id = u.client_id
|
|
24
|
-
WHERE u.email_address = $1 LIMIT 1`,
|
|
25
|
-
[email]
|
|
26
|
-
);
|
|
27
|
-
if (!userResult.rows.length) {
|
|
28
|
-
return createErrorResponse(403, 'User not found');
|
|
29
|
-
}
|
|
30
|
-
if (userResult.rows[0].subscription_tier !== 'enterprise') {
|
|
31
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
32
|
-
}
|
|
33
|
-
const clientId = userResult.rows[0].client_id;
|
|
34
|
-
|
|
35
|
-
const requestBody = body || {};
|
|
36
|
-
const { title, content, category, tags, invariant_id } = requestBody;
|
|
37
|
-
|
|
38
|
-
if (!title || !content) {
|
|
39
|
-
return createErrorResponse(400, 'title and content are required');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const result = await executeQuery(
|
|
43
|
-
`INSERT INTO rapport.curated_knowledge
|
|
44
|
-
(client_id, invariant_id, title, content, category, tags, curator_email, status)
|
|
45
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, 'draft')
|
|
46
|
-
RETURNING *`,
|
|
47
|
-
[
|
|
48
|
-
clientId,
|
|
49
|
-
invariant_id || null,
|
|
50
|
-
title,
|
|
51
|
-
content,
|
|
52
|
-
category || null,
|
|
53
|
-
tags || [],
|
|
54
|
-
email,
|
|
55
|
-
]
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
const created = result.rows[0];
|
|
59
|
-
|
|
60
|
-
await logModificationEvent(AuditEventType.DATA_CREATE, EntityType.KNOWLEDGE, String(created.knowledge_id), {
|
|
61
|
-
requestContext,
|
|
62
|
-
client_id: clientId,
|
|
63
|
-
after_state: { title, category, status: 'draft' },
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return createSuccessResponse(
|
|
67
|
-
{ Records: [created] },
|
|
68
|
-
'Knowledge item created',
|
|
69
|
-
{ Total_Records: 1, Request_ID, Timestamp: new Date().toISOString() }
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
} catch (error) {
|
|
73
|
-
return handleError(error);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
exports.handler = wrapHandler(createEnterpriseKnowledge);
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Knowledge Delete Handler
|
|
3
|
-
*
|
|
4
|
-
* DELETE /api/enterprise/knowledge?knowledge_id=xxx
|
|
5
|
-
* Returns: { Records: [] } + meta
|
|
6
|
-
* Auth: Cognito JWT required, enterprise tier only
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, logModificationEvent, AuditEventType, EntityType } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
async function deleteEnterpriseKnowledge({ queryStringParameters, requestContext }) {
|
|
12
|
-
try {
|
|
13
|
-
const Request_ID = requestContext.requestId;
|
|
14
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
15
|
-
if (!email) {
|
|
16
|
-
return createErrorResponse(401, 'Authentication required');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const userResult = await executeQuery(
|
|
20
|
-
`SELECT u.client_id, c.subscription_tier
|
|
21
|
-
FROM rapport.users u
|
|
22
|
-
JOIN rapport.clients c ON c.client_id = u.client_id
|
|
23
|
-
WHERE u.email_address = $1 LIMIT 1`,
|
|
24
|
-
[email]
|
|
25
|
-
);
|
|
26
|
-
if (!userResult.rows.length) {
|
|
27
|
-
return createErrorResponse(403, 'User not found');
|
|
28
|
-
}
|
|
29
|
-
if (userResult.rows[0].subscription_tier !== 'enterprise') {
|
|
30
|
-
return createErrorResponse(403, 'Enterprise subscription required');
|
|
31
|
-
}
|
|
32
|
-
const clientId = userResult.rows[0].client_id;
|
|
33
|
-
|
|
34
|
-
const knowledgeId = (queryStringParameters || {}).knowledge_id;
|
|
35
|
-
if (!knowledgeId) {
|
|
36
|
-
return createErrorResponse(400, 'knowledge_id is required');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Fetch current state for audit trail
|
|
40
|
-
const current = await executeQuery(
|
|
41
|
-
'SELECT * FROM rapport.curated_knowledge WHERE knowledge_id = $1 AND client_id = $2',
|
|
42
|
-
[knowledgeId, clientId]
|
|
43
|
-
);
|
|
44
|
-
if (!current.rows.length) {
|
|
45
|
-
return createErrorResponse(404, 'Knowledge item not found');
|
|
46
|
-
}
|
|
47
|
-
const beforeState = current.rows[0];
|
|
48
|
-
|
|
49
|
-
await executeQuery(
|
|
50
|
-
'DELETE FROM rapport.curated_knowledge WHERE knowledge_id = $1 AND client_id = $2',
|
|
51
|
-
[knowledgeId, clientId]
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
await logModificationEvent(AuditEventType.DATA_DELETE, EntityType.KNOWLEDGE, String(knowledgeId), {
|
|
55
|
-
requestContext,
|
|
56
|
-
client_id: clientId,
|
|
57
|
-
before_state: { title: beforeState.title, status: beforeState.status },
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return createSuccessResponse(
|
|
61
|
-
{ Records: [] },
|
|
62
|
-
'Knowledge item deleted',
|
|
63
|
-
{ Total_Records: 0, Request_ID, Timestamp: new Date().toISOString() }
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
} catch (error) {
|
|
67
|
-
return handleError(error);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
exports.handler = wrapHandler(deleteEnterpriseKnowledge);
|