feat: interaktif panel kontrolleri (Faz 3)

Yayın durdurma, kanal duraklat/devam (tekli+toplu), şimdi kontrol et,
manuel URL ile zorla kayıt başlatma, canlı player+log önizleme, oturum
süre sayacı, başarısız/takılı render için tekrar dene ve iptal, poll
aralığı + ham segment TTL'yi panelden canlı değiştirme, cookie bayatlık
uyarısı, Telegram bildirim aç/kapa, ve kullanım/maliyet özeti (/stats).

api-daemon'ın yeni state-değiştiren endpoint'leri (stop/force-start/
render/cancel) INTERNAL_API_TOKEN ile korunuyor — bu daemon'ın portu
Coolify'de public'e açık olduğu için korumasız bırakmak güvenlik açığı
olurdu.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 13:38:47 +03:00
co-authored by Claude Sonnet 5
parent 2fc3254d64
commit 43ccbd9ec7
23 changed files with 742 additions and 75 deletions
+25 -1
View File
@@ -1,6 +1,7 @@
import { prisma } from "@streamclipper/db";
import { deleteRawSegment, deleteCandidateSegment } from "../actions";
import { deleteRawSegment, deleteCandidateSegment, retryRender, cancelRender } from "../actions";
import { DeleteButton } from "../components/DeleteButton";
import { ConfirmButton } from "../components/ConfirmButton";
export const dynamic = "force-dynamic";
@@ -92,8 +93,31 @@ export default async function SegmentsPage() {
{c.short.status === "FAILED" && c.short.errorMessage && (
<p className="mono" style={{ fontSize: "0.75rem", marginTop: "0.3rem" }}>{c.short.errorMessage}</p>
)}
{c.short.status === "FAILED" && (
<form action={retryRender.bind(null, c.id)} style={{ marginTop: "0.3rem" }}>
<button type="submit" className="badge ok" style={{ border: "none", cursor: "pointer" }}>
Tekrar Dene
</button>
</form>
)}
{c.short.status === "RENDERING" && (
<form action={cancelRender.bind(null, c.id)} style={{ marginTop: "0.3rem" }}>
<ConfirmButton
confirmText="Render'ı başarısız say ve kilidi aç? (Çalışan process'i öldürmez, sadece durumu sıfırlar)"
label="Takıldıysa İptal Et"
variant="muted"
/>
</form>
)}
</div>
)}
{!c.short && c.status === "TRANSCRIBED" && (
<form action={retryRender.bind(null, c.id)} style={{ marginTop: "0.5rem" }}>
<button type="submit" className="badge ok" style={{ border: "none", cursor: "pointer" }}>
9:16 Render Et
</button>
</form>
)}
<form action={deleteCandidateSegment.bind(null, c.id)} style={{ marginTop: "0.35rem" }}>
<DeleteButton confirmText="Bu aday klibi silmek istediğine emin misin?" />