index.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
  2. const corsHeaders = {
  3. 'Access-Control-Allow-Origin': '*',
  4. 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
  5. };
  6. interface RSSFeed {
  7. url: string;
  8. title: string;
  9. type: string;
  10. }
  11. serve(async (req) => {
  12. // Handle CORS preflight requests
  13. if (req.method === 'OPTIONS') {
  14. return new Response(null, { headers: corsHeaders });
  15. }
  16. try {
  17. const { url } = await req.json();
  18. if (!url) {
  19. return new Response(
  20. JSON.stringify({ error: 'URL is required' }),
  21. { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
  22. );
  23. }
  24. console.log('Fetching website RSS for URL:', url);
  25. // Fetch the webpage
  26. const response = await fetch(url, {
  27. headers: {
  28. 'User-Agent': 'Mozilla/5.0 (compatible; RSS Feed Detector/1.0)',
  29. },
  30. });
  31. if (!response.ok) {
  32. throw new Error(`Failed to fetch website: ${response.status}`);
  33. }
  34. const html = await response.text();
  35. const feeds: RSSFeed[] = [];
  36. // Extract site name from <title> tag
  37. const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
  38. const siteName = titleMatch ? titleMatch[1].trim() : new URL(url).hostname;
  39. // Look for RSS/Atom links in <link> tags
  40. const linkRegex = /<link[^>]+rel=["']alternate["'][^>]+>/gi;
  41. const links = html.match(linkRegex) || [];
  42. for (const link of links) {
  43. const typeMatch = link.match(/type=["']([^"']+)["']/i);
  44. const hrefMatch = link.match(/href=["']([^"']+)["']/i);
  45. const titleMatch = link.match(/title=["']([^"']+)["']/i);
  46. if (typeMatch && hrefMatch) {
  47. const type = typeMatch[1];
  48. const href = hrefMatch[1];
  49. const title = titleMatch ? titleMatch[1] : 'RSS Feed';
  50. if (
  51. type.includes('application/rss+xml') ||
  52. type.includes('application/atom+xml') ||
  53. type.includes('application/xml')
  54. ) {
  55. // Convert relative URLs to absolute
  56. const feedUrl = href.startsWith('http') ? href : new URL(href, url).toString();
  57. feeds.push({
  58. url: feedUrl,
  59. title,
  60. type,
  61. });
  62. }
  63. }
  64. }
  65. // Fallback: Check common RSS paths if no feeds found
  66. if (feeds.length === 0) {
  67. const commonPaths = ['/feed', '/rss', '/feed.xml', '/rss.xml', '/atom.xml', '/index.xml'];
  68. const baseUrl = new URL(url);
  69. for (const path of commonPaths) {
  70. const testUrl = `${baseUrl.origin}${path}`;
  71. try {
  72. const testResponse = await fetch(testUrl, { method: 'HEAD' });
  73. if (testResponse.ok) {
  74. feeds.push({
  75. url: testUrl,
  76. title: 'RSS Feed',
  77. type: 'application/rss+xml',
  78. });
  79. }
  80. } catch (error) {
  81. // Ignore errors for fallback checks
  82. console.log(`Failed to check ${testUrl}:`, error.message);
  83. }
  84. }
  85. }
  86. console.log('Found feeds:', feeds);
  87. if (feeds.length === 0) {
  88. return new Response(
  89. JSON.stringify({
  90. success: false,
  91. error: 'No RSS feeds found on this website',
  92. siteName
  93. }),
  94. { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
  95. );
  96. }
  97. // Return the first feed by default, or all feeds if multiple
  98. return new Response(
  99. JSON.stringify({
  100. success: true,
  101. rssUrl: feeds[0].url,
  102. siteName,
  103. feeds: feeds.length > 1 ? feeds : undefined,
  104. }),
  105. { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
  106. );
  107. } catch (error) {
  108. console.error('Error fetching website RSS:', error);
  109. return new Response(
  110. JSON.stringify({
  111. success: false,
  112. error: error.message || 'Failed to detect RSS feed'
  113. }),
  114. { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
  115. );
  116. }
  117. });