mabel/src/content/post.rs

170 lines
3.8 KiB
Rust

use async_trait::async_trait;
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use sqlx::{types::Json, FromRow};
use uuid::Uuid;
use crate::cursor::{AsCursor, CursorList};
use super::Error;
#[derive(Deserialize, Serialize, FromRow)]
pub struct Post {
/// Post ID
pub id: Uuid,
/// Site ID
pub site: Uuid,
/// Author ID
pub author: Uuid,
/// URL-friendly short code for the post
pub slug: String,
/// Post title
pub title: String,
/// Post description (for SEO/content)
pub description: Option<String>,
/// Post tags (for internal search)
pub tags: Vec<String>,
/// Post blocks (content)
pub blocks: Json<Vec<PostBlock>>,
/// Collections the post belongs to
pub collections: Vec<Uuid>,
/// Is the post published?
pub published: bool,
/// Times
pub created_at: NaiveDateTime,
pub modified_at: Option<NaiveDateTime>,
pub deleted_at: Option<NaiveDateTime>,
}
#[derive(Deserialize, Serialize)]
#[serde(tag = "kind")]
pub enum PostBlock {
MarkupV1 {
/// Markup format (markdown, html, plain)
format: String,
/// Markup content (before rendering)
content: String,
},
GalleryV1 {
/// Images in the gallery
images: Vec<ImageElement>,
},
}
/// Picture inside a gallery
#[derive(Deserialize, Serialize)]
pub struct ImageElement {
/// URL of the picture
pub url: String,
/// Image width in pixels
pub width: i32,
/// Image height in pixels
pub height: i32,
/// List of thumbnails, if available
pub thumbnails: Vec<ImageElement>,
/// Optional caption to put near the image
pub caption: Option<String>,
}
/// Data to create a new post
#[derive(Deserialize)]
pub struct CreatePostData {
pub slug: String,
pub title: String,
pub description: Option<String>,
pub tags: Vec<String>,
pub blocks: Vec<PostBlock>,
pub collections: Vec<Uuid>,
pub published: bool,
}
/// Data to update a new post
#[derive(Deserialize)]
pub struct UpdatePostData {
pub slug: Option<String>,
pub title: Option<String>,
pub description: Option<String>,
pub tags: Option<Vec<String>>,
pub blocks: Option<Vec<PostBlock>>,
pub collections: Option<Vec<Uuid>>,
pub published: Option<bool>,
}
/// Post with site and author dereferenced for convenience
#[derive(Serialize)]
pub struct PostWithAuthor {
pub author_display_name: Option<String>,
pub author_username: String,
pub slug: String,
pub title: String,
pub description: Option<String>,
pub tags: Vec<String>,
pub blocks: Json<Vec<PostBlock>>,
pub created_at: NaiveDateTime,
pub modified_at: Option<NaiveDateTime>,
pub published: bool,
pub collections: Vec<Uuid>,
}
impl AsCursor<NaiveDateTime> for PostWithAuthor {
fn cursor(&self) -> NaiveDateTime {
self.created_at
}
}
#[async_trait]
pub trait PostRepository {
/// Get a post from its slug
async fn get_post_from_url(&self, site: &str, slug: &str) -> Result<PostWithAuthor, Error>;
/// List posts in a collection
async fn list_posts_in_collection(
&self,
site: &Uuid,
collection: &Uuid,
cursor: Option<NaiveDateTime>,
limit: i64,
) -> Result<CursorList<PostWithAuthor, NaiveDateTime>, Error>;
/// Create a new post
async fn create_post(
&self,
owner: &Uuid,
site: &Uuid,
data: CreatePostData,
) -> Result<(), Error>;
/// Update a post
async fn update_post(
&self,
owner: &Uuid,
site: &Uuid,
slug: &str,
data: UpdatePostData,
) -> Result<(), Error>;
/// Delete a post
async fn delete_post(
&self,
owner: &Uuid,
site: &Uuid,
name: &str,
soft_delete: bool,
) -> Result<(), Error>;
}