Adiciona autenticação opcional, VNC integrado, GPU por stream, proxy HLS e melhorias de segurança
---
- Adicionado sistema de autenticação opcional via AUTH_USER/AUTH_PASS: middleware Next.js, página de login, cookie rolling de
30 dias, timingSafeEqual para comparação segura de credenciais;
- Adicionado proxy HLS em /api/hls/[...path] que roteia para localhost:8888 internamente; player e player-static atualizados
para usar a rota proxy;
- Adicionada página /vnc/[id] integrada na UI (iframe + botão Back com auto-hide), substituindo abertura em nova aba;
- Adicionado campo gpu: boolean por stream; controlado via {{GPU_FLAGS}} no template do Chromium e no reprovision.mjs;
- Ajustado delay da primeira thumbnail para stream.delay + 60 para garantir conclusão do autologin antes da captura;
- Atualizado docker-compose.yml: porta 6080 vinculada a localhost, portas 1935 e 8888 comentadas por padrão;
- Traduzidos todos os comentários de código do português para o inglês;
- Adicionado crédito riguetto.dev no header com underline no hover;
- README e CLAUDE.md atualizados com arquitetura, portas e features corretas;
---
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import crypto from "crypto"
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { AUTH_ENABLED, COOKIE_NAME, computeSessionToken } from "@/lib/auth"
|
||||
|
||||
function hash(s: string) {
|
||||
return crypto.createHash("sha256").update(s).digest()
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
if (!AUTH_ENABLED) {
|
||||
return NextResponse.json({ ok: true })
|
||||
}
|
||||
|
||||
let user: string, pass: string
|
||||
try {
|
||||
const body = await request.json()
|
||||
user = String(body.user ?? "")
|
||||
pass = String(body.pass ?? "")
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Invalid request" }, { status: 400 })
|
||||
}
|
||||
|
||||
try {
|
||||
const userOk = crypto.timingSafeEqual(hash(user), hash(process.env.AUTH_USER!))
|
||||
const passOk = crypto.timingSafeEqual(hash(pass), hash(process.env.AUTH_PASS!))
|
||||
if (!userOk || !passOk) throw new Error()
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Invalid credentials" }, { status: 401 })
|
||||
}
|
||||
|
||||
const token = await computeSessionToken()
|
||||
const res = NextResponse.json({ ok: true })
|
||||
res.cookies.set(COOKIE_NAME, token, {
|
||||
httpOnly: true,
|
||||
sameSite: "strict",
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 30, // 30 days — auto-renewed on every request (rolling session)
|
||||
})
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { COOKIE_NAME } from "@/lib/auth"
|
||||
|
||||
export async function POST() {
|
||||
const res = NextResponse.json({ ok: true })
|
||||
res.cookies.delete(COOKIE_NAME)
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { AUTH_ENABLED } from "@/lib/auth"
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({ enabled: AUTH_ENABLED })
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
|
||||
type Ctx = { params: Promise<{ path: string[] }> }
|
||||
|
||||
export async function GET(req: NextRequest, { params }: Ctx) {
|
||||
const { path } = await params
|
||||
const upstream = `http://localhost:8888/${path.join("/")}`
|
||||
|
||||
try {
|
||||
const res = await fetch(upstream, {
|
||||
headers: { Accept: req.headers.get("Accept") ?? "*/*" },
|
||||
})
|
||||
|
||||
if (!res.ok) return new NextResponse(null, { status: res.status })
|
||||
|
||||
const headers = new Headers()
|
||||
const ct = res.headers.get("content-type")
|
||||
if (ct) headers.set("content-type", ct)
|
||||
headers.set("cache-control", "no-cache")
|
||||
|
||||
return new NextResponse(res.body, { status: 200, headers })
|
||||
} catch {
|
||||
return new NextResponse(null, { status: 502 })
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,15 @@ export async function POST(_req: Request, { params }: Ctx) {
|
||||
|
||||
switch (action) {
|
||||
case "start":
|
||||
saveStream({ ...stream, desiredState: "running", updatedAt: new Date().toISOString() }) // #19
|
||||
saveStream({ ...stream, desiredState: "running", updatedAt: new Date().toISOString() })
|
||||
startStream(id)
|
||||
break
|
||||
case "stop":
|
||||
saveStream({ ...stream, desiredState: "stopped", updatedAt: new Date().toISOString() }) // #19
|
||||
saveStream({ ...stream, desiredState: "stopped", updatedAt: new Date().toISOString() })
|
||||
stopStream(id)
|
||||
break
|
||||
case "restart":
|
||||
saveStream({ ...stream, desiredState: "running", updatedAt: new Date().toISOString() }) // #19
|
||||
saveStream({ ...stream, desiredState: "running", updatedAt: new Date().toISOString() })
|
||||
restartStream(id)
|
||||
break
|
||||
default:
|
||||
|
||||
@@ -18,7 +18,7 @@ export async function PATCH(req: Request, { params }: Ctx) {
|
||||
if (!stream) return NextResponse.json({ error: "not found" }, { status: 404 })
|
||||
|
||||
const body = (await req.json()) as StreamUpdate
|
||||
// id e portas não podem ser alterados via PATCH
|
||||
// id and ports are immutable — strip them from PATCH body
|
||||
const { id: _id, ...safe } = body as StreamUpdate & { id?: string }
|
||||
void _id
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ export async function POST(req: Request) {
|
||||
const stream = {
|
||||
...STREAM_DEFAULTS,
|
||||
...body,
|
||||
scale: normalizeScale(body.scale ?? STREAM_DEFAULTS.scale), // #13
|
||||
scale: normalizeScale(body.scale ?? STREAM_DEFAULTS.scale),
|
||||
...ports,
|
||||
desiredState: "running" as const, // #19
|
||||
desiredState: "running" as const,
|
||||
order: nextOrder,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
@@ -40,7 +40,7 @@ export async function POST(req: Request) {
|
||||
saveStream(stream)
|
||||
provisionStream(stream)
|
||||
startStream(stream.id)
|
||||
captureThumb(stream.id, 60)
|
||||
captureThumb(stream.id, stream.delay + 60)
|
||||
|
||||
return NextResponse.json(stream, { status: 201 })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user