securite: #87 T5a supprime 16 handlers inline au profit de listeners (CSP inchangee)
This commit is contained in:
+5
-1
@@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/),
|
||||
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||
> [Unreleased](#unreleased). La dernière version livrée est **2.28.5**.
|
||||
> [Unreleased](#unreleased). La dernière version livrée est **2.28.6**.
|
||||
|
||||
---
|
||||
|
||||
@@ -14,6 +14,10 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
## [2.28.6] — 2026-09-26
|
||||
|
||||
---
|
||||
|
||||
## [2.28.5] — 2026-09-26
|
||||
|
||||
---
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
|
||||
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.docker.com/)
|
||||
[](https://www.python.org/)
|
||||
@@ -976,8 +976,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.28.5).
|
||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.28.6).
|
||||
|
||||
---
|
||||
|
||||
*Projet : ObsiGate | Version : 2.28.5 | Dernière mise à jour : Septembre 2026*
|
||||
*Projet : ObsiGate | Version : 2.28.6 | Dernière mise à jour : Septembre 2026*
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.docker.com/)
|
||||
[](https://www.python.org/)
|
||||
@@ -1151,8 +1151,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.28.5).
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.28.6).
|
||||
|
||||
---
|
||||
|
||||
*Project: ObsiGate | Version: 2.28.5 | Last updated: September 2026*
|
||||
*Project: ObsiGate | Version: 2.28.6 | Last updated: September 2026*
|
||||
|
||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "obsigate-desktop"
|
||||
version = "2.28.5"
|
||||
version = "2.28.6"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"env_logger",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "obsigate-desktop"
|
||||
version = "2.28.5"
|
||||
version = "2.28.6"
|
||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||
authors = ["Bruno Charest"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||
"productName": "ObsiGate",
|
||||
"version": "2.28.5",
|
||||
"version": "2.28.6",
|
||||
"identifier": "com.obsigate.desktop",
|
||||
"build": {
|
||||
"frontendDist": "../frontend",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# ObsiGate — Roadmap
|
||||
|
||||
> **Version :** 2.28.5 | **Dernière mise à jour :** 2026-09-26
|
||||
> **Version :** 2.28.6 | **Dernière mise à jour :** 2026-09-26
|
||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||
> vers les fonctionnalités livrées.
|
||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||
|
||||
+1
-31
@@ -1576,17 +1576,6 @@
|
||||
class="help-search-clear"
|
||||
id="config-search-clear"
|
||||
title="Effacer"
|
||||
onclick="
|
||||
var s =
|
||||
document.getElementById(
|
||||
'config-nav-search',
|
||||
);
|
||||
if (s) {
|
||||
s.value = '';
|
||||
s.dispatchEvent(new Event('input'));
|
||||
s.focus();
|
||||
}
|
||||
"
|
||||
>
|
||||
X
|
||||
</button>
|
||||
@@ -1698,7 +1687,7 @@
|
||||
<input type="text" id="profile-name" class="config-input" placeholder="Votre nom" maxlength="60">
|
||||
</div>
|
||||
<button class="config-save-btn" id="profile-save" data-i18n="config.save">Enregistrer</button>
|
||||
<button class="config-save-btn" id="profile-logout" style="background:var(--danger-bg);color:var(--danger);border-color:var(--danger);margin-left:8px" data-i18n="config.logout" onclick="if(window.handleLogout)window.handleLogout();else{doLogoutFallback()}">Déconnexion</button>
|
||||
<button class="config-save-btn" id="profile-logout" style="background:var(--danger-bg);color:var(--danger);border-color:var(--danger);margin-left:8px" data-i18n="config.logout">Déconnexion</button>
|
||||
<span class="profile-saved" id="profile-saved" style="display:none" data-i18n="config.saved">✓ Sauvegardé</span>
|
||||
</div>
|
||||
</section>
|
||||
@@ -3005,14 +2994,6 @@
|
||||
id="help-hamburger"
|
||||
title="Sommaire"
|
||||
aria-label="Afficher le sommaire"
|
||||
onclick="
|
||||
var n = document.getElementById('help-nav');
|
||||
if (n) {
|
||||
var d = n.style.display;
|
||||
n.style.display =
|
||||
d === 'none' || d === '' ? 'flex' : 'none';
|
||||
}
|
||||
"
|
||||
>
|
||||
<i
|
||||
data-lucide="menu"
|
||||
@@ -3077,17 +3058,6 @@
|
||||
id="help-search-clear"
|
||||
title="Effacer la recherche"
|
||||
aria-label="Effacer"
|
||||
onclick="
|
||||
var s =
|
||||
document.getElementById(
|
||||
'help-nav-search',
|
||||
);
|
||||
if (s) {
|
||||
s.value = '';
|
||||
s.dispatchEvent(new Event('input'));
|
||||
s.focus();
|
||||
}
|
||||
"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
+12
-2
@@ -1305,8 +1305,7 @@ async function _startMfaSetup() {
|
||||
// secret when the backend has no QR generator available.
|
||||
const qrImg = data.qr_data_url
|
||||
? `<img id="mfa-qr-img" alt="QR Code" class="mfa-qr-code-img"
|
||||
src="${data.qr_data_url}"
|
||||
onerror="this.style.display='none';document.getElementById('mfa-qr-fallback').style.display='block';">`
|
||||
src="${data.qr_data_url}">`
|
||||
: "";
|
||||
const fallbackStyle = data.qr_data_url ? "display:none" : "";
|
||||
flowArea.innerHTML = `
|
||||
@@ -1335,6 +1334,17 @@ async function _startMfaSetup() {
|
||||
codeInput.value = codeInput.value.replace(/[^0-9]/g, "");
|
||||
});
|
||||
|
||||
// QR fallback (#87, ex-onerror inline) : si l'image ne charge pas,
|
||||
// afficher la saisie manuelle du secret.
|
||||
const qrImgEl = document.getElementById("mfa-qr-img");
|
||||
if (qrImgEl) {
|
||||
qrImgEl.addEventListener("error", () => {
|
||||
qrImgEl.style.display = "none";
|
||||
const fallback = document.getElementById("mfa-qr-fallback");
|
||||
if (fallback) fallback.style.display = "block";
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById("mfa-confirm-btn").addEventListener("click", async () => {
|
||||
const code = codeInput.value.trim();
|
||||
if (code.length !== 6) return;
|
||||
|
||||
+39
-3
@@ -363,6 +363,27 @@ function initHelpModal() {
|
||||
}
|
||||
});
|
||||
|
||||
// Help TOC hamburger + search clear (#87, ex-onclick inline in index.html).
|
||||
var helpHamburger = document.getElementById("help-hamburger");
|
||||
if (helpHamburger) {
|
||||
helpHamburger.addEventListener("click", function() {
|
||||
var n = document.getElementById("help-nav");
|
||||
if (n) {
|
||||
var d = n.style.display;
|
||||
n.style.display = d === "none" || d === "" ? "flex" : "none";
|
||||
}
|
||||
});
|
||||
}
|
||||
var helpSearch = document.getElementById("help-nav-search");
|
||||
var helpSearchClear = document.getElementById("help-search-clear");
|
||||
if (helpSearchClear && helpSearch) {
|
||||
helpSearchClear.addEventListener("click", function() {
|
||||
helpSearch.value = "";
|
||||
helpSearch.dispatchEvent(new Event("input"));
|
||||
helpSearch.focus();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" && modal.classList.contains("active")) {
|
||||
closeHelpModal();
|
||||
@@ -883,7 +904,7 @@ function initConfigModal() {
|
||||
});
|
||||
}
|
||||
|
||||
// Logout button — handled inline in index.html (onclick)
|
||||
// Logout button — wired here with addEventListener (#87, no inline onclick)
|
||||
|
||||
// Config nav search
|
||||
var cfgSearch = document.getElementById("config-nav-search");
|
||||
@@ -901,6 +922,16 @@ function initConfigModal() {
|
||||
});
|
||||
}
|
||||
|
||||
// Config search clear button (#87, ex-onclick inline).
|
||||
var cfgSearchClear = document.getElementById("config-search-clear");
|
||||
if (cfgSearchClear && cfgSearch) {
|
||||
cfgSearchClear.addEventListener("click", function() {
|
||||
cfgSearch.value = "";
|
||||
cfgSearch.dispatchEvent(new Event("input"));
|
||||
cfgSearch.focus();
|
||||
});
|
||||
}
|
||||
|
||||
// BUG-071/#114: mobile table of contents. #config-nav shares the .help-nav
|
||||
// rule that hides it below 768px, but — unlike the help modal — the config
|
||||
// modal had no toggle to reveal it, leaving mobile users with no way to
|
||||
@@ -1575,13 +1606,15 @@ export async function openShareDialog(vault, path) {
|
||||
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:4px">${escapeHtml(vault)}/${escapeHtml(path)}</p>
|
||||
${expiresInfo}
|
||||
<p style="font-size:0.75rem;color:var(--text-muted);margin-bottom:8px">${existingShare.access_count} vue(s)</p>
|
||||
<input type="text" class="share-url-input" value="${url}" readonly onclick="this.select()">
|
||||
<input type="text" class="share-url-input" value="${url}" readonly>
|
||||
<div class="share-dialog-actions">
|
||||
<button class="share-copy-btn">📋 Copier le lien</button>
|
||||
<button class="share-revoke-btn">🗑 Révoquer</button>
|
||||
<button class="share-close-btn">Fermer</button>
|
||||
</div>
|
||||
</div>`;
|
||||
const shareUrlInput = div.querySelector(".share-url-input");
|
||||
if (shareUrlInput) shareUrlInput.addEventListener("click", function() { shareUrlInput.select(); });
|
||||
div.querySelector(".share-copy-btn").addEventListener("click", async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
@@ -2525,7 +2558,10 @@ function initProfile() {
|
||||
} catch(e) {}
|
||||
});
|
||||
|
||||
// Logout button — handled inline in index.html (onclick)
|
||||
// Logout button (#87, ex-onclick inline in index.html).
|
||||
if (logoutBtn && window.handleLogout) {
|
||||
logoutBtn.addEventListener('click', function() { window.handleLogout(); });
|
||||
}
|
||||
|
||||
// ── Avatar (#113) ────────────────────────────────────────────────
|
||||
var avatarField = document.getElementById('profile-avatar-field');
|
||||
|
||||
@@ -478,8 +478,8 @@ function openTemplateModal() {
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="modal-footer">' +
|
||||
'<button class="btn btn-primary" data-i18n="common.close" onclick="this.closest(\'.modal-overlay\').remove()">Close</button>' +
|
||||
'<button class="btn btn-secondary" onclick="copyTemplate()">Copy to Clipboard</button>' +
|
||||
'<button class="btn btn-primary btn-modal-close" data-i18n="common.close">Close</button>' +
|
||||
'<button class="btn btn-secondary btn-copy-template">Copy to Clipboard</button>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
@@ -487,11 +487,11 @@ function openTemplateModal() {
|
||||
|
||||
modal.querySelector('.modal-close')?.addEventListener('click', () => modal.remove());
|
||||
modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); });
|
||||
|
||||
window.copyTemplate = () => {
|
||||
modal.querySelector('.btn-modal-close')?.addEventListener('click', () => modal.remove());
|
||||
modal.querySelector('.btn-copy-template')?.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(JSON.stringify(template, null, 2));
|
||||
showToast('Template copied to clipboard', 'success');
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -508,12 +508,13 @@ function openCodeModal(name, code) {
|
||||
'<pre class="code-block" style="max-height: 500px; overflow: auto;">' + escapeHtml(code) + '</pre>' +
|
||||
'</div>' +
|
||||
'<div class="modal-footer">' +
|
||||
'<button class="btn btn-primary" data-i18n="common.close" onclick="this.closest(\'.modal-overlay\').remove()">Close</button>' +
|
||||
'<button class="btn btn-primary btn-modal-close" data-i18n="common.close">Close</button>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
document.body.appendChild(modal);
|
||||
modal.querySelector('.modal-close')?.addEventListener('click', () => modal.remove());
|
||||
modal.querySelector('.btn-modal-close')?.addEventListener('click', () => modal.remove());
|
||||
modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); });
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -543,10 +543,16 @@ function showUpdateNotification() {
|
||||
message.innerHTML = `
|
||||
<div class="pwa-update-content">
|
||||
<span>Une nouvelle version d'ObsiGate est disponible !</span>
|
||||
<button class="pwa-update-btn" onclick="window.location.reload()">Mettre à jour</button>
|
||||
<button class="pwa-update-dismiss" onclick="this.parentElement.parentElement.remove()">×</button>
|
||||
<button class="pwa-update-btn">Mettre à jour</button>
|
||||
<button class="pwa-update-dismiss">×</button>
|
||||
</div>
|
||||
`;
|
||||
message.querySelector(".pwa-update-btn").addEventListener("click", function() {
|
||||
window.location.reload();
|
||||
});
|
||||
message.querySelector(".pwa-update-dismiss").addEventListener("click", function() {
|
||||
message.remove();
|
||||
});
|
||||
document.body.appendChild(message);
|
||||
|
||||
// Auto-dismiss after 30 seconds
|
||||
|
||||
@@ -1140,10 +1140,10 @@ export function renderFile(data) {
|
||||
<div class="pdf-viewer-container">
|
||||
<div class="pdf-toolbar">
|
||||
<span class="pdf-info">PDF — ${pages} pages</span>
|
||||
<button class="btn-action" onclick="window.open('${pdfUrl}', '_blank')">
|
||||
<button class="btn-action" data-pdf-url="${pdfUrl}">
|
||||
<i data-lucide="external-link" style="width:14px;height:14px"></i> Plein écran
|
||||
</button>
|
||||
<button class="btn-action" onclick="window.open('/api/file/${encodeURIComponent(data.vault)}/download?path=${encodeURIComponent(data.path)}', '_blank')">
|
||||
<button class="btn-action" data-download-url="/api/file/${encodeURIComponent(data.vault)}/download?path=${encodeURIComponent(data.path)}">
|
||||
<i data-lucide="download" style="width:14px;height:14px"></i> Télécharger
|
||||
</button>
|
||||
</div>
|
||||
@@ -1152,6 +1152,11 @@ export function renderFile(data) {
|
||||
<iframe src="${pdfUrl}" data-pdf-url="${pdfUrl}" class="pdf-iframe" title="${escapeHtml(data.title)}"></iframe>
|
||||
</div>
|
||||
</div>`;
|
||||
area.querySelectorAll('.pdf-toolbar .btn-action').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
window.open(btn.dataset.pdfUrl || btn.dataset.downloadUrl, '_blank');
|
||||
});
|
||||
});
|
||||
area.querySelectorAll('.pdf-toc a[data-page]').forEach((link) => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsigate",
|
||||
"version": "2.28.5",
|
||||
"version": "2.28.6",
|
||||
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
||||
"main": "patch.js",
|
||||
"directories": {
|
||||
|
||||
Reference in New Issue
Block a user