
Complete church management system with bulletin management, media processing, live streaming integration, and web interface. Includes authentication, email notifications, database migrations, and comprehensive test suite.
315 lines
10 KiB
Bash
Executable file
315 lines
10 KiB
Bash
Executable file
#!/bin/bash
|
|
# PocketBase to PostgreSQL Migration Script
|
|
set -e
|
|
|
|
echo "🚀 Migrating PocketBase data to PostgreSQL..."
|
|
|
|
# Configuration
|
|
POCKETBASE_URL="http://localhost:8090" # Adjust if different
|
|
POSTGRES_URL="$DATABASE_URL"
|
|
MIGRATION_DIR="/tmp/pb_migration"
|
|
API_URL="https://api.rockvilletollandsda.church"
|
|
|
|
# Create migration directory
|
|
mkdir -p "$MIGRATION_DIR"
|
|
cd "$MIGRATION_DIR"
|
|
|
|
echo "📦 Step 1: Export data from PocketBase..."
|
|
|
|
# Function to export PocketBase collection data
|
|
export_collection() {
|
|
local collection=$1
|
|
echo " Exporting $collection..."
|
|
|
|
# Get all records from collection (adjust perPage if you have many records)
|
|
curl -s "${POCKETBASE_URL}/api/collections/${collection}/records?perPage=500" \
|
|
-o "${collection}.json"
|
|
|
|
if [ $? -eq 0 ]; then
|
|
echo " ✅ Exported $(jq '.items | length' ${collection}.json) records from $collection"
|
|
else
|
|
echo " ❌ Failed to export $collection"
|
|
fi
|
|
}
|
|
|
|
# Export all collections
|
|
export_collection "bulletins"
|
|
export_collection "events"
|
|
export_collection "pending_events"
|
|
export_collection "config"
|
|
export_collection "bible_verses"
|
|
export_collection "Quarterly_Schedule"
|
|
export_collection "Offering_and_Sunset_Times_Schedule"
|
|
export_collection "rtsda_android"
|
|
|
|
echo "📥 Step 2: Transform and import data..."
|
|
|
|
# Create Python script for data transformation
|
|
cat > transform_data.py << 'EOF'
|
|
import json
|
|
import sys
|
|
import uuid
|
|
from datetime import datetime
|
|
import psycopg2
|
|
from psycopg2.extras import RealDictCursor
|
|
import os
|
|
|
|
# Database connection
|
|
conn = psycopg2.connect(os.environ['DATABASE_URL'])
|
|
cur = conn.cursor(cursor_factory=RealDictCursor)
|
|
|
|
def load_json(filename):
|
|
try:
|
|
with open(filename, 'r') as f:
|
|
data = json.load(f)
|
|
return data.get('items', [])
|
|
except FileNotFoundError:
|
|
print(f"⚠️ {filename} not found, skipping...")
|
|
return []
|
|
|
|
def convert_date(pb_date):
|
|
"""Convert PocketBase date to PostgreSQL format"""
|
|
if not pb_date:
|
|
return None
|
|
try:
|
|
# PocketBase uses ISO format
|
|
dt = datetime.fromisoformat(pb_date.replace('Z', '+00:00'))
|
|
return dt
|
|
except:
|
|
return None
|
|
|
|
def generate_uuid():
|
|
"""Generate PostgreSQL-compatible UUID"""
|
|
return str(uuid.uuid4())
|
|
|
|
print("🔄 Transforming bulletins...")
|
|
bulletins = load_json('bulletins.json')
|
|
for bulletin in bulletins:
|
|
cur.execute("""
|
|
INSERT INTO bulletins (id, title, date, url, pdf_url, is_active, pdf_file,
|
|
sabbath_school, divine_worship, scripture_reading, sunset,
|
|
cover_image, created_at, updated_at)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""", (
|
|
generate_uuid(),
|
|
bulletin.get('title'),
|
|
convert_date(bulletin.get('date')),
|
|
bulletin.get('url'),
|
|
bulletin.get('pdf_url'),
|
|
bulletin.get('is_active', True),
|
|
bulletin.get('pdf'),
|
|
bulletin.get('sabbath_school'),
|
|
bulletin.get('divine_worship'),
|
|
bulletin.get('scripture_reading'),
|
|
bulletin.get('sunset'),
|
|
bulletin.get('cover_image'),
|
|
convert_date(bulletin.get('created')),
|
|
convert_date(bulletin.get('updated'))
|
|
))
|
|
|
|
print("🔄 Transforming events...")
|
|
events = load_json('events.json')
|
|
for event in events:
|
|
cur.execute("""
|
|
INSERT INTO events (id, title, description, start_time, end_time, location,
|
|
location_url, image, thumbnail, category, is_featured,
|
|
recurring_type, approved_from, created_at, updated_at)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""", (
|
|
generate_uuid(),
|
|
event.get('title'),
|
|
event.get('description'),
|
|
convert_date(event.get('start_time')),
|
|
convert_date(event.get('end_time')),
|
|
event.get('location'),
|
|
event.get('location_url'),
|
|
event.get('image'),
|
|
event.get('thumbnail'),
|
|
event.get('category'),
|
|
event.get('is_featured', False),
|
|
event.get('reoccuring'), # Note: PB uses 'reoccuring', PG uses 'recurring_type'
|
|
event.get('approved_from'),
|
|
convert_date(event.get('created')),
|
|
convert_date(event.get('updated'))
|
|
))
|
|
|
|
print("🔄 Transforming pending events...")
|
|
pending_events = load_json('pending_events.json')
|
|
for event in pending_events:
|
|
cur.execute("""
|
|
INSERT INTO pending_events (id, title, description, start_time, end_time, location,
|
|
location_url, image, thumbnail, category, is_featured,
|
|
recurring_type, approval_status, submitted_at, bulletin_week,
|
|
admin_notes, submitter_email, email_sent, pending_email_sent,
|
|
rejection_email_sent, approval_email_sent, created_at, updated_at)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""", (
|
|
generate_uuid(),
|
|
event.get('title'),
|
|
event.get('description'),
|
|
convert_date(event.get('start_time')),
|
|
convert_date(event.get('end_time')),
|
|
event.get('location'),
|
|
event.get('location_url'),
|
|
event.get('image'),
|
|
event.get('thumbnail'),
|
|
event.get('category'),
|
|
event.get('is_featured', False),
|
|
event.get('reoccuring'),
|
|
event.get('approval_status', 'pending'),
|
|
convert_date(event.get('submitted_at')),
|
|
event.get('bulletin_week'),
|
|
event.get('admin_notes'),
|
|
event.get('submitter_email'),
|
|
event.get('email_sent', False),
|
|
event.get('pending_email_sent', False),
|
|
event.get('rejection_email_sent', False),
|
|
event.get('approval_email_sent', False),
|
|
convert_date(event.get('created')),
|
|
convert_date(event.get('updated'))
|
|
))
|
|
|
|
print("🔄 Transforming church config...")
|
|
configs = load_json('config.json')
|
|
for config in configs:
|
|
cur.execute("""
|
|
INSERT INTO church_config (id, church_name, contact_email, contact_phone,
|
|
church_address, po_box, google_maps_url, about_text,
|
|
api_keys, created_at, updated_at)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""", (
|
|
generate_uuid(),
|
|
config.get('church_name'),
|
|
config.get('contact_email'),
|
|
config.get('contact_phone'),
|
|
config.get('church_address'),
|
|
config.get('po_box'),
|
|
config.get('google_maps_url'),
|
|
config.get('about_text'),
|
|
json.dumps(config.get('api_key', {})),
|
|
convert_date(config.get('created')),
|
|
convert_date(config.get('updated'))
|
|
))
|
|
|
|
print("🔄 Transforming bible verses...")
|
|
verses = load_json('bible_verses.json')
|
|
for verse_record in verses:
|
|
cur.execute("""
|
|
INSERT INTO bible_verses (id, verses, created_at, updated_at)
|
|
VALUES (%s, %s, %s, %s)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""", (
|
|
generate_uuid(),
|
|
json.dumps(verse_record.get('verses', {})),
|
|
convert_date(verse_record.get('created')),
|
|
convert_date(verse_record.get('updated'))
|
|
))
|
|
|
|
print("🔄 Transforming schedules...")
|
|
# Quarterly schedules
|
|
quarterly = load_json('Quarterly_Schedule.json')
|
|
for schedule in quarterly:
|
|
cur.execute("""
|
|
INSERT INTO schedules (id, schedule_type, year, quarter, schedule_data, created_at, updated_at)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""", (
|
|
generate_uuid(),
|
|
'quarterly',
|
|
schedule.get('year'),
|
|
schedule.get('quarter'),
|
|
json.dumps(schedule.get('schedule_data', {})),
|
|
convert_date(schedule.get('created')),
|
|
convert_date(schedule.get('updated'))
|
|
))
|
|
|
|
# Offering and sunset schedules
|
|
offering = load_json('Offering_and_Sunset_Times_Schedule.json')
|
|
for schedule in offering:
|
|
cur.execute("""
|
|
INSERT INTO schedules (id, schedule_type, year, quarter, schedule_data, created_at, updated_at)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""", (
|
|
generate_uuid(),
|
|
'offering_sunset',
|
|
schedule.get('year'),
|
|
None,
|
|
json.dumps(schedule.get('schedule_data', {})),
|
|
convert_date(schedule.get('created')),
|
|
convert_date(schedule.get('updated'))
|
|
))
|
|
|
|
print("🔄 Transforming app versions...")
|
|
app_versions = load_json('rtsda_android.json')
|
|
for app in app_versions:
|
|
cur.execute("""
|
|
INSERT INTO app_versions (id, platform, version_name, version_code, download_url,
|
|
update_required, description, created_at, updated_at)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""", (
|
|
generate_uuid(),
|
|
'android',
|
|
app.get('version_name'),
|
|
app.get('version_code'),
|
|
None, # You'll need to set download URLs manually
|
|
app.get('update_required', False),
|
|
app.get('update_description'),
|
|
convert_date(app.get('created')),
|
|
convert_date(app.get('updated'))
|
|
))
|
|
|
|
# Commit all changes
|
|
conn.commit()
|
|
cur.close()
|
|
conn.close()
|
|
|
|
print("✅ Data transformation complete!")
|
|
EOF
|
|
|
|
# Install required Python packages
|
|
pip3 install psycopg2-binary > /dev/null 2>&1
|
|
|
|
# Run the transformation
|
|
python3 transform_data.py
|
|
|
|
echo "📊 Step 3: Verifying migration..."
|
|
|
|
# Check what was migrated
|
|
psql "$POSTGRES_URL" -c "
|
|
SELECT
|
|
'bulletins' as table_name, COUNT(*) as records FROM bulletins
|
|
UNION ALL SELECT
|
|
'events', COUNT(*) FROM events
|
|
UNION ALL SELECT
|
|
'pending_events', COUNT(*) FROM pending_events
|
|
UNION ALL SELECT
|
|
'church_config', COUNT(*) FROM church_config
|
|
UNION ALL SELECT
|
|
'bible_verses', COUNT(*) FROM bible_verses
|
|
UNION ALL SELECT
|
|
'schedules', COUNT(*) FROM schedules
|
|
UNION ALL SELECT
|
|
'app_versions', COUNT(*) FROM app_versions;
|
|
"
|
|
|
|
echo "🎉 Migration complete!"
|
|
echo ""
|
|
echo "📋 Next steps:"
|
|
echo "1. Verify data looks correct in PostgreSQL"
|
|
echo "2. Test API endpoints to ensure data is accessible"
|
|
echo "3. Update any file URLs that point to PocketBase"
|
|
echo "4. Shut down PocketBase once everything is working"
|
|
echo ""
|
|
echo "🧪 Test your migrated data:"
|
|
echo " curl $API_URL/api/bulletins"
|
|
echo " curl $API_URL/api/events"
|
|
|
|
# Cleanup
|
|
rm -rf "$MIGRATION_DIR"
|