История миграций данных
Бэкенд Финапки был мигрирован с Firebase Realtime Database на Supabase Postgres + PowerSync (offline-first синхронизация) с Supabase Auth (email/password). На этой странице описана и сама миграция, и скрипт, который импортирует Firebase-экспорт в Supabase.
Исходный код Firebase (v6) хранится на ветке firebase.
Что изменилось
| Сущность | Firebase (v6) | Текущее (Supabase + PowerSync) |
|---|---|---|
| Счета | accounts со строковыми ключами | Таблица wallets, text-идентификаторы |
| Категории | Строковые ID, parentId строкой | text-идентификаторы, parentId = id категории или null (корень) |
| Транзакции | Строковые ID, ссылки старыми ключами | text-идентификаторы, id из Firebase переиспользуются (ссылки сохраняются) |
| Настройки | settings.lang (en, ru) | user_settings.locale (en, ru) |
| Иконки | Формат mdi mdi-xxx | Формат mdi:xxx |
Инструмент импорта
Импортёр лежит в app/scripts/import-firebase.mjs. Он читает Firebase-экспорт (структура { accounts, categories, trns, settings, user }) и записывает его в проект Supabase для одного пользователя.
cd app
SUPABASE_URL=https://<ref>.supabase.co \
SUPABASE_SERVICE_ROLE_KEY=<service_role> \
node scripts/import-firebase.mjs <backup.json> --email <owner-email> --wipe
- Владелец - определяется по
--email(по умолчаниюuser.emailиз экспорта) в существующий Supabase auth uid через GoTrue admin API, либо задаётся напрямую через--user-id. Пользователь должен хотя бы раз войти (например, через Google), чтобыuserIdимпортированных строк совпал с uid, под которым он логинится. - Конфиг - читает
SUPABASE_URL+SUPABASE_SERVICE_ROLE_KEYиз env; если их нет, откатывается на локальный стек черезsupabase status. Запись идёт через PostgREST с ключомservice_role, поэтому обходит RLS и работает по HTTPS (ограничение «только IPv6» прямого подключения к Postgres здесь не применяется). --wipeсначала удаляет существующиеtrns/categories/walletsпользователя;--dry-runпечатает преобразованные счётчики и примеры без записи.- Идемпотентность - строки upsert'ятся по первичному ключу, а id из Firebase переиспользуются дословно (ниже), поэтому повторный запуск безопасен.
Какие преобразования применяются
- Id переиспользуются, не ремапятся - id-колонки в Supabase это обычный
textбез FK-ограничений, поэтому скрипт сохраняет исходные ключи Firebase как id; ссылкиparentId/walletId/categoryIdостаются валидными без отдельного прохода ремаппинга. - Accounts -> wallets;
settings.lang->user_settings.locale;settings.baseCurrency->user_settings.baseCurrency. - Иконки -
mdi mdi-xxx->mdi:xxx; пустая ->mdi:category-outline. - Transfer / adjustment - категории с именами
Transfer/Перевод/Adjustmentне являются настоящими категориями и пропускаются; их транзакции получаютcategoryId = 'transfer'(тип 2, с полямиexpense*/income*) илиcategoryId = 'adjustment'(иначе). - Легаси-поля -
walletFromId/walletToId->expenseWalletId/incomeWalletId;amountFrom/amountTo->expenseAmount/incomeAmount;description->desc. Поля только из Firebase, которым нет колонки (orderу категорий,showStat,budgets,groups), отбрасываются.
В схеме нет FK-ограничений, поэтому порядок импорта не имеет значения. RLS-политики и правила синхронизации PowerSync обеспечивают изоляцию по пользователям; при следующем входе владельца PowerSync синхронизирует импортированные строки на его устройство.
Текущий бэкенд
Текущий бэкенд - Supabase Postgres + PowerSync. Полное описание - в разделе Архитектура, настройка локальной среды - в разделе Установка.