diff --git a/app/api/auth/route.ts b/app/api/auth/route.ts index cb705ae..9893622 100644 --- a/app/api/auth/route.ts +++ b/app/api/auth/route.ts @@ -2,20 +2,28 @@ import { NextResponse } from 'next/server' import * as crypto from 'crypto' const SECRET = process.env.APP_SECRET || 'smart-home-default-secret-change-me' +const PIN = process.env.APP_PIN || '1234' function makeToken(pin: string): string { return crypto.createHmac('sha256', SECRET).update(pin).digest('hex') } +export async function GET(req: Request) { + const cookieHeader = req.headers.get('cookie') || '' + const match = cookieHeader.match(/auth_token=([^;]+)/) + const token = match ? match[1] : null + const expected = makeToken(PIN) + return NextResponse.json({ authenticated: token === expected }) +} + export async function POST(req: Request) { const { pin } = await req.json() - const correctPin = process.env.APP_PIN || '1234' - if (pin !== correctPin) { + if (pin !== PIN) { return NextResponse.json({ error: 'wrong_pin' }, { status: 401 }) } - const token = makeToken(correctPin) + const token = makeToken(PIN) const res = NextResponse.json({ success: true }) res.cookies.set('auth_token', token, { diff --git a/app/page.tsx b/app/page.tsx index cce7c4e..973d71d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,6 @@ 'use client' -import { useState, useEffect, useCallback, Suspense } from 'react' -import { useSearchParams } from 'next/navigation' +import { useState, useEffect, useCallback } from 'react' import { Thermometer, Droplets, Wind, Calendar, Lock, Settings as SettingsIcon, LogOut, Delete } from 'lucide-react' import Sidebar from '@/components/Sidebar' import TopBar from '@/components/TopBar' @@ -374,9 +373,14 @@ function HomeTab({ weather, sensors }: { weather: WeatherData | null; sensors: S } function HomePageInner() { - const searchParams = useSearchParams() - const isLocked = searchParams.get('locked') === '1' - const [unlocked, setUnlocked] = useState(!isLocked) + const [unlocked, setUnlocked] = useState(null) + + useEffect(() => { + fetch('/api/auth') + .then(r => r.json()) + .then(d => setUnlocked(d.authenticated)) + .catch(() => setUnlocked(false)) + }, []) const [tab, setTab] = useState('home') const [activeRoom, setActiveRoom] = useState('living') @@ -431,8 +435,12 @@ function HomePageInner() { window.location.reload() } + if (unlocked === null) { + return
+ } + if (!unlocked) { - return { setUnlocked(true); window.history.replaceState({}, '', '/') }} /> + return setUnlocked(true)} /> } return ( @@ -514,9 +522,5 @@ function HomePageInner() { export default function HomePage() { - return ( - - - - ) + return } diff --git a/middleware.ts b/middleware.ts index a724a1f..1b64d2f 100644 --- a/middleware.ts +++ b/middleware.ts @@ -13,12 +13,8 @@ async function hmacSha256(secret: string, message: string): Promise { export async function middleware(request: NextRequest) { const { pathname } = request.nextUrl - if ( - pathname.startsWith('/api/auth') || - pathname.startsWith('/_next') || - pathname.startsWith('/favicon') || - pathname === '/manifest.json' - ) { + // Only protect API routes (except /api/auth) + if (!pathname.startsWith('/api/') || pathname.startsWith('/api/auth')) { return NextResponse.next() } @@ -28,17 +24,12 @@ export async function middleware(request: NextRequest) { const expectedToken = await hmacSha256(secret, pin) if (token !== expectedToken) { - if (pathname.startsWith('/api/')) { - return NextResponse.json({ error: 'unauthorized' }, { status: 401 }) - } - const url = request.nextUrl.clone() - url.searchParams.set('locked', '1') - return NextResponse.rewrite(url) + return NextResponse.json({ error: 'unauthorized' }, { status: 401 }) } return NextResponse.next() } export const config = { - matcher: ['/((?!_next/static|_next/image|favicon.ico|manifest.json).*)'], + matcher: ['/api/:path*'], }