use anyhow::{Context, Result};
use sqlx::PgPool;
use std::collections::HashMap;
use uuid::Uuid;
use crate::db::models::CustomDomain;
pub async fn list_project_custom_domains(
pool: &PgPool,
project_id: Uuid,
) -> Result<Vec<CustomDomain>> {
let domains = sqlx::query_as!(
CustomDomain,
r#"
SELECT id, project_id, domain, is_primary, created_at, updated_at
FROM project_custom_domains
WHERE project_id = $1
ORDER BY domain ASC
"#,
project_id
)
.fetch_all(pool)
.await
.context("Failed to list project custom domains")?;
Ok(domains)
}
pub async fn get_custom_domain(
pool: &PgPool,
project_id: Uuid,
domain: &str,
) -> Result<Option<CustomDomain>> {
let domain = sqlx::query_as!(
CustomDomain,
r#"
SELECT id, project_id, domain, is_primary, created_at, updated_at
FROM project_custom_domains
WHERE project_id = $1 AND domain = $2
"#,
project_id,
domain
)
.fetch_optional(pool)
.await
.context("Failed to get custom domain")?;
Ok(domain)
}
pub async fn add_custom_domain(
pool: &PgPool,
project_id: Uuid,
domain: &str,
) -> Result<CustomDomain> {
let domain = sqlx::query_as!(
CustomDomain,
r#"
INSERT INTO project_custom_domains (project_id, domain)
VALUES ($1, $2)
RETURNING id, project_id, domain, is_primary, created_at, updated_at
"#,
project_id,
domain
)
.fetch_one(pool)
.await
.context("Failed to add custom domain")?;
Ok(domain)
}
pub async fn delete_custom_domain(pool: &PgPool, project_id: Uuid, domain: &str) -> Result<bool> {
let result = sqlx::query!(
r#"
DELETE FROM project_custom_domains
WHERE project_id = $1 AND domain = $2
"#,
project_id,
domain
)
.execute(pool)
.await
.context("Failed to delete custom domain")?;
Ok(result.rows_affected() > 0)
}
#[allow(dead_code)]
pub async fn get_custom_domains_batch(
pool: &PgPool,
project_ids: &[Uuid],
) -> Result<HashMap<Uuid, Vec<CustomDomain>>> {
let domains = sqlx::query_as!(
CustomDomain,
r#"
SELECT id, project_id, domain, is_primary, created_at, updated_at
FROM project_custom_domains
WHERE project_id = ANY($1)
ORDER BY project_id, domain ASC
"#,
project_ids
)
.fetch_all(pool)
.await
.context("Failed to fetch custom domains batch")?;
let mut map: HashMap<Uuid, Vec<CustomDomain>> = HashMap::new();
for domain in domains {
map.entry(domain.project_id).or_default().push(domain);
}
Ok(map)
}
pub async fn set_primary_domain(
pool: &PgPool,
project_id: Uuid,
domain: &str,
) -> Result<CustomDomain> {
let mut tx = pool.begin().await.context("Failed to begin transaction")?;
sqlx::query!(
r#"
UPDATE project_custom_domains
SET is_primary = false
WHERE project_id = $1 AND is_primary = true
"#,
project_id
)
.execute(&mut *tx)
.await
.context("Failed to unset existing primary domain")?;
let domain = sqlx::query_as!(
CustomDomain,
r#"
UPDATE project_custom_domains
SET is_primary = true
WHERE project_id = $1 AND domain = $2
RETURNING id, project_id, domain, is_primary, created_at, updated_at
"#,
project_id,
domain
)
.fetch_one(&mut *tx)
.await
.context("Failed to set primary domain")?;
tx.commit().await.context("Failed to commit transaction")?;
Ok(domain)
}
pub async fn unset_primary_domain(pool: &PgPool, project_id: Uuid, domain: &str) -> Result<bool> {
let result = sqlx::query!(
r#"
UPDATE project_custom_domains
SET is_primary = false
WHERE project_id = $1 AND domain = $2 AND is_primary = true
"#,
project_id,
domain
)
.execute(pool)
.await
.context("Failed to unset primary domain")?;
Ok(result.rows_affected() > 0)
}