Files
narlblog/backend/src/models.rs
T
2026-05-14 18:34:07 +02:00

214 lines
5.6 KiB
Rust

use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Clone)]
pub struct ContactLink {
pub kind: String,
pub label: String,
pub value: String,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct SiteConfig {
pub title: String,
pub subtitle: String,
pub welcome_title: String,
pub welcome_subtitle: String,
pub footer: String,
pub favicon: String,
pub theme: String,
pub custom_css: String,
#[serde(default)]
pub contact_intro: String,
#[serde(default)]
pub contact_links: Vec<ContactLink>,
}
impl Default for SiteConfig {
fn default() -> Self {
Self {
title: "Ela's Atelier".to_string(),
subtitle: "Works on paper, canvas, and elsewhere".to_string(),
welcome_title: "Works on view".to_string(),
welcome_subtitle:
"An ongoing arrangement of pieces, sketches, and stray observations."
.to_string(),
footer: "Hand-arranged with care".to_string(),
favicon: "/favicon.svg".to_string(),
theme: "salon".to_string(),
custom_css: "".to_string(),
contact_intro: "".to_string(),
contact_links: Vec::new(),
}
}
}
#[derive(Deserialize, Default)]
pub struct SiteConfigPatch {
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub subtitle: Option<String>,
#[serde(default)]
pub welcome_title: Option<String>,
#[serde(default)]
pub welcome_subtitle: Option<String>,
#[serde(default)]
pub footer: Option<String>,
#[serde(default)]
pub favicon: Option<String>,
#[serde(default)]
pub theme: Option<String>,
#[serde(default)]
pub custom_css: Option<String>,
#[serde(default)]
pub contact_intro: Option<String>,
#[serde(default)]
pub contact_links: Option<Vec<ContactLink>>,
}
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct PostMeta {
pub date: NaiveDate,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(default)]
pub draft: bool,
}
#[derive(Serialize, Clone, Copy)]
pub struct ImageDim {
pub w: u32,
pub h: u32,
}
#[derive(Serialize, Clone)]
pub struct CoverImage {
pub url: String,
pub alt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub w: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub h: Option<u32>,
}
#[derive(Serialize, Clone)]
pub struct PostInfo {
pub slug: String,
pub date: NaiveDate,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
pub tags: Vec<String>,
pub draft: bool,
pub reading_time: u32,
pub excerpt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub cover_image: Option<CoverImage>,
pub image_count: u32,
}
#[derive(Serialize, Clone)]
pub struct PostNeighbor {
pub slug: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
}
#[derive(Serialize)]
pub struct PostDetail {
pub slug: String,
pub date: NaiveDate,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
pub tags: Vec<String>,
pub draft: bool,
pub reading_time: u32,
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub cover_image: Option<CoverImage>,
pub image_count: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub prev: Option<PostNeighbor>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next: Option<PostNeighbor>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub dimensions: HashMap<String, ImageDim>,
}
#[derive(Deserialize)]
pub struct CreatePostRequest {
pub slug: String,
#[serde(default)]
pub old_slug: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub date: Option<NaiveDate>,
#[serde(default)]
pub summary: Option<String>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub draft: bool,
pub content: String,
}
#[derive(Deserialize)]
pub struct ContactSubmission {
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub email: Option<String>,
#[serde(default)]
pub subject: Option<String>,
pub message: String,
#[serde(default)]
pub website: Option<String>,
pub started_at: i64,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct Message {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
pub body: String,
pub received_at: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ip_hash: Option<String>,
}
#[derive(Serialize)]
pub struct ContactResponse {
pub ok: bool,
}
#[derive(Serialize)]
pub struct ErrorResponse {
pub error: String,
}
#[derive(Serialize)]
pub struct UploadResponse {
pub url: String,
}
#[derive(Serialize)]
pub struct FileInfo {
pub name: String,
pub url: String,
}