
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.
10 KiB
10 KiB
Frontend Migration Guide
Backend API Overview
The backend provides two API versions with smart timezone handling and proper URL generation:
API Versions
- V1 API (
/api/*
): Legacy compatibility, returns EST timezone, existing URL formats - V2 API (
/api/v2/*
): Modern API, returns UTC timestamps, client handles timezone conversion
Authentication
Login
POST /api/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "password"
}
Response:
{
"success": true,
"data": {
"token": "jwt_token_here",
"user": {
"id": "uuid",
"username": "admin"
}
}
}
Protected Routes
- Add header:
Authorization: Bearer {token}
- Admin routes are under
/api/admin/*
Bulletins API
List Bulletins
GET /api/bulletins?page=1&per_page=20&active_only=true
GET /api/v2/bulletins?page=1&per_page=20
Get Current Bulletin (≤ today's date)
GET /api/bulletins/current
GET /api/v2/bulletins/current
Get Next Bulletin (> today's date) - NEW!
GET /api/bulletins/next
GET /api/v2/bulletins/next
Get Bulletin by ID
GET /api/bulletins/{id}
GET /api/v2/bulletins/{id}
Create Bulletin (Admin)
POST /api/admin/bulletins
Authorization: Bearer {token}
Content-Type: application/json
{
"title": "Weekly Bulletin",
"date": "2025-08-02",
"url": "https://example.com",
"cover_image": null,
"sabbath_school": "Elder Smith",
"divine_worship": "Pastor Johnson",
"scripture_reading": "John 3:16",
"sunset": "7:45 PM",
"is_active": true
}
Update Bulletin (Admin)
PUT /api/admin/bulletins/{id}
Authorization: Bearer {token}
Content-Type: application/json
{...same fields as create...}
Delete Bulletin (Admin)
DELETE /api/admin/bulletins/{id}
Authorization: Bearer {token}
Events API
List Events
GET /api/events?page=1&per_page=20
GET /api/v2/events?page=1&per_page=20
Get Upcoming Events
GET /api/events/upcoming?limit=10
GET /api/v2/events/upcoming?limit=10
Get Featured Events
GET /api/events/featured?limit=5
GET /api/v2/events/featured?limit=5
Get Event by ID
GET /api/events/{id}
GET /api/v2/events/{id}
Submit Event (Public)
POST /api/events/submit
Content-Type: application/json
{
"title": "Prayer Meeting",
"description": "Weekly prayer meeting",
"start_time": "2025-08-02T19:00:00",
"end_time": "2025-08-02T20:00:00",
"location": "Fellowship Hall",
"location_url": "https://maps.google.com/...",
"category": "worship",
"is_featured": false,
"recurring_type": "weekly",
"bulletin_week": "2025-08-02",
"submitter_email": "user@example.com"
}
Admin Event Management
POST /api/admin/events # Create event
PUT /api/admin/events/{id} # Update event
DELETE /api/admin/events/{id} # Delete event
GET /api/admin/events/pending # List pending submissions
POST /api/admin/events/pending/{id}/approve # Approve pending
POST /api/admin/events/pending/{id}/reject # Reject pending
DELETE /api/admin/events/pending/{id} # Delete pending
Admin User Management
GET /api/admin/users # List all users
File Uploads (Admin)
Upload Bulletin PDF
POST /api/upload/bulletins/{id}/pdf
Authorization: Bearer {token}
Content-Type: multipart/form-data
file: bulletin.pdf
Upload Bulletin Cover Image
POST /api/upload/bulletins/{id}/cover
Authorization: Bearer {token}
Content-Type: multipart/form-data
file: cover.jpg
Upload Event Image
POST /api/upload/events/{id}/image
Authorization: Bearer {token}
Content-Type: multipart/form-data
file: event.jpg
Upload Response:
{
"success": true,
"file_path": "uploads/bulletins/uuid.pdf",
"pdf_path": "https://api.rockvilletollandsda.church/uploads/bulletins/uuid.pdf",
"message": "File uploaded successfully"
}
Note: Files are served at /uploads/*
path (handled by Caddy, not API)
Scripture Processing
The API now automatically processes scripture references in bulletin fields:
Automatic Scripture Lookup
- Input: Short reference like
"John 3:16 KJV"
- Output: Enhanced with full verse text:
"For God so loved the world... - John 3:16 KJV"
- Fallback: If no match found, returns original text unchanged
- Smart Detection: Already long texts (>50 chars) are left unchanged
How It Works
- When creating/updating bulletins,
scripture_reading
field is processed - Uses existing Bible verse database with fuzzy search
- Matches on both reference and partial text content
- Returns best match from database
Example API Response
{
"success": true,
"data": {
"id": "...",
"title": "Weekly Bulletin",
"scripture_reading": "For God so loved the world, that he gave his only begotten Son, that whosoever believeth in him should not perish, but have everlasting life. - John 3:16 KJV",
...
}
}
Other APIs
Bible Verses
GET /api/bible_verses/random
GET /api/bible_verses?page=1&per_page=20
GET /api/bible_verses/search?q=love&limit=10
GET /api/v2/bible_verses/random
GET /api/v2/bible_verses?page=1&per_page=20
GET /api/v2/bible_verses/search?q=love&limit=10
Contact Form
POST /api/contact
POST /api/v2/contact
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com",
"subject": "Question",
"message": "Hello..."
}
Schedule
GET /api/schedule?date=2025-08-02
GET /api/conference-data
GET /api/v2/schedule?date=2025-08-02
GET /api/v2/conference-data
Admin Schedule Management
POST /api/admin/schedule # Create schedule
PUT /api/admin/schedule/{date} # Update schedule by date
DELETE /api/admin/schedule/{date} # Delete schedule by date
GET /api/admin/schedule # List all schedules
Sermons & Livestreams
GET /api/sermons
GET /api/livestreams
Configuration
GET /api/config # Public config
GET /api/admin/config # Admin config (protected)
Legacy Android App Support
GET /api/collections/rtsda_android/records # Legacy Android app update check
Debug Endpoints
GET /api/debug/jellyfin # Debug Jellyfin connectivity (development only)
Response Format
All responses follow this format:
{
"success": true,
"data": {...},
"message": "Optional message"
}
Paginated responses:
{
"success": true,
"data": {
"items": [...],
"total": 150,
"page": 1,
"per_page": 20,
"total_pages": 8
}
}
Error responses:
{
"success": false,
"message": "Error description"
}
Timezone Handling
V1 API (Legacy)
- Input: Accepts times in any format
- Output: Converts all timestamps to EST timezone
- Use case: Existing clients that expect EST times
V2 API (Modern)
- Input: Expects UTC timestamps with timezone info when needed
- Output: Returns UTC timestamps
- Client responsibility: Convert to local timezone for display
V2 Timezone Example:
{
"start_time": "2025-08-02T23:00:00Z",
"timezone_info": {
"utc": "2025-08-02T23:00:00Z",
"local_display": "2025-08-02T19:00:00-04:00"
}
}
Frontend Migration Strategy
Phase 1: Update Shared Rust Crate
- Add V2 API models with UTC timestamp handling
- Keep V1 models for backward compatibility
- Add timezone conversion utilities
- Update HTTP client to handle both API versions
Phase 2: Client-by-Client Migration
- Web Admin Panel: Migrate to V2 API first
- Mobile App: Update to use new bulletin endpoints (
/next
) - Website: Gradually migrate public endpoints
- Keep V1 for old clients until all are updated
Phase 3: New Features
- Use V2 API only for new features
- Proper UTC handling from day one
- Client-side timezone conversion
Breaking Changes to Watch For
URL Structure
- Old: Some inconsistent URL patterns
- New: Consistent
/api/v2/*
structure - Files: Always served at
/uploads/*
(via Caddy)
Timestamp Format
- V1: Mixed timezone handling, EST output
- V2: Consistent UTC timestamps
- Migration: Update date parsing/formatting code
Response Fields
- V2 may have additional fields for timezone info
- V1 fields remain unchanged for compatibility
- New endpoints (like
/next
) available in both versions
Authentication
- Same JWT tokens work for both API versions
- Admin routes use same authorization header
- No changes needed to auth flow
Implementation Notes
Error Handling
// Example error handling in shared crate
match api_client.get_current_bulletin().await {
Ok(response) if response.success => {
// Handle response.data
},
Ok(response) => {
// Handle API error: response.message
},
Err(e) => {
// Handle network/parsing error
}
}
Timezone Conversion (V2)
// Example timezone handling
fn convert_utc_to_local(utc_time: &str, timezone: &str) -> Result<String> {
let utc = DateTime::parse_from_rfc3339(utc_time)?;
let local_tz: Tz = timezone.parse()?;
Ok(utc.with_timezone(&local_tz).to_string())
}
File Upload
// Example multipart upload
let form = multipart::Form::new()
.file("file", path_to_file)?;
let response = client
.post(&format!("{}/api/upload/bulletins/{}/pdf", base_url, bulletin_id))
.bearer_auth(&token)
.multipart(form)
.send()
.await?;
Testing Endpoints
Development
- API Base:
http://localhost:3002
- Files:
http://localhost:3002/uploads/*
Production
- API Base:
https://api.rockvilletollandsda.church
- Files:
https://api.rockvilletollandsda.church/uploads/*
Health Check
GET /api/config
Should return basic configuration without authentication.