Підготовка контенту платформи Ring
Підготовка контенту платформи Ring
Підготовка контенту платформи Ring
Концепції, цінність і типові сценарії
Концепції, цінність і типові сценарії
Концепції, цінність і типові сценарії
k8s-postgres-fcm — це рекомендований бекенд-режим для продакшну та локальної розробки. PostgreSQL на Kubernetes (або локально) обробляє всі дані застосунку, тоді як Firebase Admin SDK ініціалізується виключно для FCM push-повідомлень та Apple Push. Жодної залежності від Firebase для даних застосунку.
TUNNEL_HUB_MODE=k8s-postgres (або in-memory хаб для локальної розробки).| Сценарій | Рекомендований режим |
|---|---|
| Власний продакшн на k3s | k8s-postgres-fcm |
| Локальна розробка з PostgreSQL | k8s-postgres-fcm |
| Хмарний PostgreSQL (без K8s) | supabase-fcm |
| Швидке прототипування / Vercel Edge | firebase-full |
| Push-сповіщення потрібні | будь-який режим — FCM є опціональним у всіх трьох |
getAdminDb() повертає імітацію Firestore, яка ніколи не підключається.k8s-postgres-fcm — це рекомендований бекенд-режим для продакшну та локальної розробки. PostgreSQL на Kubernetes (або локально) обробляє всі дані застосунку, тоді як Firebase Admin SDK ініціалізується виключно для FCM push-повідомлень та Apple Push. Жодної залежності від Firebase для даних застосунку.
TUNNEL_HUB_MODE=k8s-postgres (або in-memory хаб для локальної розробки).| Сценарій | Рекомендований режим |
|---|---|
| Власний продакшн на k3s | k8s-postgres-fcm |
| Локальна розробка з PostgreSQL | k8s-postgres-fcm |
| Хмарний PostgreSQL (без K8s) | supabase-fcm |
| Швидке прототипування / Vercel Edge | firebase-full |
| Push-сповіщення потрібні | будь-який режим — FCM є опціональним у всіх трьох |
getAdminDb() повертає імітацію Firestore, яка ніколи не підключається.k8s-postgres-fcm — це рекомендований бекенд-режим для продакшну та локальної розробки. PostgreSQL на Kubernetes (або локально) обробляє всі дані застосунку, тоді як Firebase Admin SDK ініціалізується виключно для FCM push-повідомлень та Apple Push. Жодної залежності від Firebase для даних застосунку.
TUNNEL_HUB_MODE=k8s-postgres (або in-memory хаб для локальної розробки).| Сценарій | Рекомендований режим |
|---|---|
| Власний продакшн на k3s | k8s-postgres-fcm |
| Локальна розробка з PostgreSQL | k8s-postgres-fcm |
| Хмарний PostgreSQL (без K8s) | supabase-fcm |
| Швидке прототипування / Vercel Edge | firebase-full |
| Push-сповіщення потрібні | будь-який режим — FCM є опціональним у всіх трьох |
getAdminDb() повертає імітацію Firestore, яка ніколи не підключається.DB_NAME | Так | ring_platform | Назва бази даних |
DB_USER | Так | ring_user | Користувач бази даних |
DB_PASSWORD | Так | ring_dev_password | Пароль бази даних |
DB_POOL_SIZE | Ні | 20 | Максимальний розмір пулу з'єднань |
DB_TIMEOUT | Ні | 30000 | Час очікування з'єднання (мс) |
DB_RETRIES | Ні | 3 | Кількість повторних спроб |
DB_SSL | Ні | false | Увімкнути TLS для PostgreSQL |
DB_ENABLE_POSTGIS | Ні | false | Увімкнути розширення PostGIS |
Потрібні лише якщо вам потрібні push-сповіщення. Без них платформа працює виключно з PostgreSQL.
PostgreSQLAdapter будує конфігурацію пулу зі змінних середовища. Джерело: lib/database/adapters/PostgreSQLAdapter.ts:
Адаптер використовує гібридну JSONB-схему: більшість таблиць зберігають бізнес-дані в стовпці data типу JSONB разом із загальними стовпцями id, created_at, updated_at. Таблиці з повністю нормалізованими схемами (наприклад, vendor_applications, vendor_profiles») перераховують усі стовпці в мапі fieldMappings, тому конструктор запитів використовує прямі посилання на стовпці замість data->>'field'`.
Адаптер підтримує повний контракт DatabaseService:
Запустіть PostgreSQL (два варіанти):
# Варіант A: Docker (рекомендовано)
docker run -d --name ring-postgres-dev \
-e POSTGRES_USER=ring_user \
-e POSTGRES_PASSWORD=ring_password_2024 \
-e POSTGRES_DB=ring_platform \
-p 5432:5432 \
postgres:16-alpine
# Варіант B: Homebrew (macOS)
brew install postgresql@16
brew services start postgresql@16
createdb ring_platformСтворіть .env.local з шаблону та встановіть:
DB_BACKEND_MODE=k8s-postgres-fcm
DB_HOST=localhost
DB_PORT=5432
DB_NAME=ring_platform
DB_USER=ring_user
DB_PASSWORD=ring_password_2024Застосуйте схему:
psql -h localhost -U ring_user -d ring_platform -f data/schema.sqlЗапустіть сервер розробки:
npm run devПлатформа перевіряє DB_BACKEND_MODE під час запуску та логує конфігурацію бекенду. Ви маєте побачити Database: PostgreSQL у логах запуску.
Тунельний хаб для функцій реального часу:
У локальній розробці тунель за замовчуванням використовує in-memory хаб. Для поведінки, наближеної до продакшну, встановіть:
Це зберігає стан тунелю в PostgreSQL замість пам'яті процесу.
Кожен клон ring отримує власний простір імен: k8s/namespace.yaml
Маніфест k8s/postgres.yaml розгортає PostgreSQL 16 (образ: postgres:15) як Deployment з одним реплікою та сервісом ClusterIP:
postgres-config) — POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DBplatform_org_user) та надає привілеїuuid-ossp та btree_gin увімкнено за замовчуванням/data/postgres-<clone-name>postgres.<namespace>.svc.cluster.localСекрети (k8s/secrets.yaml — Opaque, stringData):
ConfigMap (k8s/secrets.yaml — секція ConfigMap):
Важливо: DB_PASSWORD має бути в Secret, а не в ConfigMap. Розгортання посилається на нього через secretKeyRef.
k8s/deployment.yaml — застосунок Next.js 16 з:
ghcr.io/connectplatform/ring:v<version>-<clone>-amd64/api/health)pvc-local-file-storage.yaml — 20Gi)k8s/ingress.yaml — контролер ingress nginx з:
letsencrypt-prod)websocket-services)Для локальних або продакшн-розгортань на основі Docker docker-compose.yml підтримує режим k8s-postgres-fcm. Переконайтеся, що ваш .env містить DB_BACKEND_MODE=k8s-postgres-fcm та змінні для підключення PostgreSQL:
Примітка: Стандартний docker-compose.yml не включає сервіс PostgreSQL. Ви повинні або:
docker run postgres:16-alpine)postgres до вашого перевизначення (docker/docker-compose.dev.yml)Цей режим використовує Firebase Admin виключно для push:
AUTH_FIREBASE_*. SDK потрібен для генерації FCM-токенів та надсилання push-повідомлень.getAdminDb() повертає імітацію Firestore (ніколи не підключається до Firebase). Це контролюється тим, що shouldUseFirebaseForDatabase() повертає false.getAdminAuth() також повертає імітацію. Auth.js v5 обробляє всю автентифікацію.public/firebase-messaging-sw.js) працює в браузері та завантажує Firebase compat SDK з CDN. На нього не впливає режим бекенду — він завжди підключається до Firebase для push-підписки.fcm_tokens PostgreSQL через Server Action upsertFcmToken.AUTH_FIREBASE_* та NEXT_PUBLIC_FIREBASE_*, ініціалізація Firebase Admin повністю пропускається. PostgreSQL продовжує працювати.| Аспект | k8s-postgres-fcm | supabase-fcm |
|---|---|---|
| Хост PostgreSQL | Ваш K8s або локальний | Хмара Supabase |
| SSL | Необов'язково (DB_SSL=false за замовчуванням) | Обов'язково (DB_SSL=true) |
| Пул з'єднань | DB_POOL_SIZE=20 за замовчуванням | DB_POOL_SIZE=10 за замовчуванням |
| Облікові дані | DB_USER / DB_PASSWORD | SUPABASE_URL / SUPABASE_SERVICE_KEY |
| Ціль розгортання | K8s, Docker, bare metal | Платформа Supabase |
| Маніфести K8s | Повний набір у k8s/ | Не застосовно |
| Тунельний хаб | k8s-postgres або memory | memory |
| PostGIS | DB_ENABLE_POSTGIS=true | Потребує додатку Supabase PostGIS |
| Варіант використання | Повний контроль інфраструктури | Керований хмарний PostgreSQL |
Обидва режими використовують той самий клас PostgreSQLAdapter. Єдина відмінність — хто хостить екземпляр PostgreSQL та як надаються параметри підключення.
# Вибір режиму (ОБОВ'ЯЗКОВО — платформа не запуститься без нього)
DB_BACKEND_MODE=k8s-postgres-fcm
# Підключення до PostgreSQL
DB_HOST=localhost # K8s: postgres.<namespace>.svc.cluster.local
DB_PORT=5432
DB_NAME=ring_platform
DB_USER=ring_user
DB_PASSWORD=ring_password_2024
# Налаштування пулу з'єднань (необов'язково, показано зі значеннями за замовчуванням)
DB_POOL_SIZE=20 # Максимум з'єднань у пулі
DB_TIMEOUT=30000 # Час очікування з'єднання в мс
DB_RETRIES=3 # Спроби повтору при збої
DB_SSL=false # Встановіть true для продакшну та Supabase
# PostGIS (необов'язково)
DB_ENABLE_POSTGIS=false # Увімкнути просторові розширення
┌─────────────────────────────────────────────────┐
│ Next.js 16 App │
│ │
│ ┌──────────────────────┐ ┌──────────────────┐ │
│ │ PostgreSQLAdapter │ │ Firebase Admin │ │
│ │ (all CRUD / query) │ │ (FCM + Apple │ │
│ │ │ │ Push only) │ │
│ │ • Auth.js tables │ │ │ │
│ │ • Entities │ │ • getAdminDb() │ │
│ │ • Store products │ │ → mock (never │ │
│ │ • Conversations │ │ connects) │ │
│ │ • FCM tokens │ │ • getAdminAuth() │ │
│ │ • All business data │ │ → mock │ │
│ └────────┬─────────────┘ └────────┬─────────┘ │
│ │ │ │
└───────────┼─────────────────────────┼────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ PostgreSQL 16 │ │ Firebase FCM │
│ + PostGIS │ │ (push only) │
│ StatefulSet │ └──────────────────┘
│ (k8s or local) │
└─────────────────┘
# Firebase Admin SDK (серверний) — потрібен для FCM
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 client SDK (браузерний) — для реєстрації push через service worker
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_VAPID_KEY=your_vapid_key
const poolConfig = {
host: config.connection.host || 'localhost',
port: config.connection.port || 5432,
database: config.connection.database || 'ring_platform',
user: config.connection.username || 'ring_user',
password: config.connection.password || 'ring_dev_password',
max: config.options.poolSize || 20,
idleTimeoutMillis: config.options.timeout || 30000,
connectionTimeoutMillis: config.options.timeout || 2000,
}
// Операції з окремими документами
await db.findById('users', userId)
await db.create('users', { name: 'Аліса', email: 'alice@example.com' })
await db.update('users', userId, { name: 'Боб' })
await db.delete('users', userId)
// Запити з фільтрами, сортуванням, пагінацією
await db.query({
collection: 'entities',
filters: [{ field: 'status', operator: 'eq', value: 'active' }],
orderBy: [{ field: 'createdAt', direction: 'desc' }],
pagination: { limit: 20, offset: 0 },
})
// Транзакції
await db.transaction(async (txn) => {
const order = await txn.create('orders', { total: 100 })
await txn.update('stock', productId, { quantity: 5 })
})
TUNNEL_HUB_MODE=k8s-postgres
apiVersion: v1
kind: Namespace
metadata:
name: ring-platform-org
labels:
app.kubernetes.io/name: ring-platform-org
app.kubernetes.io/part-of: ringdom
apiVersion: v1
kind: Secret
metadata:
name: ring-platform-org-secrets
type: Opaque
stringData:
DB_PASSWORD: <value>
AUTH_FIREBASE_PROJECT_ID: <value>
AUTH_FIREBASE_CLIENT_EMAIL: <value>
AUTH_FIREBASE_PRIVATE_KEY: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
apiVersion: v1
kind: ConfigMap
metadata:
name: ring-platform-org-config
data:
DB_BACKEND_MODE: k8s-postgres-fcm
DB_HOST: postgres.ring-platform-org.svc.cluster.local
DB_PORT: "5432"
DB_NAME: ring_platform
DB_USER: ring_user
DB_POOL_SIZE: "20"
DB_TIMEOUT: "30000"
DB_RETRIES: "3"
DB_SSL: "false"
NEXT_PUBLIC_FIREBASE_PROJECT_ID: <value>
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/postgres.yaml
kubectl apply -f k8s/secrets.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
kubectl apply -f k8s/pvc-local-file-storage.yaml
docker compose up -dDB_NAME | Так | ring_platform | Назва бази даних |
DB_USER | Так | ring_user | Користувач бази даних |
DB_PASSWORD | Так | ring_dev_password | Пароль бази даних |
DB_POOL_SIZE | Ні | 20 | Максимальний розмір пулу з'єднань |
DB_TIMEOUT | Ні | 30000 | Час очікування з'єднання (мс) |
DB_RETRIES | Ні | 3 | Кількість повторних спроб |
DB_SSL | Ні | false | Увімкнути TLS для PostgreSQL |
DB_ENABLE_POSTGIS | Ні | false | Увімкнути розширення PostGIS |
Потрібні лише якщо вам потрібні push-сповіщення. Без них платформа працює виключно з PostgreSQL.
PostgreSQLAdapter будує конфігурацію пулу зі змінних середовища. Джерело: lib/database/adapters/PostgreSQLAdapter.ts:
Адаптер використовує гібридну JSONB-схему: більшість таблиць зберігають бізнес-дані в стовпці data типу JSONB разом із загальними стовпцями id, created_at, updated_at. Таблиці з повністю нормалізованими схемами (наприклад, vendor_applications, vendor_profiles») перераховують усі стовпці в мапі fieldMappings, тому конструктор запитів використовує прямі посилання на стовпці замість data->>'field'`.
Адаптер підтримує повний контракт DatabaseService:
Запустіть PostgreSQL (два варіанти):
# Варіант A: Docker (рекомендовано)
docker run -d --name ring-postgres-dev \
-e POSTGRES_USER=ring_user \
-e POSTGRES_PASSWORD=ring_password_2024 \
-e POSTGRES_DB=ring_platform \
-p 5432:5432 \
postgres:16-alpine
# Варіант B: Homebrew (macOS)
brew install postgresql@16
brew services start postgresql@16
createdb ring_platformСтворіть .env.local з шаблону та встановіть:
DB_BACKEND_MODE=k8s-postgres-fcm
DB_HOST=localhost
DB_PORT=5432
DB_NAME=ring_platform
DB_USER=ring_user
DB_PASSWORD=ring_password_2024Застосуйте схему:
psql -h localhost -U ring_user -d ring_platform -f data/schema.sqlЗапустіть сервер розробки:
npm run devПлатформа перевіряє DB_BACKEND_MODE під час запуску та логує конфігурацію бекенду. Ви маєте побачити Database: PostgreSQL у логах запуску.
Тунельний хаб для функцій реального часу:
У локальній розробці тунель за замовчуванням використовує in-memory хаб. Для поведінки, наближеної до продакшну, встановіть:
Це зберігає стан тунелю в PostgreSQL замість пам'яті процесу.
Кожен клон ring отримує власний простір імен: k8s/namespace.yaml
Маніфест k8s/postgres.yaml розгортає PostgreSQL 16 (образ: postgres:15) як Deployment з одним реплікою та сервісом ClusterIP:
postgres-config) — POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DBplatform_org_user) та надає привілеїuuid-ossp та btree_gin увімкнено за замовчуванням/data/postgres-<clone-name>postgres.<namespace>.svc.cluster.localСекрети (k8s/secrets.yaml — Opaque, stringData):
ConfigMap (k8s/secrets.yaml — секція ConfigMap):
Важливо: DB_PASSWORD має бути в Secret, а не в ConfigMap. Розгортання посилається на нього через secretKeyRef.
k8s/deployment.yaml — застосунок Next.js 16 з:
ghcr.io/connectplatform/ring:v<version>-<clone>-amd64/api/health)pvc-local-file-storage.yaml — 20Gi)k8s/ingress.yaml — контролер ingress nginx з:
letsencrypt-prod)websocket-services)Для локальних або продакшн-розгортань на основі Docker docker-compose.yml підтримує режим k8s-postgres-fcm. Переконайтеся, що ваш .env містить DB_BACKEND_MODE=k8s-postgres-fcm та змінні для підключення PostgreSQL:
Примітка: Стандартний docker-compose.yml не включає сервіс PostgreSQL. Ви повинні або:
docker run postgres:16-alpine)postgres до вашого перевизначення (docker/docker-compose.dev.yml)Цей режим використовує Firebase Admin виключно для push:
AUTH_FIREBASE_*. SDK потрібен для генерації FCM-токенів та надсилання push-повідомлень.getAdminDb() повертає імітацію Firestore (ніколи не підключається до Firebase). Це контролюється тим, що shouldUseFirebaseForDatabase() повертає false.getAdminAuth() також повертає імітацію. Auth.js v5 обробляє всю автентифікацію.public/firebase-messaging-sw.js) працює в браузері та завантажує Firebase compat SDK з CDN. На нього не впливає режим бекенду — він завжди підключається до Firebase для push-підписки.fcm_tokens PostgreSQL через Server Action upsertFcmToken.AUTH_FIREBASE_* та NEXT_PUBLIC_FIREBASE_*, ініціалізація Firebase Admin повністю пропускається. PostgreSQL продовжує працювати.| Аспект | k8s-postgres-fcm | supabase-fcm |
|---|---|---|
| Хост PostgreSQL | Ваш K8s або локальний | Хмара Supabase |
| SSL | Необов'язково (DB_SSL=false за замовчуванням) | Обов'язково (DB_SSL=true) |
| Пул з'єднань | DB_POOL_SIZE=20 за замовчуванням | DB_POOL_SIZE=10 за замовчуванням |
| Облікові дані | DB_USER / DB_PASSWORD | SUPABASE_URL / SUPABASE_SERVICE_KEY |
| Ціль розгортання | K8s, Docker, bare metal | Платформа Supabase |
| Маніфести K8s | Повний набір у k8s/ | Не застосовно |
| Тунельний хаб | k8s-postgres або memory | memory |
| PostGIS | DB_ENABLE_POSTGIS=true | Потребує додатку Supabase PostGIS |
| Варіант використання | Повний контроль інфраструктури | Керований хмарний PostgreSQL |
Обидва режими використовують той самий клас PostgreSQLAdapter. Єдина відмінність — хто хостить екземпляр PostgreSQL та як надаються параметри підключення.
# Вибір режиму (ОБОВ'ЯЗКОВО — платформа не запуститься без нього)
DB_BACKEND_MODE=k8s-postgres-fcm
# Підключення до PostgreSQL
DB_HOST=localhost # K8s: postgres.<namespace>.svc.cluster.local
DB_PORT=5432
DB_NAME=ring_platform
DB_USER=ring_user
DB_PASSWORD=ring_password_2024
# Налаштування пулу з'єднань (необов'язково, показано зі значеннями за замовчуванням)
DB_POOL_SIZE=20 # Максимум з'єднань у пулі
DB_TIMEOUT=30000 # Час очікування з'єднання в мс
DB_RETRIES=3 # Спроби повтору при збої
DB_SSL=false # Встановіть true для продакшну та Supabase
# PostGIS (необов'язково)
DB_ENABLE_POSTGIS=false # Увімкнути просторові розширення
┌─────────────────────────────────────────────────┐
│ Next.js 16 App │
│ │
│ ┌──────────────────────┐ ┌──────────────────┐ │
│ │ PostgreSQLAdapter │ │ Firebase Admin │ │
│ │ (all CRUD / query) │ │ (FCM + Apple │ │
│ │ │ │ Push only) │ │
│ │ • Auth.js tables │ │ │ │
│ │ • Entities │ │ • getAdminDb() │ │
│ │ • Store products │ │ → mock (never │ │
│ │ • Conversations │ │ connects) │ │
│ │ • FCM tokens │ │ • getAdminAuth() │ │
│ │ • All business data │ │ → mock │ │
│ └────────┬─────────────┘ └────────┬─────────┘ │
│ │ │ │
└───────────┼─────────────────────────┼────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ PostgreSQL 16 │ │ Firebase FCM │
│ + PostGIS │ │ (push only) │
│ StatefulSet │ └──────────────────┘
│ (k8s or local) │
└─────────────────┘
# Firebase Admin SDK (серверний) — потрібен для FCM
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 client SDK (браузерний) — для реєстрації push через service worker
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_VAPID_KEY=your_vapid_key
const poolConfig = {
host: config.connection.host || 'localhost',
port: config.connection.port || 5432,
database: config.connection.database || 'ring_platform',
user: config.connection.username || 'ring_user',
password: config.connection.password || 'ring_dev_password',
max: config.options.poolSize || 20,
idleTimeoutMillis: config.options.timeout || 30000,
connectionTimeoutMillis: config.options.timeout || 2000,
}
// Операції з окремими документами
await db.findById('users', userId)
await db.create('users', { name: 'Аліса', email: 'alice@example.com' })
await db.update('users', userId, { name: 'Боб' })
await db.delete('users', userId)
// Запити з фільтрами, сортуванням, пагінацією
await db.query({
collection: 'entities',
filters: [{ field: 'status', operator: 'eq', value: 'active' }],
orderBy: [{ field: 'createdAt', direction: 'desc' }],
pagination: { limit: 20, offset: 0 },
})
// Транзакції
await db.transaction(async (txn) => {
const order = await txn.create('orders', { total: 100 })
await txn.update('stock', productId, { quantity: 5 })
})
TUNNEL_HUB_MODE=k8s-postgres
apiVersion: v1
kind: Namespace
metadata:
name: ring-platform-org
labels:
app.kubernetes.io/name: ring-platform-org
app.kubernetes.io/part-of: ringdom
apiVersion: v1
kind: Secret
metadata:
name: ring-platform-org-secrets
type: Opaque
stringData:
DB_PASSWORD: <value>
AUTH_FIREBASE_PROJECT_ID: <value>
AUTH_FIREBASE_CLIENT_EMAIL: <value>
AUTH_FIREBASE_PRIVATE_KEY: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
apiVersion: v1
kind: ConfigMap
metadata:
name: ring-platform-org-config
data:
DB_BACKEND_MODE: k8s-postgres-fcm
DB_HOST: postgres.ring-platform-org.svc.cluster.local
DB_PORT: "5432"
DB_NAME: ring_platform
DB_USER: ring_user
DB_POOL_SIZE: "20"
DB_TIMEOUT: "30000"
DB_RETRIES: "3"
DB_SSL: "false"
NEXT_PUBLIC_FIREBASE_PROJECT_ID: <value>
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/postgres.yaml
kubectl apply -f k8s/secrets.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
kubectl apply -f k8s/pvc-local-file-storage.yaml
docker compose up -dDB_NAME | Так | ring_platform | Назва бази даних |
DB_USER | Так | ring_user | Користувач бази даних |
DB_PASSWORD | Так | ring_dev_password | Пароль бази даних |
DB_POOL_SIZE | Ні | 20 | Максимальний розмір пулу з'єднань |
DB_TIMEOUT | Ні | 30000 | Час очікування з'єднання (мс) |
DB_RETRIES | Ні | 3 | Кількість повторних спроб |
DB_SSL | Ні | false | Увімкнути TLS для PostgreSQL |
DB_ENABLE_POSTGIS | Ні | false | Увімкнути розширення PostGIS |
Потрібні лише якщо вам потрібні push-сповіщення. Без них платформа працює виключно з PostgreSQL.
PostgreSQLAdapter будує конфігурацію пулу зі змінних середовища. Джерело: lib/database/adapters/PostgreSQLAdapter.ts:
Адаптер використовує гібридну JSONB-схему: більшість таблиць зберігають бізнес-дані в стовпці data типу JSONB разом із загальними стовпцями id, created_at, updated_at. Таблиці з повністю нормалізованими схемами (наприклад, vendor_applications, vendor_profiles») перераховують усі стовпці в мапі fieldMappings, тому конструктор запитів використовує прямі посилання на стовпці замість data->>'field'`.
Адаптер підтримує повний контракт DatabaseService:
Запустіть PostgreSQL (два варіанти):
# Варіант A: Docker (рекомендовано)
docker run -d --name ring-postgres-dev \
-e POSTGRES_USER=ring_user \
-e POSTGRES_PASSWORD=ring_password_2024 \
-e POSTGRES_DB=ring_platform \
-p 5432:5432 \
postgres:16-alpine
# Варіант B: Homebrew (macOS)
brew install postgresql@16
brew services start postgresql@16
createdb ring_platformСтворіть .env.local з шаблону та встановіть:
DB_BACKEND_MODE=k8s-postgres-fcm
DB_HOST=localhost
DB_PORT=5432
DB_NAME=ring_platform
DB_USER=ring_user
DB_PASSWORD=ring_password_2024Застосуйте схему:
psql -h localhost -U ring_user -d ring_platform -f data/schema.sqlЗапустіть сервер розробки:
npm run devПлатформа перевіряє DB_BACKEND_MODE під час запуску та логує конфігурацію бекенду. Ви маєте побачити Database: PostgreSQL у логах запуску.
Тунельний хаб для функцій реального часу:
У локальній розробці тунель за замовчуванням використовує in-memory хаб. Для поведінки, наближеної до продакшну, встановіть:
Це зберігає стан тунелю в PostgreSQL замість пам'яті процесу.
Кожен клон ring отримує власний простір імен: k8s/namespace.yaml
Маніфест k8s/postgres.yaml розгортає PostgreSQL 16 (образ: postgres:15) як Deployment з одним реплікою та сервісом ClusterIP:
postgres-config) — POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DBplatform_org_user) та надає привілеїuuid-ossp та btree_gin увімкнено за замовчуванням/data/postgres-<clone-name>postgres.<namespace>.svc.cluster.localСекрети (k8s/secrets.yaml — Opaque, stringData):
ConfigMap (k8s/secrets.yaml — секція ConfigMap):
Важливо: DB_PASSWORD має бути в Secret, а не в ConfigMap. Розгортання посилається на нього через secretKeyRef.
k8s/deployment.yaml — застосунок Next.js 16 з:
ghcr.io/connectplatform/ring:v<version>-<clone>-amd64/api/health)pvc-local-file-storage.yaml — 20Gi)k8s/ingress.yaml — контролер ingress nginx з:
letsencrypt-prod)websocket-services)Для локальних або продакшн-розгортань на основі Docker docker-compose.yml підтримує режим k8s-postgres-fcm. Переконайтеся, що ваш .env містить DB_BACKEND_MODE=k8s-postgres-fcm та змінні для підключення PostgreSQL:
Примітка: Стандартний docker-compose.yml не включає сервіс PostgreSQL. Ви повинні або:
docker run postgres:16-alpine)postgres до вашого перевизначення (docker/docker-compose.dev.yml)Цей режим використовує Firebase Admin виключно для push:
AUTH_FIREBASE_*. SDK потрібен для генерації FCM-токенів та надсилання push-повідомлень.getAdminDb() повертає імітацію Firestore (ніколи не підключається до Firebase). Це контролюється тим, що shouldUseFirebaseForDatabase() повертає false.getAdminAuth() також повертає імітацію. Auth.js v5 обробляє всю автентифікацію.public/firebase-messaging-sw.js) працює в браузері та завантажує Firebase compat SDK з CDN. На нього не впливає режим бекенду — він завжди підключається до Firebase для push-підписки.fcm_tokens PostgreSQL через Server Action upsertFcmToken.AUTH_FIREBASE_* та NEXT_PUBLIC_FIREBASE_*, ініціалізація Firebase Admin повністю пропускається. PostgreSQL продовжує працювати.| Аспект | k8s-postgres-fcm | supabase-fcm |
|---|---|---|
| Хост PostgreSQL | Ваш K8s або локальний | Хмара Supabase |
| SSL | Необов'язково (DB_SSL=false за замовчуванням) | Обов'язково (DB_SSL=true) |
| Пул з'єднань | DB_POOL_SIZE=20 за замовчуванням | DB_POOL_SIZE=10 за замовчуванням |
| Облікові дані | DB_USER / DB_PASSWORD | SUPABASE_URL / SUPABASE_SERVICE_KEY |
| Ціль розгортання | K8s, Docker, bare metal | Платформа Supabase |
| Маніфести K8s | Повний набір у k8s/ | Не застосовно |
| Тунельний хаб | k8s-postgres або memory | memory |
| PostGIS | DB_ENABLE_POSTGIS=true | Потребує додатку Supabase PostGIS |
| Варіант використання | Повний контроль інфраструктури | Керований хмарний PostgreSQL |
Обидва режими використовують той самий клас PostgreSQLAdapter. Єдина відмінність — хто хостить екземпляр PostgreSQL та як надаються параметри підключення.
# Вибір режиму (ОБОВ'ЯЗКОВО — платформа не запуститься без нього)
DB_BACKEND_MODE=k8s-postgres-fcm
# Підключення до PostgreSQL
DB_HOST=localhost # K8s: postgres.<namespace>.svc.cluster.local
DB_PORT=5432
DB_NAME=ring_platform
DB_USER=ring_user
DB_PASSWORD=ring_password_2024
# Налаштування пулу з'єднань (необов'язково, показано зі значеннями за замовчуванням)
DB_POOL_SIZE=20 # Максимум з'єднань у пулі
DB_TIMEOUT=30000 # Час очікування з'єднання в мс
DB_RETRIES=3 # Спроби повтору при збої
DB_SSL=false # Встановіть true для продакшну та Supabase
# PostGIS (необов'язково)
DB_ENABLE_POSTGIS=false # Увімкнути просторові розширення
┌─────────────────────────────────────────────────┐
│ Next.js 16 App │
│ │
│ ┌──────────────────────┐ ┌──────────────────┐ │
│ │ PostgreSQLAdapter │ │ Firebase Admin │ │
│ │ (all CRUD / query) │ │ (FCM + Apple │ │
│ │ │ │ Push only) │ │
│ │ • Auth.js tables │ │ │ │
│ │ • Entities │ │ • getAdminDb() │ │
│ │ • Store products │ │ → mock (never │ │
│ │ • Conversations │ │ connects) │ │
│ │ • FCM tokens │ │ • getAdminAuth() │ │
│ │ • All business data │ │ → mock │ │
│ └────────┬─────────────┘ └────────┬─────────┘ │
│ │ │ │
└───────────┼─────────────────────────┼────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ PostgreSQL 16 │ │ Firebase FCM │
│ + PostGIS │ │ (push only) │
│ StatefulSet │ └──────────────────┘
│ (k8s or local) │
└─────────────────┘
# Firebase Admin SDK (серверний) — потрібен для FCM
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 client SDK (браузерний) — для реєстрації push через service worker
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_VAPID_KEY=your_vapid_key
const poolConfig = {
host: config.connection.host || 'localhost',
port: config.connection.port || 5432,
database: config.connection.database || 'ring_platform',
user: config.connection.username || 'ring_user',
password: config.connection.password || 'ring_dev_password',
max: config.options.poolSize || 20,
idleTimeoutMillis: config.options.timeout || 30000,
connectionTimeoutMillis: config.options.timeout || 2000,
}
// Операції з окремими документами
await db.findById('users', userId)
await db.create('users', { name: 'Аліса', email: 'alice@example.com' })
await db.update('users', userId, { name: 'Боб' })
await db.delete('users', userId)
// Запити з фільтрами, сортуванням, пагінацією
await db.query({
collection: 'entities',
filters: [{ field: 'status', operator: 'eq', value: 'active' }],
orderBy: [{ field: 'createdAt', direction: 'desc' }],
pagination: { limit: 20, offset: 0 },
})
// Транзакції
await db.transaction(async (txn) => {
const order = await txn.create('orders', { total: 100 })
await txn.update('stock', productId, { quantity: 5 })
})
TUNNEL_HUB_MODE=k8s-postgres
apiVersion: v1
kind: Namespace
metadata:
name: ring-platform-org
labels:
app.kubernetes.io/name: ring-platform-org
app.kubernetes.io/part-of: ringdom
apiVersion: v1
kind: Secret
metadata:
name: ring-platform-org-secrets
type: Opaque
stringData:
DB_PASSWORD: <value>
AUTH_FIREBASE_PROJECT_ID: <value>
AUTH_FIREBASE_CLIENT_EMAIL: <value>
AUTH_FIREBASE_PRIVATE_KEY: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
apiVersion: v1
kind: ConfigMap
metadata:
name: ring-platform-org-config
data:
DB_BACKEND_MODE: k8s-postgres-fcm
DB_HOST: postgres.ring-platform-org.svc.cluster.local
DB_PORT: "5432"
DB_NAME: ring_platform
DB_USER: ring_user
DB_POOL_SIZE: "20"
DB_TIMEOUT: "30000"
DB_RETRIES: "3"
DB_SSL: "false"
NEXT_PUBLIC_FIREBASE_PROJECT_ID: <value>
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/postgres.yaml
kubectl apply -f k8s/secrets.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
kubectl apply -f k8s/pvc-local-file-storage.yaml
docker compose up -d