Spaces:
Running
Running
ICC Interac Manager β Scan Implementation
What This Fixes
The "Scanner maintenant" button now actually connects to Gmail, fetches Interac e-Transfer emails, parses them with AI (Groq/Mistral), and saves parsed transactions to the database β all with real-time progress via WebSocket.
File Map
icc-scan-impl/
βββ .env.example β Copy to .env and fill in API keys
β
βββ shared/ β Shared types & constants
β βββ types/
β β βββ scan.ts β ScanPreset, ScanDateRange, resolveScanDates()
β β βββ transaction.ts β InteracTransaction schema
β β βββ aiProvider.ts β ProviderSlot, SwitcherEvent
β βββ constants/
β βββ branches.ts β 48-branch emailβcity mapping
β
βββ server/ β Backend (Express + SQLite)
β βββ index.ts β Server entry point
β βββ db/
β β βββ database.ts β SQLite schema + operations
β βββ middleware/
β β βββ auth.ts β JWT middleware
β βββ routes/
β β βββ auth.ts β Google OAuth routes
β β βββ scan.ts β POST /api/scan/start
β β βββ transactions.ts β GET /api/transactions
β βββ services/
β β βββ gmailService.ts β Gmail API: query builder + email fetcher
β β βββ aiService.ts β AI parsing: Groq + Mistral + auto-switcher
β β βββ scanService.ts β Scan pipeline: Gmail β AI β DB
β βββ websocket/
β βββ setup.ts β WebSocket server for progress events
β
βββ web/ β Frontend (React)
βββ services/
β βββ api.ts β HTTP client with auth
β βββ websocket.ts β WebSocket client
βββ hooks/
β βββ useEmailScan.ts β React hook for scan state + progress
βββ components/scan/
β βββ ScanControls.tsx β β
THE scan button component
βββ pages/
βββ AuthCallback.tsx β Handles OAuth redirect
Setup Steps
1. Install Dependencies
In your packages/server/ directory:
npm install express cors helmet dotenv googleapis jsonwebtoken better-sqlite3 ws uuid p-limit zod
npm install -D typescript @types/express @types/cors @types/jsonwebtoken @types/better-sqlite3 @types/ws @types/uuid tsx
In your packages/web/ directory (most likely already installed):
npm install react-router-dom
2. Configure Google OAuth
- Go to Google Cloud Console
- Create a project (or use existing)
- Enable the Gmail API
- Create OAuth 2.0 Client ID (Web application)
- Add authorized redirect URI:
http://localhost:3001/api/auth/google/callback - Copy the Client ID and Client Secret
3. Get AI API Keys (Free)
Pick at least one:
- Groq (recommended primary): console.groq.com/keys β 30 RPM free
- Mistral (recommended fallback): console.mistral.ai/api-keys β free tier
4. Create .env File
cp .env.example .env
# Edit .env with your actual keys
5. Copy Files Into Your Project
Copy each file from this package into the corresponding location in your monorepo. The file paths in this package mirror the target paths in your project.
6. Add Vite Proxy (vite.config.ts)
Make sure your Vite dev server proxies API calls to the backend:
// packages/web/vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': 'http://localhost:3001',
'/ws': {
target: 'ws://localhost:3001',
ws: true,
},
},
},
});
7. Add Route for Auth Callback
In your React Router config:
import AuthCallback from './pages/AuthCallback';
// Add this route:
<Route path="/auth/callback" element={<AuthCallback />} />
8. Wire Up ScanControls in Your Dashboard
Replace your current scan button section with the new ScanControls component:
import ScanControls from '@/components/scan/ScanControls';
function DashboardPage() {
const { refetch } = useTransactions(); // your existing hook
return (
<div>
<ScanControls
onScanComplete={(result) => {
// Refresh transaction table after scan completes
refetch();
}}
/>
{/* ... rest of your dashboard ... */}
</div>
);
}
9. Start the Backend
cd packages/server
npx tsx src/index.ts
You should see:
π ICC Interac Manager API running on http://localhost:3001
π WebSocket available at ws://localhost:3001/ws
π Google OAuth: http://localhost:3001/api/auth/google
10. Start the Frontend
cd packages/web
npm run dev
How It Works (Flow)
User clicks "Scanner maintenant"
β
βΌ
ββ ScanControls.tsx βββββββββββββββββββββββββββ
β 1. Reads selected preset (today/7days/custom)β
β 2. Calls api.startScan({ preset, dates }) β
β 3. Connects to WebSocket for progress events β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββ
β POST /api/scan/start
βΌ
ββ scan.ts (route) βββββββββββββββββββββββββββ
β 1. Validates preset + dates β
β 2. Calls scanService.executeScan() β
β 3. Returns immediately (async background) β
βββββββββββββββββββββββ¬βββββββββββββββββββββββ
β
βΌ
ββ scanService.ts ββββββββββββββββββββββββββββ
β PHASE 1: DISCOVER β
β β’ Build Gmail query: from:notify@... β
β β’ Fetch all matching message IDs β
β β’ Deduplicate against existing DB records β
β β
β PHASE 2: PIPELINE (parallel) β
β β’ 10x concurrent Gmail fetches β
β β’ 5x concurrent AI parses β
β β’ Auto-switch GroqβMistral on rate limit β
β β’ Batch INSERT 25 rows at a time β
β β’ Emit scan:progress via WebSocket β
β β
β PHASE 3: COMPLETE β
β β’ Flush remaining DB buffer β
β β’ Emit scan:completed β
β β’ Update scan_logs table β
ββββββββββββββββββββββββββββββββββββββββββββββ
WebSocket Events (Frontend receives these)
| Event | Data | When |
|---|---|---|
scan:started |
{ jobId, totalEmails, newEmails, skipped } |
Scan begins |
scan:progress |
{ processed, total, errored, latest, currentProvider } |
Each email processed |
scan:completed |
{ jobId, summary: { found, parsed, skipped, errors } } |
Scan finished |
scan:error |
{ jobId, error } |
Scan failed |
ai:switch |
{ from, to, reason } |
AI provider auto-switched |
transaction:new |
{ transaction } |
New transaction saved |
Troubleshooting
| Issue | Fix |
|---|---|
| "User has no Gmail tokens" | User needs to log in via Google OAuth first: visit /api/auth/google |
| "No AI providers configured" | Set GROQ_API_KEY and/or MISTRAL_API_KEY in .env |
| "All AI providers rate-limited" | Wait 60s for cooldown, or add more API keys |
| Scan returns 0 emails | Check Gmail query β make sure the connected account actually has emails from notify@payments.interac.ca |
| WebSocket not connecting | Check Vite proxy config includes /ws β ws://localhost:3001 |
| "Invalid token" | JWT expired β log in again via Google OAuth |