@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,185 +0,0 @@
1
- /**
2
- * Pattern Violation Handler
3
- * Records standards violations detected during Claude Code sessions
4
- *
5
- * POST /api/patterns/violations
6
- * Body: { pattern, violations[], session_id, user_id, project_id, timestamp }
7
- *
8
- * Called by: pre-compact.js hook (recordViolation method)
9
- */
10
-
11
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
12
-
13
- /**
14
- * Record pattern violation
15
- * Stores violation for compliance tracking and team learning
16
- */
17
- async function recordPatternViolation({ body: requestBody = {}, requestContext }) {
18
- try {
19
- const Request_ID = requestContext?.requestId || 'unknown';
20
-
21
- const {
22
- pattern,
23
- violations = [],
24
- session_id,
25
- user_id,
26
- project_id,
27
- timestamp
28
- } = requestBody;
29
-
30
- // Validate required fields
31
- if (!pattern || !pattern.element) {
32
- return createErrorResponse(400, 'pattern.element is required');
33
- }
34
-
35
- if (!violations || violations.length === 0) {
36
- return createErrorResponse(400, 'violations array is required');
37
- }
38
-
39
- if (!session_id) {
40
- return createErrorResponse(400, 'session_id is required');
41
- }
42
-
43
- // Generate pattern_id from element if not provided
44
- const patternId = pattern.pattern_id || `pat_${pattern.element.toLowerCase().replace(/\s+/g, '_').substring(0, 50)}`;
45
-
46
- // If no project_id, we can't create a pattern record (FK constraint)
47
- // Return success anyway - hooks should not fail
48
- if (!project_id) {
49
- console.log('[patternViolationPost] No project_id provided, skipping violation record');
50
- return createSuccessResponse(
51
- {
52
- Records: [{
53
- pattern_id: patternId,
54
- violation_count: violations.length,
55
- recorded: false,
56
- reason: 'no_project_id',
57
- message: 'Violation logged but not recorded (no project context)'
58
- }]
59
- },
60
- 'Violation acknowledged (no project context)',
61
- {
62
- Total_Records: 0,
63
- Request_ID,
64
- Timestamp: new Date().toISOString()
65
- }
66
- );
67
- }
68
-
69
- // Ensure the pattern exists (upsert with violation count)
70
- const upsertPatternQuery = `
71
- INSERT INTO rapport.patterns (
72
- pattern_id,
73
- project_id,
74
- intent,
75
- constraints,
76
- outcome_criteria,
77
- maturity,
78
- handoff_count,
79
- successful_handoffs,
80
- failed_handoffs,
81
- discovered_by,
82
- discovered_at,
83
- last_used,
84
- pattern_data
85
- ) VALUES (
86
- $1, $2, $3, $4, $5, 'provisional', 1, 0, 1, $6, NOW(), NOW(), $7
87
- )
88
- ON CONFLICT (pattern_id) DO UPDATE SET
89
- handoff_count = rapport.patterns.handoff_count + 1,
90
- failed_handoffs = rapport.patterns.failed_handoffs + 1,
91
- last_used = NOW()
92
- RETURNING pattern_id
93
- `;
94
-
95
- await executeQuery(upsertPatternQuery, [
96
- patternId,
97
- project_id || null,
98
- pattern.intent || pattern.element,
99
- JSON.stringify(pattern.constraints || []),
100
- JSON.stringify(pattern.outcome_criteria || []),
101
- user_id || 'anonymous',
102
- JSON.stringify({
103
- type: pattern.type,
104
- category: pattern.category,
105
- file: pattern.file
106
- })
107
- ]);
108
-
109
- // Record each violation
110
- const violationRecords = [];
111
- for (const violation of violations) {
112
- const violationQuery = `
113
- INSERT INTO rapport.pattern_usage (
114
- pattern_id,
115
- email_address,
116
- session_id,
117
- success,
118
- context,
119
- used_at
120
- ) VALUES (
121
- $1, $2, $3, false, $4, $5
122
- )
123
- RETURNING usage_id
124
- `;
125
-
126
- const result = await executeQuery(violationQuery, [
127
- patternId,
128
- user_id || 'anonymous',
129
- session_id,
130
- JSON.stringify({
131
- violation: true,
132
- standard_id: violation.standard_id,
133
- rule: violation.rule,
134
- description: violation.description,
135
- file: pattern.file,
136
- category: pattern.category
137
- }),
138
- timestamp ? new Date(timestamp) : new Date()
139
- ]);
140
-
141
- violationRecords.push({
142
- usage_id: result.rows[0]?.usage_id,
143
- standard_id: violation.standard_id,
144
- rule: violation.rule
145
- });
146
- }
147
-
148
- // Also record in session_standards if tracking standards shown
149
- // Update to mark standard as violated
150
- const updateStandardsQuery = `
151
- UPDATE rapport.session_standards
152
- SET violated = true
153
- WHERE session_id = $1
154
- AND pattern_id = ANY($2::varchar[])
155
- `;
156
-
157
- const standardIds = violations.map(v => v.standard_id).filter(Boolean);
158
- if (standardIds.length > 0) {
159
- await executeQuery(updateStandardsQuery, [session_id, standardIds]);
160
- }
161
-
162
- return createSuccessResponse(
163
- {
164
- Records: [{
165
- pattern_id: patternId,
166
- violation_count: violationRecords.length,
167
- violations: violationRecords,
168
- recorded: true
169
- }]
170
- },
171
- `Recorded ${violationRecords.length} violation(s)`,
172
- {
173
- Total_Records: violationRecords.length,
174
- Request_ID,
175
- Timestamp: new Date().toISOString()
176
- }
177
- );
178
-
179
- } catch (error) {
180
- console.error('Handler Error:', error);
181
- return handleError(error);
182
- }
183
- }
184
-
185
- exports.handler = wrapHandler(recordPatternViolation);
@@ -1,248 +0,0 @@
1
- /**
2
- * Project Create Handler
3
- * Creates new project for a company
4
- *
5
- * POST /api/projects
6
- * Body: { Company_ID, project_name, description, private }
7
- */
8
-
9
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, checkSubscriptionLimits } = require('./helpers');
10
- const crypto = require('crypto');
11
- const https = require('https');
12
-
13
- /**
14
- * Create project
15
- * Requires Admin access to company
16
- */
17
- async function createProject({ body: requestBody = {}, requestContext }) {
18
- try {
19
- const Request_ID = requestContext.requestId;
20
- // REST API: requestContext.authorizer.claims.email
21
- // HTTP API: requestContext.authorizer.jwt.claims.email
22
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
23
- const { Company_ID, project_name, description, private: isPrivate, repo_url } = requestBody;
24
-
25
- // Validate required fields
26
- if (!Company_ID || !project_name) {
27
- return createErrorResponse(400, 'Company_ID and project_name are required');
28
- }
29
-
30
- // Check user has admin access to company
31
- const adminQuery = `
32
- SELECT ue.admin, c.company_name
33
- FROM rapport.user_entitlements ue
34
- JOIN rapport.companies c ON ue.company_id = c.company_id
35
- WHERE ue.email_address = $1
36
- AND ue.company_id = $2
37
- `;
38
- const adminCheck = await executeQuery(adminQuery, [email, Company_ID]);
39
-
40
- if (adminCheck.rowCount === 0 || !adminCheck.rows[0].admin) {
41
- return createErrorResponse(403, 'Admin access required to create projects');
42
- }
43
-
44
- // Check project limit for subscription tier
45
- const clientQuery = `
46
- SELECT c.subscription_tier
47
- FROM rapport.clients c
48
- JOIN rapport.user_entitlements ue ON c.client_id = ue.client_id
49
- WHERE ue.email_address = $1 AND ue.company_id = $2
50
- `;
51
- const clientResult = await executeQuery(clientQuery, [email, Company_ID]);
52
- const subscriptionTier = clientResult.rows[0]?.subscription_tier || 'free';
53
-
54
- const countQuery = `
55
- SELECT COUNT(*) as project_count
56
- FROM rapport.projects
57
- WHERE company_id = $1
58
- `;
59
- const countResult = await executeQuery(countQuery, [Company_ID]);
60
- const projectCount = parseInt(countResult.rows[0]?.project_count || '0');
61
-
62
- const limitCheck = checkSubscriptionLimits(
63
- { subscription_tier: subscriptionTier },
64
- 'create_project',
65
- { projects: projectCount }
66
- );
67
- if (!limitCheck.allowed) {
68
- return createErrorResponse(402, limitCheck.message);
69
- }
70
-
71
- // Generate business-scoped project ID
72
- // Format: prj_COMPANYID_timestamp
73
- const timestamp = Date.now();
74
- const project_id = `prj_${Company_ID}_${timestamp}`.toLowerCase().replace(/\s/g, '_');
75
-
76
- // Create project
77
- const query = `
78
- INSERT INTO rapport.projects (
79
- project_id,
80
- company_id,
81
- project_name,
82
- description,
83
- private,
84
- repo_url
85
- )
86
- VALUES ($1, $2, $3, $4, $5, $6)
87
- RETURNING
88
- project_id,
89
- company_id,
90
- project_name,
91
- description,
92
- private,
93
- repo_url,
94
- created_at
95
- `;
96
-
97
- const result = await executeQuery(query, [
98
- project_id,
99
- Company_ID,
100
- project_name,
101
- description || null,
102
- isPrivate || false,
103
- repo_url || null
104
- ]);
105
-
106
- // Add creator as owner
107
- const collabQuery = `
108
- INSERT INTO rapport.project_collaborators (
109
- project_id,
110
- email_address,
111
- role
112
- )
113
- VALUES ($1, $2, 'owner')
114
- `;
115
- await executeQuery(collabQuery, [project_id, email]);
116
-
117
- // Auto-create GitHub webhook if repo_url is a GitHub URL
118
- let webhookCreated = false;
119
- if (repo_url && repo_url.includes('github.com')) {
120
- try {
121
- webhookCreated = await createGitHubWebhook(email, repo_url, Company_ID);
122
- } catch (webhookErr) {
123
- console.warn('Webhook creation failed (non-blocking):', webhookErr.message);
124
- }
125
- }
126
-
127
- return createSuccessResponse(
128
- { Records: result.rows, webhook_created: webhookCreated },
129
- 'Project created successfully',
130
- {
131
- Total_Records: result.rowCount,
132
- Request_ID,
133
- Timestamp: new Date().toISOString()
134
- }
135
- );
136
-
137
- } catch (error) {
138
- console.error('Handler Error:', error);
139
- if (error.code === '23505') {
140
- return createErrorResponse(409, 'Project already exists');
141
- }
142
- return handleError(error);
143
- }
144
- }
145
-
146
- /**
147
- * Create GitHub webhook on a repo when a project is connected
148
- * Uses the customer's stored GitHub OAuth token and a per-repo secret
149
- */
150
- async function createGitHubWebhook(email, repoUrl, companyId) {
151
- // Extract owner/repo from URL
152
- const match = repoUrl.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
153
- if (!match) {
154
- console.log('Could not parse GitHub owner/repo from:', repoUrl);
155
- return false;
156
- }
157
- const [, owner, repo] = match;
158
-
159
- // Get the user's GitHub token
160
- const connResult = await executeQuery(`
161
- SELECT access_token_encrypted FROM rapport.github_connections
162
- WHERE email_address = $1 AND revoked = FALSE
163
- `, [email]);
164
-
165
- if (connResult.rowCount === 0) {
166
- console.log('No GitHub connection for', email);
167
- return false;
168
- }
169
-
170
- const encryptionKey = process.env.GITHUB_TOKEN_ENCRYPTION_KEY;
171
- if (!encryptionKey) return false;
172
-
173
- const accessToken = decryptToken(connResult.rows[0].access_token_encrypted, encryptionKey);
174
-
175
- // Generate per-repo webhook secret
176
- const webhookSecret = crypto.randomBytes(32).toString('hex');
177
- const webhookUrl = process.env.WEBHOOK_URL || 'https://api.mindmeld.dev/api/webhooks/github';
178
-
179
- // Create webhook via GitHub API
180
- const payload = JSON.stringify({
181
- name: 'web',
182
- active: true,
183
- events: ['push', 'pull_request'],
184
- config: {
185
- url: webhookUrl,
186
- content_type: 'json',
187
- secret: webhookSecret,
188
- insecure_ssl: '0'
189
- }
190
- });
191
-
192
- const response = await new Promise((resolve, reject) => {
193
- const req = https.request({
194
- hostname: 'api.github.com',
195
- path: `/repos/${owner}/${repo}/hooks`,
196
- method: 'POST',
197
- headers: {
198
- 'Authorization': `Bearer ${accessToken}`,
199
- 'User-Agent': 'MindMeld-App',
200
- 'Accept': 'application/vnd.github+json',
201
- 'Content-Type': 'application/json',
202
- 'Content-Length': Buffer.byteLength(payload)
203
- }
204
- }, (res) => {
205
- let data = '';
206
- res.on('data', chunk => data += chunk);
207
- res.on('end', () => {
208
- try { resolve({ statusCode: res.statusCode, body: JSON.parse(data) }); }
209
- catch (e) { resolve({ statusCode: res.statusCode, body: data }); }
210
- });
211
- });
212
- req.on('error', reject);
213
- req.write(payload);
214
- req.end();
215
- });
216
-
217
- if (response.statusCode !== 201) {
218
- console.warn(`GitHub webhook creation returned ${response.statusCode}:`, JSON.stringify(response.body).substring(0, 200));
219
- return false;
220
- }
221
-
222
- const githubWebhookId = response.body.id;
223
-
224
- // Register repo in git_repositories with per-repo webhook secret
225
- // Unique constraint is on (company_id, repo_url)
226
- await executeQuery(`
227
- INSERT INTO rapport.git_repositories (
228
- repo_id, repo_name, repo_url, company_id, webhook_secret, created_at, updated_at
229
- ) VALUES (gen_random_uuid(), $1, $2, $3, $4, NOW(), NOW())
230
- ON CONFLICT (company_id, repo_url) DO UPDATE SET
231
- webhook_secret = EXCLUDED.webhook_secret,
232
- updated_at = NOW()
233
- `, [`${owner}/${repo}`, repoUrl, companyId, webhookSecret]);
234
-
235
- console.log(`Created GitHub webhook ${githubWebhookId} on ${owner}/${repo}`);
236
- return true;
237
- }
238
-
239
- function decryptToken(encryptedData, key) {
240
- const [ivHex, encrypted] = encryptedData.split(':');
241
- const iv = Buffer.from(ivHex, 'hex');
242
- const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key, 'hex'), iv);
243
- let decrypted = decipher.update(encrypted, 'hex', 'utf8');
244
- decrypted += decipher.final('utf8');
245
- return decrypted;
246
- }
247
-
248
- exports.handler = wrapHandler(createProject);
@@ -1,82 +0,0 @@
1
- /**
2
- * Project Delete Handler
3
- * Archives (soft deletes) a project
4
- *
5
- * DELETE /api/projects?project_id=xxx
6
- */
7
-
8
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
9
-
10
- /**
11
- * Archive project (soft delete)
12
- * Requires owner role or company admin
13
- */
14
- async function deleteProject({ queryStringParameters: queryParams = {}, requestContext }) {
15
- try {
16
- const Request_ID = requestContext.requestId;
17
- // REST API: requestContext.authorizer.claims.email
18
- // HTTP API: requestContext.authorizer.jwt.claims.email
19
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
20
- const projectId = queryParams.project_id;
21
-
22
- if (!projectId) {
23
- return createErrorResponse(400, 'projectId is required');
24
- }
25
-
26
- // Check user has access to project
27
- const accessQuery = `
28
- SELECT
29
- p.project_id,
30
- p.company_id,
31
- pc.role,
32
- ue.admin as company_admin
33
- FROM rapport.projects p
34
- LEFT JOIN rapport.project_collaborators pc
35
- ON p.project_id = pc.project_id
36
- AND pc.email_address = $1
37
- LEFT JOIN rapport.user_entitlements ue
38
- ON ue.email_address = $1
39
- AND ue.company_id = p.company_id
40
- WHERE p.project_id = $2
41
- `;
42
- const accessCheck = await executeQuery(accessQuery, [email, projectId]);
43
-
44
- if (accessCheck.rowCount === 0) {
45
- return createErrorResponse(404, 'Project not found');
46
- }
47
-
48
- const access = accessCheck.rows[0];
49
-
50
- // Check permissions (only owner or company admin can delete)
51
- const canDelete = access.role === 'owner' || access.company_admin === true;
52
- if (!canDelete) {
53
- return createErrorResponse(403, 'Only project owner or company admin can delete project');
54
- }
55
-
56
- // Archive project (soft delete)
57
- const query = `
58
- UPDATE rapport.projects
59
- SET archived = true, updated_at = NOW()
60
- WHERE project_id = $1
61
- RETURNING project_id, project_name, archived
62
- `;
63
-
64
- const result = await executeQuery(query, [projectId]);
65
-
66
- return createSuccessResponse(
67
- { Records: result.rows },
68
- 'Project archived successfully',
69
- {
70
- Total_Records: result.rowCount,
71
- Request_ID,
72
- Timestamp: new Date().toISOString()
73
- }
74
- );
75
-
76
- } catch (error) {
77
- console.error('Handler Error:', error);
78
- return handleError(error);
79
- }
80
- }
81
-
82
- exports.handler = wrapHandler(deleteProject);
@@ -1,95 +0,0 @@
1
- /**
2
- * Project Get Handler
3
- * Retrieves projects for a company with collaborator access check
4
- *
5
- * GET /api/projects?Company_ID=xxx
6
- */
7
-
8
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
9
-
10
- /**
11
- * Get projects
12
- * Returns all projects user has access to (via company entitlements or direct collaboration)
13
- */
14
- async function getProjects({ queryStringParameters: queryParams = {}, requestContext }) {
15
- try {
16
- const Request_ID = requestContext.requestId;
17
- // REST API: requestContext.authorizer.claims.email
18
- // HTTP API: requestContext.authorizer.jwt.claims.email
19
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
20
-
21
- // Get user's company access
22
- const entitlementQuery = `
23
- SELECT ue.company_id, ue.admin, c.company_name
24
- FROM rapport.user_entitlements ue
25
- JOIN rapport.companies c ON ue.company_id = c.company_id
26
- WHERE ue.email_address = $1
27
- `;
28
- const entitlements = await executeQuery(entitlementQuery, [email]);
29
-
30
- if (entitlements.rowCount === 0) {
31
- return createErrorResponse(403, 'No company access');
32
- }
33
-
34
- const companyIds = entitlements.rows.map(e => e.company_id);
35
-
36
- // Optional filter by specific company
37
- let targetCompanyIds = companyIds;
38
- if (queryParams.Company_ID) {
39
- if (!companyIds.includes(queryParams.Company_ID)) {
40
- return createErrorResponse(403, 'No access to specified company');
41
- }
42
- targetCompanyIds = [queryParams.Company_ID];
43
- }
44
-
45
- // Get projects for user's companies
46
- const query = `
47
- SELECT
48
- p.project_id,
49
- p.company_id,
50
- p.project_name,
51
- p.description,
52
- p.private,
53
- p.created_at,
54
- p.last_active,
55
- p.archived,
56
- c.company_name,
57
- COUNT(DISTINCT pc.email_address) as collaborator_count,
58
- COALESCE(
59
- json_agg(
60
- json_build_object(
61
- 'email', pc.email_address,
62
- 'role', pc.role,
63
- 'is_external', pc.is_external
64
- )
65
- ) FILTER (WHERE pc.email_address IS NOT NULL),
66
- '[]'
67
- ) as collaborators
68
- FROM rapport.projects p
69
- JOIN rapport.companies c ON p.company_id = c.company_id
70
- LEFT JOIN rapport.project_collaborators pc ON p.project_id = pc.project_id
71
- WHERE p.company_id = ANY($1::varchar[])
72
- AND p.archived = false
73
- GROUP BY p.project_id, c.company_name
74
- ORDER BY p.last_active DESC NULLS LAST, p.created_at DESC
75
- `;
76
-
77
- const result = await executeQuery(query, [targetCompanyIds]);
78
-
79
- return createSuccessResponse(
80
- { Records: result.rows },
81
- 'Projects retrieved successfully',
82
- {
83
- Total_Records: result.rowCount,
84
- Request_ID,
85
- Timestamp: new Date().toISOString()
86
- }
87
- );
88
-
89
- } catch (error) {
90
- console.error('Handler Error:', error);
91
- return handleError(error);
92
- }
93
- }
94
-
95
- exports.handler = wrapHandler(getProjects);