diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index f988a6d..dea882e 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -53,6 +53,7 @@ export const getDefaultCertOptions = (): Promise => _GetDefaultCert
export const exportCACertificate = (): Promise => _ExportCACertificate();
export const exportServerCertificate = (): Promise => _ExportServerCertificate();
export const exportCertificate = (): Promise => _ExportCertificate();
+export const isCAKeyUnencrypted = (): Promise => callGo('IsCAKeyUnencrypted');
// --- Alerts ---
// These use window.go directly since bindings are generated at build time
diff --git a/frontend/src/lib/events.ts b/frontend/src/lib/events.ts
index 5ca0ae0..8c9c0c1 100644
--- a/frontend/src/lib/events.ts
+++ b/frontend/src/lib/events.ts
@@ -38,8 +38,15 @@ function formatNotification(event: AlertEvent): { title: string; body: string }
};
}
+// notificationsEnabled reflects the Settings "system notifications" toggle,
+// persisted to localStorage (default on).
+function notificationsEnabled(): boolean {
+ return localStorage.getItem('syslogstudio-notifications') !== 'false';
+}
+
function sendSystemNotification(event: AlertEvent) {
if (!notificationsReady || !window.runtime?.SendNotification) return;
+ if (!notificationsEnabled()) return;
try {
const { title, body } = formatNotification(event);
window.runtime.SendNotification({ id: event.id, title, body });
diff --git a/frontend/src/lib/i18n/de.json b/frontend/src/lib/i18n/de.json
index bd96e58..1347ee3 100644
--- a/frontend/src/lib/i18n/de.json
+++ b/frontend/src/lib/i18n/de.json
@@ -1,4 +1,10 @@
{
+ "common": {
+ "close": "Schließen",
+ "previousPage": "Vorherige Seite",
+ "nextPage": "Nächste Seite",
+ "delete": "Löschen"
+ },
"nav": {
"logs": "Protokolle",
"stats": "Statistiken",
@@ -16,7 +22,9 @@
"rate": "Nachrichtenrate",
"buffer": "Ringpuffer-Auslastung",
"dbMessages": "Gespeicherte Nachrichten",
- "dbSize": "Datenbankgröße"
+ "dbSize": "Datenbankgröße",
+ "dbLoading": "Datenbank wird geladen …",
+ "dbIndexing": "Indizierung …"
},
"server": {
"start": "Starten",
@@ -47,8 +55,11 @@
"textExportFailed": "Textexport fehlgeschlagen",
"dateFrom": "Von",
"dateTo": "Bis",
- "regex": "Regex",
- "regexTitle": "Regex-Suche aktivieren"
+ "modeText": "Textsuche (klicken für FTS)",
+ "modeFts": "FTS erweitert: OR, AND, NOT, \"Phrase\", Präfix* (klicken für Regex)",
+ "modeRegex": "Regex-Suche (klicken für Text)",
+ "ftsPlaceholder": "error OR fail OR timeout",
+ "regexPlaceholder": "(error|fail|timeout)"
},
"log": {
"severity": "Schweregrad",
@@ -88,7 +99,8 @@
"rawMessage": "Rohnachricht",
"copy": "Kopieren",
"copiedToClipboard": "In die Zwischenablage kopiert",
- "failedToCopy": "Kopieren in die Zwischenablage fehlgeschlagen"
+ "failedToCopy": "Kopieren in die Zwischenablage fehlgeschlagen",
+ "matchesFound": "Treffer gefunden"
},
"dashboard": {
"totalMessages": "Nachrichten gesamt",
@@ -179,7 +191,8 @@
"checkNow": "Nach Updates suchen",
"checkingUpdates": "Wird geprüft…",
"upToDate": "Sie verwenden die neueste Version.",
- "updateCheckFailed": "Update-Prüfung fehlgeschlagen: {error}"
+ "updateCheckFailed": "Update-Prüfung fehlgeschlagen: {error}",
+ "autoUpdateCheck": "Beim Start nach Updates suchen"
},
"alerts": {
"rules": "Warnregeln",
@@ -211,24 +224,40 @@
"clearFailed": "Verlauf konnte nicht geleert werden"
},
"encryption": {
- "title": "Encryption",
- "enableEncryption": "Encrypt database at rest",
- "hint": "When enabled, the database is encrypted when the app closes and decrypted on startup with your password.",
- "password": "Password",
- "confirmPassword": "Confirm password",
- "currentPassword": "Current password",
- "enable": "Enable Encryption",
- "disable": "Disable Encryption",
- "passwordMismatch": "Passwords do not match.",
- "passwordRequired": "Password is required.",
- "wrongPassword": "Incorrect password. Please try again.",
- "enableSuccess": "Encryption enabled. The database will be encrypted when the app closes.",
- "disableSuccess": "Encryption disabled.",
- "unlockTitle": "Database Locked",
- "unlockMessage": "The log database is encrypted. Enter your password to unlock.",
- "unlock": "Unlock",
- "unlocking": "Unlocking...",
- "lockedOutSeconds": "Zu viele Fehlversuche. Gesperrt für {seconds}s."
+ "title": "Verschlüsselung",
+ "enableEncryption": "Datenbank im Ruhezustand verschlüsseln",
+ "hint": "Wenn aktiviert, wird die Datenbank beim Schließen der App verschlüsselt und beim Start mit Ihrem Passwort entschlüsselt.",
+ "password": "Passwort",
+ "confirmPassword": "Passwort bestätigen",
+ "currentPassword": "Aktuelles Passwort",
+ "enable": "Verschlüsselung aktivieren",
+ "disable": "Verschlüsselung deaktivieren",
+ "passwordMismatch": "Die Passwörter stimmen nicht überein.",
+ "passwordRequired": "Passwort ist erforderlich.",
+ "wrongPassword": "Falsches Passwort. Bitte erneut versuchen.",
+ "enableSuccess": "Verschlüsselung aktiviert. Die Datenbank wird beim Schließen der App verschlüsselt.",
+ "disableSuccess": "Verschlüsselung deaktiviert.",
+ "unlockTitle": "Datenbank gesperrt",
+ "unlockMessage": "Die Protokolldatenbank ist verschlüsselt. Geben Sie Ihr Passwort zum Entsperren ein.",
+ "unlock": "Entsperren",
+ "unlocking": "Wird entsperrt …",
+ "lockedOutSeconds": "Zu viele Fehlversuche. Gesperrt für {seconds}s.",
+ "wrongPasswordAttempts": "Falsches Passwort. Noch {remaining} Versuch(e).",
+ "lockedOut": "Zu viele Fehlversuche. Die Anwendung wird geschlossen.",
+ "warningTitle": "Wichtig — bitte aufmerksam lesen",
+ "warningMessage": "Das Verschlüsselungspasswort wird niemals auf der Festplatte gespeichert. Es existiert nur im Arbeitsspeicher, solange die App läuft.",
+ "warningPoint1": "Wenn Sie Ihr Passwort vergessen, ist Ihre Protokolldatenbank dauerhaft unzugänglich.",
+ "warningPoint2": "Es gibt keinen Wiederherstellungsmechanismus — die Daten sind mit AES-256 verschlüsselt.",
+ "warningPoint3": "Merken Sie sich Ihr Passwort oder speichern Sie es in einem Passwort-Manager.",
+ "warningCancel": "Abbrechen",
+ "warningConfirm": "Verstanden, Verschlüsselung aktivieren",
+ "progressReading": "Verschlüsselte Datenbank wird gelesen …",
+ "progressDeriving": "Passwort wird überprüft …",
+ "progressDecrypting": "Wird entschlüsselt …",
+ "progressWriting": "Datenbank wird geschrieben …",
+ "progressInitializing": "Datenbank wird geöffnet …",
+ "progressIndexing": "Suchindex wird neu aufgebaut …",
+ "progressDone": "Fertig"
},
"update": {
"available": "SyslogStudio {version} ist verfügbar",
@@ -238,5 +267,8 @@
"download": "Herunterladen",
"skip": "Diese Version überspringen",
"later": "Später"
+ },
+ "warnings": {
+ "caKeyUnencrypted": "Warnung: Der private Schlüssel der Zertifizierungsstelle ist unverschlüsselt auf der Festplatte gespeichert."
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/lib/i18n/en.json b/frontend/src/lib/i18n/en.json
index 6cbba22..6e4ccca 100644
--- a/frontend/src/lib/i18n/en.json
+++ b/frontend/src/lib/i18n/en.json
@@ -1,4 +1,10 @@
{
+ "common": {
+ "close": "Close",
+ "previousPage": "Previous page",
+ "nextPage": "Next page",
+ "delete": "Delete"
+ },
"nav": {
"logs": "Logs",
"stats": "Stats",
@@ -261,5 +267,8 @@
"download": "Download",
"skip": "Skip this version",
"later": "Later"
+ },
+ "warnings": {
+ "caKeyUnencrypted": "Warning: the certificate authority private key is stored unencrypted on disk."
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/lib/i18n/es.json b/frontend/src/lib/i18n/es.json
index 7ee25cc..961ab16 100644
--- a/frontend/src/lib/i18n/es.json
+++ b/frontend/src/lib/i18n/es.json
@@ -1,4 +1,10 @@
{
+ "common": {
+ "close": "Cerrar",
+ "previousPage": "Página anterior",
+ "nextPage": "Página siguiente",
+ "delete": "Eliminar"
+ },
"nav": {
"logs": "Registros",
"stats": "Estadísticas",
@@ -16,7 +22,9 @@
"rate": "Tasa de mensajes",
"buffer": "Uso del búfer circular",
"dbMessages": "Mensajes almacenados",
- "dbSize": "Tamaño de la base de datos"
+ "dbSize": "Tamaño de la base de datos",
+ "dbLoading": "Cargando base de datos…",
+ "dbIndexing": "Indexando…"
},
"server": {
"start": "Iniciar",
@@ -47,8 +55,11 @@
"textExportFailed": "Error al exportar texto",
"dateFrom": "Desde",
"dateTo": "Hasta",
- "regex": "Regex",
- "regexTitle": "Activar búsqueda con regex"
+ "modeText": "Búsqueda de texto (clic para cambiar a FTS)",
+ "modeFts": "FTS avanzada: OR, AND, NOT, \"frase\", prefijo* (clic para cambiar a Regex)",
+ "modeRegex": "Búsqueda por regex (clic para cambiar a Texto)",
+ "ftsPlaceholder": "error OR fail OR timeout",
+ "regexPlaceholder": "(error|fail|timeout)"
},
"log": {
"severity": "Severidad",
@@ -88,7 +99,8 @@
"rawMessage": "Mensaje sin procesar",
"copy": "Copiar",
"copiedToClipboard": "Copiado al portapapeles",
- "failedToCopy": "Error al copiar al portapapeles"
+ "failedToCopy": "Error al copiar al portapapeles",
+ "matchesFound": "coincidencias encontradas"
},
"dashboard": {
"totalMessages": "Total de mensajes",
@@ -179,7 +191,8 @@
"checkNow": "Buscar actualizaciones",
"checkingUpdates": "Comprobando…",
"upToDate": "Ya tienes la última versión.",
- "updateCheckFailed": "Error al buscar actualizaciones: {error}"
+ "updateCheckFailed": "Error al buscar actualizaciones: {error}",
+ "autoUpdateCheck": "Buscar actualizaciones al iniciar"
},
"alerts": {
"rules": "Reglas de alerta",
@@ -211,24 +224,40 @@
"clearFailed": "Error al limpiar el historial"
},
"encryption": {
- "title": "Encryption",
- "enableEncryption": "Encrypt database at rest",
- "hint": "When enabled, the database is encrypted when the app closes and decrypted on startup with your password.",
- "password": "Password",
- "confirmPassword": "Confirm password",
- "currentPassword": "Current password",
- "enable": "Enable Encryption",
- "disable": "Disable Encryption",
- "passwordMismatch": "Passwords do not match.",
- "passwordRequired": "Password is required.",
- "wrongPassword": "Incorrect password. Please try again.",
- "enableSuccess": "Encryption enabled. The database will be encrypted when the app closes.",
- "disableSuccess": "Encryption disabled.",
- "unlockTitle": "Database Locked",
- "unlockMessage": "The log database is encrypted. Enter your password to unlock.",
- "unlock": "Unlock",
- "unlocking": "Unlocking...",
- "lockedOutSeconds": "Demasiados intentos fallidos. Bloqueado durante {seconds}s."
+ "title": "Cifrado",
+ "enableEncryption": "Cifrar la base de datos en reposo",
+ "hint": "Si se activa, la base de datos se cifra al cerrar la app y se descifra al iniciar con tu contraseña.",
+ "password": "Contraseña",
+ "confirmPassword": "Confirmar contraseña",
+ "currentPassword": "Contraseña actual",
+ "enable": "Activar cifrado",
+ "disable": "Desactivar cifrado",
+ "passwordMismatch": "Las contraseñas no coinciden.",
+ "passwordRequired": "La contraseña es obligatoria.",
+ "wrongPassword": "Contraseña incorrecta. Inténtalo de nuevo.",
+ "enableSuccess": "Cifrado activado. La base de datos se cifrará al cerrar la app.",
+ "disableSuccess": "Cifrado desactivado.",
+ "unlockTitle": "Base de datos bloqueada",
+ "unlockMessage": "La base de datos de registros está cifrada. Introduce tu contraseña para desbloquear.",
+ "unlock": "Desbloquear",
+ "unlocking": "Desbloqueando…",
+ "lockedOutSeconds": "Demasiados intentos fallidos. Bloqueado durante {seconds}s.",
+ "wrongPasswordAttempts": "Contraseña incorrecta. Quedan {remaining} intento(s).",
+ "lockedOut": "Demasiados intentos fallidos. La aplicación se cerrará.",
+ "warningTitle": "Importante — léelo con atención",
+ "warningMessage": "La contraseña de cifrado nunca se guarda en disco. Solo existe en memoria mientras la app está en ejecución.",
+ "warningPoint1": "Si olvidas tu contraseña, tu base de datos de registros será permanentemente inaccesible.",
+ "warningPoint2": "No hay mecanismo de recuperación: los datos están cifrados con AES-256.",
+ "warningPoint3": "Asegúrate de recordar tu contraseña o guárdala en un gestor de contraseñas.",
+ "warningCancel": "Cancelar",
+ "warningConfirm": "Entiendo, activar cifrado",
+ "progressReading": "Leyendo la base de datos cifrada…",
+ "progressDeriving": "Verificando la contraseña…",
+ "progressDecrypting": "Descifrando…",
+ "progressWriting": "Escribiendo la base de datos…",
+ "progressInitializing": "Abriendo la base de datos…",
+ "progressIndexing": "Reconstruyendo el índice de búsqueda…",
+ "progressDone": "Listo"
},
"update": {
"available": "SyslogStudio {version} está disponible",
@@ -238,5 +267,8 @@
"download": "Descargar",
"skip": "Omitir esta versión",
"later": "Más tarde"
+ },
+ "warnings": {
+ "caKeyUnencrypted": "Advertencia: la clave privada de la autoridad de certificación se almacena sin cifrar en el disco."
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/lib/i18n/fr.json b/frontend/src/lib/i18n/fr.json
index b5dde49..94d323b 100644
--- a/frontend/src/lib/i18n/fr.json
+++ b/frontend/src/lib/i18n/fr.json
@@ -1,4 +1,10 @@
{
+ "common": {
+ "close": "Fermer",
+ "previousPage": "Page précédente",
+ "nextPage": "Page suivante",
+ "delete": "Supprimer"
+ },
"nav": {
"logs": "Journaux",
"stats": "Statistiques",
@@ -261,5 +267,8 @@
"download": "Télécharger",
"skip": "Ignorer cette version",
"later": "Plus tard"
+ },
+ "warnings": {
+ "caKeyUnencrypted": "Attention : la clé privée de l'autorité de certification est stockée en clair sur le disque."
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/lib/i18n/it.json b/frontend/src/lib/i18n/it.json
index c97673a..0c3a546 100644
--- a/frontend/src/lib/i18n/it.json
+++ b/frontend/src/lib/i18n/it.json
@@ -1,4 +1,10 @@
{
+ "common": {
+ "close": "Chiudi",
+ "previousPage": "Pagina precedente",
+ "nextPage": "Pagina successiva",
+ "delete": "Elimina"
+ },
"nav": {
"logs": "Registri",
"stats": "Statistiche",
@@ -16,7 +22,9 @@
"rate": "Frequenza messaggi",
"buffer": "Utilizzo buffer circolare",
"dbMessages": "Messaggi archiviati",
- "dbSize": "Dimensione database"
+ "dbSize": "Dimensione database",
+ "dbLoading": "Caricamento del database…",
+ "dbIndexing": "Indicizzazione…"
},
"server": {
"start": "Avvia",
@@ -47,8 +55,11 @@
"textExportFailed": "Esportazione testo non riuscita",
"dateFrom": "Da",
"dateTo": "A",
- "regex": "Regex",
- "regexTitle": "Attiva ricerca con regex"
+ "modeText": "Ricerca testuale (clic per passare a FTS)",
+ "modeFts": "FTS avanzata: OR, AND, NOT, \"frase\", prefisso* (clic per passare a Regex)",
+ "modeRegex": "Ricerca regex (clic per passare a Testo)",
+ "ftsPlaceholder": "error OR fail OR timeout",
+ "regexPlaceholder": "(error|fail|timeout)"
},
"log": {
"severity": "Gravità",
@@ -88,7 +99,8 @@
"rawMessage": "Messaggio grezzo",
"copy": "Copia",
"copiedToClipboard": "Copiato negli appunti",
- "failedToCopy": "Impossibile copiare negli appunti"
+ "failedToCopy": "Impossibile copiare negli appunti",
+ "matchesFound": "corrispondenze trovate"
},
"dashboard": {
"totalMessages": "Totale messaggi",
@@ -179,7 +191,8 @@
"checkNow": "Controlla aggiornamenti",
"checkingUpdates": "Controllo…",
"upToDate": "Stai usando l'ultima versione.",
- "updateCheckFailed": "Controllo aggiornamenti non riuscito: {error}"
+ "updateCheckFailed": "Controllo aggiornamenti non riuscito: {error}",
+ "autoUpdateCheck": "Controlla aggiornamenti all'avvio"
},
"alerts": {
"rules": "Regole di avviso",
@@ -211,24 +224,40 @@
"clearFailed": "Impossibile cancellare la cronologia"
},
"encryption": {
- "title": "Encryption",
- "enableEncryption": "Encrypt database at rest",
- "hint": "When enabled, the database is encrypted when the app closes and decrypted on startup with your password.",
+ "title": "Crittografia",
+ "enableEncryption": "Cifra il database a riposo",
+ "hint": "Se abilitata, il database viene cifrato alla chiusura dell'app e decifrato all'avvio con la tua password.",
"password": "Password",
- "confirmPassword": "Confirm password",
- "currentPassword": "Current password",
- "enable": "Enable Encryption",
- "disable": "Disable Encryption",
- "passwordMismatch": "Passwords do not match.",
- "passwordRequired": "Password is required.",
- "wrongPassword": "Incorrect password. Please try again.",
- "enableSuccess": "Encryption enabled. The database will be encrypted when the app closes.",
- "disableSuccess": "Encryption disabled.",
- "unlockTitle": "Database Locked",
- "unlockMessage": "The log database is encrypted. Enter your password to unlock.",
- "unlock": "Unlock",
- "unlocking": "Unlocking...",
- "lockedOutSeconds": "Troppi tentativi falliti. Bloccato per {seconds}s."
+ "confirmPassword": "Conferma password",
+ "currentPassword": "Password attuale",
+ "enable": "Abilita crittografia",
+ "disable": "Disabilita crittografia",
+ "passwordMismatch": "Le password non corrispondono.",
+ "passwordRequired": "La password è obbligatoria.",
+ "wrongPassword": "Password errata. Riprova.",
+ "enableSuccess": "Crittografia abilitata. Il database verrà cifrato alla chiusura dell'app.",
+ "disableSuccess": "Crittografia disabilitata.",
+ "unlockTitle": "Database bloccato",
+ "unlockMessage": "Il database dei log è cifrato. Inserisci la password per sbloccarlo.",
+ "unlock": "Sblocca",
+ "unlocking": "Sblocco in corso…",
+ "lockedOutSeconds": "Troppi tentativi falliti. Bloccato per {seconds}s.",
+ "wrongPasswordAttempts": "Password errata. {remaining} tentativo/i rimasti.",
+ "lockedOut": "Troppi tentativi falliti. L'applicazione verrà chiusa.",
+ "warningTitle": "Importante — leggi attentamente",
+ "warningMessage": "La password di crittografia non viene mai salvata su disco. Esiste solo in memoria mentre l'app è in esecuzione.",
+ "warningPoint1": "Se dimentichi la password, il database dei log sarà definitivamente inaccessibile.",
+ "warningPoint2": "Non esiste alcun meccanismo di recupero: i dati sono cifrati con AES-256.",
+ "warningPoint3": "Assicurati di ricordare la password o conservala in un gestore di password.",
+ "warningCancel": "Annulla",
+ "warningConfirm": "Ho capito, abilita la crittografia",
+ "progressReading": "Lettura del database cifrato…",
+ "progressDeriving": "Verifica della password…",
+ "progressDecrypting": "Decifratura…",
+ "progressWriting": "Scrittura del database…",
+ "progressInitializing": "Apertura del database…",
+ "progressIndexing": "Ricostruzione dell'indice di ricerca…",
+ "progressDone": "Fatto"
},
"update": {
"available": "SyslogStudio {version} è disponibile",
@@ -238,5 +267,8 @@
"download": "Scarica",
"skip": "Ignora questa versione",
"later": "Più tardi"
+ },
+ "warnings": {
+ "caKeyUnencrypted": "Attenzione: la chiave privata dell'autorità di certificazione è archiviata non cifrata su disco."
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/lib/i18n/ja.json b/frontend/src/lib/i18n/ja.json
index 5970928..fc89e01 100644
--- a/frontend/src/lib/i18n/ja.json
+++ b/frontend/src/lib/i18n/ja.json
@@ -1,4 +1,10 @@
{
+ "common": {
+ "close": "閉じる",
+ "previousPage": "前のページ",
+ "nextPage": "次のページ",
+ "delete": "削除"
+ },
"nav": {
"logs": "ログ",
"stats": "統計",
@@ -16,7 +22,9 @@
"rate": "メッセージレート",
"buffer": "リングバッファ使用率",
"dbMessages": "保存済みメッセージ",
- "dbSize": "データベースサイズ"
+ "dbSize": "データベースサイズ",
+ "dbLoading": "データベースを読み込み中…",
+ "dbIndexing": "インデックス作成中…"
},
"server": {
"start": "開始",
@@ -47,8 +55,11 @@
"textExportFailed": "テキストエクスポートに失敗しました",
"dateFrom": "開始日",
"dateTo": "終了日",
- "regex": "Regex",
- "regexTitle": "正規表現検索を有効にする"
+ "modeText": "テキスト検索(クリックで FTS に切替)",
+ "modeFts": "FTS 詳細検索:OR、AND、NOT、\"フレーズ\"、接頭辞*(クリックで正規表現に切替)",
+ "modeRegex": "正規表現検索(クリックでテキストに切替)",
+ "ftsPlaceholder": "error OR fail OR timeout",
+ "regexPlaceholder": "(error|fail|timeout)"
},
"log": {
"severity": "重大度",
@@ -88,7 +99,8 @@
"rawMessage": "生メッセージ",
"copy": "コピー",
"copiedToClipboard": "クリップボードにコピーしました",
- "failedToCopy": "クリップボードへのコピーに失敗しました"
+ "failedToCopy": "クリップボードへのコピーに失敗しました",
+ "matchesFound": "件の一致"
},
"dashboard": {
"totalMessages": "メッセージ合計",
@@ -179,7 +191,8 @@
"checkNow": "更新を確認",
"checkingUpdates": "確認中…",
"upToDate": "最新バージョンを使用しています。",
- "updateCheckFailed": "更新の確認に失敗しました: {error}"
+ "updateCheckFailed": "更新の確認に失敗しました: {error}",
+ "autoUpdateCheck": "起動時に更新を確認"
},
"alerts": {
"rules": "アラートルール",
@@ -211,24 +224,40 @@
"clearFailed": "履歴のクリアに失敗しました"
},
"encryption": {
- "title": "Encryption",
- "enableEncryption": "Encrypt database at rest",
- "hint": "When enabled, the database is encrypted when the app closes and decrypted on startup with your password.",
- "password": "Password",
- "confirmPassword": "Confirm password",
- "currentPassword": "Current password",
- "enable": "Enable Encryption",
- "disable": "Disable Encryption",
- "passwordMismatch": "Passwords do not match.",
- "passwordRequired": "Password is required.",
- "wrongPassword": "Incorrect password. Please try again.",
- "enableSuccess": "Encryption enabled. The database will be encrypted when the app closes.",
- "disableSuccess": "Encryption disabled.",
- "unlockTitle": "Database Locked",
- "unlockMessage": "The log database is encrypted. Enter your password to unlock.",
- "unlock": "Unlock",
- "unlocking": "Unlocking...",
- "lockedOutSeconds": "試行回数が多すぎます。{seconds}秒間ロックされます。"
+ "title": "暗号化",
+ "enableEncryption": "データベースを保存時に暗号化",
+ "hint": "有効にすると、アプリ終了時にデータベースが暗号化され、起動時にパスワードで復号されます。",
+ "password": "パスワード",
+ "confirmPassword": "パスワードの確認",
+ "currentPassword": "現在のパスワード",
+ "enable": "暗号化を有効にする",
+ "disable": "暗号化を無効にする",
+ "passwordMismatch": "パスワードが一致しません。",
+ "passwordRequired": "パスワードは必須です。",
+ "wrongPassword": "パスワードが正しくありません。もう一度お試しください。",
+ "enableSuccess": "暗号化を有効にしました。アプリ終了時にデータベースが暗号化されます。",
+ "disableSuccess": "暗号化を無効にしました。",
+ "unlockTitle": "データベースがロックされています",
+ "unlockMessage": "ログデータベースは暗号化されています。ロックを解除するにはパスワードを入力してください。",
+ "unlock": "ロック解除",
+ "unlocking": "ロック解除中…",
+ "lockedOutSeconds": "失敗回数が多すぎます。{seconds}秒間ロックされます。",
+ "wrongPasswordAttempts": "パスワードが正しくありません。残り {remaining} 回。",
+ "lockedOut": "失敗回数が多すぎます。アプリケーションを終了します。",
+ "warningTitle": "重要 — よくお読みください",
+ "warningMessage": "暗号化パスワードはディスクに保存されません。アプリの実行中、メモリ上にのみ存在します。",
+ "warningPoint1": "パスワードを忘れると、ログデータベースには永久にアクセスできなくなります。",
+ "warningPoint2": "復旧手段はありません。データは AES-256 で暗号化されます。",
+ "warningPoint3": "パスワードを必ず覚えるか、パスワードマネージャーに保存してください。",
+ "warningCancel": "キャンセル",
+ "warningConfirm": "理解しました。暗号化を有効にする",
+ "progressReading": "暗号化されたデータベースを読み込み中…",
+ "progressDeriving": "パスワードを検証中…",
+ "progressDecrypting": "復号中…",
+ "progressWriting": "データベースを書き込み中…",
+ "progressInitializing": "データベースを開いています…",
+ "progressIndexing": "検索インデックスを再構築中…",
+ "progressDone": "完了"
},
"update": {
"available": "SyslogStudio {version} が利用可能です",
@@ -238,5 +267,8 @@
"download": "ダウンロード",
"skip": "このバージョンをスキップ",
"later": "後で"
+ },
+ "warnings": {
+ "caKeyUnencrypted": "警告:認証局の秘密鍵が暗号化されずにディスクに保存されています。"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/lib/i18n/pt.json b/frontend/src/lib/i18n/pt.json
index 63e034a..6d007c2 100644
--- a/frontend/src/lib/i18n/pt.json
+++ b/frontend/src/lib/i18n/pt.json
@@ -1,4 +1,10 @@
{
+ "common": {
+ "close": "Fechar",
+ "previousPage": "Página anterior",
+ "nextPage": "Próxima página",
+ "delete": "Excluir"
+ },
"nav": {
"logs": "Registos",
"stats": "Estatísticas",
@@ -16,7 +22,9 @@
"rate": "Taxa de mensagens",
"buffer": "Utilização do buffer circular",
"dbMessages": "Mensagens armazenadas",
- "dbSize": "Tamanho da base de dados"
+ "dbSize": "Tamanho da base de dados",
+ "dbLoading": "Carregando banco de dados…",
+ "dbIndexing": "Indexando…"
},
"server": {
"start": "Iniciar",
@@ -47,8 +55,11 @@
"textExportFailed": "Falha ao exportar texto",
"dateFrom": "De",
"dateTo": "Até",
- "regex": "Regex",
- "regexTitle": "Ativar pesquisa com regex"
+ "modeText": "Busca de texto (clique para mudar para FTS)",
+ "modeFts": "FTS avançada: OR, AND, NOT, \"frase\", prefixo* (clique para mudar para Regex)",
+ "modeRegex": "Busca por regex (clique para mudar para Texto)",
+ "ftsPlaceholder": "error OR fail OR timeout",
+ "regexPlaceholder": "(error|fail|timeout)"
},
"log": {
"severity": "Severidade",
@@ -88,7 +99,8 @@
"rawMessage": "Mensagem em bruto",
"copy": "Copiar",
"copiedToClipboard": "Copiado para a área de transferência",
- "failedToCopy": "Falha ao copiar para a área de transferência"
+ "failedToCopy": "Falha ao copiar para a área de transferência",
+ "matchesFound": "correspondências encontradas"
},
"dashboard": {
"totalMessages": "Total de mensagens",
@@ -179,7 +191,8 @@
"checkNow": "Procurar atualizações",
"checkingUpdates": "Verificando…",
"upToDate": "Você está na versão mais recente.",
- "updateCheckFailed": "Falha ao procurar atualizações: {error}"
+ "updateCheckFailed": "Falha ao procurar atualizações: {error}",
+ "autoUpdateCheck": "Procurar atualizações ao iniciar"
},
"alerts": {
"rules": "Regras de alerta",
@@ -211,24 +224,40 @@
"clearFailed": "Falha ao limpar o histórico"
},
"encryption": {
- "title": "Encryption",
- "enableEncryption": "Encrypt database at rest",
- "hint": "When enabled, the database is encrypted when the app closes and decrypted on startup with your password.",
- "password": "Password",
- "confirmPassword": "Confirm password",
- "currentPassword": "Current password",
- "enable": "Enable Encryption",
- "disable": "Disable Encryption",
- "passwordMismatch": "Passwords do not match.",
- "passwordRequired": "Password is required.",
- "wrongPassword": "Incorrect password. Please try again.",
- "enableSuccess": "Encryption enabled. The database will be encrypted when the app closes.",
- "disableSuccess": "Encryption disabled.",
- "unlockTitle": "Database Locked",
- "unlockMessage": "The log database is encrypted. Enter your password to unlock.",
- "unlock": "Unlock",
- "unlocking": "Unlocking...",
- "lockedOutSeconds": "Muitas tentativas com falha. Bloqueado por {seconds}s."
+ "title": "Criptografia",
+ "enableEncryption": "Criptografar o banco de dados em repouso",
+ "hint": "Quando ativado, o banco de dados é criptografado ao fechar o app e descriptografado ao iniciar com sua senha.",
+ "password": "Senha",
+ "confirmPassword": "Confirmar senha",
+ "currentPassword": "Senha atual",
+ "enable": "Ativar criptografia",
+ "disable": "Desativar criptografia",
+ "passwordMismatch": "As senhas não coincidem.",
+ "passwordRequired": "A senha é obrigatória.",
+ "wrongPassword": "Senha incorreta. Tente novamente.",
+ "enableSuccess": "Criptografia ativada. O banco de dados será criptografado ao fechar o app.",
+ "disableSuccess": "Criptografia desativada.",
+ "unlockTitle": "Banco de dados bloqueado",
+ "unlockMessage": "O banco de dados de logs está criptografado. Digite sua senha para desbloquear.",
+ "unlock": "Desbloquear",
+ "unlocking": "Desbloqueando…",
+ "lockedOutSeconds": "Muitas tentativas com falha. Bloqueado por {seconds}s.",
+ "wrongPasswordAttempts": "Senha incorreta. Resta(m) {remaining} tentativa(s).",
+ "lockedOut": "Muitas tentativas com falha. O aplicativo será fechado.",
+ "warningTitle": "Importante — leia com atenção",
+ "warningMessage": "A senha de criptografia nunca é armazenada em disco. Ela existe apenas na memória enquanto o app está em execução.",
+ "warningPoint1": "Se você esquecer sua senha, seu banco de dados de logs ficará permanentemente inacessível.",
+ "warningPoint2": "Não há mecanismo de recuperação — os dados são criptografados com AES-256.",
+ "warningPoint3": "Certifique-se de lembrar sua senha ou guarde-a em um gerenciador de senhas.",
+ "warningCancel": "Cancelar",
+ "warningConfirm": "Entendi, ativar criptografia",
+ "progressReading": "Lendo o banco de dados criptografado…",
+ "progressDeriving": "Verificando a senha…",
+ "progressDecrypting": "Descriptografando…",
+ "progressWriting": "Gravando o banco de dados…",
+ "progressInitializing": "Abrindo o banco de dados…",
+ "progressIndexing": "Reconstruindo o índice de busca…",
+ "progressDone": "Concluído"
},
"update": {
"available": "SyslogStudio {version} está disponível",
@@ -238,5 +267,8 @@
"download": "Baixar",
"skip": "Ignorar esta versão",
"later": "Mais tarde"
+ },
+ "warnings": {
+ "caKeyUnencrypted": "Aviso: a chave privada da autoridade de certificação está armazenada sem criptografia no disco."
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/lib/i18n/zh.json b/frontend/src/lib/i18n/zh.json
index 0e1e252..5780df7 100644
--- a/frontend/src/lib/i18n/zh.json
+++ b/frontend/src/lib/i18n/zh.json
@@ -1,4 +1,10 @@
{
+ "common": {
+ "close": "关闭",
+ "previousPage": "上一页",
+ "nextPage": "下一页",
+ "delete": "删除"
+ },
"nav": {
"logs": "日志",
"stats": "统计",
@@ -16,7 +22,9 @@
"rate": "消息速率",
"buffer": "环形缓冲区使用率",
"dbMessages": "已存储消息",
- "dbSize": "数据库大小"
+ "dbSize": "数据库大小",
+ "dbLoading": "正在加载数据库…",
+ "dbIndexing": "正在建立索引…"
},
"server": {
"start": "启动",
@@ -47,8 +55,11 @@
"textExportFailed": "文本导出失败",
"dateFrom": "起始",
"dateTo": "截止",
- "regex": "Regex",
- "regexTitle": "启用正则表达式搜索"
+ "modeText": "文本搜索(点击切换到 FTS)",
+ "modeFts": "FTS 高级:OR、AND、NOT、\"短语\"、前缀*(点击切换到正则)",
+ "modeRegex": "正则搜索(点击切换到文本)",
+ "ftsPlaceholder": "error OR fail OR timeout",
+ "regexPlaceholder": "(error|fail|timeout)"
},
"log": {
"severity": "严重程度",
@@ -88,7 +99,8 @@
"rawMessage": "原始消息",
"copy": "复制",
"copiedToClipboard": "已复制到剪贴板",
- "failedToCopy": "复制到剪贴板失败"
+ "failedToCopy": "复制到剪贴板失败",
+ "matchesFound": "个匹配项"
},
"dashboard": {
"totalMessages": "消息总数",
@@ -179,7 +191,8 @@
"checkNow": "检查更新",
"checkingUpdates": "正在检查…",
"upToDate": "您使用的是最新版本。",
- "updateCheckFailed": "检查更新失败:{error}"
+ "updateCheckFailed": "检查更新失败:{error}",
+ "autoUpdateCheck": "启动时检查更新"
},
"alerts": {
"rules": "告警规则",
@@ -211,24 +224,40 @@
"clearFailed": "清除历史记录失败"
},
"encryption": {
- "title": "Encryption",
- "enableEncryption": "Encrypt database at rest",
- "hint": "When enabled, the database is encrypted when the app closes and decrypted on startup with your password.",
- "password": "Password",
- "confirmPassword": "Confirm password",
- "currentPassword": "Current password",
- "enable": "Enable Encryption",
- "disable": "Disable Encryption",
- "passwordMismatch": "Passwords do not match.",
- "passwordRequired": "Password is required.",
- "wrongPassword": "Incorrect password. Please try again.",
- "enableSuccess": "Encryption enabled. The database will be encrypted when the app closes.",
- "disableSuccess": "Encryption disabled.",
- "unlockTitle": "Database Locked",
- "unlockMessage": "The log database is encrypted. Enter your password to unlock.",
- "unlock": "Unlock",
- "unlocking": "Unlocking...",
- "lockedOutSeconds": "失败次数过多。已锁定{seconds}秒。"
+ "title": "加密",
+ "enableEncryption": "静态加密数据库",
+ "hint": "启用后,应用关闭时数据库会被加密,启动时用您的密码解密。",
+ "password": "密码",
+ "confirmPassword": "确认密码",
+ "currentPassword": "当前密码",
+ "enable": "启用加密",
+ "disable": "禁用加密",
+ "passwordMismatch": "两次输入的密码不一致。",
+ "passwordRequired": "必须输入密码。",
+ "wrongPassword": "密码错误,请重试。",
+ "enableSuccess": "已启用加密。应用关闭时数据库将被加密。",
+ "disableSuccess": "已禁用加密。",
+ "unlockTitle": "数据库已锁定",
+ "unlockMessage": "日志数据库已加密。请输入密码以解锁。",
+ "unlock": "解锁",
+ "unlocking": "正在解锁…",
+ "lockedOutSeconds": "失败次数过多。已锁定 {seconds} 秒。",
+ "wrongPasswordAttempts": "密码错误。还剩 {remaining} 次尝试。",
+ "lockedOut": "失败次数过多,应用将关闭。",
+ "warningTitle": "重要 — 请仔细阅读",
+ "warningMessage": "加密密码绝不会保存到磁盘。它仅在应用运行期间存在于内存中。",
+ "warningPoint1": "如果忘记密码,您的日志数据库将永久无法访问。",
+ "warningPoint2": "没有任何恢复机制——数据使用 AES-256 加密。",
+ "warningPoint3": "请务必记住密码,或将其保存在密码管理器中。",
+ "warningCancel": "取消",
+ "warningConfirm": "我已了解,启用加密",
+ "progressReading": "正在读取加密的数据库…",
+ "progressDeriving": "正在验证密码…",
+ "progressDecrypting": "正在解密…",
+ "progressWriting": "正在写入数据库…",
+ "progressInitializing": "正在打开数据库…",
+ "progressIndexing": "正在重建搜索索引…",
+ "progressDone": "完成"
},
"update": {
"available": "SyslogStudio {version} 可用",
@@ -238,5 +267,8 @@
"download": "下载",
"skip": "跳过此版本",
"later": "稍后"
+ },
+ "warnings": {
+ "caKeyUnencrypted": "警告:证书颁发机构的私钥以未加密形式存储在磁盘上。"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/style.css b/frontend/src/style.css
index 0580c9a..03aaef7 100644
--- a/frontend/src/style.css
+++ b/frontend/src/style.css
@@ -7,7 +7,7 @@
--bg-input: #1a2535;
--text-primary: #e0e6ed;
--text-secondary: #8899aa;
- --text-muted: #5a6a7a;
+ --text-muted: #8b9db0;
--border-color: #2a3f58;
--accent: #4a9eff;
--accent-hover: #3a8eef;
@@ -25,9 +25,11 @@
--overlay-bg: rgba(0, 0, 0, 0.5);
--shadow-color: rgba(0, 0, 0, 0.3);
--success-text: #69db7c;
+ color-scheme: dark;
}
[data-theme="light"] {
+ color-scheme: light;
--bg-primary: #f5f7fa;
--bg-secondary: #ffffff;
--bg-tertiary: #edf0f5;
@@ -35,7 +37,7 @@
--bg-input: #ffffff;
--text-primary: #1a2332;
--text-secondary: #4a5568;
- --text-muted: #8899aa;
+ --text-muted: #5a6675;
--border-color: #d1d9e6;
--accent: #2b7de9;
--accent-hover: #1a6dd8;
diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts
index 167c53c..8e3fc54 100644
--- a/frontend/wailsjs/go/main/App.d.ts
+++ b/frontend/wailsjs/go/main/App.d.ts
@@ -74,6 +74,8 @@ export function GetUnlockLockoutSeconds():Promise;
export function GetUpdateConfig():Promise;
+export function IsCAKeyUnencrypted():Promise;
+
export function IsEncryptionEnabled():Promise;
export function IsEncryptionLocked():Promise;
diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js
index 06d69d2..af02052 100644
--- a/frontend/wailsjs/go/main/App.js
+++ b/frontend/wailsjs/go/main/App.js
@@ -146,6 +146,10 @@ export function GetUpdateConfig() {
return window['go']['main']['App']['GetUpdateConfig']();
}
+export function IsCAKeyUnencrypted() {
+ return window['go']['main']['App']['IsCAKeyUnencrypted']();
+}
+
export function IsEncryptionEnabled() {
return window['go']['main']['App']['IsEncryptionEnabled']();
}
From 0e6d10f38cc76152b0721f5bac53a98d6d9c71bd Mon Sep 17 00:00:00 2001
From: Wasabules <39313803+Wasabules@users.noreply.github.com>
Date: Mon, 13 Jul 2026 15:04:23 +0200
Subject: [PATCH 9/9] =?UTF-8?q?Durcissement=20(suite=20audit=20r=C3=A9gres?=
=?UTF-8?q?sion):=20concurrence,=20durabilit=C3=A9,=20updater,=20a11y?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Serveur syslog
- Sérialise le cycle de vie Start/Stop (startStopMu) : un toggle rapide pouvait
faire écraser par un Stop en cours les maps de connexions d'un Start concurrent,
laissant le listener rejeter toutes les connexions (data race + panne).
- Cap de connexions par IP configurable (ServerConfig.MaxConnsPerIP), défaut relevé
de 16 à 128 : 16 cassait les relais/NAT/agrégateurs multiplexant derrière une IP.
- firstReadTimeout 30s -> 120s : ne coupe plus les clients « connect tôt / envoi tard ».
- Journalise le rejet per-IP (diagnostic des resets côté relais).
Stockage
- Chiffrement/déchiffrement : fsync avant rename + fsync du répertoire, pour que le
.enc soit durable avant la suppression du plaintext (évite une perte totale sur crash).
- config.json : écriture fsyncée avant rename (plus de fichier vide sur crash).
- Buffer d'écriture : drop par lot (O(1) amorti) au lieu d'un memmove O(n) par message
sous surcharge. Expose DroppedWrites dans StorageStats + affichage dans les réglages.
Updater
- Détection d'installation Windows par frontière de chemin (plus de faux positif/négatif
entre un exe portable et une install voisine au préfixe commun).
- Téléchargement de l'asset : redirection https non épinglée à GitHub (l'intégrité reste
garantie par le checksum signé), pin GitHub conservé pour API/manifest/signature.
- CanSelfApply=false au check si le manifest signé ou sa signature manquent.
- Nettoie l'installeur renommé si son lancement échoue.
Frontend
- Échap dans les réglages ferme d'abord l'overlay d'avertissement de chiffrement.
Tests
- Ajoute deux tests -race : overlap Start/Stop et balance du compteur per-IP.
---
frontend/src/components/ServerControls.svelte | 16 ++-
frontend/src/components/Settings.svelte | 22 +++-
frontend/src/lib/i18n/de.json | 7 +-
frontend/src/lib/i18n/en.json | 7 +-
frontend/src/lib/i18n/es.json | 7 +-
frontend/src/lib/i18n/fr.json | 7 +-
frontend/src/lib/i18n/it.json | 7 +-
frontend/src/lib/i18n/ja.json | 7 +-
frontend/src/lib/i18n/pt.json | 7 +-
frontend/src/lib/i18n/zh.json | 7 +-
frontend/src/lib/stores.ts | 4 +-
frontend/wailsjs/go/models.ts | 10 +-
internal/models/models.go | 16 +++
internal/storage/config.go | 36 ++++++-
internal/storage/crypto.go | 17 +++
internal/storage/logstore.go | 28 +++--
internal/syslog/server.go | 42 ++++++--
internal/syslog/server_race_test.go | 101 ++++++++++++++++++
internal/updater/apply.go | 7 +-
internal/updater/apply_windows.go | 20 +++-
internal/updater/updater.go | 41 +++++--
21 files changed, 360 insertions(+), 56 deletions(-)
diff --git a/frontend/src/components/ServerControls.svelte b/frontend/src/components/ServerControls.svelte
index 7a142fd..87a783c 100644
--- a/frontend/src/components/ServerControls.svelte
+++ b/frontend/src/components/ServerControls.svelte
@@ -13,7 +13,7 @@
udpPort: 514, tcpPort: 514, tlsPort: 6514, bindAddress: '', allowedSources: [],
maxBuffer: 10000, certFile: '', keyFile: '', useSelfSigned: false,
certOptions: { algorithm: 'ECDSA-P256', validityDays: 365, commonName: 'SyslogStudio', organization: 'SyslogStudio', dnsNames: ['localhost'], ipAddresses: ['127.0.0.1', '::1'] },
- mutualTLS: false, caFile: '',
+ mutualTLS: false, caFile: '', maxConnsPerIP: 128,
};
let error = '';
let allowedSourcesText = '';
@@ -118,6 +118,11 @@
placeholder={$_('server.allowedSources')}
title={$_('server.allowedSourcesHint')}
disabled={$serverStatus.running} class="sources-input" />
+
+ {$_('server.maxConnsPerIP')}
+
+
{#if config.tlsEnabled}
@@ -233,6 +238,15 @@
width: 180px;
}
+ .conns-group {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 12px;
+ color: var(--text-secondary);
+ white-space: nowrap;
+ }
+
.tls-btn {
background: var(--bg-tertiary);
color: var(--text-secondary);
diff --git a/frontend/src/components/Settings.svelte b/frontend/src/components/Settings.svelte
index bd0a2e8..85869ea 100644
--- a/frontend/src/components/Settings.svelte
+++ b/frontend/src/components/Settings.svelte
@@ -35,7 +35,14 @@
}
function handleKeydown(e: KeyboardEvent) {
- if (visible && e.key === 'Escape') onClose();
+ if (!visible || e.key !== 'Escape') return;
+ // Escape dismisses the confirmation overlay first, if open, rather than
+ // closing the whole Settings panel underneath it.
+ if (showEncryptionWarning) {
+ showEncryptionWarning = false;
+ return;
+ }
+ onClose();
}
async function saveUpdatePrefs() {
@@ -74,6 +81,7 @@
let storageMessageCount = 0;
let storageDbSizeMB = 0;
let storageOldest = '';
+ let storageDropped = 0;
let storageLoading = false;
// --- Encryption ---
@@ -138,6 +146,7 @@
storageMessageCount = s.messageCount ?? 0;
storageDbSizeMB = s.databaseSizeMB ?? 0;
storageOldest = s.oldestTimestamp ?? '';
+ storageDropped = s.droppedWrites ?? 0;
}
} catch {}
storageLoading = false;
@@ -566,6 +575,12 @@
{$_('settings.oldestMessage')}
{formatOldest(storageOldest)}
+ {#if storageDropped > 0}
+
+ {/if}
{/if}