Context
We want contact mining to include Google Contacts, not only Gmail emails, while keeping the current mining UX simple.
Decision
Implement a lightweight version of Approach 2:
- Keep Gmail mining as the primary flow.
- Add Sync Google Contacts under Advanced mining options.
- Toggle is enabled by default for Google sources.
- Google Contacts sync runs as a byproduct of the Gmail mining run.
Architecture
High-Level Flow
User clicks \"Start Mining\"
│
▼
┌─────────────────────────────────┐
│ Existing Gmail mining starts │
│ (TasksManager / fetch/extract) │
└─────────────────────────────────┘
│
├──────────────────────────────┐
│ (if Google source + toggle) │
▼ ▼
┌─────────────────────┐ ┌─────────────────────────┐
│ Continue Gmail flow │ │ Fetch Google Contacts │
│ (no change) │ │ via People API │
└─────────────────────┘ └─────────────────────────┘
│
▼
┌─────────────────────┐
│ Normalize & persist │
│ via contacts.create │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ Dedupe + refine │
│ (existing pipeline) │
└─────────────────────┘
Key Design Points
- No separate task manager - reuse existing
TasksManager
- No new task type - lightweight internal path handled in fetch phase
- Same persistence path - contacts flow through
contacts.create() so dedupe/refine works automatically
- Source metadata - set
person.source = 'google-contacts:<email>' to track origin
- Failure isolation - wrap Google Contacts sync in try/catch; if it fails, mining succeeds with warning
Files to Change
Frontend
| File |
Change |
frontend/src/components/mining/stepper-panels/mine/MiningSettingsDialog.vue |
Add toggle for "Sync Google Contacts" (Google sources only, default ON) |
frontend/src/types/mining.ts |
Add googleContactsSync?: boolean to mining task or extend mining options |
frontend/src/stores/leadminer.ts |
Add googleContactsSync state, pass in mining start payload |
frontend/src/utils/sources.ts |
(Optional) Update types if needed |
Backend
| File |
Change |
backend/src/controllers/mining.controller.ts |
Pass googleContactsSync option to task creation; if true, trigger contacts sync after fetch starts |
backend/src/services/tasks-manager/TasksManager.ts |
Add googleContactsSync to task details; trigger sync in startMining |
backend/src/services/tasks-manager/types.ts |
Add googleContactsSync?: boolean to mining source details |
backend/src/services/extractors/Extractor.ts |
Add google_contacts extractor type |
backend/src/services/extractors/engines/ |
Create GoogleContacts.ts - fetches from Google People API, normalizes to contact format |
backend/src/db/types.ts |
Add GOOGLE_CONTACTS mining type if needed |
backend/src/workers/email-message/emailMessageHandlers.ts |
Add handler for Google Contacts data ingestion |
backend/src/services/OAuth2/google.ts |
Ensure reusable token refresh for People API calls |
Supabase Edge Functions
| File |
Change |
supabase/functions/fetch-mining-source/ |
May need to extend to support People API scope (already has contacts scope in mining.helpers.ts) |
Implementation Order
- Backend extractor - Create Google Contacts fetcher/normalizer
- Backend integration - Wire into mining controller (trigger after fetch)
- Frontend toggle - Add advanced option (default ON)
- End-to-end test - Verify sync runs and contacts appear
Out of Scope
- Separate standalone Google Contacts mining workflow
- Large task-manager redesign
- DB schema redesign
- User-facing source selector for Google Contacts
Acceptance Criteria
Related Code Context
- Current Google OAuth scopes in
backend/src/controllers/mining.helpers.ts already include https://www.googleapis.com/auth/contacts
- Existing Google People API code in
backend/src/services/export/exports/google/contacts-api.ts for export - can reuse patterns
- Mining sources stored in
private.mining_sources with OAuth credentials
Context
We want contact mining to include Google Contacts, not only Gmail emails, while keeping the current mining UX simple.
Decision
Implement a lightweight version of Approach 2:
Architecture
High-Level Flow
Key Design Points
TasksManagercontacts.create()so dedupe/refine works automaticallyperson.source = 'google-contacts:<email>'to track originFiles to Change
Frontend
frontend/src/components/mining/stepper-panels/mine/MiningSettingsDialog.vuefrontend/src/types/mining.tsgoogleContactsSync?: booleanto mining task or extend mining optionsfrontend/src/stores/leadminer.tsgoogleContactsSyncstate, pass in mining start payloadfrontend/src/utils/sources.tsBackend
backend/src/controllers/mining.controller.tsgoogleContactsSyncoption to task creation; if true, trigger contacts sync after fetch startsbackend/src/services/tasks-manager/TasksManager.tsgoogleContactsSyncto task details; trigger sync instartMiningbackend/src/services/tasks-manager/types.tsgoogleContactsSync?: booleanto mining source detailsbackend/src/services/extractors/Extractor.tsgoogle_contactsextractor typebackend/src/services/extractors/engines/GoogleContacts.ts- fetches from Google People API, normalizes to contact formatbackend/src/db/types.tsGOOGLE_CONTACTSmining type if neededbackend/src/workers/email-message/emailMessageHandlers.tsbackend/src/services/OAuth2/google.tsSupabase Edge Functions
supabase/functions/fetch-mining-source/contactsscope inmining.helpers.ts)Implementation Order
Out of Scope
Acceptance Criteria
Related Code Context
backend/src/controllers/mining.helpers.tsalready includehttps://www.googleapis.com/auth/contactsbackend/src/services/export/exports/google/contacts-api.tsfor export - can reuse patternsprivate.mining_sourceswith OAuth credentials