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
+11 -3
View File
@@ -3,6 +3,13 @@ import { prisma } from "@streamclipper/db";
import { env } from "./env";
const CHECK_INTERVAL_MS = 60 * 60 * 1000; // hourly
const TTL_SETTING_KEY = "raw_segment_ttl_hours";
async function getTtlHours(): Promise<number> {
const setting = await prisma.appSetting.findUnique({ where: { key: TTL_SETTING_KEY } });
const parsed = setting ? Number(setting.value) : NaN;
return Number.isFinite(parsed) && parsed > 0 ? parsed : env.rawSegmentTtlHours;
}
/**
* PRD's auto-purge lifecycle: processed raw recordings get deleted 24h after
@@ -11,7 +18,8 @@ const CHECK_INTERVAL_MS = 60 * 60 * 1000; // hourly
* file mid-analysis or mid-transcription would break the pipeline for it.
*/
async function cleanupOnce(): Promise<void> {
const cutoff = new Date(Date.now() - env.rawSegmentTtlHours * 60 * 60 * 1000);
const ttlHours = await getTtlHours();
const cutoff = new Date(Date.now() - ttlHours * 60 * 60 * 1000);
const staleSegments = await prisma.rawSegment.findMany({
where: {
@@ -28,12 +36,12 @@ async function cleanupOnce(): Promise<void> {
}
if (staleSegments.length > 0) {
console.log(`[cleanup] removed ${staleSegments.length} raw segment(s) older than ${env.rawSegmentTtlHours}h`);
console.log(`[cleanup] removed ${staleSegments.length} raw segment(s) older than ${ttlHours}h`);
}
}
export function startCleanupLoop(): NodeJS.Timeout {
console.log(`[cleanup] checking every ${CHECK_INTERVAL_MS}ms, TTL ${env.rawSegmentTtlHours}h`);
console.log(`[cleanup] checking every ${CHECK_INTERVAL_MS}ms, default TTL ${env.rawSegmentTtlHours}h`);
cleanupOnce().catch((err) => console.error("[cleanup] initial run failed:", err));
return setInterval(() => {
cleanupOnce().catch((err) => console.error("[cleanup] run failed:", err));