diff --git a/app/login/actions.ts b/app/login/actions.ts index 12030d9..5438a3c 100644 --- a/app/login/actions.ts +++ b/app/login/actions.ts @@ -12,7 +12,7 @@ export async function login(prevState: any, formData: FormData) { return { error: 'Bitte Benutzername und Passwort eingeben' }; } - const isValid = verifyCredentials(username, password); + const isValid = await verifyCredentials(username, password); if (!isValid) { return { error: 'Ungültige Anmeldedaten' }; diff --git a/app/page.tsx b/app/page.tsx index be831bd..2f89259 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -30,6 +30,7 @@ export default function Home() { 'Cache-Control': 'no-cache', }, }); + if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); if (data.success) { setEntries(data.data); diff --git a/components/MonatsStatistik.tsx b/components/MonatsStatistik.tsx index 70243d4..4468e9c 100644 --- a/components/MonatsStatistik.tsx +++ b/components/MonatsStatistik.tsx @@ -36,6 +36,7 @@ export default function MonatsStatistik({ typ, refreshKey }: MonatsStatistikProp setIsLoading(true); try { const response = await fetch(`/api/ausgaben/stats?year=${y}&month=${m}&typ=${typ}`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); if (data.success) setStats(data.data); } catch (error) { diff --git a/deploy.sh b/deploy.sh index f71e9da..2fed808 100755 --- a/deploy.sh +++ b/deploy.sh @@ -21,6 +21,7 @@ echo "Registry: ${REGISTRY}" echo "Image: ${IMAGE_NAME}" echo "Tag: ${TAG}" echo "Build-Datum: ${BUILD_DATE}" + echo "==========================================" echo "" diff --git a/lib/auth.ts b/lib/auth.ts index 7b1fc5b..b494be3 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,20 +1,10 @@ -/** - * Reusable authentication library - * Configure users via environment variables in .env: - * AUTH_USERS=user1:$2a$10$hash1,user2:$2a$10$hash2 - * - * Use scripts/generate-password.js to generate password hashes - */ +import bcrypt from 'bcryptjs'; export interface User { username: string; password: string; } -/** - * Parse users from environment variable - * Format: username:password,username2:password2 - */ export function getUsers(): User[] { const usersString = process.env.AUTH_USERS || ''; if (!usersString) { @@ -30,21 +20,15 @@ export function getUsers(): User[] { .filter((user) => user.username && user.password); } -/** - * Verify user credentials - */ -export function verifyCredentials(username: string, password: string): boolean { +export async function verifyCredentials(username: string, password: string): Promise { const users = getUsers(); const user = users.find(u => u.username === username); if (!user) { return false; } - return user.password === password; + return bcrypt.compare(password, user.password); } -/** - * Check if authentication is enabled - */ export function isAuthEnabled(): boolean { return !!process.env.AUTH_USERS; } diff --git a/next.config.ts b/next.config.ts index 225e495..dcb685f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,6 +2,20 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: 'standalone', + async headers() { + return [ + { + source: '/(.*)', + headers: [ + { key: 'X-Frame-Options', value: 'DENY' }, + { key: 'X-Content-Type-Options', value: 'nosniff' }, + { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, + { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' }, + { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }, + ], + }, + ]; + }, }; export default nextConfig; diff --git a/package.json b/package.json index 0f02fbb..65ae9f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ausgaben_next", - "version": "2.0.2", + "version": "2.1.0", "private": true, "scripts": { "dev": "next dev -p 3005",