release: v0.16.0

See CHANGELOG.md for the full v0.16.0 entry.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
This commit is contained in:
Ralksta
2026-09-22 14:12:13 +02:00
committed by GitHub
parent c3f92ca4ec
commit a3a6319c29
120 changed files with 6818 additions and 975 deletions

View File

@@ -6,6 +6,7 @@ import { usePathname } from 'next/navigation';
import BackupManagerModal from './BackupManagerModal';
import type { DoctorLevel } from '@/lib/admin/doctor';
import { DOCTOR_LEVEL_EVENT, systemHealth } from './systemHealth';
import { reportIfSessionExpired } from './sessionExpiry';
import * as Icons from './Icons';
interface Props {
@@ -15,10 +16,11 @@ interface Props {
}
const TABS = [
{ label: 'Pages', href: '/admin/pages', match: /^\/admin(\/pages)?$/ },
{ label: 'Pages', href: '/admin/pages', match: /^\/admin\/pages$/ },
{ label: 'Journal', href: '/admin/journal', match: /^\/admin\/journal/ },
{ label: 'Settings', href: '/admin/settings', match: /^\/admin\/settings/ },
{ label: 'Analytics', href: '/admin/analytics', match: /^\/admin\/analytics/ },
{ label: 'Diagnostics', href: '/admin/diagnostics', match: /^\/admin\/diagnostics/ },
{ label: 'Help', href: '/admin/help', match: /^\/admin\/help/ },
];
@@ -47,7 +49,15 @@ export default function AdminDashboard({ onLogout, children }: Props) {
async function handleReload() {
setSaving(true);
try {
await fetch('/api/admin/reload', { method: 'POST' });
const res = await fetch('/api/admin/reload', { method: 'POST' });
// A 401 or a 500 used to be indistinguishable from success here — the
// result was never checked at all (#596).
if (!res.ok) {
if (!reportIfSessionExpired(res)) {
alert(`Reload failed (HTTP ${res.status}).`);
}
return;
}
// Refresh status after reload
await fetchStatus();
} finally {
@@ -141,15 +151,19 @@ export default function AdminDashboard({ onLogout, children }: Props) {
<div className="admin-header-left">
<h1>Immich Folio</h1>
<nav className="admin-tabs">
{TABS.map((t) => (
<Link
key={t.href}
href={t.href}
className={`admin-tab ${t.match.test(pathname) ? 'active' : ''}`}
>
{t.label}
</Link>
))}
{TABS.map((t) => {
const active = t.match.test(pathname);
return (
<Link
key={t.href}
href={t.href}
className={`admin-tab ${active ? 'active' : ''}`}
aria-current={active ? 'page' : undefined}
>
{t.label}
</Link>
);
})}
</nav>
</div>
<div className="admin-header-right">
@@ -247,24 +261,6 @@ export default function AdminDashboard({ onLogout, children }: Props) {
)}
</div>
</div>
<div className="status-dropdown-footer">
<Link
href="/admin/diagnostics"
className="admin-btn admin-btn-sm"
onClick={() => setShowStatus(false)}
>
<Icons.IconShieldCheck size={14} /> Diagnostics
</Link>
<button
className="admin-btn admin-btn-sm"
onClick={() => {
setShowStatus(false);
setShowBackupModal(true);
}}
>
<Icons.IconArchive size={14} /> Manage Backups
</button>
</div>
</div>
</>
)}