192 lines
6.5 KiB
Rust
192 lines
6.5 KiB
Rust
use async_trait::async_trait;
|
|
use loco_rs::{
|
|
app::{AppContext, Initializer},
|
|
Result,
|
|
};
|
|
use sea_orm::{ActiveModelTrait, EntityTrait, IntoActiveModel, QueryOrder, Set};
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use crate::models::_entities::{comments, posts, site_settings};
|
|
use crate::services::content;
|
|
|
|
const FIXTURES_DIR: &str = "src/fixtures";
|
|
|
|
pub struct ContentSyncInitializer;
|
|
|
|
#[async_trait]
|
|
impl Initializer for ContentSyncInitializer {
|
|
fn name(&self) -> String {
|
|
"content-sync".to_string()
|
|
}
|
|
|
|
async fn before_run(&self, app_context: &AppContext) -> Result<()> {
|
|
sync_content(app_context, Path::new(FIXTURES_DIR)).await
|
|
}
|
|
}
|
|
|
|
async fn sync_content(ctx: &AppContext, base: &Path) -> Result<()> {
|
|
content::sync_markdown_posts(ctx).await?;
|
|
sync_site_settings(ctx, base).await?;
|
|
sync_comment_post_slugs(ctx, base).await?;
|
|
Ok(())
|
|
}
|
|
|
|
fn read_fixture_rows(base: &Path, file_name: &str) -> Vec<serde_json::Value> {
|
|
let path: PathBuf = base.join(file_name);
|
|
let seed_data = match std::fs::read_to_string(path) {
|
|
Ok(data) => data,
|
|
Err(_) => return vec![],
|
|
};
|
|
|
|
serde_yaml::from_str(&seed_data).unwrap_or_default()
|
|
}
|
|
|
|
fn as_optional_string(value: &serde_json::Value) -> Option<String> {
|
|
value.as_str().and_then(|item| {
|
|
let trimmed = item.trim();
|
|
if trimmed.is_empty() {
|
|
None
|
|
} else {
|
|
Some(trimmed.to_string())
|
|
}
|
|
})
|
|
}
|
|
|
|
fn is_blank(value: &Option<String>) -> bool {
|
|
value.as_deref().map(str::trim).unwrap_or("").is_empty()
|
|
}
|
|
|
|
async fn sync_site_settings(ctx: &AppContext, base: &Path) -> Result<()> {
|
|
let rows = read_fixture_rows(base, "site_settings.yaml");
|
|
let Some(seed) = rows.first() else {
|
|
return Ok(());
|
|
};
|
|
|
|
let tech_stack = seed["tech_stack"]
|
|
.as_array()
|
|
.map(|items| {
|
|
items
|
|
.iter()
|
|
.filter_map(|item| item.as_str())
|
|
.map(ToString::to_string)
|
|
.collect::<Vec<_>>()
|
|
})
|
|
.filter(|items| !items.is_empty())
|
|
.map(|items| serde_json::json!(items));
|
|
|
|
let existing = site_settings::Entity::find()
|
|
.order_by_asc(site_settings::Column::Id)
|
|
.one(&ctx.db)
|
|
.await?;
|
|
|
|
if let Some(existing) = existing {
|
|
let mut model = existing.clone().into_active_model();
|
|
|
|
if is_blank(&existing.site_name) {
|
|
model.site_name = Set(as_optional_string(&seed["site_name"]));
|
|
}
|
|
if is_blank(&existing.site_short_name) {
|
|
model.site_short_name = Set(as_optional_string(&seed["site_short_name"]));
|
|
}
|
|
if is_blank(&existing.site_url) {
|
|
model.site_url = Set(as_optional_string(&seed["site_url"]));
|
|
}
|
|
if is_blank(&existing.site_title) {
|
|
model.site_title = Set(as_optional_string(&seed["site_title"]));
|
|
}
|
|
if is_blank(&existing.site_description) {
|
|
model.site_description = Set(as_optional_string(&seed["site_description"]));
|
|
}
|
|
if is_blank(&existing.hero_title) {
|
|
model.hero_title = Set(as_optional_string(&seed["hero_title"]));
|
|
}
|
|
if is_blank(&existing.hero_subtitle) {
|
|
model.hero_subtitle = Set(as_optional_string(&seed["hero_subtitle"]));
|
|
}
|
|
if is_blank(&existing.owner_name) {
|
|
model.owner_name = Set(as_optional_string(&seed["owner_name"]));
|
|
}
|
|
if is_blank(&existing.owner_title) {
|
|
model.owner_title = Set(as_optional_string(&seed["owner_title"]));
|
|
}
|
|
if is_blank(&existing.owner_bio) {
|
|
model.owner_bio = Set(as_optional_string(&seed["owner_bio"]));
|
|
}
|
|
if is_blank(&existing.owner_avatar_url) {
|
|
model.owner_avatar_url = Set(as_optional_string(&seed["owner_avatar_url"]));
|
|
}
|
|
if is_blank(&existing.social_github) {
|
|
model.social_github = Set(as_optional_string(&seed["social_github"]));
|
|
}
|
|
if is_blank(&existing.social_twitter) {
|
|
model.social_twitter = Set(as_optional_string(&seed["social_twitter"]));
|
|
}
|
|
if is_blank(&existing.social_email) {
|
|
model.social_email = Set(as_optional_string(&seed["social_email"]));
|
|
}
|
|
if is_blank(&existing.location) {
|
|
model.location = Set(as_optional_string(&seed["location"]));
|
|
}
|
|
if existing.tech_stack.is_none() {
|
|
model.tech_stack = Set(tech_stack);
|
|
}
|
|
|
|
let _ = model.update(&ctx.db).await;
|
|
return Ok(());
|
|
}
|
|
|
|
let model = site_settings::ActiveModel {
|
|
id: Set(seed["id"].as_i64().unwrap_or(1) as i32),
|
|
site_name: Set(as_optional_string(&seed["site_name"])),
|
|
site_short_name: Set(as_optional_string(&seed["site_short_name"])),
|
|
site_url: Set(as_optional_string(&seed["site_url"])),
|
|
site_title: Set(as_optional_string(&seed["site_title"])),
|
|
site_description: Set(as_optional_string(&seed["site_description"])),
|
|
hero_title: Set(as_optional_string(&seed["hero_title"])),
|
|
hero_subtitle: Set(as_optional_string(&seed["hero_subtitle"])),
|
|
owner_name: Set(as_optional_string(&seed["owner_name"])),
|
|
owner_title: Set(as_optional_string(&seed["owner_title"])),
|
|
owner_bio: Set(as_optional_string(&seed["owner_bio"])),
|
|
owner_avatar_url: Set(as_optional_string(&seed["owner_avatar_url"])),
|
|
social_github: Set(as_optional_string(&seed["social_github"])),
|
|
social_twitter: Set(as_optional_string(&seed["social_twitter"])),
|
|
social_email: Set(as_optional_string(&seed["social_email"])),
|
|
location: Set(as_optional_string(&seed["location"])),
|
|
tech_stack: Set(tech_stack),
|
|
..Default::default()
|
|
};
|
|
|
|
let _ = model.insert(&ctx.db).await;
|
|
Ok(())
|
|
}
|
|
|
|
async fn sync_comment_post_slugs(ctx: &AppContext, base: &Path) -> Result<()> {
|
|
let rows = read_fixture_rows(base, "comments.yaml");
|
|
|
|
for seed in rows {
|
|
let id = seed["id"].as_i64().unwrap_or(0) as i32;
|
|
let pid = seed["pid"].as_i64().unwrap_or(0) as i32;
|
|
if id == 0 || pid == 0 {
|
|
continue;
|
|
}
|
|
|
|
let Some(existing) = comments::Entity::find_by_id(id).one(&ctx.db).await? else {
|
|
continue;
|
|
};
|
|
|
|
if existing.post_slug.is_some() {
|
|
continue;
|
|
}
|
|
|
|
let Some(post) = posts::Entity::find_by_id(pid).one(&ctx.db).await? else {
|
|
continue;
|
|
};
|
|
|
|
let mut model = existing.into_active_model();
|
|
model.post_slug = Set(Some(post.slug));
|
|
let _ = model.update(&ctx.db).await;
|
|
}
|
|
|
|
Ok(())
|
|
}
|