Files
mymoney/README.md
2026-06-02 21:30:09 +02:00

108 lines
4.0 KiB
Markdown

# App Spese
Full-stack personal finance app built with Next.js App Router, TypeScript,
Tailwind CSS, and PostgreSQL.
The browser only talks to Next.js Route Handlers. PostgreSQL credentials and
session tokens stay on the server.
## Docker Compose
Copy the example environment file and choose a strong database password:
```bash
cp .env.example .env
docker compose up --build -d
```
The first startup initializes the PostgreSQL schema from `database/init.sql`.
Open `http://localhost:3000`: the app redirects to `/setup` and asks you to
create the first account. The setup page becomes unavailable as soon as that
account exists.
## Local Development
Start PostgreSQL with Docker:
```bash
docker compose up -d postgres
```
The compose file publishes PostgreSQL on `localhost:5432` by default so the
local Next.js development server can reach it.
Create `.env.local`:
```env
DATABASE_URL=postgresql://appspese:change-me@localhost:5432/appspese
```
Install dependencies and run Next.js:
```bash
pnpm install
pnpm dev
```
## Data Model
Tables:
- `users` and `sessions`: local credentials and revocable login sessions.
- `subscriptions`: recurring payment rules.
- `subscription_payments`: paid or unpaid state for generated renewals.
- `bills`: expected manual bills with due dates.
- `one_time_payments`: immediate manual outgoing payments.
- `payments_ledger`: single source of truth for real paid outgoing money.
- `payment_titles`: reusable deduplicated payment names.
- `income`: incoming money.
- `user_settings`: one row of preferences per user.
Subscriptions and bills count as projected obligations until paid. Paid
outgoing money is written to `payments_ledger`, with unique database constraints
preventing duplicate ledger rows.
Subscriptions keep service activation and billing separate. Recurring payments
start from the first payment date, can stop at an optional end date or after an
optional number of installments, and may otherwise continue indefinitely.
Suspended and terminated subscriptions do not generate new virtual renewals,
while already recorded payments remain available as history.
Custom renewal intervals use an exact quantity and unit, supporting schedules
such as every two weeks, every three months, or every two years.
## Database Migrations
`database/init.sql` contains the complete schema for new installations.
Versioned files in `database/migrations` update existing volumes. After pulling
an update that includes a new migration, apply it once with:
```bash
cat database/migrations/001_subscription_lifecycle.sql | docker compose exec -T postgres sh -lc 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
cat database/migrations/002_subscription_custom_intervals.sql | docker compose exec -T postgres sh -lc 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
cat database/migrations/003_subscription_ledger_renewal_dates.sql | docker compose exec -T postgres sh -lc 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
cat database/migrations/004_income_titles.sql | docker compose exec -T postgres sh -lc 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
cat database/migrations/005_user_avatar.sql | docker compose exec -T postgres sh -lc 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
cat database/migrations/006_subscription_payment_snapshots.sql | docker compose exec -T postgres sh -lc 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
```
## Authentication
There is no public registration page. The `/setup` page and its API endpoint are
available only while the database contains no users. The first request that
creates an account takes a PostgreSQL transaction lock, preventing concurrent
requests from creating multiple initial accounts.
- Passwords are hashed with bcrypt.
- Login sessions use random secrets stored in an HTTP-only cookie.
- Only a SHA-256 hash of each session secret is stored in PostgreSQL.
- API Route Handlers verify the current user server-side and ignore
client-provided user identifiers.
## Verification
```bash
pnpm lint
pnpm build
docker compose config
```