use anyhow::Result; use sqlx::SqlitePool; use crate::models::{Activity, Plan, Relay, Tenant}; #[derive(Clone)] pub struct Query { pool: SqlitePool, } impl Query { pub fn new(pool: SqlitePool) -> Self { Self { pool } } pub async fn list_tenants(&self) -> Result> { let rows = sqlx::query_as::<_, Tenant>( "SELECT pubkey, nwc_url, nwc_error, created_at, stripe_customer_id, stripe_subscription_id, past_due_at FROM tenant ORDER BY pubkey", ) .fetch_all(&self.pool) .await?; Ok(rows) } pub async fn get_tenant(&self, pubkey: &str) -> Result> { let row = sqlx::query_as::<_, Tenant>( "SELECT pubkey, nwc_url, nwc_error, created_at, stripe_customer_id, stripe_subscription_id, past_due_at FROM tenant WHERE pubkey = ?", ) .bind(pubkey) .fetch_optional(&self.pool) .await?; Ok(row) } pub fn list_plans() -> Vec { vec![ Plan { id: "free".to_string(), name: "Free".to_string(), amount: 0, members: Some(10), blossom: false, livekit: false, stripe_price_id: None, }, Plan { id: "basic".to_string(), name: "Basic".to_string(), amount: 500, members: Some(100), blossom: true, livekit: true, stripe_price_id: Some(std::env::var("STRIPE_PRICE_BASIC").unwrap_or_default()), }, Plan { id: "growth".to_string(), name: "Growth".to_string(), amount: 2500, members: None, blossom: true, livekit: true, stripe_price_id: Some(std::env::var("STRIPE_PRICE_GROWTH").unwrap_or_default()), }, ] } pub fn get_plan(plan_id: &str) -> Option { Self::list_plans().into_iter().find(|p| p.id == plan_id) } pub fn is_paid_plan(plan_id: &str) -> bool { Self::get_plan(plan_id) .map(|p| p.id != "free") .unwrap_or(false) } pub async fn list_relays(&self) -> Result> { let rows = sqlx::query_as::<_, Relay>( "SELECT id, tenant, schema, subdomain, plan, stripe_subscription_item_id, status, sync_error, info_name, info_icon, info_description, policy_public_join, policy_strip_signatures, groups_enabled, management_enabled, blossom_enabled, livekit_enabled, push_enabled, synced FROM relay ORDER BY id", ) .fetch_all(&self.pool) .await?; Ok(rows) } pub async fn list_relays_with_sync_error(&self) -> Result> { let rows = sqlx::query_as::<_, Relay>( "SELECT id, tenant, schema, subdomain, plan, stripe_subscription_item_id, status, sync_error, info_name, info_icon, info_description, policy_public_join, policy_strip_signatures, groups_enabled, management_enabled, blossom_enabled, livekit_enabled, push_enabled, synced FROM relay WHERE TRIM(sync_error) != '' ORDER BY id", ) .fetch_all(&self.pool) .await?; Ok(rows) } pub async fn list_relays_for_tenant(&self, tenant_id: &str) -> Result> { let rows = sqlx::query_as::<_, Relay>( "SELECT id, tenant, schema, subdomain, plan, stripe_subscription_item_id, status, sync_error, info_name, info_icon, info_description, policy_public_join, policy_strip_signatures, groups_enabled, management_enabled, blossom_enabled, livekit_enabled, push_enabled, synced FROM relay WHERE tenant = ? ORDER BY id", ) .bind(tenant_id) .fetch_all(&self.pool) .await?; Ok(rows) } pub async fn get_relay(&self, id: &str) -> Result> { let row = sqlx::query_as::<_, Relay>( "SELECT id, tenant, schema, subdomain, plan, stripe_subscription_item_id, status, sync_error, info_name, info_icon, info_description, policy_public_join, policy_strip_signatures, groups_enabled, management_enabled, blossom_enabled, livekit_enabled, push_enabled, synced FROM relay WHERE id = ?", ) .bind(id) .fetch_optional(&self.pool) .await?; Ok(row) } pub async fn get_tenant_by_stripe_customer_id( &self, stripe_customer_id: &str, ) -> Result> { let row = sqlx::query_as::<_, Tenant>( "SELECT pubkey, nwc_url, nwc_error, created_at, stripe_customer_id, stripe_subscription_id, past_due_at FROM tenant WHERE stripe_customer_id = ?", ) .bind(stripe_customer_id) .fetch_optional(&self.pool) .await?; Ok(row) } pub async fn has_active_paid_relays(&self, tenant_id: &str) -> Result { let plans = sqlx::query_scalar::<_, String>( "SELECT plan FROM relay WHERE tenant = ? AND status = 'active'", ) .bind(tenant_id) .fetch_all(&self.pool) .await?; Ok(plans.into_iter().any(|plan| Self::is_paid_plan(&plan))) } pub async fn list_activity_for_relay(&self, relay_id: &str) -> Result> { let rows = sqlx::query_as::<_, Activity>( "SELECT id, tenant, created_at, activity_type, resource_type, resource_id FROM activity WHERE resource_type = 'relay' AND resource_id = ? ORDER BY created_at DESC, id DESC", ) .bind(relay_id) .fetch_all(&self.pool) .await?; Ok(rows) } }