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 Tailwind

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: "auto-load" so dev and SSR resolve the same schema (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 reads secrets via SvelteKit’s $env/static/private and $env/static/public (for example DATABASE_URL in src/lib/db/index.ts).

  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/db/schema.ts and writes migrations under ./src/lib/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 checks
  • bun run check:watch - Run Svelte check in watch mode
  • 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 format - Format source with Prettier
  • bun run lint - Run Prettier check and ESLint
  • bun run lint:fix - Auto-fix lint and formatting issues
  • bun run ultracite:upgrade - Re-run Ultracite init/upgrade for this stack
  • bun run fix - Run Ultracite fix (ultracite fix)

Tech Stack

  • Framework: SvelteKit 5 with Svelte 5 runes
  • API layer: ElysiaJS (src/lib/server route modules), mounted at /api via src/routes/api/[...slugs]/+server.ts, Eden Treaty client in src/lib/client.ts
  • Database: PostgreSQL with Neon serverless
  • ORM: Drizzle ORM (beta) with Neon serverless driver (WebSocket Pool)
  • Authentication: Better Auth with email/password (src/lib/auth.ts, Drizzle adapter)
  • ID generation: Bun.randomUUIDv7() via src/lib/features/pagination/utils/create-uuidv7.ts (cursor-friendly IDs)
  • Deployment: Vercel adapter
  • Package manager: Bun
  • Validation: ArkType for runtime-safe form validation
  • Bundler: Vite 8 for dev and production builds
  • UI components: Bits UI with Tailwind CSS
  • Styling: Tailwind CSS 4 with Tailwind Variants
  • Lint/format: Ultracite (ESLint, Prettier, Stylelint)

Project Structure

src/
├── lib/
│   ├── auth.ts              # Better Auth configuration (Drizzle adapter, UUIDv7 user IDs)
│   ├── auth-client.ts       # Client-side auth helpers
│   ├── client.ts            # Eden Treaty client for the Elysia API
│   ├── 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
│   │   └── migrate.ts
│   ├── server/
│   │   ├── auth-plugin.ts   # Better Auth integration for Elysia
│   │   ├── better-auth-openapi.ts
│   │   ├── api-response-schemas.ts
│   │   └── routes/        # API modules (clients, invoices, settings)
│   ├── features/          # Domain features: components, stores, schemas, list helpers
│   │   ├── auth/
│   │   ├── clients/
│   │   ├── invoices/
│   │   ├── line-items/
│   │   ├── pagination/    # Cursor pagination, list-query utilities, UUIDv7 helper
│   │   └── settings/
│   ├── components/        # Shared UI (icons under components/icons/)
│   ├── attachments/       # Svelte 5 @attach helpers (e.g. swipe, clickOutside)
│   ├── runes/             # Shared rune modules (ItemPanel, etc.)
│   ├── stores/            # Shared list-store bases, dashboard context
│   ├── utils/
│   └── validators.ts
├── routes/
│   ├── (auth)/            # Login, signup, password reset, logout
│   ├── (dashboard)/
│   │   ├── (shell)/       # App shell: clients, invoice list, settings
│   │   └── invoices/      # Invoice detail, line items, thanks
│   ├── 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.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: Bits UI components with Tailwind CSS 4
  • 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. Choose one approach:

  • 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. If the host cannot run Varlock the same way as local dev, you may need ssrInjectMode: "resolved-env" in vite.config.ts for production builds (Varlock Vite SSR options).

Notes

  • Uses Vite 8 (vite in package.json). Production builds use rolldownOptions in vite.config.ts (for example dropConsole). If issues arise with third-party plugins, see Vite's documentation for compatibility.
  • ESLint configuration is in eslint.config.mjs and uses Svelte 5 rules and Prettier integration. Prettier is configured in prettier.config.mjs. Use bun run format before bun run lint.
  • The project uses Svelte 5's @attach directive for modern component patterns and the Spring class for smooth animations.
  • Better Auth is configured 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