Fetch raw markdown body for one article and language (`zh` or `en`).
(id: &str, lang: &str)
| 554 | |
| 555 | /// Fetch raw markdown body for one article and language (`zh` or `en`). |
| 556 | pub async fn fetch_article_raw_markdown(id: &str, lang: &str) -> Result<String, String> { |
| 557 | #[cfg(feature = "mock")] |
| 558 | { |
| 559 | let article = |
| 560 | models::get_mock_article_detail(id).ok_or_else(|| "Article not found".to_string())?; |
| 561 | let normalized_lang = lang.trim().to_ascii_lowercase(); |
| 562 | let content = match normalized_lang.as_str() { |
| 563 | "zh" => article.content, |
| 564 | "en" => article |
| 565 | .content_en |
| 566 | .filter(|value| !value.trim().is_empty()) |
| 567 | .ok_or_else(|| "English article markdown not found".to_string())?, |
| 568 | _ => return Err("`lang` must be `zh` or `en`".to_string()), |
| 569 | }; |
| 570 | Ok(content) |
| 571 | } |
| 572 | |
| 573 | #[cfg(not(feature = "mock"))] |
| 574 | { |
| 575 | let normalized_lang = lang.trim().to_ascii_lowercase(); |
| 576 | if normalized_lang != "zh" && normalized_lang != "en" { |
| 577 | return Err("`lang` must be `zh` or `en`".to_string()); |
| 578 | } |
| 579 | |
| 580 | let url = format!( |
| 581 | "{}/articles/{}/raw/{}?_ts={}", |
| 582 | API_BASE, |
| 583 | urlencoding::encode(id), |
| 584 | urlencoding::encode(&normalized_lang), |
| 585 | Date::now() as u64 |
| 586 | ); |
| 587 | |
| 588 | let response = api_get(&url) |
| 589 | .header("Cache-Control", "no-cache, no-store, max-age=0") |
| 590 | .header("Pragma", "no-cache") |
| 591 | .send() |
| 592 | .await |
| 593 | .map_err(|e| format!("Network error: {:?}", e))?; |
| 594 | |
| 595 | if response.status() == 404 { |
| 596 | return Err("Raw article markdown not found".to_string()); |
| 597 | } |
| 598 | if !response.ok() { |
| 599 | return Err(format!("HTTP error: {}", response.status())); |
| 600 | } |
| 601 | |
| 602 | response |
| 603 | .text() |
| 604 | .await |
| 605 | .map_err(|e| format!("Parse error: {:?}", e)) |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | /// Track one article detail view with backend-side dedupe. |
| 610 | pub async fn track_article_view(id: &str) -> Result<ArticleViewTrackResponse, String> { |
no test coverage detected