2026-06-02 04:47:49 +02:00
2026-06-02 04:02:59 +02:00
2026-06-02 04:02:59 +02:00
2026-06-02 04:47:49 +02:00
2026-06-02 04:02:59 +02:00
2026-06-02 04:02:59 +02:00
2026-05-29 08:53:17 +02:00
2026-06-02 04:16:51 +02:00
2026-06-02 04:02:59 +02:00
2026-06-02 04:47:49 +02:00
2026-06-02 04:02:59 +02:00
2026-06-02 04:02:59 +02:00
2026-06-02 04:02:59 +02:00
2026-06-02 04:02:59 +02:00
2026-06-02 04:02:59 +02:00

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:

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:

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:

DATABASE_URL=postgresql://appspese:change-me@localhost:5432/appspese

Install dependencies and run Next.js:

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:

cat database/migrations/001_subscription_lifecycle.sql | docker compose exec -T postgres psql -v ON_ERROR_STOP=1 -U appspese -d appspese
cat database/migrations/002_subscription_custom_intervals.sql | docker compose exec -T postgres psql -v ON_ERROR_STOP=1 -U appspese -d appspese

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

pnpm lint
pnpm build
docker compose config
Description
app di gestione contabile
Readme 453 KiB
Languages
TypeScript 98%
PLpgSQL 1.4%
CSS 0.4%
Dockerfile 0.2%