diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 0000000..1b8fc9e --- /dev/null +++ b/src/api.rs @@ -0,0 +1,527 @@ +use crate::{ + ChurchApiClient, ChurchCoreConfig, + models::{NewSchedule, ScheduleUpdate, NewBulletin, BulletinUpdate, NewEvent, EventUpdate}, +}; +use tokio::runtime::Runtime; +use std::sync::OnceLock; + +static CLIENT: OnceLock = OnceLock::new(); +static RT: OnceLock = OnceLock::new(); + +fn get_client() -> &'static ChurchApiClient { + CLIENT.get_or_init(|| { + let config = ChurchCoreConfig::default(); + ChurchApiClient::new(config).expect("Failed to create church client") + }) +} + +fn get_runtime() -> &'static Runtime { + RT.get_or_init(|| { + Runtime::new().expect("Failed to create async runtime") + }) +} + +// Configuration functions +pub fn get_church_name() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.church_name.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_contact_phone() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.contact_phone.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_contact_email() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.contact_email.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_church_address() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.church_address.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_church_physical_address() -> String { + get_church_address() +} + +pub fn get_church_po_box() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.po_box.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_mission_statement() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.mission_statement.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_facebook_url() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.facebook_url.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_youtube_url() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.youtube_url.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_instagram_url() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => config.instagram_url.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +pub fn get_stream_live_status() -> bool { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_stream_status()) { + Ok(status) => status.is_live, + Err(_) => false, + } +} + +pub fn get_livestream_url() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_live_stream()) { + Ok(stream) => stream.stream_title.unwrap_or_else(|| "".to_string()), + Err(e) => format!("Error: {}", e), + } +} + +// JSON API functions +pub fn fetch_events_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_upcoming_events(Some(50))) { + Ok(events) => { + // Format events with display formatting using existing Event methods + let formatted_events: Vec<_> = events.iter().map(|event| { + let mut event_json = serde_json::to_value(event).unwrap_or_default(); + + // Add formatted fields using Event's built-in methods + if let Some(obj) = event_json.as_object_mut() { + obj.insert("formatted_date".to_string(), serde_json::Value::String(event.formatted_date_range())); + obj.insert("formatted_time".to_string(), serde_json::Value::String(event.formatted_start_time())); + } + event_json + }).collect(); + + serde_json::to_string(&formatted_events).unwrap_or_else(|_| "[]".to_string()) + }, + Err(_) => "[]".to_string(), + } +} + +pub fn fetch_featured_events_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_featured_events_v2(Some(10))) { + Ok(events) => serde_json::to_string(&events).unwrap_or_else(|_| "[]".to_string()), + Err(_) => "[]".to_string(), + } +} + +pub fn fetch_sermons_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_recent_sermons(Some(20))) { + Ok(sermons) => serde_json::to_string(&sermons).unwrap_or_else(|_| "[]".to_string()), + Err(_) => "[]".to_string(), + } +} + +pub fn fetch_config_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_config()) { + Ok(config) => serde_json::to_string(&config).unwrap_or_else(|_| "{}".to_string()), + Err(_) => "{}".to_string(), + } +} + +pub fn fetch_random_bible_verse_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_random_verse()) { + Ok(verse) => serde_json::to_string(&verse).unwrap_or_else(|_| "{}".to_string()), + Err(_) => "{}".to_string(), + } +} + +pub fn fetch_bulletins_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_bulletins(true)) { + Ok(bulletins) => serde_json::to_string(&bulletins).unwrap_or_else(|_| "[]".to_string()), + Err(_) => "[]".to_string(), + } +} + +pub fn fetch_current_bulletin_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_current_bulletin()) { + Ok(Some(bulletin)) => serde_json::to_string(&bulletin).unwrap_or_else(|_| "{}".to_string()), + Ok(None) => "{}".to_string(), + Err(_) => "{}".to_string(), + } +} + +pub fn fetch_bible_verse_json(query: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_verse_by_reference(&query)) { + Ok(Some(verse)) => serde_json::to_string(&verse).unwrap_or_else(|_| "{}".to_string()), + Ok(None) => "{}".to_string(), + Err(_) => "{}".to_string(), + } +} + +pub fn fetch_livestream_archive_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_livestreams()) { + Ok(streams) => serde_json::to_string(&streams).unwrap_or_else(|_| "[]".to_string()), + Err(_) => "[]".to_string(), + } +} + +pub fn submit_contact_v2_json(name: String, email: String, subject: String, message: String, phone: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + let contact = crate::models::ContactForm::new(name, email, subject, message) + .with_phone(phone); + + match rt.block_on(client.submit_contact_form_v2(contact)) { + Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn validate_contact_form_json(form_json: String) -> String { + match serde_json::from_str::(&form_json) { + Ok(_) => serde_json::to_string(&crate::utils::ValidationResult::valid()).unwrap_or_else(|_| "{}".to_string()), + Err(_) => serde_json::to_string(&crate::utils::ValidationResult::invalid(vec!["Invalid JSON format".to_string()])).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn submit_event_json( + title: String, + description: String, + start_time: String, + end_time: String, + location: String, + location_url: Option, + category: String, + recurring_type: Option, + submitter_email: Option +) -> String { + let client = get_client(); + let rt = get_runtime(); + + let submission = crate::models::EventSubmission { + title, + description, + start_time, + end_time, + location, + location_url, + category, + recurring_type, + submitter_email: submitter_email.unwrap_or_else(|| "".to_string()), + is_featured: false, + bulletin_week: None, + image_data: None, + image_filename: None, + image_mime_type: None, + }; + + match rt.block_on(client.submit_event(submission)) { + Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn submit_event_with_image_json( + title: String, + description: String, + start_time: String, + end_time: String, + location: String, + location_url: Option, + category: String, + recurring_type: Option, + submitter_email: Option, + image_data: Option>, + image_filename: Option +) -> String { + let client = get_client(); + let rt = get_runtime(); + + let submission = crate::models::EventSubmission { + title, + description, + start_time, + end_time, + location, + location_url, + category, + recurring_type, + submitter_email: submitter_email.unwrap_or_else(|| "".to_string()), + is_featured: false, + bulletin_week: None, + image_data: image_data.clone(), + image_filename: image_filename.clone(), + image_mime_type: None, // Could be improved to detect from filename or data + }; + + // Convert image data to format expected by multipart method + let image_multipart = if let (Some(data), Some(filename)) = (image_data, image_filename) { + Some((data, filename)) + } else { + None + }; + + match rt.block_on(crate::client::events::submit_event_with_image(client, submission, image_multipart)) { + Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +// Admin functions +pub fn fetch_all_schedules_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.get_all_admin_schedules()) { + Ok(schedules) => serde_json::to_string(&schedules).unwrap_or_else(|_| "[]".to_string()), + Err(_) => "[]".to_string(), + } +} + +pub fn create_schedule_json(schedule_json: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match serde_json::from_str::(&schedule_json) { + Ok(schedule) => { + match rt.block_on(client.create_admin_schedule(schedule)) { + Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } + }, + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn update_schedule_json(date: String, update_json: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match serde_json::from_str::(&update_json) { + Ok(update) => { + match rt.block_on(client.update_admin_schedule(&date, update)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } + }, + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn delete_schedule_json(date: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.delete_admin_schedule(&date)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +// Admin Auth Functions +pub fn admin_login_json(email: String, password: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.admin_login(&email, &password)) { + Ok(token) => serde_json::to_string(&serde_json::json!({"success": true, "token": token})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn validate_admin_token_json(token: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(client.validate_admin_token(&token)) { + Ok(valid) => serde_json::to_string(&serde_json::json!({"success": true, "valid": valid})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +// Admin Events Functions +pub fn fetch_pending_events_json() -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(crate::client::admin::get_pending_events(client)) { + Ok(events) => serde_json::to_string(&events).unwrap_or_else(|_| "[]".to_string()), + Err(_) => "[]".to_string(), + } +} + +pub fn approve_pending_event_json(event_id: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(crate::client::admin::approve_pending_event(client, &event_id)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn reject_pending_event_json(event_id: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(crate::client::admin::reject_pending_event(client, &event_id)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn delete_pending_event_json(event_id: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(crate::client::admin::delete_pending_event(client, &event_id)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + + +pub fn update_admin_event_json(event_id: String, update_json: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match serde_json::from_str::(&update_json) { + Ok(update) => { + match rt.block_on(crate::client::admin::update_admin_event(client, &event_id, update)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } + }, + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn delete_admin_event_json(event_id: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(crate::client::admin::delete_admin_event(client, &event_id)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} + +// Admin Bulletins Functions +pub fn create_bulletin_json(bulletin_json: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match serde_json::from_str::(&bulletin_json) { + Ok(bulletin) => { + match rt.block_on(crate::client::admin::create_bulletin(client, bulletin)) { + Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } + }, + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn update_bulletin_json(bulletin_id: String, update_json: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match serde_json::from_str::(&update_json) { + Ok(update) => { + match rt.block_on(crate::client::admin::update_bulletin(client, &bulletin_id, update)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } + }, + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), + } +} + +pub fn delete_bulletin_json(bulletin_id: String) -> String { + let client = get_client(); + let rt = get_runtime(); + + match rt.block_on(crate::client::admin::delete_bulletin(client, &bulletin_id)) { + Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), + Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), + } +} \ No newline at end of file diff --git a/src/bin/test-date-submission.rs b/src/bin/test-date-submission.rs index 090076a..a6fe003 100644 --- a/src/bin/test-date-submission.rs +++ b/src/bin/test-date-submission.rs @@ -21,6 +21,9 @@ async fn main() { recurring_type: None, bulletin_week: None, submitter_email: "test@example.com".to_string(), + image_data: None, + image_filename: None, + image_mime_type: None, }; println!("Testing date validation:"); diff --git a/src/bin/test_consolidation.rs b/src/bin/test_consolidation.rs new file mode 100644 index 0000000..63545af --- /dev/null +++ b/src/bin/test_consolidation.rs @@ -0,0 +1,46 @@ +// Test to verify all consolidated functions are available +use church_core::{ + ChurchApiClient, ChurchCoreConfig, + // Test that API functions are exported + fetch_events_json, fetch_sermons_json, submit_event_json, + admin_login_json, validate_admin_token_json, + // Test that models have new fields + models::EventSubmission, +}; + +fn main() { + println!("✅ All imports successful!"); + + // Test EventSubmission has new image fields + let submission = EventSubmission { + title: "Test".to_string(), + description: "Test".to_string(), + start_time: "2025-01-01T10:00:00Z".to_string(), + end_time: "2025-01-01T11:00:00Z".to_string(), + location: "Test".to_string(), + location_url: None, + category: "Other".to_string(), + is_featured: false, + recurring_type: None, + bulletin_week: None, + submitter_email: "test@test.com".to_string(), + image_data: Some(vec![1, 2, 3, 4]), // NEW FIELD + image_filename: Some("test.jpg".to_string()), // NEW FIELD + image_mime_type: Some("image/jpeg".to_string()), // NEW FIELD + }; + + println!("✅ EventSubmission with image fields created successfully!"); + + // Test that we can create a client and that admin methods exist + let config = ChurchCoreConfig::default(); + let client = ChurchApiClient::new(config).expect("Failed to create client"); + println!("✅ ChurchApiClient created successfully!"); + + // Verify admin methods exist (just check they compile, don't actually call them) + let _has_admin_login = client.admin_login("test", "test"); + let _has_validate_token = client.validate_admin_token("test"); + let _has_submit_multipart = church_core::client::events::submit_event_with_image(&client, submission, None); + + println!("✅ All admin and multipart functions compile successfully!"); + println!("🎉 Consolidation test PASSED - all functions available!"); +} \ No newline at end of file diff --git a/src/client/events.rs b/src/client/events.rs index 0678941..aef35a2 100644 --- a/src/client/events.rs +++ b/src/client/events.rs @@ -189,4 +189,8 @@ pub async fn get_event_v2(client: &ChurchApiClient, id: &str) -> Result Result { client.post_api("/events/submit", &submission).await +} + +pub async fn submit_event_with_image(client: &ChurchApiClient, submission: EventSubmission, image_data: Option<(Vec, String)>) -> Result { + client.submit_event_multipart(&submission, image_data).await } \ No newline at end of file diff --git a/src/client/http.rs b/src/client/http.rs index 8dfc08b..be2c3cf 100644 --- a/src/client/http.rs +++ b/src/client/http.rs @@ -399,4 +399,60 @@ impl ChurchApiClient { Ok(cached_response) } + + pub(crate) async fn submit_event_multipart(&self, submission: &crate::models::EventSubmission, image_data: Option<(Vec, String)>) -> Result { + let url = self.build_url("/events/submit"); + + let mut form = reqwest::multipart::Form::new() + .text("title", submission.title.clone()) + .text("description", submission.description.clone()) + .text("start_time", submission.start_time.clone()) + .text("end_time", submission.end_time.clone()) + .text("location", submission.location.clone()) + .text("category", submission.category.clone()) + .text("submitter_email", submission.submitter_email.clone()); + + if let Some(ref location_url) = submission.location_url { + form = form.text("location_url", location_url.clone()); + } + + if let Some(ref recurring_type) = submission.recurring_type { + form = form.text("recurring_type", recurring_type.clone()); + } + + if let Some(ref bulletin_week) = submission.bulletin_week { + form = form.text("bulletin_week", bulletin_week.clone()); + } + + if let Some((image_bytes, filename)) = image_data { + let part = reqwest::multipart::Part::bytes(image_bytes) + .file_name(filename) + .mime_str("image/jpeg") // Default to JPEG, could be improved to detect actual type + .map_err(|e| crate::error::ChurchApiError::Internal(format!("Failed to create image part: {}", e)))?; + form = form.part("image", part); + } + + let request = self.client.post(&url).multipart(form); + let request = self.add_auth_header(request).await; + + let response = self.send_with_retry(request).await?; + + if !response.status().is_success() { + let status = response.status(); + let error_text = response.text().await?; + return Err(crate::error::ChurchApiError::Api(format!("HTTP {}: {}", status, error_text))); + } + + let result: crate::models::ApiResponse = response.json().await?; + + if result.success { + result.data.ok_or_else(|| { + crate::error::ChurchApiError::Api("Event submission succeeded but no ID returned".to_string()) + }) + } else { + Err(crate::error::ChurchApiError::Api( + result.error.unwrap_or_else(|| "Unknown error".to_string()) + )) + } + } } \ No newline at end of file diff --git a/src/client/mod.rs b/src/client/mod.rs index e8a1557..a9a0785 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -409,4 +409,42 @@ pub async fn get_livestreams(&self) -> Result> { pub async fn get_cache_stats(&self) -> (usize, usize) { (self.cache.len().await, self.config.max_cache_size) } + + // Admin Auth operations + pub async fn admin_login(&self, email: &str, password: &str) -> Result { + let url = self.build_url("/auth/login"); + let request_body = serde_json::json!({ + "email": email, + "password": password + }); + + let response = self.client + .post(&url) + .json(&request_body) + .send() + .await?; + + if response.status().is_success() { + let auth_response: serde_json::Value = response.json().await?; + if let Some(token) = auth_response.get("token").and_then(|t| t.as_str()) { + Ok(token.to_string()) + } else { + Err(crate::error::ChurchApiError::Api("No token in response".to_string())) + } + } else { + let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string()); + Err(crate::error::ChurchApiError::Api(error_text)) + } + } + + pub async fn validate_admin_token(&self, token: &str) -> Result { + let url = self.build_url("/admin/events/pending"); + let response = self.client + .get(&url) + .header("Authorization", format!("Bearer {}", token)) + .send() + .await?; + + Ok(response.status().is_success()) + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 29d00fd..c94bd73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,11 +5,13 @@ pub mod cache; pub mod utils; pub mod error; pub mod config; +pub mod api; pub use client::ChurchApiClient; pub use config::ChurchCoreConfig; pub use error::{ChurchApiError, Result}; pub use models::*; pub use cache::*; +pub use api::*; #[cfg(feature = "wasm")] pub mod wasm; diff --git a/src/models/event.rs b/src/models/event.rs index 1077346..1137ded 100644 --- a/src/models/event.rs +++ b/src/models/event.rs @@ -300,6 +300,12 @@ pub struct EventSubmission { #[serde(skip_serializing_if = "Option::is_none")] pub bulletin_week: Option, // Date string in YYYY-MM-DD format pub submitter_email: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub image_data: Option>, // Binary image data + #[serde(skip_serializing_if = "Option::is_none")] + pub image_filename: Option, // Original filename + #[serde(skip_serializing_if = "Option::is_none")] + pub image_mime_type: Option, // MIME type (e.g., "image/jpeg") } impl EventSubmission {