docbox_database/models/
link_resolved_metadata.rs1use crate::{DbExecutor, DbResult};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use sqlx::{postgres::PgQueryResult, prelude::FromRow};
5
6#[derive(Debug, Clone, FromRow, Serialize)]
7pub struct LinkResolvedMetadata {
8 pub url: String,
10 #[sqlx(json)]
12 pub metadata: StoredResolvedWebsiteMetadata,
13 pub expires_at: DateTime<Utc>,
15}
16
17#[derive(Debug, Clone, Serialize, PartialEq, Eq, Deserialize)]
18pub struct StoredResolvedWebsiteMetadata {
19 pub title: Option<String>,
20 pub og_title: Option<String>,
21 pub og_description: Option<String>,
22 pub og_image: Option<String>,
23 pub best_favicon: Option<String>,
24}
25
26pub struct CreateLinkResolvedMetadata {
27 pub url: String,
28 pub metadata: StoredResolvedWebsiteMetadata,
29 pub expires_at: DateTime<Utc>,
30}
31
32impl LinkResolvedMetadata {
33 pub async fn create(
35 db: impl DbExecutor<'_>,
36 create: CreateLinkResolvedMetadata,
37 ) -> DbResult<()> {
38 let metadata = serde_json::to_value(&create.metadata)
39 .map_err(|error| sqlx::Error::Encode(error.into()))?;
40
41 sqlx::query(
42 r#"
43 INSERT INTO "docbox_links_resolved_metadata" ("url", "metadata", "expires_at")
44 VALUES ($1, $2, $3)
45 ON CONFLICT ("url") DO UPDATE
46 SET
47 "metadata" = EXCLUDED."metadata",
48 "expires_at" = EXCLUDED."expires_at"
49 "#,
50 )
51 .bind(create.url)
52 .bind(metadata)
53 .bind(create.expires_at)
54 .execute(db)
55 .await?;
56
57 Ok(())
58 }
59
60 pub async fn query(
62 db: impl DbExecutor<'_>,
63 url: &str,
64 ) -> DbResult<Option<LinkResolvedMetadata>> {
65 sqlx::query_as(r#"SELECT * FROM "docbox_links_resolved_metadata" WHERE "url" = $1"#)
66 .bind(url)
67 .fetch_optional(db)
68 .await
69 }
70
71 pub async fn delete_expired(
73 db: impl DbExecutor<'_>,
74 before: DateTime<Utc>,
75 ) -> DbResult<PgQueryResult> {
76 sqlx::query(r#"DELETE FROM "docbox_links_resolved_metadata" WHERE "expires_at" < $1"#)
77 .bind(before)
78 .execute(db)
79 .await
80 }
81}