Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 3.8k
feat(wealthbox): added wealthbox crm#669
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
df166ccd94e1a69b3921a262465d5a59e5f1255dfa8d25ed1af6e95fcbd83d3c49c5d5a936c7eFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -48,6 +48,7 @@ | ||
| "twilio_sms", | ||
| "typeform", | ||
| "vision", | ||
| "wealthbox", | ||
| "whatsapp", | ||
| "x", | ||
| "youtube" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| --- | ||
| title: Wealthbox | ||
| description: Interact with Wealthbox | ||
| --- | ||
| import { BlockInfoCard } from "@/components/ui/block-info-card" | ||
| <BlockInfoCard | ||
| type="wealthbox" | ||
| color="#E0E0E0" | ||
| icon={true} | ||
| iconSvg={`<svg className="block-icon" | ||
| xmlns='http://www.w3.org/2000/svg' | ||
| version='1.0' | ||
| viewBox='50 -50 200 200' | ||
| > | ||
| <g fill='#106ED4' stroke='none' transform='translate(0, 200) scale(0.15, -0.15)'> | ||
| <path d='M764 1542 c-110 -64 -230 -134 -266 -156 -42 -24 -71 -49 -78 -65 -7 -19 -10 -126 -8 -334 3 -291 4 -307 23 -326 11 -11 103 -67 205 -126 102 -59 219 -127 261 -151 42 -24 85 -44 96 -44 23 0 527 288 561 320 22 22 22 23 22 340 0 288 -2 320 -17 338 -32 37 -537 322 -569 321 -18 0 -107 -46 -230 -117z m445 -144 c108 -62 206 -123 219 -135 22 -22 22 -26 22 -261 0 -214 -2 -242 -17 -260 -23 -26 -414 -252 -437 -252 -9 0 -70 31 -134 69 -64 37 -161 94 -215 125 l-97 57 2 261 3 261 210 123 c116 67 219 123 229 123 10 1 107 -50 215 -111z' /> | ||
| <path d='M700 1246 l-55 -32 -3 -211 -2 -211 37 -23 c21 -12 52 -30 69 -40 l30 -18 103 59 c56 33 109 60 117 60 8 0 62 -27 119 -60 l104 -60 63 37 c35 21 66 42 70 48 4 5 8 101 8 212 l0 202 -62 35 -63 35 -3 -197 c-1 -108 -6 -200 -11 -205 -5 -5 -54 17 -114 52 -58 34 -108 61 -111 61 -2 0 -51 -27 -107 -60 -56 -32 -106 -57 -111 -54 -4 3 -8 95 -8 205 0 109 -3 199 -7 199 -5 -1 -33 -16 -63 -34z' /> | ||
| </g> | ||
| </svg>`} | ||
| /> | ||
| ## Usage Instructions | ||
| Integrate Wealthbox functionality to manage notes, contacts, and tasks. Read content from existing notes, contacts, and tasks and write to them using OAuth authentication. Supports text content manipulation for note creation and editing. | ||
| ## Tools | ||
| ### `wealthbox_read_note` | ||
| Read content from a Wealthbox note | ||
| #### Input | ||
| | Parameter | Type | Required | Description | | ||
| | --------- | ---- | -------- | ----------- | | ||
| | `accessToken` | string | Yes | The access token for the Wealthbox API | | ||
| | `noteId` | string | No | The ID of the note to read \(optional\) | | ||
| #### Output | ||
| | Parameter | Type | | ||
| | --------- | ---- | | ||
| | `note` | string | | ||
| | `metadata` | string | | ||
| | `noteId` | string | | ||
| | `itemType` | string | | ||
| ### `wealthbox_write_note` | ||
| Create or update a Wealthbox note | ||
| #### Input | ||
| | Parameter | Type | Required | Description | | ||
| | --------- | ---- | -------- | ----------- | | ||
| | `accessToken` | string | Yes | The access token for the Wealthbox API | | ||
| | `content` | string | Yes | The main body of the note | | ||
| | `contactId` | string | No | ID of contact to link to this note | | ||
| #### Output | ||
| | Parameter | Type | | ||
| | --------- | ---- | | ||
| | `note` | string | | ||
| | `metadata` | string | | ||
| | `itemType` | string | | ||
| ### `wealthbox_read_contact` | ||
| Read content from a Wealthbox contact | ||
| #### Input | ||
| | Parameter | Type | Required | Description | | ||
| | --------- | ---- | -------- | ----------- | | ||
| | `accessToken` | string | Yes | The access token for the Wealthbox API | | ||
| | `contactId` | string | Yes | The ID of the contact to read | | ||
| #### Output | ||
| | Parameter | Type | | ||
| | --------- | ---- | | ||
| | `contact` | string | | ||
| | `metadata` | string | | ||
| | `contactId` | string | | ||
| | `itemType` | string | | ||
| ### `wealthbox_write_contact` | ||
| Create a new Wealthbox contact | ||
| #### Input | ||
| | Parameter | Type | Required | Description | | ||
| | --------- | ---- | -------- | ----------- | | ||
| | `accessToken` | string | Yes | The access token for the Wealthbox API | | ||
| | `firstName` | string | Yes | The first name of the contact | | ||
| | `lastName` | string | Yes | The last name of the contact | | ||
| | `emailAddress` | string | No | The email address of the contact | | ||
| | `backgroundInformation` | string | No | Background information about the contact | | ||
| #### Output | ||
| | Parameter | Type | | ||
| | --------- | ---- | | ||
| | `contact` | string | | ||
| | `metadata` | string | | ||
| | `itemType` | string | | ||
| ### `wealthbox_read_task` | ||
| Read content from a Wealthbox task | ||
| #### Input | ||
| | Parameter | Type | Required | Description | | ||
| | --------- | ---- | -------- | ----------- | | ||
| | `accessToken` | string | Yes | The access token for the Wealthbox API | | ||
| | `taskId` | string | No | The ID of the task to read \(optional\) | | ||
| #### Output | ||
| | Parameter | Type | | ||
| | --------- | ---- | | ||
| | `task` | string | | ||
| | `metadata` | string | | ||
| | `taskId` | string | | ||
| | `itemType` | string | | ||
| ### `wealthbox_write_task` | ||
| Create or update a Wealthbox task | ||
| #### Input | ||
| | Parameter | Type | Required | Description | | ||
| | --------- | ---- | -------- | ----------- | | ||
| | `accessToken` | string | Yes | The access token for the Wealthbox API | | ||
| | `title` | string | Yes | The name/title of the task | | ||
| | `dueDate` | string | Yes | The due date and time of the task | | ||
| | `complete` | boolean | No | Whether the task is complete | | ||
| | `category` | number | No | The category ID the task belongs to | | ||
| | `contactId` | string | No | ID of contact to link to this task | | ||
| #### Output | ||
| | Parameter | Type | | ||
| | --------- | ---- | | ||
| | `task` | string | | ||
| | `metadata` | string | | ||
| | `taskId` | string | | ||
| | `itemType` | string | | ||
| ## Block Configuration | ||
| ### Input | ||
| | Parameter | Type | Required | Description | | ||
| | --------- | ---- | -------- | ----------- | | ||
| | `operation` | string | Yes | Operation | | ||
| ### Outputs | ||
| | Output | Type | Description | | ||
| | ------ | ---- | ----------- | | ||
| | `note` | any | note output from the block | | ||
| | `notes` | any | notes output from the block | | ||
| | `contact` | any | contact output from the block | | ||
| | `contacts` | any | contacts output from the block | | ||
| | `task` | any | task output from the block | | ||
| | `tasks` | any | tasks output from the block | | ||
| | `metadata` | json | metadata output from the block | | ||
| | `success` | any | success output from the block | | ||
| ## Notes | ||
| - Category: `tools` | ||
| - Type: `wealthbox` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| import { eq } from 'drizzle-orm' | ||
| import { type NextRequest, NextResponse } from 'next/server' | ||
| import { getSession } from '@/lib/auth' | ||
| import { createLogger } from '@/lib/logs/console-logger' | ||
| import { db } from '@/db' | ||
| import { account } from '@/db/schema' | ||
| import { refreshAccessTokenIfNeeded } from '../../utils' | ||
| export const dynamic = 'force-dynamic' | ||
| const logger = createLogger('WealthboxItemAPI') | ||
| /** | ||
| * Get a single item (note, contact, task) from Wealthbox | ||
| */ | ||
| export async function GET(request: NextRequest) { | ||
| const requestId = crypto.randomUUID().slice(0, 8) | ||
| try { | ||
| // Get the session | ||
| const session = await getSession() | ||
| // Check if the user is authenticated | ||
| if (!session?.user?.id) { | ||
| logger.warn(`[${requestId}] Unauthenticated request rejected`) | ||
| return NextResponse.json({ error: 'User not authenticated' }, { status: 401 }) | ||
| } | ||
| // Get parameters from query | ||
| const { searchParams } = new URL(request.url) | ||
| const credentialId = searchParams.get('credentialId') | ||
| const itemId = searchParams.get('itemId') | ||
| const type = searchParams.get('type') || 'contact' | ||
| if (!credentialId || !itemId) { | ||
| logger.warn(`[${requestId}] Missing required parameters`, { credentialId, itemId }) | ||
| return NextResponse.json({ error: 'Credential ID and Item ID are required' }, { status: 400 }) | ||
| } | ||
| // Validate item type - only handle contacts now | ||
| if (type !== 'contact') { | ||
| logger.warn(`[${requestId}] Invalid item type: ${type}`) | ||
| return NextResponse.json( | ||
| { error: 'Invalid item type. Only contact is supported.' }, | ||
| { status: 400 } | ||
| ) | ||
| } | ||
| // Get the credential from the database | ||
| const credentials = await db.select().from(account).where(eq(account.id, credentialId)).limit(1) | ||
| if (!credentials.length) { | ||
| logger.warn(`[${requestId}] Credential not found`, { credentialId }) | ||
| return NextResponse.json({ error: 'Credential not found' }, { status: 404 }) | ||
| } | ||
| const credential = credentials[0] | ||
| // Check if the credential belongs to the user | ||
| if (credential.userId !== session.user.id) { | ||
| logger.warn(`[${requestId}] Unauthorized credential access attempt`, { | ||
| credentialUserId: credential.userId, | ||
| requestUserId: session.user.id, | ||
| }) | ||
| return NextResponse.json({ error: 'Unauthorized' }, { status: 403 }) | ||
| } | ||
| // Refresh access token if needed | ||
| const accessToken = await refreshAccessTokenIfNeeded(credentialId, session.user.id, requestId) | ||
| if (!accessToken) { | ||
| logger.error(`[${requestId}] Failed to obtain valid access token`) | ||
| return NextResponse.json({ error: 'Failed to obtain valid access token' }, { status: 401 }) | ||
| } | ||
| // Determine the endpoint based on item type - only contacts | ||
| const endpoints = { | ||
| contact: 'contacts', | ||
| } | ||
| const endpoint = endpoints[type as keyof typeof endpoints] | ||
aadamgough marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| logger.info(`[${requestId}] Fetching ${type} ${itemId} from Wealthbox`) | ||
| // Make request to Wealthbox API | ||
| const response = await fetch(`https://api.crmworkspace.com/v1/${endpoint}/${itemId}`, { | ||
aadamgough marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| headers: { | ||
| Authorization: `Bearer ${accessToken}`, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| }) | ||
| if (!response.ok) { | ||
| const errorText = await response.text() | ||
| logger.error( | ||
| `[${requestId}] Wealthbox API error: ${response.status} ${response.statusText}`, | ||
| { | ||
| error: errorText, | ||
| endpoint, | ||
| itemId, | ||
| } | ||
| ) | ||
| if (response.status === 404) { | ||
| return NextResponse.json({ error: 'Item not found' }, { status: 404 }) | ||
| } | ||
| return NextResponse.json( | ||
| { error: `Failed to fetch ${type} from Wealthbox` }, | ||
| { status: response.status } | ||
| ) | ||
| } | ||
| const data = await response.json() | ||
| logger.info(`[${requestId}] Wealthbox API response structure`, { | ||
| type, | ||
| dataKeys: Object.keys(data || {}), | ||
| hasContacts: !!data.contacts, | ||
| totalCount: data.meta?.total_count, | ||
| }) | ||
| // Transform the response to match our expected format | ||
| let items: any[] = [] | ||
aadamgough marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (type === 'contact') { | ||
| // Handle single contact response - API returns contact data directly when fetching by ID | ||
| if (data?.id) { | ||
| // Single contact response | ||
| const item = { | ||
| id: data.id?.toString() || '', | ||
| name: `${data.first_name || ''} ${data.last_name || ''}`.trim() || `Contact ${data.id}`, | ||
| type: 'contact', | ||
| content: data.background_info || '', | ||
| createdAt: data.created_at, | ||
| updatedAt: data.updated_at, | ||
| } | ||
| items = [item] | ||
| } else { | ||
| logger.warn(`[${requestId}] Unexpected contact response format`, { data }) | ||
| items = [] | ||
| } | ||
| } | ||
| logger.info( | ||
| `[${requestId}] Successfully fetched ${items.length} ${type}s from Wealthbox (total: ${data.meta?.total_count || 'unknown'})` | ||
| ) | ||
| return NextResponse.json({ item: items[0] }, { status: 200 }) | ||
| } catch (error) { | ||
| logger.error(`[${requestId}] Error fetching Wealthbox item`, error) | ||
| return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.