Update
This commit is contained in:
102
server.ts
102
server.ts
@ -2,11 +2,11 @@ import { Hono } from "hono";
|
|||||||
import { serve } from "@hono/node-server";
|
import { serve } from "@hono/node-server";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { config, validateConfig } from "./services/config.js";
|
import { config, validateConfig } from "./services/config.js";
|
||||||
import {
|
import {
|
||||||
fetchAllEpisodes,
|
fetchAllEpisodes,
|
||||||
fetchEpisodesWithArticles,
|
fetchEpisodesWithArticles,
|
||||||
getAllFeeds,
|
getAllFeeds,
|
||||||
getFeedByUrl
|
getFeedByUrl,
|
||||||
} from "./services/database.js";
|
} from "./services/database.js";
|
||||||
import { batchProcess, addNewFeedUrl } from "./scripts/fetch_and_generate.js";
|
import { batchProcess, addNewFeedUrl } from "./scripts/fetch_and_generate.js";
|
||||||
|
|
||||||
@ -35,30 +35,34 @@ app.get("/api/feeds", async (c) => {
|
|||||||
app.post("/api/feeds", async (c) => {
|
app.post("/api/feeds", async (c) => {
|
||||||
try {
|
try {
|
||||||
const { feedUrl } = await c.req.json<{ feedUrl: string }>();
|
const { feedUrl } = await c.req.json<{ feedUrl: string }>();
|
||||||
|
|
||||||
if (!feedUrl || typeof feedUrl !== "string" || !feedUrl.startsWith('http')) {
|
if (
|
||||||
|
!feedUrl ||
|
||||||
|
typeof feedUrl !== "string" ||
|
||||||
|
!feedUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return c.json({ error: "Valid feed URL is required" }, 400);
|
return c.json({ error: "Valid feed URL is required" }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("➕ Adding new feed URL:", feedUrl);
|
console.log("➕ Adding new feed URL:", feedUrl);
|
||||||
|
|
||||||
// Check if feed already exists
|
// Check if feed already exists
|
||||||
const existingFeed = await getFeedByUrl(feedUrl);
|
const existingFeed = await getFeedByUrl(feedUrl);
|
||||||
if (existingFeed) {
|
if (existingFeed) {
|
||||||
return c.json({
|
return c.json({
|
||||||
result: "EXISTS",
|
result: "EXISTS",
|
||||||
message: "Feed URL already exists",
|
message: "Feed URL already exists",
|
||||||
feed: existingFeed
|
feed: existingFeed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new feed
|
// Add new feed
|
||||||
await addNewFeedUrl(feedUrl);
|
await addNewFeedUrl(feedUrl);
|
||||||
|
|
||||||
return c.json({
|
return c.json({
|
||||||
result: "CREATED",
|
result: "CREATED",
|
||||||
message: "Feed URL added successfully",
|
message: "Feed URL added successfully",
|
||||||
feedUrl
|
feedUrl,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error adding feed:", error);
|
console.error("Error adding feed:", error);
|
||||||
@ -89,18 +93,18 @@ app.get("/api/episodes/simple", async (c) => {
|
|||||||
app.post("/api/episodes/:id/regenerate", async (c) => {
|
app.post("/api/episodes/:id/regenerate", async (c) => {
|
||||||
try {
|
try {
|
||||||
const id = c.req.param("id");
|
const id = c.req.param("id");
|
||||||
|
|
||||||
if (!id || id.trim() === "") {
|
if (!id || id.trim() === "") {
|
||||||
return c.json({ error: "Episode ID is required" }, 400);
|
return c.json({ error: "Episode ID is required" }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("🔄 Regeneration requested for episode ID:", id);
|
console.log("🔄 Regeneration requested for episode ID:", id);
|
||||||
// TODO: Implement regeneration logic
|
// TODO: Implement regeneration logic
|
||||||
return c.json({
|
return c.json({
|
||||||
result: "PENDING",
|
result: "PENDING",
|
||||||
episodeId: id,
|
episodeId: id,
|
||||||
status: "pending",
|
status: "pending",
|
||||||
message: "Regeneration feature will be implemented in a future update"
|
message: "Regeneration feature will be implemented in a future update",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error requesting regeneration:", error);
|
console.error("Error requesting regeneration:", error);
|
||||||
@ -113,14 +117,14 @@ app.get("/api/stats", async (c) => {
|
|||||||
try {
|
try {
|
||||||
const feeds = await getAllFeeds();
|
const feeds = await getAllFeeds();
|
||||||
const episodes = await fetchAllEpisodes();
|
const episodes = await fetchAllEpisodes();
|
||||||
|
|
||||||
const stats = {
|
const stats = {
|
||||||
totalFeeds: feeds.length,
|
totalFeeds: feeds.length,
|
||||||
activeFeeds: feeds.filter(f => f.active).length,
|
activeFeeds: feeds.filter((f) => f.active).length,
|
||||||
totalEpisodes: episodes.length,
|
totalEpisodes: episodes.length,
|
||||||
lastUpdated: new Date().toISOString()
|
lastUpdated: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return c.json(stats);
|
return c.json(stats);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching stats:", error);
|
console.error("Error fetching stats:", error);
|
||||||
@ -131,16 +135,16 @@ app.get("/api/stats", async (c) => {
|
|||||||
app.post("/api/batch/trigger", async (c) => {
|
app.post("/api/batch/trigger", async (c) => {
|
||||||
try {
|
try {
|
||||||
console.log("🚀 Manual batch process triggered via API");
|
console.log("🚀 Manual batch process triggered via API");
|
||||||
|
|
||||||
// Run batch process in background
|
// Run batch process in background
|
||||||
runBatchProcess().catch(error => {
|
runBatchProcess().catch((error) => {
|
||||||
console.error("❌ Manual batch process failed:", error);
|
console.error("❌ Manual batch process failed:", error);
|
||||||
});
|
});
|
||||||
|
|
||||||
return c.json({
|
return c.json({
|
||||||
result: "TRIGGERED",
|
result: "TRIGGERED",
|
||||||
message: "Batch process started in background",
|
message: "Batch process started in background",
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error triggering batch process:", error);
|
console.error("Error triggering batch process:", error);
|
||||||
@ -155,7 +159,7 @@ app.get("/assets/*", async (c) => {
|
|||||||
try {
|
try {
|
||||||
const filePath = path.join(config.paths.frontendBuildDir, c.req.path);
|
const filePath = path.join(config.paths.frontendBuildDir, c.req.path);
|
||||||
const file = Bun.file(filePath);
|
const file = Bun.file(filePath);
|
||||||
|
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
const contentType = filePath.endsWith(".js")
|
const contentType = filePath.endsWith(".js")
|
||||||
? "application/javascript"
|
? "application/javascript"
|
||||||
@ -175,15 +179,18 @@ app.get("/assets/*", async (c) => {
|
|||||||
app.get("/podcast_audio/*", async (c) => {
|
app.get("/podcast_audio/*", async (c) => {
|
||||||
try {
|
try {
|
||||||
const audioFileName = c.req.path.substring("/podcast_audio/".length);
|
const audioFileName = c.req.path.substring("/podcast_audio/".length);
|
||||||
|
|
||||||
// Basic security check
|
// Basic security check
|
||||||
if (audioFileName.includes("..") || audioFileName.includes("/")) {
|
if (audioFileName.includes("..") || audioFileName.includes("/")) {
|
||||||
return c.notFound();
|
return c.notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const audioFilePath = path.join(config.paths.podcastAudioDir, audioFileName);
|
const audioFilePath = path.join(
|
||||||
|
config.paths.podcastAudioDir,
|
||||||
|
audioFileName,
|
||||||
|
);
|
||||||
const file = Bun.file(audioFilePath);
|
const file = Bun.file(audioFilePath);
|
||||||
|
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
const blob = await file.arrayBuffer();
|
const blob = await file.arrayBuffer();
|
||||||
return c.body(blob, 200, { "Content-Type": "audio/mpeg" });
|
return c.body(blob, 200, { "Content-Type": "audio/mpeg" });
|
||||||
@ -199,7 +206,7 @@ app.get("/podcast.xml", async (c) => {
|
|||||||
try {
|
try {
|
||||||
const filePath = path.join(config.paths.publicDir, "podcast.xml");
|
const filePath = path.join(config.paths.publicDir, "podcast.xml");
|
||||||
const file = Bun.file(filePath);
|
const file = Bun.file(filePath);
|
||||||
|
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
const blob = await file.arrayBuffer();
|
const blob = await file.arrayBuffer();
|
||||||
return c.body(blob, 200, {
|
return c.body(blob, 200, {
|
||||||
@ -207,7 +214,7 @@ app.get("/podcast.xml", async (c) => {
|
|||||||
"Cache-Control": "public, max-age=3600", // Cache for 1 hour
|
"Cache-Control": "public, max-age=3600", // Cache for 1 hour
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.warn("podcast.xml not found");
|
console.warn("podcast.xml not found");
|
||||||
return c.notFound();
|
return c.notFound();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -218,10 +225,13 @@ app.get("/podcast.xml", async (c) => {
|
|||||||
|
|
||||||
// Legacy endpoint - redirect to new one
|
// Legacy endpoint - redirect to new one
|
||||||
app.post("/api/add-feed", async (c) => {
|
app.post("/api/add-feed", async (c) => {
|
||||||
return c.json({
|
return c.json(
|
||||||
error: "This endpoint is deprecated. Use POST /api/feeds instead.",
|
{
|
||||||
newEndpoint: "POST /api/feeds"
|
error: "This endpoint is deprecated. Use POST /api/feeds instead.",
|
||||||
}, 410);
|
newEndpoint: "POST /api/feeds",
|
||||||
|
},
|
||||||
|
410,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Frontend fallback routes
|
// Frontend fallback routes
|
||||||
@ -229,12 +239,12 @@ async function serveIndex(c: any) {
|
|||||||
try {
|
try {
|
||||||
const indexPath = path.join(config.paths.frontendBuildDir, "index.html");
|
const indexPath = path.join(config.paths.frontendBuildDir, "index.html");
|
||||||
const file = Bun.file(indexPath);
|
const file = Bun.file(indexPath);
|
||||||
|
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
const blob = await file.arrayBuffer();
|
const blob = await file.arrayBuffer();
|
||||||
return c.body(blob, 200, { "Content-Type": "text/html; charset=utf-8" });
|
return c.body(blob, 200, { "Content-Type": "text/html; charset=utf-8" });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(`index.html not found at ${indexPath}`);
|
console.error(`index.html not found at ${indexPath}`);
|
||||||
return c.text("Frontend not built. Run 'bun run build:frontend'", 404);
|
return c.text("Frontend not built. Run 'bun run build:frontend'", 404);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -265,9 +275,9 @@ function scheduleFirstBatchProcess() {
|
|||||||
|
|
||||||
function scheduleSixHourlyBatchProcess() {
|
function scheduleSixHourlyBatchProcess() {
|
||||||
const SIX_HOURS_MS = 6 * 60 * 60 * 1000; // 6 hours in milliseconds
|
const SIX_HOURS_MS = 6 * 60 * 60 * 1000; // 6 hours in milliseconds
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`🕕 Next batch process scheduled in 6 hours (${new Date(Date.now() + SIX_HOURS_MS).toLocaleString()})`
|
`🕕 Next batch process scheduled in 6 hours (${new Date(Date.now() + SIX_HOURS_MS).toLocaleString()})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
@ -285,7 +295,7 @@ function scheduleSixHourlyBatchProcess() {
|
|||||||
|
|
||||||
async function runBatchProcess(): Promise<void> {
|
async function runBatchProcess(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await batchProcess();
|
Bun.spawn(["bun", "run", "scripts/fetch_and_generate.ts"]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Batch process failed:", error);
|
console.error("Batch process failed:", error);
|
||||||
throw error;
|
throw error;
|
||||||
@ -302,7 +312,7 @@ serve(
|
|||||||
console.log(`🌟 Server is running on http://localhost:${info.port}`);
|
console.log(`🌟 Server is running on http://localhost:${info.port}`);
|
||||||
console.log(`📡 Using configuration from: ${config.paths.projectRoot}`);
|
console.log(`📡 Using configuration from: ${config.paths.projectRoot}`);
|
||||||
console.log(`🗄️ Database: ${config.paths.dbPath}`);
|
console.log(`🗄️ Database: ${config.paths.dbPath}`);
|
||||||
|
|
||||||
// Schedule batch processes
|
// Schedule batch processes
|
||||||
scheduleFirstBatchProcess();
|
scheduleFirstBatchProcess();
|
||||||
scheduleSixHourlyBatchProcess();
|
scheduleSixHourlyBatchProcess();
|
||||||
|
Reference in New Issue
Block a user