7f9c5c874a
- backend/tools/hasher.py: POST /api/hash/sha256 and /api/hash/bcrypt (bcrypt added to requirements) - backend/tools/base64tool.py: POST /api/base64/encode and /api/base64/decode - backend/tools/jwtdecoder.py: POST /api/jwt/decode (signature verification disabled) - backend/tools/passwordgen.py: POST /api/password/generate with charset and length options - backend/tools/timestamp.py: POST /api/timestamp/convert (unix<->date, ISO 8601 + German format) - backend/tools/textdiff.py: POST /api/text/diff returning structured added/removed/unchanged lines - All blueprints registered in app.py and tools/__init__.py - React components with copy button, dark/light mode support via CSS variables - ToolOverview rebuilt as card grid; App.jsx routes added for all tools Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
70 lines
2.4 KiB
React
70 lines
2.4 KiB
React
import { useState } from 'react';
|
||
import axios from '../services/api';
|
||
|
||
const sectionBox = {
|
||
marginTop: '12px',
|
||
padding: '14px',
|
||
background: 'var(--surface-2)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: '12px',
|
||
};
|
||
|
||
function JwtDecoderTool() {
|
||
const [input, setInput] = useState('');
|
||
const [decoded, setDecoded] = useState(null);
|
||
const [error, setError] = useState('');
|
||
|
||
const handleDecode = async () => {
|
||
setError('');
|
||
setDecoded(null);
|
||
try {
|
||
const res = await axios.post('/api/jwt/decode', { token: input });
|
||
setDecoded(res.data);
|
||
} catch (err) {
|
||
setError(err.response?.data?.message || 'Ungültiger JWT');
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="main-content">
|
||
<h2>JWT Decoder</h2>
|
||
<p style={{ color: 'var(--accent)', fontSize: '0.875rem', marginBottom: '0.75rem' }}>
|
||
<strong>Hinweis:</strong> Dieser Decoder verifiziert keine Signatur – nur zur Analyse.
|
||
</p>
|
||
<textarea
|
||
rows={3}
|
||
value={input}
|
||
onChange={(e) => { setInput(e.target.value); setDecoded(null); setError(''); }}
|
||
placeholder="JWT Token einfügen"
|
||
style={{ resize: 'vertical', fontFamily: 'monospace', fontSize: '0.85rem' }}
|
||
/>
|
||
<button onClick={handleDecode}>Dekodieren</button>
|
||
{error && <p className="error">{error}</p>}
|
||
{decoded && (
|
||
<>
|
||
<div style={sectionBox}>
|
||
<p className="eyebrow" style={{ marginBottom: '6px' }}>Header</p>
|
||
<pre style={{ margin: 0, color: 'var(--text)', fontSize: '0.875rem', whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>
|
||
{JSON.stringify(decoded.header, null, 2)}
|
||
</pre>
|
||
</div>
|
||
<div style={sectionBox}>
|
||
<p className="eyebrow" style={{ marginBottom: '6px' }}>Payload</p>
|
||
<pre style={{ margin: 0, color: 'var(--text)', fontSize: '0.875rem', whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>
|
||
{JSON.stringify(decoded.payload, null, 2)}
|
||
</pre>
|
||
</div>
|
||
<div style={{ ...sectionBox, borderColor: decoded.expired ? '#ef4444' : '#22c55e' }}>
|
||
<p className="eyebrow" style={{ marginBottom: '4px' }}>Status</p>
|
||
<span style={{ fontWeight: 700, color: decoded.expired ? '#ef4444' : '#22c55e' }}>
|
||
{decoded.expired ? 'Abgelaufen' : 'Gültig'}
|
||
</span>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default JwtDecoderTool;
|