Skip to content

Commit adfc662

Browse files
committed
autosource views in astro
1 parent 4b4027a commit adfc662

4 files changed

Lines changed: 211 additions & 36 deletions

File tree

app.rb

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ def self.development? = ENV['RACK_ENV'] == 'development'
6060

6161
plugin :public
6262
plugin :hash_branches
63-
plugin :render, engine: 'erb', views: 'views'
6463

6564
@show_backtrace = !ENV['CI'].to_s.empty? || (ENV['RACK_ENV'] == 'development')
6665

@@ -84,8 +83,6 @@ def self.development? = ENV['RACK_ENV'] == 'development'
8483
r.on String do |encoded_url|
8584
handle_auto_source_feed(r, encoded_url)
8685
end
87-
88-
r.get { auto_source_instructions_response }
8986
end
9087

9188
# Health check route
@@ -126,12 +123,6 @@ def auto_source_disabled_response
126123
'The auto source feature is disabled.'
127124
end
128125

129-
def auto_source_instructions_response
130-
response.status = 200
131-
response['Content-Type'] = 'text/html'
132-
render(:auto_source_instructions)
133-
end
134-
135126
def handle_auto_source_feed(router, encoded_url)
136127
return unauthorized_response unless AutoSource.authenticate(router)
137128
return forbidden_origin_response unless AutoSource.allowed_origin?(router)
@@ -233,8 +224,6 @@ def handle_static_files(router)
233224
router.on do
234225
if router.path_info == '/'
235226
serve_root_path
236-
elsif File.exist?("public#{router.path_info}")
237-
router.public
238227
else
239228
serve_astro_files(router)
240229
end

frontend/src/layouts/Layout.astro

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const { title = "html2rss-web" } = Astro.props
2121
<h1><a href="/" aria-label="html2rss-web - Home">html2rss-web</a></h1>
2222
<nav aria-label="Main navigation">
2323
<a href="/gallery">Gallery</a>
24-
<a href="/auto_source/">Auto Source</a>
24+
<a href="/auto-source/">Auto Source</a>
25+
<a href="/auto-source-instructions/">Instructions</a>
2526
</nav>
2627
</header>
2728

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
import Layout from '../layouts/Layout.astro';
3+
---
4+
5+
<Layout title="Auto Source - html2rss">
6+
<div class="container">
7+
<h1>Auto Source RSS Generator</h1>
8+
<p>This endpoint generates RSS feeds from any website automatically. To use it, provide a Base64-encoded URL as a path parameter.</p>
9+
10+
<h2>Usage</h2>
11+
<p>Access: <code class="code">/auto_source/&#123;base64_encoded_url&#125;</code></p>
12+
13+
<h2>Example</h2>
14+
<div class="example">
15+
<p>To generate a feed for <code>https://example.com</code>:</p>
16+
<p>1. Encode the URL: <code>https://example.com</code> → <code>aHR0cHM6Ly9leGFtcGxlLmNvbQ==</code></p>
17+
<p>2. Access: <code>/auto_source/aHR0cHM6Ly9leGFtcGxlLmNvbQ==</code></p>
18+
</div>
19+
20+
<h2>Authentication</h2>
21+
<p>This endpoint requires HTTP Basic Authentication. Use the configured credentials.</p>
22+
23+
<h2>URL Restrictions</h2>
24+
<p>For security, only certain URLs may be processed. Check the configuration for allowed domains.</p>
25+
26+
<p><a href="/auto-source/" class="btn btn-primary">🚀 Try Auto Source</a></p>
27+
</div>
28+
</Layout>
29+
30+
<style>
31+
.container {
32+
background: #f8f9fa;
33+
padding: 30px;
34+
border-radius: 8px;
35+
margin: 20px 0;
36+
max-width: 800px;
37+
}
38+
39+
h1 {
40+
color: #333;
41+
margin-bottom: 20px;
42+
}
43+
44+
p {
45+
color: #666;
46+
line-height: 1.6;
47+
}
48+
49+
.code {
50+
background: #e9ecef;
51+
padding: 2px 6px;
52+
border-radius: 4px;
53+
font-family: 'Monaco', 'Menlo', monospace;
54+
}
55+
56+
.example {
57+
background: #f1f3f4;
58+
padding: 15px;
59+
border-radius: 4px;
60+
margin: 10px 0;
61+
}
62+
63+
.btn {
64+
display: inline-block;
65+
padding: 0.5rem 1rem;
66+
background: #007bff;
67+
color: white;
68+
text-decoration: none;
69+
border-radius: 4px;
70+
border: none;
71+
cursor: pointer;
72+
font-size: 1rem;
73+
margin: 0.5rem 0;
74+
}
75+
76+
.btn:hover {
77+
background: #0056b3;
78+
}
79+
80+
.btn-primary {
81+
background: #007bff;
82+
}
83+
84+
.btn-primary:hover {
85+
background: #0056b3;
86+
}
87+
</style>

frontend/src/pages/auto-source.astro

Lines changed: 122 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import Layout from "../layouts/Layout.astro"
77
<h1>Auto Source</h1>
88
<p>Generate RSS feeds from any website automatically</p>
99

10+
<div class="bookmarklet-section">
11+
<h3>📌 Quick Access Bookmarklet</h3>
12+
<p>Drag this button to your bookmarks bar to quickly convert any website to RSS:</p>
13+
<a id="bookmarklet" class="btn btn-bookmarklet" href="#">📰 Convert to RSS</a>
14+
<p class="bookmarklet-help">Click the button above to generate the bookmarklet, then drag it to your bookmarks bar.</p>
15+
</div>
16+
1017
<div class="auto-source-form">
1118
<form id="auto-source-form" novalidate>
1219
<div class="form-group">
@@ -70,6 +77,8 @@ import Layout from "../layouts/Layout.astro"
7077
<li>Use the generated URL in your RSS reader</li>
7178
</ul>
7279

80+
<p><a href="/auto-source-instructions/" class="btn btn-secondary">📖 View Detailed Instructions</a></p>
81+
7382
<div class="restrictions">
7483
<h4>⚠️ URL Restrictions</h4>
7584
<p>
@@ -87,28 +96,80 @@ import Layout from "../layouts/Layout.astro"
8796
</Layout>
8897

8998
<script>
90-
document.getElementById("auto-source-form").addEventListener("submit", async (e) => {
99+
// Bookmarklet functionality
100+
function initBookmarklet() {
101+
const bookmarklet = document.getElementById("bookmarklet") as HTMLAnchorElement;
102+
if (!bookmarklet) return;
103+
104+
bookmarklet.href = generateBookmarkletHref();
105+
}
106+
107+
function generateBookmarkletHref() {
108+
const baseUrl = new URL(window.location.origin);
109+
baseUrl.pathname = "auto_source/";
110+
baseUrl.search = "?url=";
111+
baseUrl.hash = "";
112+
113+
return `javascript:window.location.href='${baseUrl.toString()}'+window.location.href;`;
114+
}
115+
116+
// Initialize bookmarklet on page load
117+
initBookmarklet();
118+
119+
// Handle URL parameters from bookmarklet
120+
function handleUrlParams() {
121+
const params = new URLSearchParams(window.location.search);
122+
const url = params.get("url");
123+
const strategy = params.get("strategy");
124+
125+
if (url) {
126+
const urlInput = document.getElementById("url") as HTMLInputElement;
127+
const strategySelect = document.getElementById("strategy") as HTMLSelectElement;
128+
129+
if (urlInput) urlInput.value = url;
130+
if (strategy && strategySelect) strategySelect.value = strategy;
131+
132+
// Auto-submit if URL is provided
133+
if (urlInput && urlInput.value) {
134+
const form = document.getElementById("auto-source-form") as HTMLFormElement;
135+
if (form) {
136+
form.dispatchEvent(new Event("submit"));
137+
}
138+
}
139+
}
140+
}
141+
142+
// Handle URL parameters on page load
143+
handleUrlParams();
144+
145+
document.getElementById("auto-source-form")?.addEventListener("submit", async (e) => {
91146
e.preventDefault()
92147

93-
const formData = new FormData(e.target)
94-
const url = formData.get("url")
95-
const strategy = formData.get("strategy")
148+
const form = e.target as HTMLFormElement
149+
const formData = new FormData(form)
150+
const url = formData.get("url") as string
151+
const strategy = formData.get("strategy") as string
96152

97153
if (!url) return
98154

155+
// Show loading state
156+
const submitBtn = form.querySelector('button[type="submit"]') as HTMLButtonElement
157+
const originalText = submitBtn.textContent
158+
99159
try {
100-
// Show loading state
101-
const submitBtn = e.target.querySelector('button[type="submit"]')
102-
const originalText = submitBtn.textContent
103160
submitBtn.textContent = "Generating..."
104161
submitBtn.disabled = true
105162

106163
// Encode URL for API
107164
const encodedUrl = btoa(url)
108-
const apiUrl = `http://localhost:3000/auto_source/${encodedUrl}?strategy=${strategy}`
165+
const apiUrl = `http://127.0.0.1:3001/auto_source/${encodedUrl}?strategy=${strategy}`
109166

110-
// Test the API call
111-
const response = await fetch(apiUrl)
167+
// Test the API call with Basic authentication
168+
const response = await fetch(apiUrl, {
169+
headers: {
170+
'Authorization': 'Basic ' + btoa('admin:password')
171+
}
172+
})
112173

113174
if (!response.ok) {
114175
throw new Error(`API call failed: ${response.status} ${response.statusText}`)
@@ -117,20 +178,20 @@ import Layout from "../layouts/Layout.astro"
117178
// Show result area
118179
const resultArea = document.getElementById("result")
119180
const feedUrlSpan = document.getElementById("feed-url")
120-
const subscribeLink = document.getElementById("subscribe-link")
181+
const subscribeLink = document.getElementById("subscribe-link") as HTMLAnchorElement
121182

122-
feedUrlSpan.textContent = apiUrl
123-
subscribeLink.href = apiUrl
124-
resultArea.style.display = "block"
125-
126-
// Scroll to result
127-
resultArea.scrollIntoView({ behavior: "smooth" })
183+
if (feedUrlSpan) feedUrlSpan.textContent = apiUrl
184+
if (subscribeLink) subscribeLink.href = apiUrl
185+
if (resultArea) {
186+
resultArea.style.display = "block"
187+
resultArea.scrollIntoView({ behavior: "smooth" })
188+
}
128189
} catch (error) {
129190
console.error("Error generating feed:", error)
130191
showError(`Error generating feed: ${error.message}`)
131192
} finally {
132193
// Reset button state
133-
const submitBtn = e.target.querySelector('button[type="submit"]')
194+
const submitBtn = form.querySelector('button[type="submit"]') as HTMLButtonElement
134195
submitBtn.textContent = originalText
135196
submitBtn.disabled = false
136197
}
@@ -142,18 +203,55 @@ import Layout from "../layouts/Layout.astro"
142203
const resultArea = document.getElementById("result")
143204

144205
// Hide result area
145-
resultArea.style.display = "none"
206+
if (resultArea) resultArea.style.display = "none"
146207

147208
// Show error area
148-
errorMessage.textContent = message
149-
errorArea.style.display = "block"
150-
151-
// Scroll to error
152-
errorArea.scrollIntoView({ behavior: "smooth" })
209+
if (errorMessage) errorMessage.textContent = message
210+
if (errorArea) {
211+
errorArea.style.display = "block"
212+
errorArea.scrollIntoView({ behavior: "smooth" })
213+
}
153214
}
154215
</script>
155216

156217
<style>
218+
.bookmarklet-section {
219+
max-width: 600px;
220+
margin: 2rem 0;
221+
padding: 1.5rem;
222+
background: #f8f9fa;
223+
border: 1px solid #dee2e6;
224+
border-radius: 8px;
225+
}
226+
227+
.bookmarklet-section h3 {
228+
margin: 0 0 1rem 0;
229+
color: #495057;
230+
}
231+
232+
.bookmarklet-section p {
233+
margin: 0.5rem 0;
234+
color: #6c757d;
235+
}
236+
237+
.btn-bookmarklet {
238+
background: #28a745;
239+
font-size: 1.1rem;
240+
padding: 0.75rem 1.5rem;
241+
margin: 1rem 0;
242+
display: inline-block;
243+
}
244+
245+
.btn-bookmarklet:hover {
246+
background: #218838;
247+
}
248+
249+
.bookmarklet-help {
250+
font-size: 0.875rem;
251+
font-style: italic;
252+
color: #6c757d;
253+
}
254+
157255
.auto-source-form {
158256
max-width: 600px;
159257
margin: 2rem 0;

0 commit comments

Comments
 (0)