Add password visibility toggle to login; auto-sync admin password from ENV
- Login.tsx: Eye/EyeOff toggle button on password field - main.py: _seed_admin() now updates stored bcrypt hash when ENV password changed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|||||||
from .database import init_db, AsyncSessionLocal
|
from .database import init_db, AsyncSessionLocal
|
||||||
from .config import get_settings
|
from .config import get_settings
|
||||||
from .models import User, Library
|
from .models import User, Library
|
||||||
from .services.auth import hash_password, create_token
|
from .services.auth import hash_password, verify_password, create_token
|
||||||
from .services.file_watcher import start_file_watcher, stop_file_watcher
|
from .services.file_watcher import start_file_watcher, stop_file_watcher
|
||||||
from .services.podcast_feed import update_all_feeds
|
from .services.podcast_feed import update_all_feeds
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
@@ -25,7 +25,12 @@ async def _seed_admin():
|
|||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
async with AsyncSessionLocal() as db:
|
async with AsyncSessionLocal() as db:
|
||||||
result = await db.execute(select(User).where(User.is_admin == True))
|
result = await db.execute(select(User).where(User.is_admin == True))
|
||||||
if result.scalar_one_or_none():
|
existing = result.scalar_one_or_none()
|
||||||
|
if existing:
|
||||||
|
if not verify_password(settings.admin_password, existing.password_hash):
|
||||||
|
existing.password_hash = hash_password(settings.admin_password)
|
||||||
|
await db.commit()
|
||||||
|
logger.info("Admin-Passwort aus ENV aktualisiert.")
|
||||||
return
|
return
|
||||||
logger.info(f"Lege Admin-User an: {settings.admin_username}")
|
logger.info(f"Lege Admin-User an: {settings.admin_username}")
|
||||||
admin = User(
|
admin = User(
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { BookOpen, Loader2 } from 'lucide-react'
|
import { BookOpen, Loader2, Eye, EyeOff } from 'lucide-react'
|
||||||
import { useAuthStore } from '../store/authStore'
|
import { useAuthStore } from '../store/authStore'
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const [username, setUsername] = useState('')
|
const [username, setUsername] = useState('')
|
||||||
const [password, setPassword] = useState('')
|
const [password, setPassword] = useState('')
|
||||||
|
const [showPwd, setShowPwd] = useState(false)
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
const { login, loading } = useAuthStore()
|
const { login, loading } = useAuthStore()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@@ -43,13 +44,23 @@ export default function Login() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-gray-400 mb-1">Passwort</label>
|
<label className="block text-sm text-gray-400 mb-1">Passwort</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type={showPwd ? 'text' : 'password'}
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-primary"
|
className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 pr-10 text-white placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-primary"
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPwd(!showPwd)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-white"
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
|
{showPwd ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{error && <p className="text-red-400 text-sm">{error}</p>}
|
{error && <p className="text-red-400 text-sm">{error}</p>}
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user