This commit is contained in:
Generated
+1
@@ -4772,6 +4772,7 @@ dependencies = [
|
||||
"chrono-tz",
|
||||
"cot",
|
||||
"futures",
|
||||
"idna",
|
||||
"image",
|
||||
"multer",
|
||||
"password-auth",
|
||||
|
||||
@@ -20,6 +20,7 @@ tokio = { version = "1", features = ["fs", "process", "rt-multi-thread"] }
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
base64 = "0.22"
|
||||
urlencoding = "2"
|
||||
idna = "1"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
web-push-native = "0.5"
|
||||
|
||||
+58
-1
@@ -322,6 +322,39 @@ struct ClientFormTemplate<'a> {
|
||||
client_id: i64,
|
||||
client_status: &'a str,
|
||||
client_token: &'a str,
|
||||
/// Base origin for the portal link with any punycode host decoded to Unicode
|
||||
/// (e.g. `https://мурняня.рф`); empty falls back to the browser origin.
|
||||
portal_base: String,
|
||||
}
|
||||
|
||||
/// Decodes any punycode (`xn--…`) labels in a URL's host to human-readable
|
||||
/// Unicode, keeping the scheme and trimming a trailing slash. Returns an empty
|
||||
/// string for empty input.
|
||||
fn humanize_domain(raw: &str) -> String {
|
||||
let raw = raw.trim();
|
||||
if raw.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
let (scheme, rest) = match raw.split_once("://") {
|
||||
Some((s, r)) => (Some(s), r),
|
||||
None => (None, raw),
|
||||
};
|
||||
let (host, path) = match rest.split_once('/') {
|
||||
Some((h, p)) => (h, Some(p)),
|
||||
None => (rest, None),
|
||||
};
|
||||
let (unicode_host, _) = idna::domain_to_unicode(host);
|
||||
let mut out = String::new();
|
||||
if let Some(s) = scheme {
|
||||
out.push_str(s);
|
||||
out.push_str("://");
|
||||
}
|
||||
out.push_str(&unicode_host);
|
||||
if let Some(p) = path.filter(|p| !p.is_empty()) {
|
||||
out.push('/');
|
||||
out.push_str(p);
|
||||
}
|
||||
out.trim_end_matches('/').to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Template)]
|
||||
@@ -1270,6 +1303,7 @@ async fn client_new_page(request: Request, session: Session) -> cot::Result<Resp
|
||||
client_id: 0,
|
||||
client_status: "",
|
||||
client_token: "",
|
||||
portal_base: String::new(),
|
||||
}
|
||||
.render()?;
|
||||
html_response(body, lang)
|
||||
@@ -1295,6 +1329,13 @@ async fn client_edit_page(
|
||||
let t = lang.t();
|
||||
let action_url = format!("/admin/clients/{}/save", client.id);
|
||||
let cid = client.id.unwrap();
|
||||
let site_domain_key = "site_domain".to_string();
|
||||
let site_domain = query!(Setting, $key == site_domain_key)
|
||||
.get(&db)
|
||||
.await?
|
||||
.map(|s| s.value)
|
||||
.unwrap_or_default();
|
||||
let portal_base = humanize_domain(&site_domain);
|
||||
let body = ClientFormTemplate {
|
||||
t,
|
||||
lang,
|
||||
@@ -1312,6 +1353,7 @@ async fn client_edit_page(
|
||||
client_id: cid,
|
||||
client_status: &client.status,
|
||||
client_token: &client.media_token,
|
||||
portal_base,
|
||||
}
|
||||
.render()?;
|
||||
html_response(body, lang)
|
||||
@@ -3544,7 +3586,7 @@ pub fn admin_router() -> Router {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::preserved_secret_value;
|
||||
use super::{humanize_domain, preserved_secret_value};
|
||||
|
||||
#[test]
|
||||
fn keeps_masks_and_updates_secrets_consistently() {
|
||||
@@ -3553,4 +3595,19 @@ mod tests {
|
||||
assert_eq!(preserved_secret_value("stored", "", true), "");
|
||||
assert_eq!(preserved_secret_value("stored", "new", true), "new");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_punycode_host_to_unicode() {
|
||||
assert_eq!(
|
||||
humanize_domain("https://xn--l1acako8eb.xn--p1ai"),
|
||||
"https://мурняня.рф"
|
||||
);
|
||||
assert_eq!(
|
||||
humanize_domain("https://xn--l1acako8eb.xn--p1ai/"),
|
||||
"https://мурняня.рф"
|
||||
);
|
||||
// Plain ASCII domains are returned unchanged (minus trailing slash).
|
||||
assert_eq!(humanize_domain("https://example.com/"), "https://example.com");
|
||||
assert_eq!(humanize_domain(""), "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/qrious@4.0.2/dist/qrious.min.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
var url = window.location.origin + '/client/{{ client_token }}';
|
||||
var base = '{{ portal_base }}' || window.location.origin;
|
||||
var url = base + '/client/{{ client_token }}';
|
||||
document.getElementById('portalUrl').value = url;
|
||||
new QRious({ element: document.getElementById('qrCanvas'), value: url, size: 180, level: 'M' });
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user