码桶

发现社区成员的开源项目

Login.jsx36.8 KB
import React, { useState, useEffect } from 'react';
import { Globe, Server, User, Shield, Key, RefreshCw, Fingerprint, Languages, Zap } from 'lucide-react';
import { startAuthentication } from '@simplewebauthn/browser';
import { hashPassword, isPasswordStrong } from '../utils/auth.ts';
import SecurityBadges from './SecurityBadges.jsx';

const Login = ({ onLogin, t, lang, onLangChange }) => {
    const [username, setUsername] = useState('admin');
    const [password, setPassword] = useState('');
    const [token, setToken] = useState('');
    const [globalEmail, setGlobalEmail] = useState('');
    const [globalKey, setGlobalKey] = useState('');
    const [clientTokenType, setClientTokenType] = useState('api_token'); // 'api_token' | 'global_key'
    const [loginTab, setLoginTab] = useState('server'); // 'server' | 'client' | 'setup' | 'register'
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState('');
    const [successMsg, setSuccessMsg] = useState('');

    const [remember, setRemember] = useState(false);
    const [openRegistration, setOpenRegistration] = useState(false);

    // TOTP 2FA state
    const [totpStep, setTotpStep] = useState(false);
    const [totpCode, setTotpCode] = useState('');
    const [totpUsername, setTotpUsername] = useState('');
    const [totpPassword, setTotpPassword] = useState('');

    // Setup account fields
    const [setupUsername, setSetupUsername] = useState('');
    const [setupToken, setSetupToken] = useState('');
    const [setupPassword, setSetupPassword] = useState('');
    const [setupConfirm, setSetupConfirm] = useState('');

    // Register fields
    const [regUsername, setRegUsername] = useState('');
    const [regPassword, setRegPassword] = useState('');
    const [regConfirm, setRegConfirm] = useState('');

    // Fetch public settings on mount
    useEffect(() => {
        fetch('/api/public-settings').then(r => r.json()).then(data => {
            setOpenRegistration(!!data.openRegistration);
        }).catch((err) => { console.error('Failed to fetch public settings:', err); });
    }, []);

    const supportsPasskey = typeof window !== 'undefined' && !!window.PublicKeyCredential;

    const handlePasskeyLogin = async () => {
        const passkeyUsername = username.trim().toLowerCase();
        if (!passkeyUsername) {
            setError(t('passkeyUsernameRequired'));
            return;
        }
        setLoading(true);
        setError('');
        try {
            const optRes = await fetch('/api/passkey/login-options', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ username: passkeyUsername })
            });
            const optData = await optRes.json();
            if (!optRes.ok) {
                setError(optData.error || t('passkeyError'));
                setLoading(false);
                return;
            }
            const authResp = await startAuthentication({ optionsJSON: optData });
            const verifyRes = await fetch('/api/passkey/login-verify', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(authResp)
            });
            const verifyData = await verifyRes.json();
            if (verifyRes.ok && verifyData.token) {
                onLogin({
                    mode: 'server',
                    token: verifyData.token,
                    remember,
                    accounts: verifyData.accounts || [],
                    currentAccountIndex: verifyData.accounts?.[0]?.id || 0,
                    role: verifyData.role || 'user',
                    username: verifyData.username || passkeyUsername
                });
            } else {
                setError(verifyData.error || t('passkeyError'));
            }
        } catch (err) {
            if (err.name !== 'NotAllowedError') {
                setError(t('passkeyError'));
            }
        } finally {
            setLoading(false);
        }
    };

    const handleLogin = async (e) => {
        e.preventDefault();

        const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
        if (window.location.protocol === 'http:' && !isLocalhost) {
            setError(t('httpWarning'));
            return;
        }

        setLoading(true);
        setError('');
        setSuccessMsg('');

        try {
            if (loginTab === 'server') {
                const hashedPassword = await hashPassword(password);
                const res = await fetch('/api/login', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ username: username.trim() || 'admin', password: hashedPassword })
                });
                const data = await res.json();
                if (res.ok) {
                    if (data.requiresTOTP) {
                        setTotpStep(true);
                        setTotpUsername(data.username);
                        setTotpPassword(hashedPassword);
                        setLoading(false);
                        return;
                    }
                    onLogin({
                        mode: 'server',
                        token: data.token,
                        refreshToken: data.refreshToken,
                        remember,
                        accounts: data.accounts || [],
                        currentAccountIndex: data.accounts?.[0]?.id || 0,
                        role: data.role || 'user',
                        username: data.username || 'admin'
                    });
                } else {
                    let errMsg = data.error || t('loginFailed');
                    if (errMsg.includes('Invalid username or password')) errMsg = t('invalidPassword');
                    if (errMsg.includes('Server is not configured')) errMsg = t('serverNotConfigured');
                    if (data.lockedUntil || errMsg.includes('temporarily locked')) errMsg = t('accountLocked');
                    if (data.needsSetup) {
                        errMsg = t('needsSetup');
                        setLoginTab('setup');
                        setSetupUsername(username.trim());
                    }
                    setError(errMsg);
                }
            } else if (loginTab === 'client') {
                const isGlobal = clientTokenType === 'global_key';
                const verifyHeaders = isGlobal
                    ? { 'X-Cloudflare-Token': globalKey, 'X-Cloudflare-Email': globalEmail }
                    : { 'X-Cloudflare-Token': token };
                const res = await fetch('/api/verify-token', { headers: verifyHeaders });
                const data = await res.json();
                if (res.ok && data.success) {
                    const loginData = isGlobal
                        ? { mode: 'client', token: globalKey, email: globalEmail, remember }
                        : { mode: 'client', token: token, remember };
                    onLogin(loginData);
                } else {
                    let errMsg = data.message || t('loginFailed');
                    if (errMsg === 'Invalid token') errMsg = t('invalidToken');
                    if (errMsg === 'No token provided') errMsg = t('tokenRequired');
                    if (errMsg === 'Failed to verify token') errMsg = t('verifyFailed');
                    setError(errMsg);
                }
            }
        } catch (err) {
            setError(t('errorOccurred'));
        } finally {
            setLoading(false);
        }
    };

    const handleSetupAccount = async (e) => {
        e.preventDefault();
        if (!setupUsername.trim() || !setupToken.trim() || !setupPassword.trim()) return;

        if (setupPassword !== setupConfirm) {
            setError(t('passwordMismatch'));
            return;
        }
        if (!isPasswordStrong(setupPassword)) {
            setError(t('passwordTooWeak'));
            return;
        }

        setLoading(true);
        setError('');
        setSuccessMsg('');

        try {
            const hashedPwd = await hashPassword(setupPassword);
            const res = await fetch('/api/setup-account', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ username: setupUsername.trim().toLowerCase(), setupToken: setupToken.trim(), password: hashedPwd })
            });
            const data = await res.json();
            if (res.ok && data.success) {
                setSuccessMsg(t('setupSuccess'));
                setSetupUsername('');
                setSetupToken('');
                setSetupPassword('');
                setSetupConfirm('');
                setTimeout(() => { setLoginTab('server'); setSuccessMsg(''); }, 2000);
            } else {
                setError(data.error || t('setupFailed'));
            }
        } catch (err) {
            setError(t('errorOccurred'));
        } finally {
            setLoading(false);
        }
    };

    const handleRegister = async (e) => {
        e.preventDefault();
        if (!regUsername.trim() || !regPassword.trim()) return;

        if (regPassword !== regConfirm) {
            setError(t('passwordMismatch'));
            return;
        }
        if (!isPasswordStrong(regPassword)) {
            setError(t('passwordTooWeak'));
            return;
        }

        setLoading(true);
        setError('');
        setSuccessMsg('');

        try {
            const hashedPwd = await hashPassword(regPassword);
            const res = await fetch('/api/register', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ username: regUsername.trim().toLowerCase(), password: hashedPwd })
            });
            const data = await res.json();
            if (res.ok && data.success) {
                setSuccessMsg(t('registerSuccess'));
                setRegUsername('');
                setRegPassword('');
                setRegConfirm('');
                setTimeout(() => { setLoginTab('server'); setSuccessMsg(''); }, 2000);
            } else {
                setError(data.error || t('registerFailed'));
            }
        } catch (err) {
            setError(t('errorOccurred'));
        } finally {
            setLoading(false);
        }
    };

    const handleTOTPVerify = async (e) => {
        e.preventDefault();
        if (!totpCode.trim()) return;
        setLoading(true);
        setError('');
        try {
            const res = await fetch('/api/verify-totp', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ username: totpUsername, password: totpPassword, code: totpCode.trim() })
            });
            const data = await res.json();
            if (res.ok && data.token) {
                onLogin({
                    mode: 'server',
                    token: data.token,
                    refreshToken: data.refreshToken,
                    remember,
                    accounts: data.accounts || [],
                    currentAccountIndex: data.accounts?.[0]?.id || 0,
                    role: data.role || 'user',
                    username: data.username || totpUsername
                });
            } else {
                setError(data.error || t('totpInvalid'));
            }
        } catch (err) {
            setError(t('errorOccurred'));
        } finally {
            setLoading(false);
        }
    };

    return (
        <div className="container" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', padding: '1rem' }}>
            <div className="glass-card login-card fade-in">
                <div style={{ position: 'absolute', top: '1rem', right: '1rem', zIndex: 10 }}>
                    <button
                        onClick={(e) => {
                            e.preventDefault();
                            const cycle = ['zh', 'en', 'ja', 'ko'];
                            const idx = cycle.indexOf(lang);
                            onLangChange(cycle[(idx + 1) % cycle.length]);
                        }}
                        style={{ border: 'none', background: 'transparent', padding: '4px 8px', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '4px', color: 'var(--text-muted)', borderRadius: '8px', transition: 'background 0.2s', fontSize: '0.75rem', fontWeight: 600 }}
                        onMouseEnter={(e) => e.currentTarget.style.background = 'var(--hover-btn-bg)'}
                        onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
                        title={{ zh: 'English', en: '日本語', ja: '한국어', ko: '中文' }[lang]}
                        aria-label={{ zh: 'Switch to English', en: '日本語に切り替え', ja: '한국어로 전환', ko: '切换到中文' }[lang]}
                    >
                        <Languages size={18} />
                        <span>{lang.toUpperCase()}</span>
                    </button>
                </div>

                <div style={{ textAlign: 'center', marginBottom: '2rem' }}>
                    <div style={{ display: 'inline-flex', padding: '0.75rem', background: 'rgba(243, 128, 32, 0.1)', borderRadius: '12px', marginBottom: '1rem' }}>
                        <Zap size={32} color="var(--primary)" />
                    </div>
                    <h1 style={{ fontSize: '1.5rem', marginBottom: '0.25rem' }}>{t('title')}</h1>
                    <p style={{ color: 'var(--text-muted)', fontSize: '0.875rem' }}>{t('subtitle')}</p>
                </div>

                <div style={{ display: 'flex', gap: '0.5rem', marginBottom: '1.5rem', padding: '4px', background: 'var(--tab-bg)', borderRadius: '8px' }}>
                    <button
                        className={`btn ${loginTab === 'server' ? 'btn-primary' : 'btn-outline'}`}
                        style={{ flex: 1, padding: '0.4rem', border: 'none', fontSize: '0.8rem' }}
                        onClick={() => { setLoginTab('server'); setError(''); setSuccessMsg(''); setTotpStep(false); setTotpCode(''); }}
                    >
                        {t('serverMode')}
                    </button>
                    <button
                        className={`btn ${loginTab === 'client' ? 'btn-primary' : 'btn-outline'}`}
                        style={{ flex: 1, padding: '0.4rem', border: 'none', fontSize: '0.8rem' }}
                        onClick={() => { setLoginTab('client'); setError(''); setSuccessMsg(''); setTotpStep(false); setTotpCode(''); }}
                    >
                        {t('clientMode')}
                    </button>
                    {openRegistration ? (
                        <button
                            className={`btn ${loginTab === 'register' ? 'btn-primary' : 'btn-outline'}`}
                            style={{ flex: 1, padding: '0.4rem', border: 'none', fontSize: '0.8rem' }}
                            onClick={() => { setLoginTab('register'); setError(''); setSuccessMsg(''); setTotpStep(false); setTotpCode(''); }}
                        >
                            {t('register')}
                        </button>
                    ) : (
                        <button
                            className={`btn ${loginTab === 'setup' ? 'btn-primary' : 'btn-outline'}`}
                            style={{ flex: 1, padding: '0.4rem', border: 'none', fontSize: '0.8rem' }}
                            onClick={() => { setLoginTab('setup'); setError(''); setSuccessMsg(''); setTotpStep(false); setTotpCode(''); }}
                        >
                            {t('setupAccount')}
                        </button>
                    )}
                </div>

                {totpStep ? (
                    <form onSubmit={handleTOTPVerify}>
                        <div style={{ textAlign: 'center', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'inline-flex', padding: '0.75rem', background: 'rgba(147, 51, 234, 0.1)', borderRadius: '12px', marginBottom: '0.75rem' }}>
                                <Shield size={28} color="#9333ea" />
                            </div>
                            <h3 style={{ fontSize: '1rem', marginBottom: '0.25rem' }}>{t('totpStep')}</h3>
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>{t('totpRequired')}</p>
                        </div>
                        <div className="input-group">
                            <div style={{ position: 'relative' }}>
                                <Shield size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                <input
                                    type="text"
                                    inputMode="numeric"
                                    pattern="[0-9]*"
                                    maxLength={6}
                                    placeholder={t('totpCodePlaceholder')}
                                    value={totpCode}
                                    onChange={(e) => setTotpCode(e.target.value.replace(/\D/g, '').slice(0, 6))}
                                    style={{ paddingLeft: '38px', textAlign: 'center', fontSize: '1.2rem', letterSpacing: '0.3em' }}
                                    autoFocus
                                    required
                                />
                            </div>
                        </div>
                        {error && <p style={{ color: 'var(--error)', fontSize: '0.75rem', marginBottom: '1rem', textAlign: 'center' }}>{error}</p>}
                        <button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} disabled={loading || totpCode.length !== 6}>
                            {loading ? <RefreshCw className="spin" size={18} /> : t('totpVerify')}
                        </button>
                        <button type="button" className="btn btn-outline" style={{ width: '100%', justifyContent: 'center', marginTop: '0.5rem' }}
                            onClick={() => { setTotpStep(false); setTotpCode(''); setError(''); }}>
                            {t('cancel') || 'Back'}
                        </button>
                    </form>
                ) : loginTab === 'register' ? (
                    <form onSubmit={handleRegister}>
                        <p style={{ fontSize: '0.75rem', color: 'var(--text-muted)', marginBottom: '1rem', textAlign: 'center' }}>
                            {t('registerDesc')}
                        </p>
                        <div className="input-group">
                            <label>{t('usernameLabel')}</label>
                            <div style={{ position: 'relative' }}>
                                <User size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                <input type="text" placeholder={t('usernamePlaceholder')} value={regUsername}
                                    onChange={(e) => setRegUsername(e.target.value)} style={{ paddingLeft: '38px' }} required />
                            </div>
                        </div>
                        <div className="input-group">
                            <label>{t('newPassword')}</label>
                            <div style={{ position: 'relative' }}>
                                <Key size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                <input type="password" placeholder={t('newPasswordPlaceholder2')} value={regPassword}
                                    onChange={(e) => setRegPassword(e.target.value)} style={{ paddingLeft: '38px' }} required />
                            </div>
                        </div>
                        <div className="input-group">
                            <label>{t('confirmNewPassword')}</label>
                            <div style={{ position: 'relative' }}>
                                <Key size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                <input type="password" placeholder={t('confirmNewPasswordPlaceholder')} value={regConfirm}
                                    onChange={(e) => setRegConfirm(e.target.value)} style={{ paddingLeft: '38px' }} required />
                            </div>
                        </div>

                        {error && <p style={{ color: 'var(--error)', fontSize: '0.75rem', marginBottom: '1rem', textAlign: 'center' }}>{error}</p>}
                        {successMsg && <p style={{ color: 'var(--success)', fontSize: '0.75rem', marginBottom: '1rem', textAlign: 'center' }}>{successMsg}</p>}

                        <button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} disabled={loading}>
                            {loading ? <RefreshCw className="spin" size={18} /> : t('register')}
                        </button>
                    </form>
                ) : loginTab === 'setup' ? (
                    <form onSubmit={handleSetupAccount}>
                        <p style={{ fontSize: '0.75rem', color: 'var(--text-muted)', marginBottom: '1rem', textAlign: 'center' }}>
                            {t('setupAccountDesc')}
                        </p>
                        <div className="input-group">
                            <label>{t('usernameLabel')}</label>
                            <div style={{ position: 'relative' }}>
                                <User size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                <input type="text" placeholder={t('usernamePlaceholder')} value={setupUsername}
                                    onChange={(e) => setSetupUsername(e.target.value)} style={{ paddingLeft: '38px' }} required />
                            </div>
                        </div>
                        <div className="input-group">
                            <label>{t('setupTokenLabel')}</label>
                            <div style={{ position: 'relative' }}>
                                <Shield size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                <input type="text" placeholder={t('setupTokenPlaceholder')} value={setupToken}
                                    onChange={(e) => setSetupToken(e.target.value)} style={{ paddingLeft: '38px', fontFamily: 'monospace', fontSize: '0.8rem' }} required />
                            </div>
                            <p style={{ fontSize: '0.7rem', marginTop: '0.35rem', color: 'var(--text-muted)', fontStyle: 'italic' }}>
                                {t('tokenRequiredPermissions')}
                            </p>
                        </div>
                        <div className="input-group">
                            <label>{t('newPassword')}</label>
                            <div style={{ position: 'relative' }}>
                                <Key size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                <input type="password" placeholder={t('newPasswordPlaceholder2')} value={setupPassword}
                                    onChange={(e) => setSetupPassword(e.target.value)} style={{ paddingLeft: '38px' }} required />
                            </div>
                        </div>
                        <div className="input-group">
                            <label>{t('confirmNewPassword')}</label>
                            <div style={{ position: 'relative' }}>
                                <Key size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                <input type="password" placeholder={t('confirmNewPasswordPlaceholder')} value={setupConfirm}
                                    onChange={(e) => setSetupConfirm(e.target.value)} style={{ paddingLeft: '38px' }} required />
                            </div>
                        </div>

                        {error && <p style={{ color: 'var(--error)', fontSize: '0.75rem', marginBottom: '1rem', textAlign: 'center' }}>{error}</p>}
                        {successMsg && <p style={{ color: 'var(--success)', fontSize: '0.75rem', marginBottom: '1rem', textAlign: 'center' }}>{successMsg}</p>}

                        <button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} disabled={loading}>
                            {loading ? <RefreshCw className="spin" size={18} /> : t('setupAccount')}
                        </button>
                    </form>
                ) : (
                    <form onSubmit={handleLogin}>
                        {loginTab === 'server' ? (
                            <>
                            <div className="input-group">
                                <label>{t('usernameLabel')}</label>
                                <div style={{ position: 'relative' }}>
                                    <User size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                    <input
                                        type="text"
                                        placeholder={t('usernamePlaceholder')}
                                        value={username}
                                        onChange={(e) => setUsername(e.target.value)}
                                        style={{ paddingLeft: '38px' }}
                                        required
                                    />
                                </div>
                            </div>
                            <div className="input-group">
                                <label>{t('passwordLabel')}</label>
                                <div style={{ position: 'relative' }}>
                                    <Key size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                    <input
                                        type="password"
                                        placeholder={t('passwordPlaceholder')}
                                        value={password}
                                        onChange={(e) => setPassword(e.target.value)}
                                        style={{ paddingLeft: '38px' }}
                                        required
                                    />
                                </div>
                                <p style={{ fontSize: '0.75rem', marginTop: '0.5rem', color: 'var(--text-muted)' }}>
                                    {t('serverHint')}
                                </p>
                            </div>
                            </>
                        ) : (
                            <div>
                                {/* API Token / Global API Key sub-tabs */}
                                <div className="auth-type-tabs" style={{ display: 'flex', gap: '0', marginBottom: '1rem', borderRadius: '6px', overflow: 'hidden', border: '1px solid var(--border)' }}>
                                    <button type="button"
                                        onClick={() => { setClientTokenType('api_token'); setError(''); }}
                                        style={{
                                            flex: 1, padding: '0.5rem 0.5rem', fontSize: '0.75rem', fontWeight: 600,
                                            border: 'none', cursor: 'pointer',
                                            background: clientTokenType === 'api_token' ? 'var(--primary)' : 'transparent',
                                            color: clientTokenType === 'api_token' ? 'white' : 'var(--text-muted)',
                                        }}>
                                        {t('apiTokenTab')}
                                    </button>
                                    <button type="button"
                                        onClick={() => { setClientTokenType('global_key'); setError(''); }}
                                        style={{
                                            flex: 1, padding: '0.5rem 0.5rem', fontSize: '0.75rem', fontWeight: 600,
                                            border: 'none', borderLeft: '1px solid var(--border)', cursor: 'pointer',
                                            background: clientTokenType === 'global_key' ? 'var(--primary)' : 'transparent',
                                            color: clientTokenType === 'global_key' ? 'white' : 'var(--text-muted)',
                                        }}>
                                        {t('globalKeyTab')}
                                    </button>
                                </div>

                                {clientTokenType === 'api_token' ? (
                                    <div className="input-group">
                                        <label>{t('tokenLabel')}</label>
                                        <div style={{ position: 'relative' }}>
                                            <Shield size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                            <input type="password" placeholder={t('tokenPlaceholder')} value={token}
                                                onChange={(e) => setToken(e.target.value)} style={{ paddingLeft: '38px' }} required />
                                        </div>
                                        <p style={{ fontSize: '0.7rem', marginTop: '0.35rem', color: 'var(--text-muted)', fontStyle: 'italic' }}>
                                            {t('tokenRequiredPermissions')}
                                        </p>
                                        <p style={{ fontSize: '0.7rem', marginTop: '0.25rem', color: 'var(--text-muted)' }}>
                                            {t('tokenHint')}
                                        </p>
                                    </div>
                                ) : (
                                    <>
                                        <div style={{ padding: '0.5rem 0.75rem', borderRadius: '6px', background: 'rgba(245, 158, 11, 0.08)', border: '1px solid rgba(245, 158, 11, 0.25)', marginBottom: '0.75rem', fontSize: '0.7rem', color: '#b45309', lineHeight: 1.5 }}>
                                            {t('globalKeyWarning')}
                                        </div>
                                        <div className="input-group">
                                            <label>{t('globalKeyEmailLabel')}</label>
                                            <div style={{ position: 'relative' }}>
                                                <User size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                                <input type="email" placeholder={t('globalKeyEmailPlaceholder')} value={globalEmail}
                                                    onChange={(e) => setGlobalEmail(e.target.value)} style={{ paddingLeft: '38px' }} required />
                                            </div>
                                        </div>
                                        <div className="input-group">
                                            <label>{t('globalKeyLabel')}</label>
                                            <div style={{ position: 'relative' }}>
                                                <Key size={16} style={{ position: 'absolute', left: '12px', top: '12px', color: 'var(--text-muted)' }} />
                                                <input type="password" placeholder={t('globalKeyPlaceholder')} value={globalKey}
                                                    onChange={(e) => setGlobalKey(e.target.value)} style={{ paddingLeft: '38px' }} required />
                                            </div>
                                        </div>
                                        <p style={{ fontSize: '0.7rem', color: 'var(--text-muted)', marginBottom: '0.5rem' }}>
                                            {t('globalKeyHint')}{' '}
                                            <a href="https://dash.cloudflare.com/profile/api-tokens" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--primary)' }}>
                                                dash.cloudflare.com/profile/api-tokens
                                            </a>
                                        </p>
                                    </>
                                )}
                            </div>
                        )}

                        {error && <p style={{ color: 'var(--error)', fontSize: '0.75rem', marginBottom: '1rem', textAlign: 'center' }}>{error}</p>}

                        <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '1.5rem' }}>
                            <input
                                type="checkbox"
                                id="remember"
                                checked={remember}
                                onChange={(e) => setRemember(e.target.checked)}
                                style={{ width: '16px', height: '16px', cursor: 'pointer' }}
                            />
                            <label htmlFor="remember" style={{ fontSize: '0.875rem', color: 'var(--text-muted)', cursor: 'pointer', userSelect: 'none' }}>
                                {loginTab === 'server' ? t('rememberMe') : t('rememberToken')}
                            </label>
                        </div>

                        <button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} disabled={loading}>
                            {loading ? <RefreshCw className="spin" size={18} /> : t('loginBtn')}
                        </button>

                        {loginTab === 'server' && supportsPasskey && (
                            <>
                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', margin: '1rem 0' }}>
                                    <div style={{ flex: 1, height: '1px', background: 'var(--border)' }}></div>
                                    <span style={{ fontSize: '0.7rem', color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>or</span>
                                    <div style={{ flex: 1, height: '1px', background: 'var(--border)' }}></div>
                                </div>
                                <button
                                    type="button"
                                    className="btn btn-outline"
                                    style={{ width: '100%', justifyContent: 'center', display: 'flex', alignItems: 'center', gap: '8px' }}
                                    disabled={loading}
                                    onClick={handlePasskeyLogin}
                                >
                                    <Fingerprint size={18} />
                                    {t('passkeyLoginBtn')}
                                </button>
                            </>
                        )}
                    </form>
                )}

                {/* Security Badges */}
                <div style={{ marginTop: '1.5rem', paddingTop: '1rem', borderTop: '1px solid var(--border)' }}>
                    <SecurityBadges t={t} />
                    <div style={{ textAlign: 'center', marginTop: '0.75rem' }}>
                        <a
                            href="https://github.com/C-NF/cloudflare-dns-manager"
                            target="_blank"
                            rel="noopener noreferrer"
                            style={{ display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '0.7rem', color: 'var(--text-muted)', textDecoration: 'none', opacity: 0.6, transition: 'opacity 0.2s' }}
                            onMouseEnter={e => e.currentTarget.style.opacity = '1'}
                            onMouseLeave={e => e.currentTarget.style.opacity = '0.6'}
                        >
                            <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
                            GitHub
                        </a>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default Login;