| 5 |
5 |
|
response::IntoResponse,
|
| 6 |
6 |
|
Form, Json,
|
| 7 |
7 |
|
};
|
| 8 |
|
- |
use serde::{Deserialize, Serialize};
|
|
8 |
+ |
use serde::Deserialize;
|
| 9 |
9 |
|
use serde_json::json;
|
| 10 |
10 |
|
|
| 11 |
11 |
|
use crate::{
|
| 20 |
20 |
|
domain: String,
|
| 21 |
21 |
|
}
|
| 22 |
22 |
|
|
| 23 |
|
- |
#[derive(Serialize)]
|
| 24 |
|
- |
struct DomainResponse {
|
| 25 |
|
- |
id: CustomDomainId,
|
| 26 |
|
- |
domain: String,
|
| 27 |
|
- |
verified: bool,
|
| 28 |
|
- |
verification_token: String,
|
| 29 |
|
- |
instructions: String,
|
| 30 |
|
- |
}
|
| 31 |
23 |
|
|
| 32 |
24 |
|
/// POST /api/domains — add a custom domain.
|
| 33 |
25 |
|
#[tracing::instrument(skip_all, name = "api::domains::add")]
|
| 34 |
26 |
|
pub(super) async fn add_domain(
|
| 35 |
27 |
|
State(state): State<AppState>,
|
| 36 |
28 |
|
AuthUser(session_user): AuthUser,
|
| 37 |
|
- |
Json(req): Json<AddDomainRequest>,
|
|
29 |
+ |
Form(req): Form<AddDomainRequest>,
|
| 38 |
30 |
|
) -> Result<impl IntoResponse> {
|
| 39 |
31 |
|
session_user.check_not_sandbox()?;
|
| 40 |
32 |
|
let domain = normalize_domain(&req.domain)?;
|
| 42 |
34 |
|
|
| 43 |
35 |
|
let verification_token = generate_verification_token();
|
| 44 |
36 |
|
|
| 45 |
|
- |
let row =
|
|
37 |
+ |
let _row =
|
| 46 |
38 |
|
db::custom_domains::create_custom_domain(&state.db, session_user.id, &domain, &verification_token)
|
| 47 |
39 |
|
.await?;
|
| 48 |
40 |
|
|
| 49 |
41 |
|
let instructions = format!(
|
| 50 |
|
- |
"Add a DNS TXT record: _mnw-verify.{} with value {}",
|
|
42 |
+ |
"Add a DNS TXT record: <code>_mnw-verify.{}</code> with value <code>{}</code>",
|
| 51 |
43 |
|
domain, verification_token
|
| 52 |
44 |
|
);
|
| 53 |
45 |
|
|
| 54 |
|
- |
Ok(Json(DomainResponse {
|
| 55 |
|
- |
id: row.id,
|
| 56 |
|
- |
domain: row.domain,
|
| 57 |
|
- |
verified: row.verified,
|
| 58 |
|
- |
verification_token: row.verification_token,
|
| 59 |
|
- |
instructions,
|
| 60 |
|
- |
}))
|
|
46 |
+ |
Ok(axum::response::Html(format!(
|
|
47 |
+ |
"<p class=\"success\">{}</p>",
|
|
48 |
+ |
instructions
|
|
49 |
+ |
)))
|
| 61 |
50 |
|
}
|
| 62 |
51 |
|
|
| 63 |
52 |
|
#[derive(Deserialize)]
|
| 82 |
71 |
|
}
|
| 83 |
72 |
|
|
| 84 |
73 |
|
if cd.verified {
|
| 85 |
|
- |
return Ok(Json(json!({"verified": true, "message": "Domain already verified."})));
|
|
74 |
+ |
return Ok(axum::response::Html("<p class=\"success\">Domain already verified.</p>".to_string()));
|
| 86 |
75 |
|
}
|
| 87 |
76 |
|
|
| 88 |
77 |
|
// Query DNS via Cloudflare DNS-over-HTTPS
|
| 94 |
83 |
|
.any(|txt| txt.trim() == cd.verification_token);
|
| 95 |
84 |
|
|
| 96 |
85 |
|
if !matched {
|
| 97 |
|
- |
return Ok(Json(json!({
|
| 98 |
|
- |
"verified": false,
|
| 99 |
|
- |
"message": format!(
|
| 100 |
|
- |
"TXT record not found. Add _mnw-verify.{} TXT {} and try again.",
|
| 101 |
|
- |
cd.domain, cd.verification_token
|
| 102 |
|
- |
)
|
| 103 |
|
- |
})));
|
|
86 |
+ |
return Ok(axum::response::Html(format!(
|
|
87 |
+ |
"<p class=\"error\">TXT record not found. Add <code>_mnw-verify.{}</code> TXT <code>{}</code> and try again.</p>",
|
|
88 |
+ |
cd.domain, cd.verification_token
|
|
89 |
+ |
)));
|
| 104 |
90 |
|
}
|
| 105 |
91 |
|
|
| 106 |
92 |
|
// Mark verified in DB and update cache
|
| 109 |
95 |
|
.domain_cache
|
| 110 |
96 |
|
.insert(cd.domain.clone(), session_user.id);
|
| 111 |
97 |
|
|
| 112 |
|
- |
Ok(Json(json!({"verified": true, "message": "Domain verified successfully."})))
|
|
98 |
+ |
Ok(axum::response::Html("<p class=\"success\">Domain verified successfully. Reload to see changes.</p>".to_string()))
|
| 113 |
99 |
|
}
|
| 114 |
100 |
|
|
| 115 |
101 |
|
/// DELETE /api/domains/{id} — remove a custom domain.
|