WalletLens is a full-stack personal finance web app for tracking income/expenses, scanning receipts with OCR + AI parsing, building budgets, and viewing reports.
- Authentication with JWT + session tracking
- Google OAuth login/registration option
- Optional email-based 2FA (enable/disable from Settings)
- Home dashboard with KPI cards, category chart, recent transactions, and net-worth snapshot
- Receipt uploads (PDF/images), OCR extraction, AI parsing, and auto-created expense records
- Records management for both expenses and income (search across row content, filter, column-header sort, paginate, export CSV)
- Budgeting by cadence/period with saved budget sheets and custom categories
- Reports with date ranges, category breakdowns, time-series trends (time on X-axis, amount on Y-axis), and currency conversion support
- Profile management (editable profile, avatar picker, activity feed)
- Settings for theme, currency, timezone, dashboard defaults, password change, and account/session controls
- Help/support form wired to backend email delivery
- Legal and help pages are included in the React app routes.
- Frontend: React, Vite, CSS, and JavaScript modules (
web/) - Backend: Node.js 20, Express (
api/) - Database: PostgreSQL
- Object storage: Cloudflare R2 (S3-compatible presigned upload/download)
- OCR worker: Python + Tesseract + PyMuPDF (
worker/) - AI parsing: Google GenAI (
@google/genai)
WalletLens/
web/ # static frontend pages + scripts + styles
api/ # Express API, models, controllers, routes, migrations
worker/ # Python OCR worker script
Dockerfile # container setup for API + OCR runtime
- Node.js >= 20
- Python 3.10+
- PostgreSQL
- Tesseract OCR installed on your machine
# API depscd api
npm install
# OCR worker depscd ../worker
python3 -m pip install -r requirements.txtCreate api/.env:
NODE_ENV=developmentPORT=4000DB_PROVIDER=postgresDB_URL=postgresql://USER:PASSWORD@HOST:5432/DBNAMEDB_SSL=falseJWT_SECRET=replace-with-a-strong-secretJWT_EXPIRES_IN=7dSESSION_IDLE_DAYS=1SESSION_CLEANUP_DAYS=30TWO_FA_CODE_MINUTES=10TWO_FA_TRUSTED_DAYS=10GOOGLE_CLIENT_ID=your_google_oauth_client_idGOOGLE_CLIENT_SECRET=your_google_oauth_client_secretGOOGLE_REDIRECT_URI=http://localhost:4000/api/auth/google/callbackCORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173,http://localhost:5500,http://127.0.0.1:5500OCR_ENABLED=trueOCR_WORKER_SCRIPT=../worker/ocr_demo.pyPYTHON_BIN=python3RECEIPT_KEEP_FILES=trueAI_PROVIDER=geminiAI_API_KEY=your_google_ai_keyAI_RECEIPT_API_KEY=your_google_ai_key_for_receiptsAI_TAX_API_KEY=your_google_ai_key_for_tax_syncAI_MODEL=models/gemma-3-4b-itAI_CHAT_MODEL=models/gemini-2.5-flashAI_RECEIPT_MODEL=models/gemini-2.5-flashAI_MAX_CHARS=5000OBJECT_STORE_PROVIDER=r2OBJECT_STORE_BUCKET=your_bucketOBJECT_STORE_ENDPOINT=https://<accountid>.r2.cloudflarestorage.comOBJECT_STORE_ACCESS_KEY_ID=your_access_keyOBJECT_STORE_SECRET_ACCESS_KEY=your_secret_keyOBJECT_STORE_REGION=autoOBJECT_STORE_FORCE_PATH_STYLE=true# Optional email settings (for support + 2FA emails)EMAIL_FROM=no-reply@wisewallet.localSUPPORT_EMAIL=support@example.com# SMTP_HOST=# SMTP_PORT=# SMTP_USER=# SMTP_PASS=# SMTP_SECURE=false# BREVO_API_KEY=# BREVO_API_URL=https://api.brevo.com/v3/smtp/emailYou can keep one shared Gemini key, but the better setup is separate keys by workload:
AI_API_KEY: WalterLens chat and general AI text featuresAI_RECEIPT_API_KEY: receipt OCR parsing and category extractionAI_TAX_API_KEY: admin tax-rate sync
All dedicated keys are optional. If you leave them unset, the app falls back to AI_API_KEY.
Recommended setup:
- Open Google AI Studio and sign in:
https://aistudio.google.com/apikey
- Create three API keys:
- one for chat/general AI
- one for receipt parsing
- one for tax sync
- Name them clearly so usage is easy to track:
walletlens-chatwalletlens-receiptswalletlens-tax
- Put them in
api/.env:
AI_API_KEY=your_chat_keyAI_RECEIPT_API_KEY=your_receipt_keyAI_TAX_API_KEY=your_tax_key- Restart the API after editing env vars.
Notes:
AI_RECEIPT_API_KEYfalls back toAI_API_KEYif omitted.AI_TAX_API_KEYfalls back toAI_API_KEYif omitted.- You can also use the alternate names
GEMINI_RECEIPT_API_KEYandGEMINI_TAX_API_KEY. - If you are on Gemini free tier, splitting keys helps isolate features, but quota still depends on the limits of each key/project arrangement in your Google setup.
- In Google Cloud Console, create/select a project.
- Configure OAuth consent screen.
- Create an OAuth client of type
Web application. - Add authorized JavaScript origins for your frontend, for example:
http://localhost:5173http://127.0.0.1:5173- your production frontend origin (e.g.
https://yourdomain.com)
- Add authorized redirect URIs, for example:
http://localhost:4000/api/auth/google/callback- your production API callback URL (e.g.
https://api.yourdomain.com/api/auth/google/callback)
- Set
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET, andGOOGLE_REDIRECT_URIinapi/.env.
Note: If your OAuth app is in Google "Testing" mode, only listed test users can sign in. For any Google account to sign in, publish the app to production in Google Cloud.
Run all SQL files in api/src/db/migrations against your Postgres database (in filename order).
Example:
forfin api/src/db/migrations/*.sql;do
psql "$DB_URL" -f "$f"doneFrom the repo root, start both the API and the Vite React frontend:
npm run dev:allOpen: http://localhost:5173
You can also run the API and frontend separately:
cd api
npm run devAPI health check:
curl http://localhost:4000/healthDatabase readiness check:
curl http://localhost:4000/readynpm run dev:webThe frontend calls the local API at http://localhost:4000/api by default.
/api/auth/api/records/api/receipts/api/budget-sheets/api/fx-rates/api/activity/api/support
Google OAuth routes:
GET /api/auth/google/configGET /api/auth/google/startGET /api/auth/google/callback
cd api
npm run dev # run API with nodemon
npm start # run API with node
npm run worker # run dedicated receipt job worker
npm run migrate # apply SQL migrations in order (tracked)
npm test# run API tests (node:test)
npm run cleanup:sessions # cleanup expired/stale sessions
npm run replace:categories # category replacement utility- Run migrations in every environment before deploy:
cd api && npm run migrate
- Dedicated receipt worker setup (recommended):
- API: set
RUN_RECEIPT_WORKER_IN_API=false - Worker process: run
npm run worker
- API: set
- Optional automatic migrations on startup:
- set
AUTO_RUN_MIGRATIONS=true
- set
- Optional captcha on public support endpoint:
- set
TURNSTILE_SECRET_KEY=<secret> - frontend should send
captchaTokentoPOST /api/support/public
- set
Dockerfileinstalls Node, Python, and Tesseract for OCR-compatible API deployment.- Frontend API base is auto-switched in
web/scripts/api.js:- localhost ->
http://localhost:4000/api - non-localhost -> hosted Render API URL
- override locally with
VITE_API_BASE_URL
- localhost ->