Files
Cosmo b0fb9d0c54
All checks were successful
Deploy / deploy (push) Successful in 4m35s
fix: 4 bugs — MSK today events, settings scroll, note dates, persistent notes volume
- calendar API: today/week ranges use Moscow time (UTC+3) instead of UTC — previously today events did not appear until 03:00 MSK
- settings tab: add -webkit-overflow-scrolling: touch + touchAction pan-y for tablet scroll
- NotesTab: add date picker (pinDate) in editor header + date badge in list
- home: pinnedNotes now filters by pinDate (today or future), falls back to latest
- notes/auth: storage moved from /tmp to /data (falls back to /tmp if /data missing)
- deploy workflow: mount /opt/digital-home/smart-home-tablet-data:/data so notes survive redeploys
2026-04-23 06:13:16 +00:00

96 lines
2.4 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 DATA_DIR = fs.existsSync('/data') ? '/data' : '/tmp'
const CONFIG_PATH = `${DATA_DIR}/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
}