feat: implement user authentication and admin management

- Add user management API endpoints for listing and creating users.
- Implement login and logout functionality with rate limiting.
- Create UI components for login form, logout button, and animated elements.
- Add session management with JWT and secure cookie handling.
- Seed initial admin user if no users exist.
- Introduce utility functions for password hashing and user authentication.
- Set up proxy middleware for route protection and security headers.
- Create a JSON file for user data storage.
- Add links configuration for external resources.
This commit is contained in:
Anders Böttcher
2026-05-12 14:58:11 +02:00
parent 50566f2a22
commit 0d58fafe4e
34 changed files with 4263 additions and 5012 deletions
+53
View File
@@ -0,0 +1,53 @@
import type { Metadata } from 'next'
import LoginForm from './LoginForm'
export const metadata: Metadata = {
title: 'Sign In — Backerup',
}
interface Props {
searchParams: Promise<{ from?: string }>
}
export default async function LoginPage({ searchParams }: Props) {
const { from = '/browse' } = await searchParams
// Only allow relative paths to prevent open redirects
const safePath = from.startsWith('/') && !from.startsWith('//') ? from : '/browse'
return (
<div className="flex min-h-[calc(100vh-72px)] items-center justify-center px-4 py-16">
<div className="w-full max-w-md">
{/* Logo + heading */}
<div className="text-center mb-10">
<div
className="mx-auto mb-5 w-14 h-14 rounded-2xl flex items-center justify-center"
style={{ background: 'linear-gradient(135deg, #22d3ee, #0891b2)' }}
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-7 h-7 text-slate-900" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" />
</svg>
</div>
<h1 className="text-2xl font-bold text-white tracking-tight">Welcome back</h1>
<p className="mt-1.5 text-sm text-slate-400">Sign in to your Backerup account</p>
</div>
{/* Card */}
<div
className="rounded-2xl p-8"
style={{
background: 'rgba(255,255,255,0.03)',
border: '1px solid rgba(255,255,255,0.08)',
backdropFilter: 'blur(16px)',
}}
>
<LoginForm from={safePath} />
</div>
<p className="mt-6 text-center text-xs text-slate-500">
Access is managed by your administrator.
</p>
</div>
</div>
)
}