Commit Graph

8 Commits

Author SHA1 Message Date
ZProger
f9413aa6e1 security: устранить находки аудита, версия 1.1.7
Конфигурация и эксплуатационный слой:
- nginx: client_max_body_size 64m — восстановление из бэкапа не работало
  вовсе, дефолтный лимит в 1 МБ резал дампы (реальный экспорт ~3.4 МБ)
  с 413 ещё до backend;
- nginx: заголовки безопасности (nosniff, Referrer-Policy,
  Permissions-Policy) и полный Content-Security-Policy. Ради него
  inline-скрипт темы вынесен в public/theme-init.js;
- nginx: limit_req на /api/ — перебор пароля Basic Auth шёл со
  скоростью сети, ни блокировки, ни задержки не было;
- nginx: server_tokens off;
- порт web привязан к 127.0.0.1, адрес настраивается через
  AURUM_BIND_ADDRESS (раньше слушался на всех интерфейсах);
- backend работает от непривилегированного пользователя,
  no-new-privileges для backend и web;
- дефолты пароля Postgres сведены к одному значению в обоих сервисах,
  в .env.example описана ловушка с инициализацией тома.

Приложение:
- версия убрана из неавторизованного /api/health и отдаётся как
  app_version в GET /api/settings;
- /api/docs, /api/redoc и /api/openapi.json отключаются через
  AURUM_ENABLE_DOCS;
- текст исключения при неудачном восстановлении уходит в лог, а не
  клиенту;
- max_length для notes в схемах транзакций, активов и регулярных
  платежей (и в соответствующих полях UI);
- срок «запомнить меня» сокращён с 30 дней до 7;
- pytest обновлён до 9.0.3 (PYSEC-2026-1845), pytest-asyncio до 1.4.0;
- CI: permissions contents:read для GITHUB_TOKEN.
2026-09-10 21:12:31 +03:00
ZProger
0134cdb6bf feat: split a transaction across multiple categories
Один чек часто смешивает несколько категорий трат (гипермаркет: часть — продукты,
часть — бытовая химия). Раньше такую транзакцию приходилось огрублять до одной
категории или дробить вручную на несколько записей. Теперь у транзакции есть
второй режим: 2+ доли (TransactionSplit) с суммами по разным категориям, которые
обязаны в точности складываться в сумму транзакции.

- Новая модель TransactionSplit + миграция.
- split_rule_violation() проверяет инварианты на создании и редактировании.
- category_rollup.py — общий helper, которым теперь пользуются Dashboard, отчёт
  по категориям и бюджеты, учитывая доли сплитов наравне с обычными транзакциями.
- Фильтр по категории в списке транзакций находит и сплит-транзакции.
- Сплиты попадают в полный бэкап/восстановление БД.
- 13 новых backend-тестов.
- Фронтенд: переключатель «Разбить на несколько категорий» в форме транзакции
  с повторяемыми строками и индикатором остатка суммы; строка транзакции в
  списке показывает все категории сплита.
2026-08-30 18:42:13 +03:00
technosexual
20f41b444a fix: don't enforce write-time transfer rules when reading transactions (#7)
TransactionRead inherited TransactionBase's model_validator, so the rules a
new row must satisfy were also applied on the way out, to rows already in
the database. A row can stop satisfying them through no fault of its own:
transactions.transfer_account_id is ON DELETE SET NULL, so deleting an
account leaves every transfer that pointed at it without a destination.

The result was that deleting an account could 500 the entire transactions
list — permanently, and with the list gone there was no way in the UI to
find and delete the offending row.

The fields move to TransactionFields, which reading uses; TransactionBase
keeps the invariants for the write path. The frontend already tolerates a
missing destination (TransactionsTable renders the transfer suffix only when
transfer_account_id is set, and the edit modal makes you pick a destination
before saving), so such a row is now visible, editable and deletable
instead of fatal.
2026-08-30 18:10:23 +03:00
technosexual
949d2df37f fix: enforce transfer invariants when updating a transaction (#3)
The transfer rules — destination required, destination different from the
source account, no category, and no destination on a non-transfer — were
enforced by TransactionBase's validator on create, but the PATCH route only
re-checked category-vs-type.

So `PATCH /api/transactions/{id} {"type": "transfer"}` on an ordinary
expense committed a transfer row with transfer_account_id = NULL, and only
then failed serializing the response (TransactionRead inherits the same
validator). The caller got a 500 and would reasonably assume nothing was
saved — but from that point on every `GET /api/transactions` failed the same
validation, so the transactions list, the app's main screen, returned 500
permanently, with no way left in the UI to delete the offending row.

The rules now live in one place, transfer_rule_violation(), used by both the
create schema and the update route, so the two paths can't drift apart
again. The update path validates the row as it would look after the patch
rather than only the fields sent, since switching type alone invalidates
fields that were left untouched.

The frontend already sends a complete payload on edit (explicit nulls for
category_id / transfer_account_id) and does these same checks client-side,
so this only closes the hole for direct API calls — no UI change needed.


Claude-Session: https://claude.ai/code/session_01UmuMDFrwdvQkce885VaaTh

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 02:42:37 +03:00
ZProger
19f5cfa429 feat: import transactions from a CSV bank statement
Fourth of the four ideas picked from the Maybe/Sure feature audit, and the
last one queued up. A 3-step wizard (account -> column mapping -> preview)
at /transactions/import, parsed entirely client-side (new lib/csv.ts —
delimiter auto-detect, 5 date formats, thousands/decimal-separator-aware
amounts) and submitted through a new all-or-nothing POST /transactions/bulk
endpoint, mirroring backup restore's validate-everything-before-touching-
anything approach.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 02:44:30 +03:00
ZProger
94782ee073 feat: add free-form tags on transactions
Third of the four ideas picked from the Maybe/Sure feature audit. Tags are
orthogonal to categories (category = what kind of spend, tag = which
event/project) — many-to-many, created inline while typing in the
transaction form, filterable from the Transactions page.

Also adds tests/test_backup.py, the first test coverage the backup
export/import path has ever had, and fixes a MissingGreenlet bug it
surfaced in restore_backup's tag-wiring.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 02:38:04 +03:00
ZProger
6cc532378b feat: auto-capitalize the first letter of transaction descriptions
Transactions/recurring templates entered with mixed casing ("траты на
продукты") are now capitalized on create/update via a Pydantic validator,
so the fix applies to every client, not just the web UI. Adds a one-off
backfill script for existing rows.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 23:10:22 +03:00
ZProger
fe9b079c12 Initial public release
Aurum — a self-hosted personal finance operating system: cash flow,
net worth tracking, budgets, goals, recurring payments, reports,
proactive alerts, and a compound-interest returns calculator, all
bilingual (RU/EN) and mobile-first. Ships as a three-container Docker
Compose stack (Postgres + FastAPI + nginx-served React frontend).

See README.md for setup and CONTRIBUTING.md for development.
2026-08-16 18:39:52 +03:00