1
0
mirror of https://gitlab.com/Anson-Projects/projects.git synced 2025-09-14 09:35:04 +00:00

Add force update functionality for Ghost posts

- Add manual CI trigger 'force-update-ghost' for updating all posts
- Support FORCE_UPDATE environment variable in Rust code
- Implement post update logic via Ghost API PUT requests
- Add get_existing_post_id() function to find existing posts
- Update README with usage instructions
- Enhanced validation script to test new functionality

Usage:
- Normal: Only syncs new posts (default behavior)
- Force: FORCE_UPDATE=true updates ALL posts including existing ones
This commit is contained in:
2025-08-21 23:30:29 -06:00
parent 05474b986d
commit 9fc6a9bae1
4 changed files with 170 additions and 20 deletions

View File

@@ -202,6 +202,42 @@ async fn check_if_post_exists(entry: &Entry) -> bool {
}
}
#[derive(Deserialize, Debug)]
struct GhostPostsResponse {
posts: Vec<GhostPost>,
}
#[derive(Deserialize, Debug)]
struct GhostPost {
id: String,
slug: String,
}
async fn get_existing_post_id(slug: &str, token: &str) -> Option<String> {
let client = Client::new();
let api_url = format!("https://notes.ansonbiggs.com/ghost/api/v3/admin/posts/slug/{}/", slug);
match client
.get(&api_url)
.header("Authorization", format!("Ghost {}", token))
.send()
.await
{
Ok(response) => {
if response.status().is_success() {
if let Ok(ghost_response) = response.json::<GhostPostsResponse>().await {
ghost_response.posts.first().map(|post| post.id.clone())
} else {
None
}
} else {
None
}
}
Err(_) => None,
}
}
async fn fetch_feed(url: &str) -> Vec<Entry> {
let content = reqwest::get(url).await.unwrap().text().await.unwrap();
@@ -269,6 +305,16 @@ async fn main() {
let ghost_api_url = "https://notes.ansonbiggs.com/ghost/api/v3/admin/posts/?source=html";
let ghost_admin_api_key = env::var("admin_api_key").unwrap();
// Check if force update is enabled
let force_update = env::var("FORCE_UPDATE").unwrap_or_default() == "true";
if force_update {
println!("🔄 FORCE UPDATE MODE ENABLED");
println!(" This will update ALL posts, including existing ones.");
} else {
println!("📝 NORMAL MODE - Only publishing new posts");
}
let feed = "https://projects.ansonbiggs.com/index.xml";
// Split the key into ID and SECRET
@@ -302,17 +348,25 @@ async fn main() {
// Prepare the post data
let entries = fetch_feed(feed).await;
let post_exists_futures = entries.into_iter().map(|entry| {
let entry_clone = entry.clone();
async move { (entry_clone, check_if_post_exists(&entry).await) }
});
let filtered_entries: Vec<Entry> = if force_update {
println!("🔄 Force update enabled - processing all {} posts", entries.len());
entries
} else {
let post_exists_futures = entries.into_iter().map(|entry| {
let entry_clone = entry.clone();
async move { (entry_clone, check_if_post_exists(&entry).await) }
});
let post_exists_results = join_all(post_exists_futures).await;
let post_exists_results = join_all(post_exists_futures).await;
let filtered_entries: Vec<Entry> = post_exists_results
.into_iter()
.filter_map(|(entry, exists)| if !exists { Some(entry) } else { None })
.collect();
let new_entries: Vec<Entry> = post_exists_results
.into_iter()
.filter_map(|(entry, exists)| if !exists { Some(entry) } else { None })
.collect();
println!("📝 Found {} new posts to publish", new_entries.len());
new_entries
};
if filtered_entries.is_empty() {
println!("Nothing to post.");
@@ -328,21 +382,46 @@ async fn main() {
posts: vec![post.clone()],
};
let response = client
.post(ghost_api_url)
.header("Authorization", format!("Ghost {}", token))
.json(&post_payload)
.send()
.await
.expect("Request failed");
// Check if this is an update (for force_update mode)
let (method, url) = if force_update {
if let Some(existing_id) = get_existing_post_id(&post.slug, &token).await {
println!("🔄 Updating existing post: {}", post.title);
("PUT", format!("https://notes.ansonbiggs.com/ghost/api/v3/admin/posts/{}/", existing_id))
} else {
println!("📝 Creating new post: {}", post.title);
("POST", ghost_api_url.to_string())
}
} else {
println!("📝 Creating new post: {}", post.title);
("POST", ghost_api_url.to_string())
};
let response = match method {
"PUT" => client
.put(&url)
.header("Authorization", format!("Ghost {}", token))
.json(&post_payload)
.send()
.await
.expect("Request failed"),
_ => client
.post(&url)
.header("Authorization", format!("Ghost {}", token))
.json(&post_payload)
.send()
.await
.expect("Request failed"),
};
// Check the response
if response.status().is_success() {
println!("Post {} published successfully.", post.title);
let action = if method == "PUT" { "updated" } else { "published" };
println!("✅ Post '{}' {} successfully.", post.title, action);
} else {
let action = if method == "PUT" { "update" } else { "publish" };
println!(
"Failed to publish post {}.\n\tResp: {:?}",
&post.title, response
"Failed to {} post '{}'.\n\tStatus: {}\n\tResponse: {:?}",
action, &post.title, response.status(), response.text().await.unwrap_or_default()
);
}
}