Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/api/src/attachments/upload-attachment.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const ALLOWED_FILE_TYPES = [
'text/plain',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
];

export class UploadAttachmentDto {
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/tasks/dto/upload-attachment.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const ALLOWED_FILE_TYPES = [
'text/plain',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
];

export class UploadAttachmentDto {
Expand Down
1 change: 1 addition & 0 deletions apps/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@ai-sdk/provider": "^2.0.0",
"@ai-sdk/react": "^2.0.60",
"@ai-sdk/rsc": "^1.0.0",
"@aws-sdk/client-ec2": "^3.911.0",
"@aws-sdk/client-lambda": "^3.891.0",
"@aws-sdk/client-s3": "^3.859.0",
"@aws-sdk/client-sts": "^3.808.0",
Expand Down
121 changes: 121 additions & 0 deletions apps/app/src/app/(app)/[orgId]/cloud-tests/actions/connect-cloud.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
'use server';

import { encrypt } from '@/lib/encryption';
import { getIntegrationHandler } from '@comp/integrations';
import { db } from '@db';
import { revalidatePath } from 'next/cache';
import { headers } from 'next/headers';
import { z } from 'zod';
import { authActionClient } from '../../../../../actions/safe-action';
import { runTests } from './run-tests';

const connectCloudSchema = z.object({
cloudProvider: z.enum(['aws', 'gcp', 'azure']),
credentials: z.record(z.string(), z.string()),
});

export const connectCloudAction = authActionClient
.inputSchema(connectCloudSchema)
.metadata({
name: 'connect-cloud',
track: {
event: 'connect-cloud',
channel: 'cloud-tests',
},
})
.action(async ({ parsedInput: { cloudProvider, credentials }, ctx: { session } }) => {
try {
if (!session.activeOrganizationId) {
return {
success: false,
error: 'No active organization found',
};
}

// Validate credentials before storing
try {
const integrationHandler = getIntegrationHandler(cloudProvider);
if (!integrationHandler) {
return {
success: false,
error: 'Integration handler not found',
};
}

// Process credentials to the format expected by the handler
const typedCredentials = await integrationHandler.processCredentials(
credentials,
async (data: any) => data, // Pass through without encryption for validation
);

// Validate by attempting to fetch (this will throw if credentials are invalid)
await integrationHandler.fetch(typedCredentials);
} catch (error) {
console.error('Credential validation failed:', error);
return {
success: false,
error:
error instanceof Error
? `Invalid credentials: ${error.message}`
: 'Failed to validate credentials. Please check your credentials and try again.',
};
}

// Encrypt all credential fields after validation
const encryptedCredentials: Record<string, unknown> = {};
for (const [key, value] of Object.entries(credentials)) {
if (value) {
encryptedCredentials[key] = await encrypt(value);
}
}

// Check if integration already exists
const existingIntegration = await db.integration.findFirst({
where: {
integrationId: cloudProvider,
organizationId: session.activeOrganizationId,
},
});

if (existingIntegration) {
// Update existing integration
await db.integration.update({
where: { id: existingIntegration.id },
data: {
userSettings: encryptedCredentials as any,
lastRunAt: null, // Reset to trigger new scan
},
});
} else {
// Create new integration
await db.integration.create({
data: {
name: cloudProvider.toUpperCase(),
integrationId: cloudProvider,
organizationId: session.activeOrganizationId,
userSettings: encryptedCredentials as any,
settings: {},
},
});
}

// Trigger immediate scan
await runTests();

// Revalidate the path
const headersList = await headers();
let path = headersList.get('x-pathname') || headersList.get('referer') || '';
path = path.replace(/\/[a-z]{2}\//, '/');
revalidatePath(path);

return {
success: true,
};
} catch (error) {
console.error('Failed to connect cloud provider:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to connect cloud provider',
};
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use server';

import { db } from '@db';
import { revalidatePath } from 'next/cache';
import { headers } from 'next/headers';
import { z } from 'zod';
import { authActionClient } from '../../../../../actions/safe-action';

const disconnectCloudSchema = z.object({
cloudProvider: z.enum(['aws', 'gcp', 'azure']),
});

export const disconnectCloudAction = authActionClient
.inputSchema(disconnectCloudSchema)
.metadata({
name: 'disconnect-cloud',
track: {
event: 'disconnect-cloud',
channel: 'cloud-tests',
},
})
.action(async ({ parsedInput: { cloudProvider }, ctx: { session } }) => {
try {
if (!session.activeOrganizationId) {
return {
success: false,
error: 'No active organization found',
};
}

// Find and delete the integration
const integration = await db.integration.findFirst({
where: {
integrationId: cloudProvider,
organizationId: session.activeOrganizationId,
},
});

if (!integration) {
return {
success: false,
error: 'Cloud provider not found',
};
}

// Delete the integration (cascade will delete results)
await db.integration.delete({
where: { id: integration.id },
});

// Revalidate the path
const headersList = await headers();
let path = headersList.get('x-pathname') || headersList.get('referer') || '';
path = path.replace(/\/[a-z]{2}\//, '/');
revalidatePath(path);

return {
success: true,
};
} catch (error) {
console.error('Failed to disconnect cloud provider:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to disconnect cloud provider',
};
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ export const runTests = async () => {
});

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

revalidatePath(path);

return {
success: true,
errors: null,
taskId: handle.id,
publicAccessToken: handle.publicAccessToken,
};
} catch (error) {
console.error('Error triggering integration tests:', error);

return {
success: false,
errors: [error instanceof Error ? error.message : 'Failed to trigger integration tests'],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
'use server';

import { encrypt } from '@/lib/encryption';
import { db } from '@db';
import { revalidatePath } from 'next/cache';
import { headers } from 'next/headers';
import { z } from 'zod';
import { authActionClient } from '../../../../../actions/safe-action';

const updateCloudCredentialsSchema = z.object({
cloudProvider: z.enum(['aws', 'gcp', 'azure']),
credentials: z.record(z.string(), z.string()),
});

export const updateCloudCredentialsAction = authActionClient
.inputSchema(updateCloudCredentialsSchema)
.metadata({
name: 'update-cloud-credentials',
track: {
event: 'update-cloud-credentials',
channel: 'cloud-tests',
},
})
.action(async ({ parsedInput: { cloudProvider, credentials }, ctx: { session } }) => {
try {
if (!session.activeOrganizationId) {
return {
success: false,
error: 'No active organization found',
};
}

// Find the integration
const integration = await db.integration.findFirst({
where: {
integrationId: cloudProvider,
organizationId: session.activeOrganizationId,
},
});

if (!integration) {
return {
success: false,
error: 'Cloud provider not found',
};
}

// Encrypt all credential fields
const encryptedCredentials: Record<string, unknown> = {};
for (const [key, value] of Object.entries(credentials)) {
if (value) {
encryptedCredentials[key] = await encrypt(value);
}
}

// Update the integration
await db.integration.update({
where: { id: integration.id },
data: {
userSettings: encryptedCredentials as any,
},
});

// Revalidate the path
const headersList = await headers();
let path = headersList.get('x-pathname') || headersList.get('referer') || '';
path = path.replace(/\/[a-z]{2}\//, '/');
revalidatePath(path);

return {
success: true,
};
} catch (error) {
console.error('Failed to update cloud credentials:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to update cloud credentials',
};
}
});
Loading
Loading