import { useMemo, useState } from 'preact/hooks'; import { AlertTriangle, Copy, RefreshCw } from 'lucide-preact'; import { copyTextToClipboard } from '@/lib/clipboard'; import StandalonePageFrame from '@/components/StandalonePageFrame'; import { t } from '@/lib/i18n'; interface JwtWarningPageProps { reason: 'missing' | 'default' | 'too_short'; minLength: number; } const CLOUDFLARE_SETTINGS_URL = 'https://dash.cloudflare.com/?to=/:account/workers/services/view/nodewarden/production/settings'; export default function JwtWarningPage(props: JwtWarningPageProps) { const [seed, setSeed] = useState(0); const [copyHint, setCopyHint] = useState(''); const generatedSecret = useMemo(() => generateJwtSecret(32), [seed]); const title = props.reason === 'missing' ? t('txt_jwt_title_missing') : props.reason === 'default' ? t('txt_jwt_title_default') : t('txt_jwt_title_too_short'); const isMissing = props.reason === 'missing'; const fixTitle = isMissing ? t('txt_jwt_how_to_fix_add') : t('txt_jwt_how_to_fix_replace'); const fixStep1 = isMissing ? t('txt_jwt_add_step_1') : t('txt_jwt_replace_step_1', { min: props.minLength }); const fixStep2Prefix = isMissing ? t('txt_jwt_add_step_2_prefix') : t('txt_jwt_replace_step_2_prefix'); const fixStep2Suffix = isMissing ? t('txt_jwt_add_step_2_suffix') : t('txt_jwt_replace_step_2_suffix'); const fixStep3 = isMissing ? t('txt_jwt_add_step_3') : t('txt_jwt_replace_step_3'); return (
{t('txt_jwt_warning_subtitle')}
{t('txt_jwt_what_is')}

{t('txt_jwt_what_is_body')}

{fixTitle}
  1. {fixStep1}
  2. {fixStep2Prefix} {t('txt_settings')} {fixStep2Suffix}
    {t('txt_jwt_secret_type_label')} {t('txt_jwt_secret_type_value')}
    {t('txt_jwt_secret_name_label')} JWT_SECRET
    {t('txt_jwt_secret_value_label')} {t('txt_jwt_secret_value_requirement', { min: props.minLength })}
  3. {fixStep3}
{t('txt_random_secret_generator')}
{copyHint && {copyHint}}
); } function generateJwtSecret(length: number): string { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; let out = ''; const maxUnbiasedByte = Math.floor(256 / chars.length) * chars.length; while (out.length < length) { const bytes = crypto.getRandomValues(new Uint8Array(length)); for (const value of bytes) { if (value >= maxUnbiasedByte) continue; out += chars[value % chars.length]; if (out.length >= length) break; } } return out; }