Skip to content

Multi-Tenant Automation & Database Setup

Overview

GaveKort now has complete automation infrastructure for managing multi-tenant golf clubs. This guide covers:

  1. Service Account Setup - Google Cloud service account configuration
  2. Club Onboarding - Automated club creation and setup
  3. Data Seeding - Complete test data generation
  4. Database Verification - Field validation and integrity checks

Quick Start

1. Service Account Setup (One-time)

Prerequisites: - Google Cloud Console access - Project: gavekort-multitenant

Steps:

  1. Navigate to Google Cloud Service Accounts

  2. Reuse existing service account: firebase-adminsdk-fbsvc

  3. Verify roles are assigned:

    • ✅ Cloud Datastore User
    • ✅ Firebase Admin SDK Administrator
    • ✅ Firebase Authentication Admin
    • ✅ Service Account Token Creator
  4. Create JSON key:

  5. Go to Keys tab
  6. Click Add KeyCreate new key
  7. Select JSON
  8. Save file to: secrets/firebase-service-account-key.json

2. Onboard Your First Club

# Run the club onboarding script
node scripts/onboard-club.js "Club Name" "admin@club.email" "InitialPassword123!"

# Example:
node scripts/onboard-club.js "Oslo Golf Club" "manager@oslo-golf.no" "WelcomePass123!"

What it creates: - ✅ Tenant document with configuration - ✅ Firebase Auth admin user - ✅ Global user profile - ✅ Tenant-scoped admin user - ✅ Configuration templates - ✅ Audit log entry

3. Seed Test Data (Optional)

# Create test data for a club
node scripts/seed-club-data.js "tenant-id" "admin-uid"

# Example (from onboarding output):
node scripts/seed-club-data.js "oslo-golf-club" "UID_FROM_ONBOARDING"

Creates: - 5 staff members - 10 members - 50 giftcards (various denominations) - Ledger transactions - Wallet projections

4. Verify Database Setup

# Verify all fields are correct
node scripts/verify-database.js "tenant-id"

# Example:
node scripts/verify-database.js "oslo-golf-club"

Scripts Reference

scripts/admin-setup.js

Purpose: Initialize Firebase Admin SDK with service account credentials

Usage:

const { admin, db, auth } = require('./scripts/admin-setup.js');

// Use directly in other scripts

Supports: - Environment variable: GOOGLE_APPLICATION_CREDENTIALS - Local file: secrets/firebase-service-account-key.json - Default: Automatic detection


scripts/onboard-club.js

Purpose: Automated club onboarding with complete setup

Usage:

node scripts/onboard-club.js "<Club Name>" "<Admin Email>" "<Password>"

Parameters: | Parameter | Type | Description | |-----------|------|-------------| | Club Name | string | Name of the golf club | | Admin Email | string | Email for club admin account | | Password | string | Initial password (user should change) |

Returns:

{
  "tenantId": "oslo-golf-club",
  "adminUid": "UUID_STRING",
  "adminEmail": "manager@oslo-golf.no",
  "created": "2025-12-29T08:00:00Z"
}

Creates: - Tenant document - Firebase Auth user - Global user profile - Tenant-scoped user - Configuration document - Audit log entry


scripts/seed-club-data.js

Purpose: Generate complete test data for a club

Usage:

node scripts/seed-club-data.js "tenant-id" "admin-uid"

Parameters: | Parameter | Type | Description | |-----------|------|-------------| | Tenant ID | string | Tenant ID (from onboarding) | | Admin UID | string | Admin user ID (from onboarding) |

Creates:

Category Count Details
Staff 5 STAFF role, can manage club data
Members 10 END_USER role, own wallet access
Products 4 Denominations: 500, 1000, 2000, 5000 NOK
Giftcards 50 Distributed among members
Transactions 50 ISSUE transactions for audit trail
Projections 10 Wallet balance calculations

Test Credentials:

Staff:
  johan@testgolf.no / StaffPass123!
  kristin@testgolf.no / StaffPass123!
  lars@testgolf.no / StaffPass123!
  maria@testgolf.no / StaffPass123!
  tor@testgolf.no / StaffPass123!

Members:
  member1@testgolf.no / MemberPass123!
  member2@testgolf.no / MemberPass123!
  ... (member3-10)

scripts/verify-database.js

Purpose: Validate all database fields and structure

Usage:

node scripts/verify-database.js "tenant-id"

Checks: - ✅ Collection existence - ✅ Required field presence - ✅ Data type validation - ✅ Enum value validation - ✅ Document count statistics

Output:

📊 VERIFICATION SUMMARY
✅ users: 16 documents checked
✅ wallet_items: 50 documents checked
✅ ledger_transactions: 50 documents checked
✅ products: 4 documents checked
✅ wallet_projections: 10 documents checked
✅ giftcard_codes: 50 documents checked

✅ ALL FIELDS VERIFIED SUCCESSFULLY!

Database Schema

Collections

Global Collections: - users/ - User accounts (admin + all roles) - admin_audit_log/ - Platform-level audit trail

Tenant Collections: - tenants/{tenantId}/ - Tenant configuration - tenants/{tenantId}/users/ - Tenant members - tenants/{tenantId}/wallet_items/ - Giftcards & tokens - tenants/{tenantId}/ledger_transactions/ - Transaction audit trail - tenants/{tenantId}/products/ - Product catalog - tenants/{tenantId}/wallet_projections/ - Balance summaries - tenants/{tenantId}/giftcard_codes/ - Claim codes - tenants/{tenantId}/configuration/ - Club settings - tenants/{tenantId}/audit_log/ - Club-level audit trail

Key Fields Verified

Users: - userId, email, displayName, role, createdAt, updatedAt, isBlocked

Wallet Items: - walletItemId, userId, type, productId, status, value, issuedAt, expiresAt

Ledger: - transactionId, userId, type, amount, status, actedBy, createdAt

Products: - productId, name, type, value, currency

Projections: - userId, totalGiftcardBalance, activeWalletItems, lastUpdatedAt


Security

Service Account Key Management

Best Practices: 1. Store key in secrets/ folder (protected by .gitignore) 2. Never commit key to version control 3. Rotate key quarterly 4. Use environment variables in CI/CD: GOOGLE_APPLICATION_CREDENTIALS 5. Delete old keys after rotation

Role-Based Access

Role Access Created By
ADMIN Own tenant + all operations onboard-club.js
STAFF Own tenant + manage data Requires ADMIN to create
END_USER Own data only Created during signup

Troubleshooting

Service Account Issues

Error: Cannot find module 'firebase-admin'

# Install dependencies at project root
npm install firebase-admin

Error: Missing credentials

# Set environment variable
$env:GOOGLE_APPLICATION_CREDENTIALS = "C:\path\to\secrets\firebase-service-account-key.json"

# Or place key in secrets/ folder (auto-detected)

Database Issues

Error: Firestore permission denied - Verify service account has correct roles in Google Cloud - Check Firestore security rules allow service account access - Verify custom claims are set correctly with setCustomUserClaims()

Data Not Appearing:

# Verify data was created
node scripts/verify-database.js "tenant-id"

# Check Firebase Console
# https://console.firebase.google.com/project/gavekort-multitenant/firestore

Example: Complete Club Setup

# 1. Onboard club
node scripts/onboard-club.js "Bergen Golf Club" "manager@bergengolf.no" "Welcome123!"

# Output:
# Tenant ID: bergen-golf-club
# Admin UID: abc123def456

# 2. Seed test data
node scripts/seed-club-data.js "bergen-golf-club" "abc123def456"

# 3. Verify setup
node scripts/verify-database.js "bergen-golf-club"

# 4. Test login
# Go to: https://gavekort-multitenant.web.app
# Login with: manager@bergengolf.no / Welcome123!

Deployment & CI/CD

GitHub Actions Integration

# .github/workflows/seed-data.yml
name: Seed Club Data

on:
  workflow_dispatch:
    inputs:
      club_name:
        description: 'Club name'
        required: true
      admin_email:
        description: 'Admin email'
        required: true

env:
  GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_KEY }}

jobs:
  seed:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm install
      - run: |
          node scripts/onboard-club.js "${{ inputs.club_name }}" "${{ inputs.admin_email }}" "GeneratedPass123!"

Support

For issues or questions: 1. Check troubleshooting section 2. Review script output and error messages 3. Check Firebase Console for data verification 4. Review Firestore security rules 5. Verify service account permissions


Last Updated: December 29, 2025
Status: ✅ Production Ready