use crate::{DbExecutor, DbResult};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{postgres::PgQueryResult, prelude::FromRow};
#[derive(Debug, Clone, FromRow, Serialize)]
pub struct LinkResolvedMetadata {
pub url: String,
#[sqlx(json)]
pub metadata: StoredResolvedWebsiteMetadata,
pub expires_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq, Deserialize)]
pub struct StoredResolvedWebsiteMetadata {
pub title: Option<String>,
pub og_title: Option<String>,
pub og_description: Option<String>,
pub og_image: Option<String>,
pub best_favicon: Option<String>,
}
pub struct CreateLinkResolvedMetadata {
pub url: String,
pub metadata: StoredResolvedWebsiteMetadata,
pub expires_at: DateTime<Utc>,
}
impl LinkResolvedMetadata {
pub async fn create(
db: impl DbExecutor<'_>,
create: CreateLinkResolvedMetadata,
) -> DbResult<()> {
let metadata = serde_json::to_value(&create.metadata)
.map_err(|error| sqlx::Error::Encode(error.into()))?;
sqlx::query(
r#"
INSERT INTO "docbox_links_resolved_metadata" ("url", "metadata", "expires_at")
VALUES ($1, $2, $3)
ON CONFLICT ("url") DO UPDATE
SET
"metadata" = EXCLUDED."metadata",
"expires_at" = EXCLUDED."expires_at"
"#,
)
.bind(create.url)
.bind(metadata)
.bind(create.expires_at)
.execute(db)
.await?;
Ok(())
}
pub async fn query(
db: impl DbExecutor<'_>,
url: &str,
) -> DbResult<Option<LinkResolvedMetadata>> {
sqlx::query_as(r#"SELECT * FROM "docbox_links_resolved_metadata" WHERE "url" = $1"#)
.bind(url)
.fetch_optional(db)
.await
}
pub async fn delete_expired(
db: impl DbExecutor<'_>,
before: DateTime<Utc>,
) -> DbResult<PgQueryResult> {
sqlx::query(r#"DELETE FROM "docbox_links_resolved_metadata" WHERE "expires_at" < $1"#)
.bind(before)
.execute(db)
.await
}
}