dollar-holler Svelte Themes

Dollar Holler

Dollar Holler is a SvelteKit invoice and client manager with auth, Elysia API, Drizzle/Postgres and a UI for creating invoices, tracking payments, managing clients, and line items, plus settings and printable views. Built with TypeScript and Pandacss

Dollar Holler

A modern invoice management application built with SvelteKit 5, featuring Better Auth authentication, Drizzle ORM with Neon database, and Bun UUIDv7 for resilient ID generation.

Prerequisites

  • Bun (required)
  • Neon account for PostgreSQL database
  • Vercel account for deployment (optional)

Getting Started

  1. Clone and install dependencies:

    git clone <repository-url>
    cd dollar-holler
    bun install
    
  2. Set up environment variables (Varlock): The committed .env.schema is the source of truth for variable names, validation, and (optional) Bitwarden Secrets Manager lookups.

    • Bun: bunfig.toml sets env = false and preload = ["varlock/auto-load"] so Bun does not load .env on its own before Varlock (see Varlock + Bun).
    • Vite / SvelteKit: vite.config.ts uses @varlock/vite-integration with ssrInjectMode: "resolved-env" (Varlock + Vite).
    • Bitwarden: Install the app deps (already in package.json), then in Bitwarden Secrets Manager create a machine account, copy its access token once, and grant it read access to the secrets you need. Put the token in a gitignored file such as .env.local as BITWARDEN_ACCESS_TOKEN=.... In .env.schema, replace the placeholder UUIDs in bitwarden("...") with your real secret IDs (Bitwarden plugin).
    • Without Bitwarden (e.g. quick local setup): Set DATABASE_URL, BETTER_AUTH_SECRET, and PUBLIC_BASE_URL in .env or .env.local. Host and CI variables still override resolved values when set.
    • Types: After changing .env.schema, run bun run env:typegen to refresh src/env-varlock.d.ts.

    The app resolves configuration from Varlock (varlock/env) for Drizzle Kit (drizzle.config.ts), auth (src/lib/auth.server.ts), and the Eden client (src/lib/api.ts). The database pool (src/lib/server/db/index.ts) reads DATABASE_URL from SvelteKit $env/static/private (populated by the Varlock Vite integration in dev/build). SvelteKit $env/static/* remains available where used.

  3. Set up the database: The bun run db:* scripts wrap Drizzle Kit with varlock run so DATABASE_URL is resolved the same way as the app (see .env.schema). drizzle.config.ts points at ./src/lib/server/db/schema.ts and writes migrations under ./src/lib/server/db/migrations.

    # Generate migrations
    bun run db:generate
    
    # Run migrations
    bun run db:migrate
    
    # Seed the database with sample data
    bun run db:seed
    
  4. Start the development server:

    bun run dev
    
  5. Optional: Preview production build

    bun run build && bun run preview
    

Available Scripts

  • bun run dev - Start development server (Vite 8)
  • bun run build - Build for production
  • bun run preview - Preview production build
  • bun run check - Run Ultracite (Biome) checks
  • bun run check:watch - svelte-kit sync then svelte-check --watch
  • bun run env:typegen - Regenerate types from .env.schema (Varlock)
  • bun run db:generate - Generate Drizzle migrations
  • bun run db:migrate - Run database migrations
  • bun run db:seed - Seed database with sample data
  • bun run db:clear - Clear application data from the database
  • bun run db:studio - Open Drizzle Studio
  • bun run db:push - Push schema directly to the database
  • bun run ultracite:upgrade - Re-run Ultracite init/upgrade for this stack
  • bun run fix - Run Ultracite fix (ultracite fix)
  • bun run fallow - Run Fallow (project graph and analysis)
  • bun run fallow:dead-code - Fallow dead-code pass
  • bun run fallow:boundaries - List configured boundaries
  • bun run fallow:boundary-violations - Dead-code with boundary violations
  • bun run fallow:dupes - Fallow duplicate detection
  • bun run prepare (auto) - panda codegen and svelte-kit sync after install

Tech Stack

Project Structure

src/
├── lib/
│   ├── auth.server.ts       # Centralized Better Auth configuration (Drizzle adapter, UUIDv7 IDs)
│   ├── api.ts               # Eden Treaty client (`apiClient`)
│   ├── server/
│   │   ├── db/
│   │   │   ├── index.ts     # Database connection (Neon serverless WebSocket pool)
│   │   │   ├── schema.ts    # Drizzle tables and enums
│   │   │   ├── types.ts     # Enum-derived types (e.g. client/invoice status)
│   │   │   ├── relations.ts # Drizzle relations v2 (`defineRelations`)
│   │   │   ├── seed.ts      # Database seeding
│   │   │   └── clear-app-data.ts
│   │   ├── app.ts           # Elysia app: OpenAPI plugin, auth mount, API routes, error mapping
│   │   ├── plugins/         # OpenAPI (dev), auth, list-query
│   │   ├── schemas.ts       # Shared API response shapes
│   │   ├── utils/           # better-auth-openapi, api-error-body, errors
│   │   └── routes/          # API modules (clients, invoices, settings)
│   ├── client/            # Client-only: @attach helpers, shared runes (ItemPanel, etc.)
│   ├── features/          # Domain features: components, stores, schemas, list helpers
│   │   ├── auth/
│   │   ├── clients/       # includes server queries (list, options, verify)
│   │   ├── invoices/      # includes server queries (list, verify)
│   │   ├── landing-page/  # Marketing sections, nav, copy constants
│   │   ├── line-items/
│   │   ├── pagination/    # PaginatedList, search, blank states, UUIDv7 `createId` (server module)
│   │   └── settings/
│   ├── components/        # Shared UI (navbar/, icons under components/icons/, ui/)
│   ├── stores/            # Shared list-store bases, dashboard context
│   ├── styles.ts          # Shared class names / style recipes
│   └── utils/
├── routes/
│   ├── (auth)/            # Login, signup, password reset, logout
│   ├── (dashboard)/
│   │   ├── (shell)/       # App shell: clients, invoice list, settings, thanks
│   │   └── invoices/      # Invoice detail, line items
│   ├── api/               # Elysia catch-all API handler
│   ├── +layout.svelte
│   └── +page.svelte       # Landing page
└── app.html

Database Schema

The application uses the following main tables:

  • user - Better Auth user accounts
  • session - User sessions
  • account - OAuth accounts
  • verification - Email verification tokens
  • clients - Client information (client_status: active, archive)
  • invoices - Invoice records (invoice_status: draft, sent, paid; optional discount)
  • line_items - Invoice line items
  • settings - User settings

Primary keys use PostgreSQL uuid columns; IDs are Bun UUIDv7 strings from createId (including Better Auth generateId in src/lib/auth.server.ts). Foreign keys use cascade deletes where appropriate.

The application uses Drizzle's relations v2 (defineRelations) to simplify nested queries (e.g., db.query.invoices.findMany({ with: { client: true, lineItems: true } })) and avoid manual joins in API routes.

Features

  • Modern Authentication: Better Auth with email/password support
  • Type-Safe Database: Drizzle ORM with full TypeScript support
  • Serverless Ready: Neon serverless driver (WebSocket pool) for Vercel deployment
  • Resilient IDs: Bun UUIDv7 for cursor-based navigation and performance
  • Recent Data: Seed script generates realistic data from the last 6 months
  • Multi-User Support: Data is distributed randomly among users
  • Auth Flows: Reset password supported; token is read from URL and validated
  • Modern UI: Ark UI components with Panda CSS
  • Svelte 5 Runes: Uses @attach directives and reactive patterns
  • Responsive Design: Mobile-first with swipe gestures

Deployment

The application is configured for Vercel deployment with the Vercel adapter. vercel.json runs the production build as varlock run -- bun --bun run build so Varlock resolves secrets the same way as local db:* scripts.

  • Platform env vars: Set DATABASE_URL, BETTER_AUTH_SECRET, and PUBLIC_BASE_URL in the Vercel project (and any other keys your schema requires). You can rely on Varlock for validation while storing values only in Vercel.
  • Bitwarden at build time: Add BITWARDEN_ACCESS_TOKEN to Vercel so the build can resolve bitwarden(...) entries in .env.schema (Varlock Vite SSR options).

Notes

  • Uses Vite 8 (vite in package.json). Varlock’s Vite plugin uses ssrInjectMode: "resolved-env". Production builds use rolldownOptions in vite.config.ts (for example dropConsole). If issues arise with third-party plugins, see Vite's documentation for compatibility.
  • Lint and format run through Ultracite (bun run check, bun run fix) with Biome rules extended from ultracite/biome for core and Svelte.
  • Panda CSS generates styled-system/; svelte.config.ts aliases styled-system and includes it in TypeScript. After dependency install, prepare runs codegen for Panda.
  • The project uses Svelte 5's @attach directive for modern component patterns and the Spring class for smooth animations.
  • Better Auth is configured in auth.server.ts to use Bun UUIDv7 for user ID generation and includes session caching for performance.
  • App configuration lives in svelte.config.ts (Vercel adapter, preprocess, Svelte 5 async compiler option).

License

MIT

Top categories

Loading Svelte Themes