Multi-Tenant Automation & Database Setup¶
Overview¶
GaveKort now has complete automation infrastructure for managing multi-tenant golf clubs. This guide covers:
- Service Account Setup - Google Cloud service account configuration
- Club Onboarding - Automated club creation and setup
- Data Seeding - Complete test data generation
- Database Verification - Field validation and integrity checks
Quick Start¶
1. Service Account Setup (One-time)¶
Prerequisites:
- Google Cloud Console access
- Project: gavekort-multitenant
Steps:
-
Navigate to Google Cloud Service Accounts
-
Reuse existing service account:
firebase-adminsdk-fbsvc -
Verify roles are assigned:
- ✅ Cloud Datastore User
- ✅ Firebase Admin SDK Administrator
- ✅ Firebase Authentication Admin
- ✅ Service Account Token Creator
-
Create JSON key:
- Go to Keys tab
- Click Add Key → Create new key
- Select JSON
- 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