95 lines
2.3 KiB
TypeScript
95 lines
2.3 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import * as crypto from 'crypto'
|
|
import * as fs from 'fs'
|
|
import * as path from 'path'
|
|
|
|
const SECRET = process.env.APP_SECRET || 'smart-home-default-secret-change-me'
|
|
const CONFIG_PATH = '/tmp/tablet-config.json'
|
|
|
|
function loadConfig(): { pin: string } {
|
|
try {
|
|
if (fs.existsSync(CONFIG_PATH)) {
|
|
return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'))
|
|
}
|
|
} catch {}
|
|
return { pin: process.env.APP_PIN || '1234' }
|
|
}
|
|
|
|
function saveConfig(config: { pin: string }) {
|
|
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config))
|
|
}
|
|
|
|
function getPin(): string {
|
|
return loadConfig().pin
|
|
}
|
|
|
|
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(getPin())
|
|
return NextResponse.json({ authenticated: token === expected })
|
|
}
|
|
|
|
export async function POST(req: Request) {
|
|
const { pin } = await req.json()
|
|
|
|
if (pin !== getPin()) {
|
|
return NextResponse.json({ error: 'wrong_pin' }, { status: 401 })
|
|
}
|
|
|
|
const token = makeToken(getPin())
|
|
const res = NextResponse.json({ success: true })
|
|
|
|
res.cookies.set('auth_token', token, {
|
|
httpOnly: true,
|
|
secure: true,
|
|
sameSite: 'strict',
|
|
path: '/',
|
|
maxAge: 60 * 60 * 24 * 365,
|
|
})
|
|
|
|
return res
|
|
}
|
|
|
|
|
|
export async function PUT(req: Request) {
|
|
const { oldPin, newPin } = await req.json()
|
|
|
|
if (!oldPin || !newPin) {
|
|
return NextResponse.json({ error: 'oldPin and newPin required' }, { status: 400 })
|
|
}
|
|
|
|
if (newPin.length < 4 || newPin.length > 8) {
|
|
return NextResponse.json({ error: 'PIN must be 4-8 digits' }, { status: 400 })
|
|
}
|
|
|
|
if (oldPin !== getPin()) {
|
|
return NextResponse.json({ error: 'wrong_pin' }, { status: 401 })
|
|
}
|
|
|
|
saveConfig({ pin: newPin })
|
|
|
|
// Set new auth cookie
|
|
const token = makeToken(newPin)
|
|
const res = NextResponse.json({ success: true })
|
|
res.cookies.set('auth_token', token, {
|
|
httpOnly: true,
|
|
secure: true,
|
|
sameSite: 'strict',
|
|
path: '/',
|
|
maxAge: 60 * 60 * 24 * 365,
|
|
})
|
|
|
|
return res
|
|
}
|
|
export async function DELETE() {
|
|
const res = NextResponse.json({ success: true })
|
|
res.cookies.delete('auth_token')
|
|
return res
|
|
}
|