Skip to content

Commit ed543f0

Browse files
committed
chore: merge main into release for new releases
2 parents a9d2df2 + 49f406c commit ed543f0

39 files changed

+2439
-1642
lines changed

apps/api/src/attachments/upload-attachment.dto.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const ALLOWED_FILE_TYPES = [
1818
'text/plain',
1919
'application/msword',
2020
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
21+
'application/vnd.ms-excel',
22+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
2123
];
2224

2325
export class UploadAttachmentDto {

apps/api/src/tasks/dto/upload-attachment.dto.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const ALLOWED_FILE_TYPES = [
1818
'text/plain',
1919
'application/msword',
2020
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
21+
'application/vnd.ms-excel',
22+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
2123
];
2224

2325
export class UploadAttachmentDto {

apps/app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@ai-sdk/provider": "^2.0.0",
99
"@ai-sdk/react": "^2.0.60",
1010
"@ai-sdk/rsc": "^1.0.0",
11+
"@aws-sdk/client-ec2": "^3.911.0",
1112
"@aws-sdk/client-lambda": "^3.891.0",
1213
"@aws-sdk/client-s3": "^3.859.0",
1314
"@aws-sdk/client-sts": "^3.808.0",
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
'use server';
2+
3+
import { encrypt } from '@/lib/encryption';
4+
import { getIntegrationHandler } from '@comp/integrations';
5+
import { db } from '@db';
6+
import { revalidatePath } from 'next/cache';
7+
import { headers } from 'next/headers';
8+
import { z } from 'zod';
9+
import { authActionClient } from '../../../../../actions/safe-action';
10+
import { runTests } from './run-tests';
11+
12+
const connectCloudSchema = z.object({
13+
cloudProvider: z.enum(['aws', 'gcp', 'azure']),
14+
credentials: z.record(z.string(), z.string()),
15+
});
16+
17+
export const connectCloudAction = authActionClient
18+
.inputSchema(connectCloudSchema)
19+
.metadata({
20+
name: 'connect-cloud',
21+
track: {
22+
event: 'connect-cloud',
23+
channel: 'cloud-tests',
24+
},
25+
})
26+
.action(async ({ parsedInput: { cloudProvider, credentials }, ctx: { session } }) => {
27+
try {
28+
if (!session.activeOrganizationId) {
29+
return {
30+
success: false,
31+
error: 'No active organization found',
32+
};
33+
}
34+
35+
// Validate credentials before storing
36+
try {
37+
const integrationHandler = getIntegrationHandler(cloudProvider);
38+
if (!integrationHandler) {
39+
return {
40+
success: false,
41+
error: 'Integration handler not found',
42+
};
43+
}
44+
45+
// Process credentials to the format expected by the handler
46+
const typedCredentials = await integrationHandler.processCredentials(
47+
credentials,
48+
async (data: any) => data, // Pass through without encryption for validation
49+
);
50+
51+
// Validate by attempting to fetch (this will throw if credentials are invalid)
52+
await integrationHandler.fetch(typedCredentials);
53+
} catch (error) {
54+
console.error('Credential validation failed:', error);
55+
return {
56+
success: false,
57+
error:
58+
error instanceof Error
59+
? `Invalid credentials: ${error.message}`
60+
: 'Failed to validate credentials. Please check your credentials and try again.',
61+
};
62+
}
63+
64+
// Encrypt all credential fields after validation
65+
const encryptedCredentials: Record<string, unknown> = {};
66+
for (const [key, value] of Object.entries(credentials)) {
67+
if (value) {
68+
encryptedCredentials[key] = await encrypt(value);
69+
}
70+
}
71+
72+
// Check if integration already exists
73+
const existingIntegration = await db.integration.findFirst({
74+
where: {
75+
integrationId: cloudProvider,
76+
organizationId: session.activeOrganizationId,
77+
},
78+
});
79+
80+
if (existingIntegration) {
81+
// Update existing integration
82+
await db.integration.update({
83+
where: { id: existingIntegration.id },
84+
data: {
85+
userSettings: encryptedCredentials as any,
86+
lastRunAt: null, // Reset to trigger new scan
87+
},
88+
});
89+
} else {
90+
// Create new integration
91+
await db.integration.create({
92+
data: {
93+
name: cloudProvider.toUpperCase(),
94+
integrationId: cloudProvider,
95+
organizationId: session.activeOrganizationId,
96+
userSettings: encryptedCredentials as any,
97+
settings: {},
98+
},
99+
});
100+
}
101+
102+
// Trigger immediate scan
103+
await runTests();
104+
105+
// Revalidate the path
106+
const headersList = await headers();
107+
let path = headersList.get('x-pathname') || headersList.get('referer') || '';
108+
path = path.replace(/\/[a-z]{2}\//, '/');
109+
revalidatePath(path);
110+
111+
return {
112+
success: true,
113+
};
114+
} catch (error) {
115+
console.error('Failed to connect cloud provider:', error);
116+
return {
117+
success: false,
118+
error: error instanceof Error ? error.message : 'Failed to connect cloud provider',
119+
};
120+
}
121+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use server';
2+
3+
import { db } from '@db';
4+
import { revalidatePath } from 'next/cache';
5+
import { headers } from 'next/headers';
6+
import { z } from 'zod';
7+
import { authActionClient } from '../../../../../actions/safe-action';
8+
9+
const disconnectCloudSchema = z.object({
10+
cloudProvider: z.enum(['aws', 'gcp', 'azure']),
11+
});
12+
13+
export const disconnectCloudAction = authActionClient
14+
.inputSchema(disconnectCloudSchema)
15+
.metadata({
16+
name: 'disconnect-cloud',
17+
track: {
18+
event: 'disconnect-cloud',
19+
channel: 'cloud-tests',
20+
},
21+
})
22+
.action(async ({ parsedInput: { cloudProvider }, ctx: { session } }) => {
23+
try {
24+
if (!session.activeOrganizationId) {
25+
return {
26+
success: false,
27+
error: 'No active organization found',
28+
};
29+
}
30+
31+
// Find and delete the integration
32+
const integration = await db.integration.findFirst({
33+
where: {
34+
integrationId: cloudProvider,
35+
organizationId: session.activeOrganizationId,
36+
},
37+
});
38+
39+
if (!integration) {
40+
return {
41+
success: false,
42+
error: 'Cloud provider not found',
43+
};
44+
}
45+
46+
// Delete the integration (cascade will delete results)
47+
await db.integration.delete({
48+
where: { id: integration.id },
49+
});
50+
51+
// Revalidate the path
52+
const headersList = await headers();
53+
let path = headersList.get('x-pathname') || headersList.get('referer') || '';
54+
path = path.replace(/\/[a-z]{2}\//, '/');
55+
revalidatePath(path);
56+
57+
return {
58+
success: true,
59+
};
60+
} catch (error) {
61+
console.error('Failed to disconnect cloud provider:', error);
62+
return {
63+
success: false,
64+
error: error instanceof Error ? error.message : 'Failed to disconnect cloud provider',
65+
};
66+
}
67+
});

apps/app/src/app/(app)/[orgId]/tests/dashboard/actions/run-tests.ts renamed to apps/app/src/app/(app)/[orgId]/cloud-tests/actions/run-tests.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,20 @@ export const runTests = async () => {
3232
});
3333

3434
const headersList = await headers();
35-
let path =
36-
headersList.get("x-pathname") || headersList.get("referer") || "";
37-
path = path.replace(/\/[a-z]{2}\//, "/");
35+
let path = headersList.get('x-pathname') || headersList.get('referer') || '';
36+
path = path.replace(/\/[a-z]{2}\//, '/');
3837

3938
revalidatePath(path);
4039

4140
return {
4241
success: true,
4342
errors: null,
4443
taskId: handle.id,
44+
publicAccessToken: handle.publicAccessToken,
4545
};
4646
} catch (error) {
4747
console.error('Error triggering integration tests:', error);
48-
48+
4949
return {
5050
success: false,
5151
errors: [error instanceof Error ? error.message : 'Failed to trigger integration tests'],
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
'use server';
2+
3+
import { encrypt } from '@/lib/encryption';
4+
import { db } from '@db';
5+
import { revalidatePath } from 'next/cache';
6+
import { headers } from 'next/headers';
7+
import { z } from 'zod';
8+
import { authActionClient } from '../../../../../actions/safe-action';
9+
10+
const updateCloudCredentialsSchema = z.object({
11+
cloudProvider: z.enum(['aws', 'gcp', 'azure']),
12+
credentials: z.record(z.string(), z.string()),
13+
});
14+
15+
export const updateCloudCredentialsAction = authActionClient
16+
.inputSchema(updateCloudCredentialsSchema)
17+
.metadata({
18+
name: 'update-cloud-credentials',
19+
track: {
20+
event: 'update-cloud-credentials',
21+
channel: 'cloud-tests',
22+
},
23+
})
24+
.action(async ({ parsedInput: { cloudProvider, credentials }, ctx: { session } }) => {
25+
try {
26+
if (!session.activeOrganizationId) {
27+
return {
28+
success: false,
29+
error: 'No active organization found',
30+
};
31+
}
32+
33+
// Find the integration
34+
const integration = await db.integration.findFirst({
35+
where: {
36+
integrationId: cloudProvider,
37+
organizationId: session.activeOrganizationId,
38+
},
39+
});
40+
41+
if (!integration) {
42+
return {
43+
success: false,
44+
error: 'Cloud provider not found',
45+
};
46+
}
47+
48+
// Encrypt all credential fields
49+
const encryptedCredentials: Record<string, unknown> = {};
50+
for (const [key, value] of Object.entries(credentials)) {
51+
if (value) {
52+
encryptedCredentials[key] = await encrypt(value);
53+
}
54+
}
55+
56+
// Update the integration
57+
await db.integration.update({
58+
where: { id: integration.id },
59+
data: {
60+
userSettings: encryptedCredentials as any,
61+
},
62+
});
63+
64+
// Revalidate the path
65+
const headersList = await headers();
66+
let path = headersList.get('x-pathname') || headersList.get('referer') || '';
67+
path = path.replace(/\/[a-z]{2}\//, '/');
68+
revalidatePath(path);
69+
70+
return {
71+
success: true,
72+
};
73+
} catch (error) {
74+
console.error('Failed to update cloud credentials:', error);
75+
return {
76+
success: false,
77+
error: error instanceof Error ? error.message : 'Failed to update cloud credentials',
78+
};
79+
}
80+
});

0 commit comments

Comments
 (0)