feat: DEV-99 blog migration (#3870)

* feat: blog migration w/ fixes

Co-authored-by: Prospector <prospectordev@gmail.com>

* feat: add changelog button to news page

* fix: lint issues

* refactor: replace nuxt content with `@modrinth/blog`

* feat: shared public folder

* feat: try lazy loading html content

* feat: rss + hide newsletter btn + blog.config.ts

* feat: add new chapter modrinth servers post

* fix: lint issues

* fix: only generate RSS feed if changes detected

* fix: utils dep

* fix: lockfile dep

* feat: GET /email/subscribe + subscription button

* fix: lint issues

* feat: articles.json for app

* Made grid more responsive

* fix: changes

* Make margin slightly smaller in lists

* Fix footer link

* feat: latest news

* Fix responsiveness

* Remove old utm link

* Update changelog

* Lint

---------

Co-authored-by: Prospector <prospectordev@gmail.com>
This commit is contained in:
IMB11
2025-07-01 02:59:08 +01:00
committed by GitHub
parent fdb2b1195e
commit eef09e1ffe
220 changed files with 5094 additions and 681 deletions

View File

@@ -51,7 +51,8 @@ pub fn config(cfg: &mut ServiceConfig) {
.service(resend_verify_email)
.service(set_email)
.service(verify_email)
.service(subscribe_newsletter),
.service(subscribe_newsletter)
.service(get_newsletter_subscription_status),
);
}
@@ -1321,6 +1322,37 @@ pub async fn sign_up_sendy(email: &str) -> Result<(), AuthenticationError> {
Ok(())
}
pub async fn check_sendy_subscription(
email: &str,
) -> Result<bool, AuthenticationError> {
let url = dotenvy::var("SENDY_URL")?;
let id = dotenvy::var("SENDY_LIST_ID")?;
let api_key = dotenvy::var("SENDY_API_KEY")?;
if url.is_empty() || url == "none" {
tracing::info!(
"Sendy URL not set, returning false for subscription check"
);
return Ok(false);
}
let mut form = HashMap::new();
form.insert("api_key", &*api_key);
form.insert("email", email);
form.insert("list_id", &*id);
let client = reqwest::Client::new();
let response = client
.post(format!("{url}/api/subscribers/subscription-status.php"))
.form(&form)
.send()
.await?
.text()
.await?;
Ok(response.trim() == "Subscribed")
}
#[derive(Deserialize, Validate)]
pub struct NewAccount {
#[validate(length(min = 1, max = 39), regex(path = *crate::util::validate::RE_URL_SAFE))]
@@ -2387,6 +2419,35 @@ pub async fn subscribe_newsletter(
}
}
#[get("email/subscribe")]
pub async fn get_newsletter_subscription_status(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(
&req,
&**pool,
&redis,
&session_queue,
Scopes::USER_READ,
)
.await?
.1;
if let Some(email) = user.email {
let is_subscribed = check_sendy_subscription(&email).await?;
Ok(HttpResponse::Ok().json(serde_json::json!({
"subscribed": is_subscribed
})))
} else {
Ok(HttpResponse::Ok().json(serde_json::json!({
"subscribed": false
})))
}
}
fn send_email_verify(
email: String,
flow: String,