Fix transcripts: use API route for server-side fs

This commit is contained in:
Horus
2026-02-27 15:25:06 +01:00
parent d7cd81b293
commit cc9dba2790
2 changed files with 116 additions and 47 deletions
+74
View File
@@ -0,0 +1,74 @@
import { NextRequest, NextResponse } from "next/server";
import fs from "fs";
import path from "path";
const STORAGE_FILE = path.join(process.cwd(), "data", "transcripts.json");
function ensureStorage() {
const dir = path.dirname(STORAGE_FILE);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
if (!fs.existsSync(STORAGE_FILE)) {
fs.writeFileSync(STORAGE_FILE, "[]");
}
}
function getTranscripts() {
ensureStorage();
return JSON.parse(fs.readFileSync(STORAGE_FILE, "utf-8"));
}
function saveTranscripts(data: any[]) {
ensureStorage();
fs.writeFileSync(STORAGE_FILE, JSON.stringify(data, null, 2));
}
export async function GET() {
const transcripts = getTranscripts();
return NextResponse.json(transcripts);
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { videoUrl, videoId, title, transcript, categories, action } = body;
if (action === "delete") {
const transcripts = getTranscripts().filter(t => t.videoId !== videoId);
saveTranscripts(transcripts);
return NextResponse.json({ success: true, transcripts });
}
// Save or update transcript
let vid = videoId;
if (!vid && videoUrl) {
const match = videoUrl.match(/(?:v=|\/)([0-9A-Za-z_-]{11})/);
vid = match ? match[1] : videoUrl;
}
const data = {
videoId: vid,
videoUrl: videoUrl || `https://www.youtube.com/watch?v=${vid}`,
title: title || `Video ${vid}`,
transcript: transcript || "",
categories: categories || [],
savedAt: new Date().toISOString()
};
const transcripts = getTranscripts();
const existing = transcripts.findIndex(t => t.videoId === vid);
if (existing >= 0) {
transcripts[existing] = data;
} else {
transcripts.unshift(data);
}
saveTranscripts(transcripts);
return NextResponse.json({ success: true, transcripts });
} catch (error) {
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}
+42 -47
View File
@@ -1,7 +1,6 @@
"use client";
import { useState, useEffect } from "react";
import fs from "fs";
const AVAILABLE_CATEGORIES = [
"SiteMente",
@@ -15,17 +14,6 @@ const AVAILABLE_CATEGORIES = [
"Personal Growth"
];
const STORAGE_FILE = "/root/.openclaw/workspace/SiteMente/data/transcripts.json";
function getTranscripts() {
try {
if (fs.existsSync(STORAGE_FILE)) {
return JSON.parse(fs.readFileSync(STORAGE_FILE, "utf-8"));
}
} catch (e) {}
return [];
}
export default function TranscriptsPage() {
const [transcripts, setTranscripts] = useState<any[]>([]);
const [videoUrl, setVideoUrl] = useState("");
@@ -36,9 +24,15 @@ export default function TranscriptsPage() {
const [saving, setSaving] = useState(false);
useEffect(() => {
setTranscripts(getTranscripts());
fetchTranscripts();
}, []);
const fetchTranscripts = async () => {
const res = await fetch("/api/transcripts");
const data = await res.json();
setTranscripts(data);
};
const toggleCategory = (cat: string) => {
setSelectedCategories(prev =>
prev.includes(cat)
@@ -51,35 +45,42 @@ export default function TranscriptsPage() {
if (!videoUrl || !transcriptText) return;
setSaving(true);
// Extract video ID
const match = videoUrl.match(/(?:v=|\/)([0-9A-Za-z_-]{11})/);
const videoId = match ? match[1] : "";
const res = await fetch("/api/transcripts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
videoUrl,
title: title || videoUrl,
transcript: transcriptText,
categories: selectedCategories
})
});
const data = {
videoId,
videoUrl,
title: title || `Video ${videoId}`,
transcript: transcriptText,
categories: selectedCategories,
savedAt: new Date().toISOString()
};
// Save directly
const current = getTranscripts();
const existing = current.findIndex(t => t.videoId === videoId);
if (existing >= 0) {
current[existing] = data;
} else {
current.unshift(data);
}
fs.writeFileSync(STORAGE_FILE, JSON.stringify(current, null, 2));
setTranscripts(current);
const data = await res.json();
setSaving(false);
setVideoUrl("");
setTranscriptText("");
setTitle("");
setSelectedCategories([]);
if (data.transcripts) {
setTranscripts(data.transcripts);
setVideoUrl("");
setTranscriptText("");
setTitle("");
setSelectedCategories([]);
}
};
const deleteTranscript = async (videoId: string) => {
if (!confirm("Delete this transcript?")) return;
const res = await fetch("/api/transcripts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "delete", videoId })
});
const data = await res.json();
if (data.transcripts) {
setTranscripts(data.transcripts);
}
};
const filteredTranscripts = filterCategory === "all"
@@ -191,13 +192,7 @@ export default function TranscriptsPage() {
{new Date(t.savedAt).toLocaleDateString()}
</span>
<button
onClick={() => {
if (confirm("Delete this transcript?")) {
const updated = transcripts.filter(x => x.videoId !== t.videoId);
fs.writeFileSync(STORAGE_FILE, JSON.stringify(updated, null, 2));
setTranscripts(updated);
}
}}
onClick={() => deleteTranscript(t.videoId)}
className="text-red-400 hover:text-red-300"
>