Repo init

This commit is contained in:
2026-04-23 23:40:34 -03:00
parent 214158a174
commit 30b0597380
34 changed files with 13201 additions and 2 deletions
@@ -0,0 +1,21 @@
import { NextResponse } from "next/server"
import { getStream } from "@/lib/db"
import { startStream, stopStream, restartStream } from "@/lib/supervisor"
type Ctx = { params: Promise<{ id: string; action: string }> }
export async function POST(_req: Request, { params }: Ctx) {
const { id, action } = await params
if (!getStream(id)) return NextResponse.json({ error: "não encontrado" }, { status: 404 })
switch (action) {
case "start": startStream(id); break
case "stop": stopStream(id); break
case "restart": restartStream(id); break
default:
return NextResponse.json({ error: "ação inválida" }, { status: 400 })
}
return NextResponse.json({ ok: true })
}
+41
View File
@@ -0,0 +1,41 @@
import { NextResponse } from "next/server"
import { getStream, saveStream, deleteStream } from "@/lib/db"
import { provisionStream, restartStream, removeStream } from "@/lib/supervisor"
import type { StreamUpdate } from "@/types/stream"
type Ctx = { params: Promise<{ id: string }> }
export async function GET(_req: Request, { params }: Ctx) {
const { id } = await params
const stream = getStream(id)
if (!stream) return NextResponse.json({ error: "não encontrado" }, { status: 404 })
return NextResponse.json(stream)
}
export async function PATCH(req: Request, { params }: Ctx) {
const { id } = await params
const stream = getStream(id)
if (!stream) return NextResponse.json({ error: "não encontrado" }, { status: 404 })
const body = (await req.json()) as StreamUpdate
// id e portas não podem ser alterados via PATCH
const { id: _id, ...safe } = body as StreamUpdate & { id?: string }
void _id
const updated = { ...stream, ...safe, updatedAt: new Date().toISOString() }
saveStream(updated)
provisionStream(updated)
restartStream(id)
return NextResponse.json(updated)
}
export async function DELETE(_req: Request, { params }: Ctx) {
const { id } = await params
if (!getStream(id)) return NextResponse.json({ error: "não encontrado" }, { status: 404 })
removeStream(id)
deleteStream(id)
return NextResponse.json({ ok: true })
}
+11
View File
@@ -0,0 +1,11 @@
import { NextResponse } from "next/server"
import { getStream } from "@/lib/db"
import { getStreamStatus } from "@/lib/supervisor"
type Ctx = { params: Promise<{ id: string }> }
export async function GET(_req: Request, { params }: Ctx) {
const { id } = await params
if (!getStream(id)) return NextResponse.json({ error: "não encontrado" }, { status: 404 })
return NextResponse.json(getStreamStatus(id))
}
+22
View File
@@ -0,0 +1,22 @@
import { readStreams } from "@/lib/db"
export async function GET(req: Request) {
const { searchParams } = new URL(req.url)
const host = searchParams.get("host") ?? "localhost"
const port = searchParams.get("port") ?? "8888"
const streams = readStreams()
const lines = ["#EXTM3U"]
for (const s of streams) {
lines.push(`#EXTINF:-1,${s.name}`)
lines.push(`http://${host}:${port}/live/${s.id}/index.m3u8`)
}
return new Response(lines.join("\n"), {
headers: {
"Content-Type": "application/x-mpegurl",
"Content-Disposition": 'attachment; filename="decap-stream.m3u"',
},
})
}
+40
View File
@@ -0,0 +1,40 @@
import { NextResponse } from "next/server"
import { readStreams, saveStream, allocatePorts, getStream } from "@/lib/db"
import { provisionStream, startStream } from "@/lib/supervisor"
import { STREAM_DEFAULTS, type StreamCreate } from "@/types/stream"
export async function GET() {
return NextResponse.json(readStreams())
}
const SLUG_RE = /^[a-z0-9-]+$/
export async function POST(req: Request) {
const body = (await req.json()) as StreamCreate
if (!body.id || !SLUG_RE.test(body.id))
return NextResponse.json({ error: "id inválido: use apenas letras minúsculas, números e hífen" }, { status: 400 })
if (!body.name || !body.url)
return NextResponse.json({ error: "name e url são obrigatórios" }, { status: 400 })
if (getStream(body.id))
return NextResponse.json({ error: "já existe uma stream com esse id" }, { status: 409 })
const ports = allocatePorts()
const now = new Date().toISOString()
const stream = {
...STREAM_DEFAULTS,
...body,
...ports,
createdAt: now,
updatedAt: now,
}
saveStream(stream)
provisionStream(stream)
startStream(stream.id)
return NextResponse.json(stream, { status: 201 })
}