feat: settings (PIN change, city selector, logout), greeting, screensaver, tab animations, HA status
Some checks failed
Deploy / deploy (push) Has been cancelled
Some checks failed
Deploy / deploy (push) Has been cancelled
This commit is contained in:
@@ -1,8 +1,27 @@
|
||||
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 PIN = process.env.APP_PIN || '1234'
|
||||
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')
|
||||
@@ -19,11 +38,11 @@ export async function GET(req: Request) {
|
||||
export async function POST(req: Request) {
|
||||
const { pin } = await req.json()
|
||||
|
||||
if (pin !== PIN) {
|
||||
if (pin !== getPin()) {
|
||||
return NextResponse.json({ error: 'wrong_pin' }, { status: 401 })
|
||||
}
|
||||
|
||||
const token = makeToken(PIN)
|
||||
const token = makeToken(getPin())
|
||||
const res = NextResponse.json({ success: true })
|
||||
|
||||
res.cookies.set('auth_token', token, {
|
||||
@@ -37,6 +56,37 @@ export async function POST(req: Request) {
|
||||
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')
|
||||
|
||||
@@ -36,14 +36,18 @@ function wmoToDesc(wmo: number): string {
|
||||
return "Облачно";
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
export async function GET(req: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const lat = searchParams.get("lat") || "59.9343";
|
||||
const lon = searchParams.get("lon") || "30.3351";
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 8000);
|
||||
|
||||
const url = "https://api.open-meteo.com/v1/forecast?" + new URLSearchParams({
|
||||
latitude: "59.9343",
|
||||
longitude: "30.3351",
|
||||
latitude: lat,
|
||||
longitude: lon,
|
||||
current: "temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m",
|
||||
daily: "weather_code,temperature_2m_max,temperature_2m_min",
|
||||
timezone: "Europe/Moscow",
|
||||
|
||||
Reference in New Issue
Block a user