TehRiehlDeal 8e1bac5430
CI / vuln-scan (push) Successful in 14s
CI / sast (push) Successful in 10s
CI / test (push) Successful in 26s
CI / lint (push) Successful in 28s
CI / secrets-scan (push) Successful in 5s
CI / build-images (push) Successful in 1m57s
CI / image-scan (push) Successful in 50s
CI / push (push) Successful in 32s
Bundle PDF.js worker via Vite ?worker so it ships as a .js asset
Production deploy returned the worker module with
Content-Type: application/octet-stream — nginx's bundled mime.types
doesn't map .mjs and X-Content-Type-Options: nosniff stopped Firefox
from executing it, so PDFs failed with "Setting up fake worker failed".

Switch the worker import from ?url to ?worker and assign workerPort, so
Vite emits the worker as a regular hashed .js chunk that nginx already
serves correctly. Also add a nginx fallback that maps .mjs to
text/javascript for any future module assets, and include text/javascript
in gzip_types.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 23:21:35 -07:00
2026-04-29 22:23:04 -07:00
2026-05-04 16:20:23 -07:00
2026-05-04 16:20:23 -07:00
2026-05-04 16:20:23 -07:00
2026-05-04 16:20:23 -07:00

TehRiehlBudget

A self-hosted personal finance application for tracking spending, balances, and net worth across all of your accounts — checking, savings, credit, loans, and investments — with manual transaction entry, receipt uploads, and a conversational AI advisor running entirely on your own hardware.

Deployed at budget.tehriehldeal.com.

Features

  • Accounts — track checking, savings, cash, credit cards, loans, stocks, brokerage, and retirement accounts. Drag-to-reorder, per-account history, and a balance integrity check that reverses counter-party transfers when an account is deleted.
  • Transactions — record income, expenses, and transfers between accounts. Filter by account, category, type, and date. Attach receipt images. Edit, delete, and bulk-export to CSV.
  • Activity history — every create / update / delete is recorded with a snapshot, so deleted transactions and accounts remain auditable for as long as you need them. Filter by entity, action, account, or date range.
  • CSV export — pull a date-ranged transaction set out as CSV from the global list or scoped to a single account, with quick-range presets (last 30/60/90 days, this year, all time) plus custom start/end pickers.
  • Confirmation guards — every destructive action prompts before running, with light/dark mode parity.
  • AI advisor — a "financial buddy" that summarizes your month against last month and answers follow-ups. Runs against a local Ollama model — no transaction data leaves your network.
  • Dashboards — net worth and total debt at a glance, spending-by-category pie, monthly cash flow bar chart, and per-account balance history with overlaid valuations.
  • Receipts — store receipt images on your own filesystem, never on a third-party blob store.
  • Field-level encryption — sensitive columns (account numbers, transaction notes) are AES-256-GCM encrypted at rest.

Tech Stack

  • Frontend — React 19 + TypeScript, Vite, TailwindCSS + ShadCN UI, Zustand, Recharts, React Router
  • Backend — NestJS (TypeScript), REST, Prisma ORM
  • Database — PostgreSQL (Dockerized for local dev)
  • Auth — Supabase (email/password + OAuth, JWT-validated on the backend)
  • AI — Ollama (self-hosted; defaults to llama3)
  • File storage — local filesystem on the self-hosted server
  • Package manager — pnpm workspaces

Project Structure

tehriehlbudget/
├── tehriehlbudget-frontend/   # React + Vite app
├── tehriehlbudget-backend/    # NestJS API + Prisma schema
├── docker-compose.yml         # Postgres for local dev
├── pnpm-workspace.yaml
└── package.json               # Workspace-level scripts

Prerequisites

  • Node.js 20+
  • pnpm 9+
  • Docker (for local Postgres)
  • A Supabase project (for auth) — free tier is fine
  • An Ollama server reachable from the backend (local or LAN) for the AI advisor

Quick Start

# Clone and install
git clone <your-fork-url>
cd tehriehlbudget
pnpm install

# Start local Postgres
docker-compose up -d

# Configure the backend
cp tehriehlbudget-backend/.env.example tehriehlbudget-backend/.env
# Edit .env to set DATABASE_URL, SUPABASE_*, ENCRYPTION_KEY, OLLAMA_URL, OLLAMA_MODEL

# Apply database migrations
pnpm --filter tehriehlbudget-backend prisma migrate deploy
pnpm --filter tehriehlbudget-backend prisma generate

# Configure the frontend
cp tehriehlbudget-frontend/.env.example tehriehlbudget-frontend/.env
# Edit .env to set VITE_API_URL and VITE_SUPABASE_*

# Run both servers (in separate terminals)
pnpm dev:backend
pnpm dev:frontend

The frontend is served at http://localhost:5173 and the API at http://localhost:3000.

Required environment variables

Backend (tehriehlbudget-backend/.env)

Variable Description
DATABASE_URL Postgres connection string
SUPABASE_URL Your Supabase project URL
SUPABASE_SERVICE_ROLE_KEY Service-role key (used to validate JWTs)
ENCRYPTION_KEY 32-byte hex key for AES-256-GCM field encryption
OLLAMA_URL URL of your Ollama server (e.g. http://localhost:11434)
OLLAMA_MODEL Ollama model id (e.g. llama3.2:latest)

Frontend (tehriehlbudget-frontend/.env)

Variable Description
VITE_API_URL Backend URL (e.g. http://localhost:3000)
VITE_SUPABASE_URL Supabase project URL
VITE_SUPABASE_ANON_KEY Supabase anon (publishable) key

Common Commands

# Run dev servers
pnpm dev:frontend
pnpm dev:backend

# Build
pnpm build:frontend
pnpm build:backend

# Tests
pnpm test                  # both packages
pnpm test:backend          # Jest
pnpm test:frontend         # Vitest
pnpm test:coverage         # both, with coverage

# Lint & format
pnpm lint
pnpm format

# Prisma
pnpm --filter tehriehlbudget-backend prisma migrate dev
pnpm --filter tehriehlbudget-backend prisma studio

Architecture Notes

  • Field-level encryption. A NestJS interceptor encrypts sensitive columns on write and decrypts on read using AES-256-GCM. The plaintext only ever exists in memory inside the request handler.
  • Receipt storage. Images are stored on the server's local filesystem. The backend issues access-controlled URLs for upload and retrieval — there is no S3 / external blob store.
  • Balance integrity on delete. When an account is deleted, related transfers' counter-party balances are reversed inside the same Prisma $transaction before the cascade fires, so a surviving account never reflects a transfer that no longer exists.
  • Audit log. ActivityLog rows capture create / update / delete actions for transactions, accounts, and account valuations. For account deletions the per-transaction snapshots are written before the cascade so the trail outlives the data.
  • AI advisor. The advisor builds a per-request snapshot of standing balances, monthly flow numbers, and top spending categories, strips PII, and sends it to Ollama. The prompt explicitly separates point-in-time balances from monthly flow and instructs the model to never invent equations or derive new figures — your transaction data does not leave your network.
  • Auth. Supabase issues JWTs; protected routes on both ends validate them. Sessions are persisted client-side via Supabase's SDK.

License

MIT

S
Description
No description provided
Readme MIT 1.9 MiB
Languages
TypeScript 97.4%
CSS 1.1%
Dockerfile 0.7%
JavaScript 0.6%
HTML 0.1%