Fixed client link
Build and Publish / Build and Publish Docker Image (push) Successful in 1m42s

This commit is contained in:
ab
2026-09-20 21:11:49 +03:00
parent 6449d8b59a
commit 46351351fc
4 changed files with 62 additions and 2 deletions
Generated
+1
View File
@@ -4772,6 +4772,7 @@ dependencies = [
"chrono-tz", "chrono-tz",
"cot", "cot",
"futures", "futures",
"idna",
"image", "image",
"multer", "multer",
"password-auth", "password-auth",
+1
View File
@@ -20,6 +20,7 @@ tokio = { version = "1", features = ["fs", "process", "rt-multi-thread"] }
uuid = { version = "1", features = ["v4"] } uuid = { version = "1", features = ["v4"] }
base64 = "0.22" base64 = "0.22"
urlencoding = "2" urlencoding = "2"
idna = "1"
tracing = "0.1" tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
web-push-native = "0.5" web-push-native = "0.5"
+58 -1
View File
@@ -322,6 +322,39 @@ struct ClientFormTemplate<'a> {
client_id: i64, client_id: i64,
client_status: &'a str, client_status: &'a str,
client_token: &'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)] #[derive(Debug, Template)]
@@ -1270,6 +1303,7 @@ async fn client_new_page(request: Request, session: Session) -> cot::Result<Resp
client_id: 0, client_id: 0,
client_status: "", client_status: "",
client_token: "", client_token: "",
portal_base: String::new(),
} }
.render()?; .render()?;
html_response(body, lang) html_response(body, lang)
@@ -1295,6 +1329,13 @@ async fn client_edit_page(
let t = lang.t(); let t = lang.t();
let action_url = format!("/admin/clients/{}/save", client.id); let action_url = format!("/admin/clients/{}/save", client.id);
let cid = client.id.unwrap(); 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 { let body = ClientFormTemplate {
t, t,
lang, lang,
@@ -1312,6 +1353,7 @@ async fn client_edit_page(
client_id: cid, client_id: cid,
client_status: &client.status, client_status: &client.status,
client_token: &client.media_token, client_token: &client.media_token,
portal_base,
} }
.render()?; .render()?;
html_response(body, lang) html_response(body, lang)
@@ -3544,7 +3586,7 @@ pub fn admin_router() -> Router {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::preserved_secret_value; use super::{humanize_domain, preserved_secret_value};
#[test] #[test]
fn keeps_masks_and_updates_secrets_consistently() { 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", "", true), "");
assert_eq!(preserved_secret_value("stored", "new", true), "new"); 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(""), "");
}
} }
+2 -1
View File
@@ -72,7 +72,8 @@
<script src="https://cdn.jsdelivr.net/npm/qrious@4.0.2/dist/qrious.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/qrious@4.0.2/dist/qrious.min.js"></script>
<script> <script>
(function() { (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; document.getElementById('portalUrl').value = url;
new QRious({ element: document.getElementById('qrCanvas'), value: url, size: 180, level: 'M' }); new QRious({ element: document.getElementById('qrCanvas'), value: url, size: 180, level: 'M' });
})(); })();