localize login page
This commit is contained in:
parent
849ff88134
commit
2bbcbe21f0
|
|
@ -97,7 +97,8 @@ app.add_middleware(
|
||||||
)
|
)
|
||||||
print(f"🌐 CORS allowed origins: {allowed_origins}")
|
print(f"🌐 CORS allowed origins: {allowed_origins}")
|
||||||
|
|
||||||
# ============================================================================
|
# ===========================================================
|
||||||
|
# =================
|
||||||
# Security Helpers
|
# Security Helpers
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
|
@ -253,14 +254,7 @@ async def login_page(request: Request, error: str = None):
|
||||||
async with aiofiles.open(login_path, 'r', encoding='utf-8') as f:
|
async with aiofiles.open(login_path, 'r', encoding='utf-8') as f:
|
||||||
content = await f.read()
|
content = await f.read()
|
||||||
|
|
||||||
# Inject error message if present
|
# No server-side manipulation needed - frontend handles error display via URL params
|
||||||
if error:
|
|
||||||
content = content.replace('<!-- ERROR_CLASS_PLACEHOLDER -->', 'class="error"')
|
|
||||||
content = content.replace('<!-- ERROR_MESSAGE_PLACEHOLDER -->',
|
|
||||||
f'<div class="error-message">{error}</div>')
|
|
||||||
else:
|
|
||||||
content = content.replace('<!-- ERROR_CLASS_PLACEHOLDER -->', '')
|
|
||||||
content = content.replace('<!-- ERROR_MESSAGE_PLACEHOLDER -->', '')
|
|
||||||
|
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
@ -281,8 +275,8 @@ async def login(request: Request, password: str = Form(...)):
|
||||||
request.session['authenticated'] = True
|
request.session['authenticated'] = True
|
||||||
return RedirectResponse(url="/", status_code=303)
|
return RedirectResponse(url="/", status_code=303)
|
||||||
else:
|
else:
|
||||||
# Redirect back to login with error message
|
# Redirect back to login with error code (frontend will translate)
|
||||||
return RedirectResponse(url="/login?error=Incorrect+password.+Please+try+again.", status_code=303)
|
return RedirectResponse(url="/login?error=incorrect_password", status_code=303)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/logout")
|
@app.get("/logout")
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,40 @@
|
||||||
<title>Login - NoteDiscovery</title>
|
<title>Login - NoteDiscovery</title>
|
||||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||||
|
|
||||||
|
<!-- Preload translations (same approach as index.html) -->
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
const locale = localStorage.getItem('locale') || 'en-US';
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', '/api/locales/' + locale, false);
|
||||||
|
try {
|
||||||
|
xhr.send();
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
window.__translations = JSON.parse(xhr.responseText);
|
||||||
|
} else if (locale !== 'en-US') {
|
||||||
|
xhr.open('GET', '/api/locales/en-US', false);
|
||||||
|
xhr.send();
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
window.__translations = JSON.parse(xhr.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to preload translations:', e);
|
||||||
|
window.__translations = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple translation function
|
||||||
|
window.t = function(key) {
|
||||||
|
const keys = key.split('.');
|
||||||
|
let value = window.__translations;
|
||||||
|
for (const k of keys) {
|
||||||
|
value = value?.[k];
|
||||||
|
}
|
||||||
|
return typeof value === 'string' ? value : key;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Theme styles will be loaded dynamically -->
|
<!-- Theme styles will be loaded dynamically -->
|
||||||
<script>
|
<script>
|
||||||
// Load theme immediately (same approach as index.html)
|
// Load theme immediately (same approach as index.html)
|
||||||
|
|
@ -191,7 +225,7 @@
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<img src="/static/logo.svg" alt="NoteDiscovery" class="logo">
|
<img src="/static/logo.svg" alt="NoteDiscovery" class="logo">
|
||||||
<h1>NoteDiscovery</h1>
|
<h1>NoteDiscovery</h1>
|
||||||
<p class="tagline">Your Self-Hosted Knowledge Base</p>
|
<p class="tagline" id="tagline"></p>
|
||||||
|
|
||||||
<form method="post" action="/login">
|
<form method="post" action="/login">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -199,18 +233,16 @@
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
id="password"
|
id="password"
|
||||||
placeholder="Enter your password"
|
|
||||||
required
|
required
|
||||||
autofocus
|
autofocus
|
||||||
<!-- ERROR_CLASS_PLACEHOLDER -->
|
|
||||||
>
|
>
|
||||||
<!-- ERROR_MESSAGE_PLACEHOLDER -->
|
<div class="error-message" id="error-message" style="display: none;"></div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit">🔓 Unlock</button>
|
<button type="submit" id="submit-btn"></button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
🔒 Secure & Self-Hosted
|
<span id="footer-text"></span>
|
||||||
<div style="margin-top: 8px; font-size: 12px; opacity: 0.6;">
|
<div style="margin-top: 8px; font-size: 12px; opacity: 0.6;">
|
||||||
<a href="https://www.notediscovery.com" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
|
<a href="https://www.notediscovery.com" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
|
||||||
<svg style="width: 14px; height: 14px;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg style="width: 14px; height: 14px;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
|
@ -221,6 +253,26 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Apply translations after DOM is ready
|
||||||
|
document.getElementById('tagline').textContent = t('login.tagline');
|
||||||
|
document.getElementById('password').placeholder = t('login.password_placeholder');
|
||||||
|
document.getElementById('submit-btn').textContent = t('login.unlock_button');
|
||||||
|
document.getElementById('footer-text').textContent = t('login.footer');
|
||||||
|
document.title = t('login.title') + ' - NoteDiscovery';
|
||||||
|
|
||||||
|
// Handle error from URL params
|
||||||
|
var urlParams = new URLSearchParams(window.location.search);
|
||||||
|
var errorCode = urlParams.get('error');
|
||||||
|
if (errorCode) {
|
||||||
|
var errorEl = document.getElementById('error-message');
|
||||||
|
var passwordEl = document.getElementById('password');
|
||||||
|
errorEl.textContent = t('login.error_' + errorCode);
|
||||||
|
errorEl.style.display = 'flex';
|
||||||
|
passwordEl.classList.add('error');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,15 @@
|
||||||
"failed": "HTML-Export fehlgeschlagen: {{error}}"
|
"failed": "HTML-Export fehlgeschlagen: {{error}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"login": {
|
||||||
|
"title": "Anmelden",
|
||||||
|
"tagline": "Deine Selbstgehostete Wissensdatenbank",
|
||||||
|
"password_placeholder": "Passwort eingeben",
|
||||||
|
"unlock_button": "🔓 Entsperren",
|
||||||
|
"footer": "🔒 Sicher & Selbstgehostet",
|
||||||
|
"error_incorrect_password": "Falsches Passwort. Bitte versuche es erneut."
|
||||||
|
},
|
||||||
|
|
||||||
"images": {
|
"images": {
|
||||||
"confirm_delete": "Bild \"{{name}}\" löschen?",
|
"confirm_delete": "Bild \"{{name}}\" löschen?",
|
||||||
"upload_failed": "Bild-Upload fehlgeschlagen",
|
"upload_failed": "Bild-Upload fehlgeschlagen",
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,15 @@
|
||||||
"failed": "Failed to export HTML: {{error}}"
|
"failed": "Failed to export HTML: {{error}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"login": {
|
||||||
|
"title": "Login",
|
||||||
|
"tagline": "Your Self-Hosted Knowledge Base",
|
||||||
|
"password_placeholder": "Enter your password",
|
||||||
|
"unlock_button": "🔓 Unlock",
|
||||||
|
"footer": "🔒 Secure & Self-Hosted",
|
||||||
|
"error_incorrect_password": "Incorrect password. Please try again."
|
||||||
|
},
|
||||||
|
|
||||||
"images": {
|
"images": {
|
||||||
"confirm_delete": "Delete image \"{{name}}\"?",
|
"confirm_delete": "Delete image \"{{name}}\"?",
|
||||||
"upload_failed": "Failed to upload image",
|
"upload_failed": "Failed to upload image",
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,15 @@
|
||||||
"failed": "Error al exportar HTML: {{error}}"
|
"failed": "Error al exportar HTML: {{error}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"login": {
|
||||||
|
"title": "Iniciar sesión",
|
||||||
|
"tagline": "Tu Base de Conocimientos Autoalojada",
|
||||||
|
"password_placeholder": "Introduce tu contraseña",
|
||||||
|
"unlock_button": "🔓 Desbloquear",
|
||||||
|
"footer": "🔒 Seguro y Autoalojado",
|
||||||
|
"error_incorrect_password": "Contraseña incorrecta. Por favor, inténtalo de nuevo."
|
||||||
|
},
|
||||||
|
|
||||||
"images": {
|
"images": {
|
||||||
"confirm_delete": "¿Eliminar imagen \"{{name}}\"?",
|
"confirm_delete": "¿Eliminar imagen \"{{name}}\"?",
|
||||||
"upload_failed": "Error al subir imagen",
|
"upload_failed": "Error al subir imagen",
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,15 @@
|
||||||
"failed": "Échec de l'export HTML : {{error}}"
|
"failed": "Échec de l'export HTML : {{error}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"login": {
|
||||||
|
"title": "Connexion",
|
||||||
|
"tagline": "Votre Base de Connaissances Auto-Hébergée",
|
||||||
|
"password_placeholder": "Entrez votre mot de passe",
|
||||||
|
"unlock_button": "🔓 Déverrouiller",
|
||||||
|
"footer": "🔒 Sécurisé et Auto-Hébergé",
|
||||||
|
"error_incorrect_password": "Mot de passe incorrect. Veuillez réessayer."
|
||||||
|
},
|
||||||
|
|
||||||
"images": {
|
"images": {
|
||||||
"confirm_delete": "Supprimer l'image \"{{name}}\" ?",
|
"confirm_delete": "Supprimer l'image \"{{name}}\" ?",
|
||||||
"upload_failed": "Échec du téléchargement de l'image",
|
"upload_failed": "Échec du téléchargement de l'image",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue