Open Finapp
Разработка

История миграций данных

Исторический обзор миграции бэкенда Финапки - с Firebase на Supabase + PowerSync.

Бэкенд Финапки был мигрирован с 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 для одного пользователя.

Terminal
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. Полное описание - в разделе Архитектура, настройка локальной среды - в разделе Установка.