---
title: "Інтеграція Firebase"
description: "Firebase Admin SDK v14, FirebaseAdapter, FCM push, кешування через service-manager, імітація на етапі збірки та налаштування клієнтського service worker для Ring Platform."
locale: "uk"
---
# Інтеграція Firebase

Ring Platform використовує **Firebase у двох окремих напрямках** — **серверний Admin SDK** для FCM push і серверних операцій, та **клієнтський браузерний SDK** для реєстрації push-токенів і відображення сповіщень. Ці напрямки використовують різні стратегії облікових даних і різні версії SDK.

> **Info**
> У режимах `k8s-postgres-fcm` і `supabase-fcm` Firebase Admin використовується **лише для FCM push**. Абстракція бази даних (`DatabaseService`) маршрутизує до PostgreSQL, а не до Firestore. Лише `DB_BACKEND_MODE=firebase-full` активує Firestore як основне сховище даних.

## Серверні файли

| Файл | Призначення | Стратегія облікових даних |
|------|-------------|--------------------------|
| `lib/firebase-admin.server.ts` | Singleton Firebase Admin SDK — FCM, Auth, Firestore, RTDB | **Завжди явний `cert()`** — потребує `AUTH_FIREBASE_CLIENT_EMAIL` + `AUTH_FIREBASE_PRIVATE_KEY` |
| `lib/database/adapters/FirebaseAdapter.ts` | Абстракція бази даних для Firestore (активний лише в режимі `firebase-full`) | **ADC-first** — резервний `cert()` за наявності облікових даних у конфігурації |
| `lib/services/firebase-service-manager.ts` | Кешовані читання Firestore з використанням React 19 `cache()`, пакетні операції, транзакції | Делегує до `firebase-admin.server.ts` |
| `lib/firebase/build-mock.server.ts` | Імітація сервісів під час SSG-збірки Next.js — запобігає 22+ зайвим ініціалізаціям | Повертає імітації без підключення |

## Клієнтські файли

| Файл | Призначення |
|------|-------------|
| `public/firebase-messaging-sw.js` | Service worker — Firebase compat SDK v12.9.0 завантажується з CDN, обробляє push-події та фонові повідомлення |

**Файл `lib/firebase.ts` відсутній** — клієнтський Firebase ініціалізується виключно всередині service worker через `importScripts()` з CDN. Реєстрація FCM-токена в браузері відбувається в React-компонентах за допомогою хука `use-fcm.ts` всередині `FCMProvider`.

### For founders

## Коли мені потрібен Firebase?

Ваш Ring-клон потребує облікових даних Firebase у **двох контекстах**:

1. **Push-сповіщення (FCM)** — опціонально для всіх значень `DB_BACKEND_MODE`. Firebase є лише транспортним засобом доставки push; ваші дані додатку залишаються в основній базі даних (PostgreSQL або Firestore).
2. **Повноцінний бекенд Firestore** — лише коли `DB_BACKEND_MODE=firebase-full`. Firebase Admin SDK слугує основною базою даних.

**Якщо вам не потрібні push-сповіщення**, ви можете опустити всі змінні середовища Firebase. Платформа працює на одному PostgreSQL.

## Які функції Firebase НЕ використовуються

- Firebase Authentication (Auth.js v5 обробляє всю автентифікацію — Google, Apple, email, криптогаманець)
- Firebase Storage (завантаження файлів здійснюється через Vercel Blob або `/api/entities/upload`)
- Firebase Realtime Database (не під'єднано до даних додатку; експорт RTDB існує в `firebase-admin.server.ts`, але не використовується в режимі `firebase-full`)
- Firebase Hosting (Ring Platform працює на k3s, Vercel або Docker)
- FirebaseUI / клієнтський auth SDK (файл `lib/firebase.ts` відсутній)

### For developers

## Firebase Admin SDK (`firebase-admin.server.ts`)

Admin SDK ініціалізується з **явними обліковими даними службового акаунта** через `cert()`. Усі три змінні середовища `AUTH_FIREBASE_*` є **обов'язковими** — резервний варіант ADC у цьому файлі відсутній.

{`import { cert, getApps, initializeApp } from 'firebase-admin/app'
import { getFirestore, Firestore } from 'firebase-admin/firestore'
import { getAuth, Auth } from 'firebase-admin/auth'

adminApp = initializeApp({
  credential: cert({
    projectId: cleanedProjectId,
    clientEmail: cleanedClientEmail,
    privateKey: cleanedPrivateKey,
  }),
  databaseURL: process.env.FIREBASE_DATABASE_URL,
})`}

**Експорти:**

| Експорт | Повертає | Примітки |
|---------|----------|----------|
| `getAdminDb()` | `Firestore` або **імітація** | Повертає імітацію в режимах postgres-primary та під час збірки |
| `getAdminAuth()` | `Auth` або **імітація** | Та ж логіка імітації |
| `getAdminRtdb()` | `Database` або **імітація** | RTDB — активно не використовується в маршрутах додатку |

У режимах `k8s-postgres-fcm` і `supabase-fcm` усі три повертають **імітації** з `build-mock.server.ts`. Імітація задовольняє типи TypeScript, але ніколи не підключається до Firebase. Це запобігає ініціалізації Firebase, коли активний лише PostgreSQL.

**Очищення змінних середовища (важливо):**

SDK агресивно очищає вхідні дані змінних середовища — видаляє зовнішні лапки, перетворює `\\n` на реальні нові рядки, обрізає пробіли. Узгодженість подвійних лапок у `env.local.template` для `AUTH_FIREBASE_PRIVATE_KEY` є навмисною:

```bash
AUTH_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n"
```

## FirebaseAdapter (`FirebaseAdapter.ts`)

`FirebaseAdapter` реалізує `IDatabaseService` і використовується **лише коли `DB_BACKEND_MODE=firebase-full`**. За замовчуванням він використовує **Application Default Credentials (ADC)**:

{`// Продакшн (Cloud Run, GKE, Cloud Functions):
//   ADC автоматично визначається з оточення — ручний cert() не потрібен.
//   Потрібен лише AUTH_FIREBASE_PROJECT_ID.

// Локальна розробка / CI:
//   Резервний варіант — явний cert(), коли credentials.clientEmail та
//   credentials.privateKey присутні в конфігурації бекенду.`}

Адаптер надає стандартний контракт `DatabaseService`:

{`// Одиничний документ
await db.findById('users', userId)
// => { success, data: T | null, error? }

// Запит з фільтрами, сортуванням, пагінацією
await db.query({
  collection: 'entities',
  filters: [{ field: 'status', operator: 'eq', value: 'active' }],
  orderBy: [{ field: 'createdAt', direction: 'desc' }],
  pagination: { limit: 20, offset: 0 },
})
// => { success, data: T[], error? }

// Мутації
await db.create('users', data)
await db.update('users', id, { name: 'New' })
await db.delete('users', id)

// Транзакції
await db.transaction(async (txn) => { ... })`}

## firebase-service-manager (`firebase-service-manager.ts`)

Надає **кешовані операції Firebase** з використанням React 19 `cache()` для дедуплікації на один запит. Використовується лише коли `getAdminDb()` повертає реальний (не імітований) екземпляр Firestore.

Ключові експорти:

| Експорт | Опис |
|---------|------|
| `getCachedDocument(collection, docId)` | Одиничний документ з дедуплікацією через `cache()` |
| `getCachedCollection(collection, options)` | Запит колекції з кешуванням |
| `getCachedDocumentBatch(requests)` | Пакетне отримання кількох документів |
| `getCachedCollectionAdvanced(collection, queryConfig)` | Складні запити (where, orderBy, paginate) |
| `getCachedSubcollection(parentCol, parentId, subCol, options)` | Запити вкладених колекцій |
| `getCachedCollectionGroup(collectionId, options)` | Міжколекційні запити |
| `createDocument`, `updateDocument`, `deleteDocument` | Операції запису (без кешування) |
| `createBatchWriter` / `executeBatch` | Пакетний запис |
| `runTransaction` | Атомні транзакції |
| `getCacheMetrics` / `logCachePerformance` | Налагоджувальні метрики (лише для розробки) |

**Оптимізація на етапі збірки:** Усі кешовані функції викликаються лише коли `getAdminDb()` повертає реальний Firestore. Під час збірки (SSG) методи імітованого Firestore повертають порожні результати, а шар `cache()` дедуплікує в межах кожного запиту.

## Імітація на етапі збірки (`build-mock.server.ts`)

Під час статичної генерації Next.js (`NEXT_PHASE=phase-production-build`) виклики Firebase Admin перехоплюються імітованими сервісами:

{`export function isBuildTime(): boolean {
  return (
    process.env.NEXT_PHASE === 'phase-production-build' ||
    process.env.NEXT_PHASE === 'phase-development-build' ||
    process.env.NODE_ENV === 'production' && process.argv.includes('build')
  )
}

export function getMockFirebaseServices() {
  // Повертає mockFirestore, mockAuth, mockRtdb
  // Усі методи повертають безпечні значення за замовчуванням
}`}

Це запобігає приблизно 22 зайвим ініціалізаціям Firebase Admin під час SSG і скорочує час збірки приблизно на 31%.

## Клієнтський Firebase (FCM service worker)

Service worker за адресою `public/firebase-messaging-sw.js` завантажує Firebase compat SDK v12.9.0 з Google CDN:

{`importScripts('https://www.gstatic.com/firebasejs/12.9.0/firebase-app-compat.js')
importScripts('https://www.gstatic.com/firebasejs/12.9.0/firebase-messaging-compat.js')

firebase.initializeApp({
  apiKey: self.FIREBASE_API_KEY,
  projectId: self.FIREBASE_PROJECT_ID,
  // ... self.* змінні впроваджуються під час виконання
})

const messaging = firebase.messaging()
messaging.onBackgroundMessage((payload) => {
  // Показує сповіщення з типізованою обробкою
  // Типи: chat, opportunity, news, entity, admin, default
})`}

Service worker підтримує **впроваджену під час виконання** конфігурацію Firebase через змінні `self.*` (встановлюються заповнювачами `env.local.template` на етапі збірки або впроваджуються в розгортаннях k8s).

**Процес реєстрації FCM-токена:**

1. React-компонент `'use client'` викликає `Notification.requestPermission()`
2. Після надання дозволу викликає `getToken(messaging, { vapidKey })`
3. Токен зберігається через Server Action `upsertFcmToken` до основної бази даних (PostgreSQL або Firestore залежно від `DB_BACKEND_MODE`)
4. При виході з системи `useAuth().signOut()` викликає `unregisterCurrentDeviceFcmToken()`, яка запускає `deleteToken` Firebase + інвалідацію на стороні сервера

## Змінні середовища

### Admin SDK (серверна частина)

```bash
# Обов'язкові для FCM + ініціалізації Firebase Admin (усі три):
AUTH_FIREBASE_PROJECT_ID=your_firebase_project_id
AUTH_FIREBASE_CLIENT_EMAIL=your_firebase_client_email
AUTH_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"

# Опціонально:
FIREBASE_DATABASE_URL=https://your-project-default-rtdb.firebaseio.com
FIREBASE_PRIVATE_KEY_ID=your_firebase_private_key_id
FIREBASE_CLIENT_ID="your_firebase_client_id"
FIREBASE_FIRESTORE_DEBUG=true
```

### Client SDK (браузерна частина)

```bash
NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_auth_domain
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_firebase_project_id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_storage_bucket
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-YCZKPV315E
NEXT_PUBLIC_FIREBASE_VAPID_KEY=your_vapid_key
```

## Ключові відмінності між двома напрямками Firebase

| Аспект | Admin SDK (`firebase-admin.server.ts`) | Адаптер (`FirebaseAdapter.ts`) |
|--------|----------------------------------------|-------------------------------|
| Облікові дані | Завжди явний `cert()` | ADC-first, резервний `cert()` |
| Змінні середовища | Усі 3 `AUTH_FIREBASE_*` обов'язкові | Лише `AUTH_FIREBASE_PROJECT_ID` для ADC |
| Активний у | Усі значення DB_BACKEND_MODE (FCM) | Лише `firebase-full` |
| Імітація у postgres-primary | Так — повертає імітовані Firestore/Auth | Ні — адаптер не зареєстровано |
| Шлях імпорту | `firebase-admin/app`, `firebase-admin/firestore` | Те саме |
| Версія | v14 (іменовані підшляхи імпорту) | v14 |

## Пов'язана документація
