Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

House Tracker

Licence: MIT Node >= 22 Russian and English

Utility bill tracking for rented housing. Meter readings, tariffs, extra expenses and an exchange rate go in; how much cash the tenant owes this month comes out. One report per month, each with a public link for the tenant.

Renting a flat abroad usually means two currencies: utilities are billed in the local one, rent is agreed in dollars, and the rate moves every month. This app does that arithmetic and keeps a record of it, instead of a spreadsheet that gets rewritten every thirty days.

Built for a single administrator - the landlord. There is no users table; the login and password hash come from the environment.

What it does

  • A report per month: three meters (electricity, water, gas) with their tariffs, additional expenses, the bank transfer and the rent
  • Fetches the exchange rate for a chosen date from a configurable source
  • Carries the balance - overpayment or debt - into the next month automatically
  • Creating a new month copies the tariffs and seeds the previous readings
  • A public link per report, toggled on and off, that a tenant can open without an account
  • Russian and English interface, follows the system colour scheme

Stack

Next.js 16 (App Router) · React 19 · TypeScript · Prisma 7 · PostgreSQL · NextAuth v4 · Tailwind 4 · shadcn/ui

Getting started

pnpm install
cp .env.example .env      # fill it in, see below
pnpm db:migrate
pnpm dev

Generate the administrator password hash (base64 because a bcrypt hash contains $, which breaks .env parsing):

node -e "console.log(Buffer.from(require('bcryptjs').hashSync('your-password',10)).toString('base64'))"

Environment

Variable Required Purpose
DATABASE_URL yes PostgreSQL connection string
NEXTAUTH_SECRET yes JWT secret (openssl rand -base64 32)
NEXTAUTH_URL yes Base URL of the app
ADMIN_USERNAME yes Administrator login
ADMIN_PASSWORD_HASH_B64 yes base64 of the bcrypt hash
RATE_PROVIDER no Exchange-rate source, defaults to nbrb
ALLOWED_ORIGIN no Origin for CORS headers on /api/*
SENTRY_DSN / NEXT_PUBLIC_SENTRY_DSN no Error reporting

Another country, another currency

Nothing is hardwired to one country. Both currencies come from the active rate provider, and so do the labels in the interface. To plug in your own bank, add a file next to src/lib/rates/nbrb.ts:

export const myBank: RateProvider = {
  id: 'my-bank',
  name: 'My Bank',
  localCurrency: 'XXX',   // what utilities are billed in
  baseCurrency: 'USD',    // what rent is settled in
  async fetchRate(date) {
    // how much localCurrency one baseCurrency costs; null if there is no quote
  },
};

Register it in PROVIDERS in src/lib/rates/index.ts and set RATE_PROVIDER to its id. Nothing else needs to change. The default tariffs in src/lib/constants.ts are Belarusian - adjust them to your own.

Languages

Strings live in src/i18n/dictionaries/. The English dictionary is typed against the Russian one, so a forgotten key fails the build, and a test checks that placeholders match. Adding a language is a dictionary file plus a code in LOCALES.

How the total is calculated

meter amount  = (current - previous) x tariff
local balance = sum of meters - bank transfer - extra expenses
base balance  = local balance / rate
cash due      = rent + base balance - balance carried from last month
carried on    = cash actually paid - cash due

A positive carry-over means the tenant overpaid, a negative one means they owe. The whole calculation lives in src/lib/calc.ts and is covered by pnpm test.

Deployment

The Dockerfile produces a standalone build, and the entrypoint runs prisma migrate deploy before starting the server, so a failed migration stops the container rather than serving against an unmigrated database.

docker network create shared   # docker-compose.yml expects this network
docker compose up -d --build

The database is external - point DATABASE_URL at your own PostgreSQL.

Limitations

  • Single user, no roles, no sign-up
  • Login rate limiting (3 attempts, 24-hour block) lives in process memory and resets when the container restarts
  • The client IP is read from CF-Connecting-IP / X-Real-IP / X-Forwarded-For; without a trusted proxy in front, those headers can be spoofed
  • Editing an old report carries the balance one month forward, not down the chain
  • NBRB is the only rate source implemented; others are one file each

More detail in docs/architecture.md.

Status

This is a snapshot of a working application, published as is.

  • It is not actively maintained. Issues and pull requests are not promised an answer.
  • Tests cover the money math, the formatting and dictionary consistency. There is no automated UI coverage - expect rough edges, and own them if you deploy it.
  • The defaults - tariffs, the meters themselves - assume a flat with electricity, water and gas on separate meters.

Licence

MIT - use it for anything, including commercially, keeping the notice.

Copyright (c) 2026 Vladislav Romanovsky.

About

Utility bill tracking for rented housing: meter readings, tariffs and exchange rates into the cash a tenant owes. Next.js 16, Prisma, PostgreSQL.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages