Refatora infraestrutura de dados, build e provisionamento de streams

---

- Movido streams.json para /app/data/streams/streams.json; volume do compose mapeado especificamente em /app/data/streams, deixando logs fora do volume persistido;
- Adicionado scripts/reprovision.mjs que regenera stream.conf e tokens VNC a partir dos templates da imagem no startup, garantindo que updates de container não exijam recriar o volume;
- Removido autologin.template.sh por-stream; substituído por scripts/autologin.sh global na imagem, com variáveis passadas via environment= no supervisor conf (com valores entre aspas
para compatibilidade com valores vazios);
- Logs de processos por stream movidos de /app/data/streams/{id}/ para /app/data/logs/{id}/;
- Adicionada função recreateStream em supervisor.ts e rota POST /api/streams/[id]/recreate; botão "Recreate" adicionado ao menu do card para limpar chrome-profile e re-provisionar;
- Adicionado auto-disparo de captureThumb no GET /api/streams/[id]/thumb quando thumb.jpg não existe e nenhuma captura está em andamento;
- Dockerfile: adicionado --mount=type=cache para /var/cache/apt e /var/lib/apt/lists (não /var/lib/apt inteiro para evitar corrupção de estado); removido --no-cache do Makefile; remoção
de pacotes limitada a curl gnupg para evitar cascata em dependências do chromium/novnc;
- Migração automática de streams.json do caminho antigo adicionada ao entrypoint.sh;

---
This commit is contained in:
2026-04-25 15:08:25 -03:00
parent f6879781c1
commit 40824c08a4
13 changed files with 174 additions and 82 deletions
@@ -0,0 +1,12 @@
import { NextResponse } from "next/server"
import { getStream } from "@/lib/db"
import { recreateStream } from "@/lib/supervisor"
type Ctx = { params: Promise<{ id: string }> }
export async function POST(_req: Request, { params }: Ctx) {
const { id } = await params
if (!getStream(id)) return NextResponse.json({ error: "not found" }, { status: 404 })
recreateStream(id)
return NextResponse.json({ ok: true })
}
+15 -5
View File
@@ -11,11 +11,21 @@ type Ctx = { params: Promise<{ id: string }> }
export async function GET(_req: Request, { params }: Ctx) {
const { id } = await params
const thumbPath = path.join(DATA_DIR, "streams", id, "thumb.jpg")
if (!fs.existsSync(thumbPath)) return new Response("not found", { status: 404 })
const buffer = fs.readFileSync(thumbPath)
return new Response(buffer, {
headers: { "Content-Type": "image/jpeg", "Cache-Control": "no-cache, no-store" },
})
const tmpPath = path.join(DATA_DIR, "streams", id, "thumb.tmp.jpg")
if (fs.existsSync(thumbPath)) {
const buffer = fs.readFileSync(thumbPath)
return new Response(buffer, {
headers: { "Content-Type": "image/jpeg", "Cache-Control": "no-cache, no-store" },
})
}
// Auto-trigger capture when no thumb exists and no capture is already in progress
if (!fs.existsSync(tmpPath) && getStream(id)) {
captureThumb(id, 5)
}
return new Response("not found", { status: 404 })
}
export async function POST(_req: Request, { params }: Ctx) {