Update for static file serving
This commit is contained in:
		
							
								
								
									
										47
									
								
								server.ts
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								server.ts
									
									
									
									
									
								
							@@ -129,15 +129,33 @@ app.get("/podcast.xml", async (c) => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Category-specific RSS feeds
 | 
					// Category-specific RSS feeds - try static file first, then generate dynamically
 | 
				
			||||||
app.get("/podcast/category/:category.xml", async (c) => {
 | 
					app.get("/podcast/category/:category.xml", async (c) => {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const category = decodeURIComponent(c.req.param("category") || "");
 | 
					    const category = decodeURIComponent(c.req.param("category") || "");
 | 
				
			||||||
    if (!category) {
 | 
					    if (!category) {
 | 
				
			||||||
      return c.notFound();
 | 
					      return c.notFound();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const { generateCategoryRSS } = await import("./services/podcast.js");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to serve static file first
 | 
				
			||||||
 | 
					    const safeCategory = encodeURIComponent(category);
 | 
				
			||||||
 | 
					    const staticFilePath = path.join(
 | 
				
			||||||
 | 
					      config.paths.publicDir,
 | 
				
			||||||
 | 
					      `podcast_category_${safeCategory}.xml`,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    const staticFile = Bun.file(staticFilePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (await staticFile.exists()) {
 | 
				
			||||||
 | 
					      const blob = await staticFile.arrayBuffer();
 | 
				
			||||||
 | 
					      return c.body(blob, 200, {
 | 
				
			||||||
 | 
					        "Content-Type": "application/xml; charset=utf-8",
 | 
				
			||||||
 | 
					        "Cache-Control": "public, max-age=3600", // Cache for 1 hour
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Fallback to dynamic generation
 | 
				
			||||||
 | 
					    console.log(`📄 Static category RSS not found for "${category}", generating dynamically...`);
 | 
				
			||||||
 | 
					    const { generateCategoryRSS } = await import("./services/podcast.js");
 | 
				
			||||||
    const rssXml = await generateCategoryRSS(category);
 | 
					    const rssXml = await generateCategoryRSS(category);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return c.body(rssXml, 200, {
 | 
					    return c.body(rssXml, 200, {
 | 
				
			||||||
@@ -146,22 +164,39 @@ app.get("/podcast/category/:category.xml", async (c) => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    console.error(
 | 
					    console.error(
 | 
				
			||||||
      `Error generating category RSS for "${c.req.param("category")}":`,
 | 
					      `Error serving category RSS for "${c.req.param("category")}":`,
 | 
				
			||||||
      error,
 | 
					      error,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    return c.notFound();
 | 
					    return c.notFound();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Feed-specific RSS feeds
 | 
					// Feed-specific RSS feeds - try static file first, then generate dynamically
 | 
				
			||||||
app.get("/podcast/feed/:feedId.xml", async (c) => {
 | 
					app.get("/podcast/feed/:feedId.xml", async (c) => {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const feedId = c.req.param("feedId");
 | 
					    const feedId = c.req.param("feedId");
 | 
				
			||||||
    if (!feedId) {
 | 
					    if (!feedId) {
 | 
				
			||||||
      return c.notFound();
 | 
					      return c.notFound();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const { generateFeedRSS } = await import("./services/podcast.js");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to serve static file first
 | 
				
			||||||
 | 
					    const staticFilePath = path.join(
 | 
				
			||||||
 | 
					      config.paths.publicDir,
 | 
				
			||||||
 | 
					      `podcast_feed_${feedId}.xml`,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    const staticFile = Bun.file(staticFilePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (await staticFile.exists()) {
 | 
				
			||||||
 | 
					      const blob = await staticFile.arrayBuffer();
 | 
				
			||||||
 | 
					      return c.body(blob, 200, {
 | 
				
			||||||
 | 
					        "Content-Type": "application/xml; charset=utf-8",
 | 
				
			||||||
 | 
					        "Cache-Control": "public, max-age=3600", // Cache for 1 hour
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Fallback to dynamic generation
 | 
				
			||||||
 | 
					    console.log(`📄 Static feed RSS not found for "${feedId}", generating dynamically...`);
 | 
				
			||||||
 | 
					    const { generateFeedRSS } = await import("./services/podcast.js");
 | 
				
			||||||
    const rssXml = await generateFeedRSS(feedId);
 | 
					    const rssXml = await generateFeedRSS(feedId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return c.body(rssXml, 200, {
 | 
					    return c.body(rssXml, 200, {
 | 
				
			||||||
@@ -170,7 +205,7 @@ app.get("/podcast/feed/:feedId.xml", async (c) => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    console.error(
 | 
					    console.error(
 | 
				
			||||||
      `Error generating feed RSS for "${c.req.param("feedId")}":`,
 | 
					      `Error serving feed RSS for "${c.req.param("feedId")}":`,
 | 
				
			||||||
      error,
 | 
					      error,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    return c.notFound();
 | 
					    return c.notFound();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -139,20 +139,118 @@ export async function updatePodcastRSS(): Promise<void> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Generate all category RSS files as static files
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function generateAllCategoryRSSFiles(): Promise<void> {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const { getAllEpisodeCategories } = await import("./database.js");
 | 
				
			||||||
 | 
					    const categories = await getAllEpisodeCategories();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    console.log(`🔄 Generating ${categories.length} category RSS files...`);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for (const category of categories) {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        await saveCategoryRSSFile(category);
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        console.error(`❌ Failed to generate RSS for category "${category}":`, error);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    console.log(`✅ Generated category RSS files for ${categories.length} categories`);
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    console.error("❌ Error generating category RSS files:", error);
 | 
				
			||||||
 | 
					    throw error;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Generate all feed RSS files as static files
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function generateAllFeedRSSFiles(): Promise<void> {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const { fetchActiveFeeds } = await import("./database.js");
 | 
				
			||||||
 | 
					    const feeds = await fetchActiveFeeds();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    console.log(`🔄 Generating ${feeds.length} feed RSS files...`);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for (const feed of feeds) {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        await saveFeedRSSFile(feed.id);
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        console.error(`❌ Failed to generate RSS for feed "${feed.id}":`, error);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    console.log(`✅ Generated feed RSS files for ${feeds.length} feeds`);
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    console.error("❌ Error generating feed RSS files:", error);
 | 
				
			||||||
 | 
					    throw error;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Save category RSS as static file with URL-safe filename
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function saveCategoryRSSFile(category: string): Promise<void> {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const rssXml = await generateCategoryRSS(category);
 | 
				
			||||||
 | 
					    const safeCategory = encodeURIComponent(category);
 | 
				
			||||||
 | 
					    const outputPath = path.join(
 | 
				
			||||||
 | 
					      config.paths.publicDir,
 | 
				
			||||||
 | 
					      `podcast_category_${safeCategory}.xml`,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Ensure directory exists
 | 
				
			||||||
 | 
					    await fs.mkdir(dirname(outputPath), { recursive: true });
 | 
				
			||||||
 | 
					    await fs.writeFile(outputPath, rssXml);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(`📄 Category RSS saved: podcast_category_${safeCategory}.xml`);
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    console.error(`❌ Error saving category RSS for "${category}":`, error);
 | 
				
			||||||
 | 
					    throw error;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Save feed RSS as static file
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function saveFeedRSSFile(feedId: string): Promise<void> {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const rssXml = await generateFeedRSS(feedId);
 | 
				
			||||||
 | 
					    const outputPath = path.join(
 | 
				
			||||||
 | 
					      config.paths.publicDir,
 | 
				
			||||||
 | 
					      `podcast_feed_${feedId}.xml`,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Ensure directory exists
 | 
				
			||||||
 | 
					    await fs.mkdir(dirname(outputPath), { recursive: true });
 | 
				
			||||||
 | 
					    await fs.writeFile(outputPath, rssXml);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(`📄 Feed RSS saved: podcast_feed_${feedId}.xml`);
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    console.error(`❌ Error saving feed RSS for "${feedId}":`, error);
 | 
				
			||||||
 | 
					    throw error;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Regenerate all static files on startup
 | 
					 * Regenerate all static files on startup
 | 
				
			||||||
 * This ensures that podcast.xml and other generated files are up-to-date
 | 
					 * This ensures that podcast.xml and other generated files are up-to-date
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function regenerateStartupFiles(): Promise<void> {
 | 
					export async function regenerateStartupFiles(): Promise<void> {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    console.log("🔄 Regenerating static files on startup...");
 | 
					    console.log("🔄 Regenerating all static files on startup...");
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Regenerate main podcast.xml
 | 
					    // Regenerate main podcast.xml
 | 
				
			||||||
    await updatePodcastRSS();
 | 
					    await updatePodcastRSS();
 | 
				
			||||||
    console.log("✅ podcast.xml regenerated successfully");
 | 
					    console.log("✅ podcast.xml regenerated successfully");
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Note: Category and feed-specific RSS files are generated dynamically on request
 | 
					    // Generate all category RSS files
 | 
				
			||||||
    // This is more efficient and ensures they're always up-to-date with current data
 | 
					    await generateAllCategoryRSSFiles();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Generate all feed RSS files
 | 
				
			||||||
 | 
					    await generateAllFeedRSSFiles();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    console.log("✅ All startup files regenerated successfully");
 | 
					    console.log("✅ All startup files regenerated successfully");
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user