2026-08-16 21:42:57 -04:00
2026-08-16 21:42:57 -04:00
2026-08-16 21:42:57 -04:00

beenvoice workspace

Local development layout for the beenvoice product: a freelancer/small-business invoicing platform with a Next.js web app and an Expo mobile companion.

beenvoice-meta/
├── beenvoice-web/      # Web API + dashboard (Next.js 16, tRPC, PostgreSQL)
├── beenvoice-app/      # Mobile app (Expo, React Native, dev client)
└── beenvoice.icon/     # iOS app icon asset (icon composer)

Each subdirectory is its own git repository. This folder is a convenience layout for working on both clients against one API.

Repo Remote
Web git.soconnor.dev/soconnor/beenvoice-web
Mobile git.soconnor.dev/soconnor/beenvoice-app

Quick start (full stack)

# 1. API + database
cd beenvoice-web
cp .env.example .env.local    # edit DATABASE_URL, AUTH_SECRET, etc.
docker compose -f docker-compose.dev.yml up -d
bun install && bun run db:push
bun run dev                   # http://localhost:3000

# 2. Mobile (simulator)
cd ../beenvoice-app
cp .env.example .env          # EXPO_PUBLIC_API_URL=http://localhost:3000
bun install
bun run ios                   # Metro on :8082, native dev build

Physical iPhone: set EXPO_PUBLIC_API_URL to your Mac's LAN IP; ensure beenvoice-web has BETTER_AUTH_URL matching a host the device can reach (trusted origins are derived from BETTER_AUTH_URL / NEXT_PUBLIC_APP_URL).

Production deploy (Docker)

From beenvoice-web with .env configured (see beenvoice-web/README.md):

cd beenvoice-web
git pull
./scripts/docker-deploy.sh   # rebuild app image + restart (not docker-compose.dev.yml)

Plain docker compose up -d without --build reuses the existing local image and will not pick up code from git pull.

How the pieces connect

Layer Repo Transport Auth
Web UI beenvoice-web tRPC /api/trpc (cookies) better-auth session
Mobile UI beenvoice-app tRPC /api/trpc (cookie header) better-auth + @better-auth/expo → SecureStore
Automation beenvoice-web MCP /api/mcp JSON-RPC API key (bv_…) only
Public invoices beenvoice-web /i/[token] HTTP unauthenticated token

Mobile imports tRPC router types from beenvoice-web/src/server/api/root via tsconfig path mapping — keep API changes type-checked in both repos.

Documentation map

Doc Contents
beenvoice-web/README.md Web setup, Docker deploy, scripts
beenvoice-web/docs/ARCHITECTURE.md Server architecture (dense)
beenvoice-app/README.md Mobile setup, troubleshooting
beenvoice-app/docs/ARCHITECTURE.md Mobile architecture (dense)
beenvoice-web/docs/ UI guides (forms, tables, breadcrumbs, email)

Shared domain concepts

  • Clients — billable contacts with optional default hourly rate
  • Businesses — sender profiles (logo, address, Resend email config)
  • Invoices — draft → sent → paid; line items; PDF + email; public share token
  • Time entries — clock in/out; one running timer per user; auto-attach to open invoice
  • Recurring invoices, expenses, payments, templates — full web support; mobile covers a core CRUD subset

Environment cheat sheet

Variable Where Purpose
DATABASE_URL beenvoice-web PostgreSQL
AUTH_SECRET, BETTER_AUTH_URL beenvoice-web better-auth
EXPO_PUBLIC_API_URL beenvoice-app API base for mobile
DISABLE_SIGNUPS beenvoice-web true blocks new registrations
RESEND_* beenvoice-web Invoice / reset email

Ports

Service Default port
Next.js (beenvoice-web) 3000
Postgres (dev compose) 5432
Metro (beenvoice-app) 8082 (intentionally not 8081)
S
Description
beenvoice | invoicing made simple
https://beenvoice.app
Readme MIT
35 MiB
Languages
TypeScript 97.3%
JavaScript 1.4%
Shell 0.8%
CSS 0.2%
Dockerfile 0.2%
Other 0.1%