- 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>
120 lines
3.4 KiB
TypeScript
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',
|
|
};
|
|
}
|
|
}
|