Update-availability fixes: - The backend read its version from npm_package_version, which is unset when the container runs `node dist/index.js` directly, so current was always "0.0.0" and every release looked newer — the "update available" banner never cleared. Read the bundled package.json instead (routes/system.ts), falling back to npm_package_version. - Move the update notification out of the top-of-app banner into the Operations dashboard (a dedicated notice under the network-status hero), shown only when genuinely behind and cleared once up to date. Security banner: - Replace the now-dead jwt_secret_default / encryption_key_default checks with the current secrets_not_persisted warning (auto-generated secrets that couldn't be persisted), matching the self-healing secrets model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
2.6 KiB
TypeScript
64 lines
2.6 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import { AlertTriangle, ShieldAlert } from 'lucide-react';
|
|
import { authApi } from '../services/api';
|
|
|
|
interface SecurityStatus {
|
|
warnings: string[];
|
|
}
|
|
|
|
export default function SecurityBanners() {
|
|
const [status, setStatus] = useState<SecurityStatus | null>(null);
|
|
|
|
useEffect(() => {
|
|
authApi.securityStatus()
|
|
.then((r) => setStatus(r.data))
|
|
.catch(() => {});
|
|
}, []);
|
|
|
|
const noWarnings = !status || status.warnings.length === 0;
|
|
if (noWarnings) return null;
|
|
|
|
const hasSecretsNotPersisted = !!status && status.warnings.includes('secrets_not_persisted');
|
|
const hasAdminWarning = !!status && status.warnings.includes('admin_password_default');
|
|
|
|
return (
|
|
<div>
|
|
{hasSecretsNotPersisted && (
|
|
<div className="flex items-center gap-2 bg-yellow-50 dark:bg-yellow-900/20 border-b border-yellow-200 dark:border-yellow-800 px-4 py-2 text-sm text-yellow-800 dark:text-yellow-200">
|
|
<AlertTriangle className="w-4 h-4 shrink-0" />
|
|
<span>
|
|
<span className="font-semibold">Security warning:</span>{' '}
|
|
Auto-generated secrets could not be saved, so they will change on
|
|
every restart — logging users out and potentially orphaning stored
|
|
device credentials. Mount a writable volume at{' '}
|
|
<code className="font-mono text-xs">SECRETS_DIR</code> (default{' '}
|
|
<code className="font-mono text-xs">/app/data</code>) or set{' '}
|
|
<code className="font-mono text-xs">JWT_SECRET</code> and{' '}
|
|
<code className="font-mono text-xs">ENCRYPTION_KEY</code> in your{' '}
|
|
<code className="font-mono text-xs">.env</code>.
|
|
</span>
|
|
</div>
|
|
)}
|
|
{hasAdminWarning && (
|
|
<div className="flex items-center justify-between gap-4 bg-red-50 dark:bg-red-900/20 border-b border-red-200 dark:border-red-800 px-4 py-2 text-sm text-red-800 dark:text-red-200">
|
|
<div className="flex items-center gap-2">
|
|
<ShieldAlert className="w-4 h-4 shrink-0" />
|
|
<span>
|
|
<span className="font-semibold">Critical:</span> The{' '}
|
|
<code className="font-mono text-xs">admin</code> account is
|
|
using the default password. Change it immediately.
|
|
</span>
|
|
</div>
|
|
<Link
|
|
to="/settings"
|
|
className="shrink-0 bg-red-600 hover:bg-red-700 text-white text-xs px-3 py-1 rounded font-medium"
|
|
>
|
|
Change password
|
|
</Link>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|