judu/ex-egopublic Fork
⑂ 16eb9ff
Commits
⬇ Clone ▾
git clone https://git.rickub.com/judu/ex-ego.git
git clone ssh://git@rickub.com/judu/ex-ego.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

feat(parametres): lien de création d'issue GitLab dans la carte CompteUnverified

Description préremplie dans la langue de l'utilisateur avec la version
compilée et le commit déployé (COMMIT_ID de Clever Cloud, « dev » en
local) ; la ligne v0.1.0 · commit s'affiche sous le lien.
Julien Durillon committed 2026-08-25T00:35:53+02:00 Browse files
16eb9ff parent: e722076
modified src/config.rs +10 -0
@@ -46,6 +46,16 @@ pub fn signup_key() -> Option<String> {
4646 .filter(|k| !k.is_empty())
4747 }
4848
49+/// Commit déployé — `COMMIT_ID`, posée par Clever Cloud dans l'environnement
50+/// de l'app. Tronqué à 8 caractères ; « dev » en local (variable absente).
51+pub fn commit_id() -> String {
52+ std::env::var("COMMIT_ID")
53+ .ok()
54+ .map(|c| c.trim().chars().take(8).collect::<String>())
55+ .filter(|c| !c.is_empty())
56+ .unwrap_or_else(|| "dev".to_string())
57+}
58+
4959 pub fn biscuit_private_key_hex() -> Result<String> {
5060 std::env::var("BISCUIT_PRIVATE_KEY")
5161 .context("BISCUIT_PRIVATE_KEY doit être définie (générer avec `exego keygen`)")
@@ -46,6 +46,16 @@ pub fn signup_key() -> Option<String> {
46 .filter(|k| !k.is_empty())46 .filter(|k| !k.is_empty())
47 }47 }
48 48
49+/// Commit déployé — `COMMIT_ID`, posée par Clever Cloud dans l'environnement
50+/// de l'app. Tronqué à 8 caractères ; « dev » en local (variable absente).
51+pub fn commit_id() -> String {
52+ std::env::var("COMMIT_ID")
53+ .ok()
54+ .map(|c| c.trim().chars().take(8).collect::<String>())
55+ .filter(|c| !c.is_empty())
56+ .unwrap_or_else(|| "dev".to_string())
57+}
58+
49 pub fn biscuit_private_key_hex() -> Result<String> {59 pub fn biscuit_private_key_hex() -> Result<String> {
50 std::env::var("BISCUIT_PRIVATE_KEY")60 std::env::var("BISCUIT_PRIVATE_KEY")
51 .context("BISCUIT_PRIVATE_KEY doit être définie (générer avec `exego keygen`)")61 .context("BISCUIT_PRIVATE_KEY doit être définie (générer avec `exego keygen`)")
modified src/routes/settings.rs +32 -0
@@ -55,6 +55,25 @@ pub struct SettingsPage {
5555 pub can_leave: bool,
5656 /// Lien d'invitation fraîchement forgé, à copier (absent le reste du temps).
5757 pub invite_link: Option<String>,
58+ /// Création d'issue GitLab préremplie (version + commit, langue de l'utilisateur).
59+ pub feedback_href: String,
60+ /// « v0.0.2 · bb2bb6b6 » — affiché sous le lien (aucun texte à traduire).
61+ pub version_line: String,
62+}
63+
64+/// Percent-encoding d'un composant de query string (RFC 3986, unreserved
65+/// seuls) — évite une dépendance pour un unique lien.
66+fn urlencode(s: &str) -> String {
67+ let mut out = String::with_capacity(s.len() * 3);
68+ for b in s.bytes() {
69+ match b {
70+ b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
71+ out.push(b as char)
72+ }
73+ _ => out.push_str(&format!("%{b:02X}")),
74+ }
75+ }
76+ out
5877 }
5978
6079 #[derive(Deserialize)]
@@ -172,6 +191,17 @@ pub async fn settings_page(
172191 current: ctx.stats_rows,
173192 },
174193 ];
194+
195+ // Lien « créer une issue » : description préremplie (langue de
196+ // l'utilisateur) portant version compilée + commit déployé.
197+ let version = env!("CARGO_PKG_VERSION");
198+ let commit = crate::config::commit_id();
199+ let feedback_href = format!(
200+ "https://gitlab.com/juuduu/ex-ego/-/issues/new?issue%5Bdescription%5D={}",
201+ urlencode(&p2(tr.issue_body_pat, version, &commit))
202+ );
203+ let version_line = format!("v{version} · {commit}");
204+
175205 Ok(HtmlTemplate(SettingsPage {
176206 csrf: ctx.user.csrf_token(&state),
177207 shell: shell(&state, &ctx, tr.title_settings, "settings", toast).await?,
@@ -188,6 +218,8 @@ pub async fn settings_page(
188218 sent,
189219 can_leave,
190220 invite_link: q.lien.filter(|l| l.starts_with("http")),
221+ feedback_href,
222+ version_line,
191223 }))
192224 }
193225
@@ -55,6 +55,25 @@ pub struct SettingsPage {
55 pub can_leave: bool,55 pub can_leave: bool,
56 /// Lien d'invitation fraîchement forgé, à copier (absent le reste du temps).56 /// Lien d'invitation fraîchement forgé, à copier (absent le reste du temps).
57 pub invite_link: Option<String>,57 pub invite_link: Option<String>,
58+ /// Création d'issue GitLab préremplie (version + commit, langue de l'utilisateur).
59+ pub feedback_href: String,
60+ /// « v0.0.2 · bb2bb6b6 » — affiché sous le lien (aucun texte à traduire).
61+ pub version_line: String,
62+}
63+
64+/// Percent-encoding d'un composant de query string (RFC 3986, unreserved
65+/// seuls) — évite une dépendance pour un unique lien.
66+fn urlencode(s: &str) -> String {
67+ let mut out = String::with_capacity(s.len() * 3);
68+ for b in s.bytes() {
69+ match b {
70+ b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
71+ out.push(b as char)
72+ }
73+ _ => out.push_str(&format!("%{b:02X}")),
74+ }
75+ }
76+ out
58 }77 }
59 78
60 #[derive(Deserialize)]79 #[derive(Deserialize)]
@@ -172,6 +191,17 @@ pub async fn settings_page(
172 current: ctx.stats_rows,191 current: ctx.stats_rows,
173 },192 },
174 ];193 ];
194+
195+ // Lien « créer une issue » : description préremplie (langue de
196+ // l'utilisateur) portant version compilée + commit déployé.
197+ let version = env!("CARGO_PKG_VERSION");
198+ let commit = crate::config::commit_id();
199+ let feedback_href = format!(
200+ "https://gitlab.com/juuduu/ex-ego/-/issues/new?issue%5Bdescription%5D={}",
201+ urlencode(&p2(tr.issue_body_pat, version, &commit))
202+ );
203+ let version_line = format!("v{version} · {commit}");
204+
175 Ok(HtmlTemplate(SettingsPage {205 Ok(HtmlTemplate(SettingsPage {
176 csrf: ctx.user.csrf_token(&state),206 csrf: ctx.user.csrf_token(&state),
177 shell: shell(&state, &ctx, tr.title_settings, "settings", toast).await?,207 shell: shell(&state, &ctx, tr.title_settings, "settings", toast).await?,
@@ -188,6 +218,8 @@ pub async fn settings_page(
188 sent,218 sent,
189 can_leave,219 can_leave,
190 invite_link: q.lien.filter(|l| l.starts_with("http")),220 invite_link: q.lien.filter(|l| l.starts_with("http")),
221+ feedback_href,
222+ version_line,
191 }))223 }))
192 }224 }
193 225
modified templates/settings.html +4 -0
@@ -26,6 +26,10 @@
2626 <div class="card-kicker">{{ shell.tr.account_kicker }}</div>
2727 <div style="font-size:19px;font-family:var(--font-heading)">{{ shell.username }}</div>
2828 <p class="card-body">{{ account_line }}</p>
29+ {# Pas .card-body : son opacité 0.8 délaverait le lien sous 4.5:1 (axe). #}
30+ <p style="margin:var(--space-2) 0 0;font-size:13px">{{ shell.tr.feedback_title }}
31+ <a href="{{ feedback_href }}" target="_blank" rel="noopener">{{ shell.tr.feedback_link }} ↗</a></p>
32+ <p style="font-size:11px;opacity:.7;margin:var(--space-1) 0 0">{{ version_line }}</p>
2933 </div>
3034
3135 <a class="card times-link" href="/temps" style="margin-bottom:var(--space-3)">
@@ -26,6 +26,10 @@
26 <div class="card-kicker">{{ shell.tr.account_kicker }}</div>26 <div class="card-kicker">{{ shell.tr.account_kicker }}</div>
27 <div style="font-size:19px;font-family:var(--font-heading)">{{ shell.username }}</div>27 <div style="font-size:19px;font-family:var(--font-heading)">{{ shell.username }}</div>
28 <p class="card-body">{{ account_line }}</p>28 <p class="card-body">{{ account_line }}</p>
29+ {# Pas .card-body : son opacité 0.8 délaverait le lien sous 4.5:1 (axe). #}
30+ <p style="margin:var(--space-2) 0 0;font-size:13px">{{ shell.tr.feedback_title }}
31+ <a href="{{ feedback_href }}" target="_blank" rel="noopener">{{ shell.tr.feedback_link }} ↗</a></p>
32+ <p style="font-size:11px;opacity:.7;margin:var(--space-1) 0 0">{{ version_line }}</p>
29 </div>33 </div>
30 34
31 <a class="card times-link" href="/temps" style="margin-bottom:var(--space-3)">35 <a class="card times-link" href="/temps" style="margin-bottom:var(--space-3)">