Initial Commit for rss-link-app
Analyze links from rss feeds
This commit is contained in:
commit
060f998c59
8 changed files with 1837 additions and 0 deletions
104
templates/index.html
Normal file
104
templates/index.html
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<section class="mx-auto max-w-3xl p-6">
|
||||
<h1 class="text-3xl font-bold mb-2">RSS Link Audit</h1>
|
||||
<p class="mb-6 opacity-90">Paste a feed URL. This version uses <strong>SQLite/SQLModel caching</strong> and streams progress over <strong>SSE</strong>.</p>
|
||||
|
||||
<form id="feed-form" class="space-y-4 bg-[var(--ra-panel)] p-5 rounded-2xl shadow">
|
||||
<label class="block">
|
||||
<span class="block mb-2 font-semibold">Feed URL</span>
|
||||
<input id="feed-input" type="url" name="feed_url" placeholder="https://example.com/feed.xml"
|
||||
required
|
||||
class="w-full p-3 rounded-xl bg-[var(--ra-ink)] text-[var(--ra-cream)] border border-[var(--ra-copper)] focus:outline-none focus:ring-2 focus:ring-[var(--ra-amber)]" />
|
||||
</label>
|
||||
<button class="px-4 py-2 rounded-xl font-semibold bg-[var(--ra-ruby)] hover:bg-[var(--ra-ruby-dark)]">
|
||||
Analyze
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div id="status" class="mt-6 text-sm opacity-80"></div>
|
||||
|
||||
<section id="summary" class="mt-6"></section>
|
||||
<section id="hosts" class="mt-4 space-y-6"></section>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const statusEl = document.getElementById('status');
|
||||
const hostsEl = document.getElementById('hosts');
|
||||
const summaryEl = document.getElementById('summary');
|
||||
const form = document.getElementById('feed-form');
|
||||
|
||||
function setStatus(html) { statusEl.innerHTML = html; }
|
||||
function appendHostCard(html) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
hostsEl.appendChild(div.firstElementChild);
|
||||
}
|
||||
function setSummary(feed_url, post_count, host_count) {
|
||||
summaryEl.innerHTML = `
|
||||
<div class="rounded-2xl bg-[var(--ra-panel)] border border-[var(--ra-copper)] p-4">
|
||||
<div class="font-semibold mb-1">Summary</div>
|
||||
<div>Feed: <a class="underline" href="${feed_url}" target="_blank" rel="noopener">${feed_url}</a></div>
|
||||
<div>Posts parsed: <strong>${post_count}</strong></div>
|
||||
<div>Hosts found: <strong>${host_count}</strong></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
hostsEl.innerHTML = '';
|
||||
summaryEl.innerHTML = '';
|
||||
setStatus('Starting…');
|
||||
|
||||
const fd = new FormData(form);
|
||||
const resp = await fetch('/start', { method: 'POST', body: fd });
|
||||
if (!resp.ok) {
|
||||
setStatus('Failed to start.');
|
||||
return;
|
||||
}
|
||||
const { job_id } = await resp.json();
|
||||
setStatus('Job started. Connecting…');
|
||||
|
||||
const es = new EventSource(`/events/${job_id}`);
|
||||
let postCount = 0, hostsCount = 0, seenCards = 0;
|
||||
|
||||
es.addEventListener('hello', () => setStatus('Connected. Parsing feed…'));
|
||||
es.addEventListener('status', (ev) => {
|
||||
const d = JSON.parse(ev.data).data;
|
||||
setStatus(`${d.message}`);
|
||||
});
|
||||
es.addEventListener('posts', (ev) => {
|
||||
const data = JSON.parse(ev.data).data;
|
||||
postCount = data.count || 0;
|
||||
setStatus(`Posts: ${postCount}. Fetching pages…`);
|
||||
});
|
||||
es.addEventListener('post_progress', (ev) => {
|
||||
const d = JSON.parse(ev.data).data;
|
||||
setStatus(`Fetching posts ${d.current}/${d.total}…`);
|
||||
});
|
||||
es.addEventListener('hosts', (ev) => {
|
||||
const data = JSON.parse(ev.data).data;
|
||||
hostsCount = data.count || 0;
|
||||
setStatus(`Found ${hostsCount} hosts. Discovering their feeds…`);
|
||||
});
|
||||
es.addEventListener('host_card', (ev) => {
|
||||
const data = JSON.parse(ev.data).data;
|
||||
appendHostCard(data.html);
|
||||
seenCards = data.index;
|
||||
setStatus(`Rendered ${seenCards}/${data.total} hosts… Still discovering feeds…`);
|
||||
});
|
||||
es.addEventListener('summary', (ev) => {
|
||||
const data = JSON.parse(ev.data).data;
|
||||
setSummary(data.feed_url, postCount, hostsCount);
|
||||
});
|
||||
es.addEventListener('error', (ev) => {
|
||||
const data = JSON.parse(ev.data).data;
|
||||
setStatus('Error: ' + (data.message || 'Unknown'));
|
||||
});
|
||||
es.addEventListener('done', () => {
|
||||
setStatus('Done.');
|
||||
es.close();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue