cre-sync/lib/ghl/services/user-provisioning.ts
BusyBee3333 4e6467ffb0 Add CRESync CRM application with Setup page
- Build complete Next.js CRM for commercial real estate
- Add authentication with JWT sessions and role-based access
- Add GoHighLevel API integration for contacts, conversations, opportunities
- Add AI-powered Control Center with tool calling
- Add Setup page with onboarding checklist (/setup)
- Add sidebar navigation with Setup menu item
- Fix type errors in onboarding API, GHL services, and control center tools
- Add Prisma schema with SQLite for local development
- Add UI components with clay morphism design system

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:30:55 -05:00

120 lines
3.4 KiB
TypeScript

import { prisma } from '@/lib/db';
import { LocationsService } from './locations';
import { GHLAgencyClient } from '../agency-client';
import { settingsService } from '@/lib/settings';
import { encrypt } from '@/lib/settings/encryption';
export interface ProvisionUserParams {
userId: string;
email: string;
firstName: string;
lastName: string;
brokerage?: string;
}
export async function provisionGHLForUser(params: ProvisionUserParams): Promise<{
success: boolean;
locationId?: string;
error?: string;
}> {
try {
// Get agency credentials from settings
const agencyApiKey = await settingsService.get('ghlAgencyApiKey');
const agencyId = await settingsService.get('ghlAgencyId');
if (!agencyApiKey || !agencyId) {
return { success: false, error: 'GHL Agency credentials not configured' };
}
// Create agency client
const agencyClient = new GHLAgencyClient({
agencyApiKey,
agencyId,
});
const locationsService = new LocationsService(agencyClient);
// Check if location already exists for this email
const exists = await locationsService.existsByEmail(params.email);
if (exists) {
return { success: false, error: 'A location already exists for this email' };
}
// Create the location
const location = await locationsService.createForUser({
email: params.email,
firstName: params.firstName,
lastName: params.lastName,
brokerage: params.brokerage,
});
// Update user record with GHL location ID
// Note: Access token would need to be obtained via OAuth flow in production
await prisma.user.update({
where: { id: params.userId },
data: {
ghlLocationId: location.id,
// In a real implementation, you'd get these from OAuth
// ghlAccessToken: encrypt(accessToken),
// ghlRefreshToken: encrypt(refreshToken),
},
});
return { success: true, locationId: location.id };
} catch (error) {
console.error('Failed to provision GHL for user:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
// Deprovision a user (when they delete their account)
export async function deprovisionGHLForUser(userId: string): Promise<{
success: boolean;
error?: string;
}> {
try {
const user = await prisma.user.findUnique({
where: { id: userId },
select: { ghlLocationId: true },
});
if (!user?.ghlLocationId) {
return { success: true }; // No location to delete
}
const agencyApiKey = await settingsService.get('ghlAgencyApiKey');
const agencyId = await settingsService.get('ghlAgencyId');
if (!agencyApiKey || !agencyId) {
return { success: false, error: 'GHL Agency credentials not configured' };
}
const agencyClient = new GHLAgencyClient({ agencyApiKey, agencyId });
const locationsService = new LocationsService(agencyClient);
// Delete the location (this is destructive!)
await locationsService.delete(user.ghlLocationId);
// Clear the user's GHL data
await prisma.user.update({
where: { id: userId },
data: {
ghlLocationId: null,
ghlAccessToken: null,
ghlRefreshToken: null,
},
});
return { success: true };
} catch (error) {
console.error('Failed to deprovision GHL for user:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}