Feed-redirect: 301 for podcast som flyttes til ny host (oppgave 30.8)
Når redirect_feed er satt i podcast-trait, returnerer maskinrommet HTTP 301 Moved Permanently med Location-header i stedet for å serve feeden. iTunes new-feed-url-taggen bevares også i RSS-en for klienter som ikke følger 301. Admin-UI: erstatter det enkle tekstfeltet med tre tilstander: - Inaktiv: knapp "Flytt podcast til annen plattform..." - Bekreftelse: advarsel + URL-felt + rød "Aktiver redirect"-knapp - Aktiv: gul statusindikator med deaktiver-knapp Backend: sjekker redirect_feed tidlig i generate_feed() og returnerer 301 før noe annet arbeid gjøres (DB-oppslag for episodes osv).
This commit is contained in:
parent
ba8d361626
commit
c5239d2923
4 changed files with 98 additions and 17 deletions
|
|
@ -202,8 +202,13 @@ Brukeren eier dataene sine. Flytte bort er enkelt:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Når satt: Caddy returnerer 301 for feed-URL.
|
Når satt: maskinrommet returnerer HTTP 301 Moved Permanently for
|
||||||
Apple/Spotify oppdaterer automatisk.
|
`/pub/{slug}/feed.xml` med `Location`-header til ny URL.
|
||||||
|
Apple/Spotify oppdaterer automatisk. I tillegg inkluderes
|
||||||
|
`<itunes:new-feed-url>` i RSS-en for klienter som ikke følger 301.
|
||||||
|
|
||||||
|
Admin-UI har én-klikks aktivering med advarsel. Redirecten kan
|
||||||
|
deaktiveres når som helst fra podcast-trait-innstillingene.
|
||||||
|
|
||||||
Brukeren kan også eksportere all data:
|
Brukeren kan også eksportere all data:
|
||||||
- RSS-feed med alle episoder
|
- RSS-feed med alle episoder
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
let explicit = $state((config.explicit as boolean) ?? false);
|
let explicit = $state((config.explicit as boolean) ?? false);
|
||||||
let language = $state((config.language as string) ?? 'no');
|
let language = $state((config.language as string) ?? 'no');
|
||||||
let redirectFeed = $state((config.redirect_feed as string) ?? '');
|
let redirectFeed = $state((config.redirect_feed as string) ?? '');
|
||||||
|
let redirectInput = $state('');
|
||||||
|
let showRedirectConfirm = $state(false);
|
||||||
|
|
||||||
// iTunes category tree (Apple Podcasts standard)
|
// iTunes category tree (Apple Podcasts standard)
|
||||||
const itunesCategories: Record<string, string[]> = {
|
const itunesCategories: Record<string, string[]> = {
|
||||||
|
|
@ -182,19 +184,81 @@
|
||||||
|
|
||||||
<!-- Redirect Feed -->
|
<!-- Redirect Feed -->
|
||||||
<div>
|
<div>
|
||||||
<label for="podcast-redirect" class="block text-xs font-medium text-gray-600 mb-1">
|
<label class="block text-xs font-medium text-gray-600 mb-1">
|
||||||
Redirect-feed (valgfritt)
|
Feed-redirect (flytte podcast)
|
||||||
</label>
|
</label>
|
||||||
<input
|
|
||||||
id="podcast-redirect"
|
{#if redirectFeed}
|
||||||
type="url"
|
<!-- Active redirect indicator -->
|
||||||
bind:value={redirectFeed}
|
<div class="rounded border border-amber-300 bg-amber-50 p-3 space-y-2">
|
||||||
oninput={emitConfig}
|
<div class="flex items-center gap-2">
|
||||||
placeholder="https://ny-host.example.com/feed.xml"
|
<span class="inline-block h-2 w-2 rounded-full bg-amber-500"></span>
|
||||||
class="w-full rounded border border-gray-300 px-3 py-1.5 text-sm focus:border-indigo-500 focus:outline-none"
|
<span class="text-xs font-semibold text-amber-800">Redirect aktiv</span>
|
||||||
/>
|
</div>
|
||||||
<p class="mt-0.5 text-[11px] text-gray-400">
|
<p class="text-xs text-amber-700 break-all">
|
||||||
Brukes for a flytte podcasten til en annen plattform. Setter itunes:new-feed-url i RSS.
|
Feed-URL returnerer 301 til:<br/>
|
||||||
</p>
|
<a href={redirectFeed} target="_blank" rel="noopener" class="underline">{redirectFeed}</a>
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => { redirectFeed = ''; emitConfig(); }}
|
||||||
|
class="rounded bg-white border border-amber-400 px-3 py-1 text-xs font-medium text-amber-800 hover:bg-amber-100"
|
||||||
|
>
|
||||||
|
Deaktiver redirect
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{:else if showRedirectConfirm}
|
||||||
|
<!-- Confirmation dialog -->
|
||||||
|
<div class="rounded border border-red-300 bg-red-50 p-3 space-y-2">
|
||||||
|
<p class="text-xs font-semibold text-red-800">
|
||||||
|
Advarsel: Dette aktiverer en permanent redirect (301)
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-red-700">
|
||||||
|
Alle som henter feeden vil bli sendt til den nye URL-en.
|
||||||
|
Podcastklienter (Apple, Spotify) oppdaterer automatisk.
|
||||||
|
Du kan deaktivere redirecten nar som helst.
|
||||||
|
</p>
|
||||||
|
<input
|
||||||
|
id="podcast-redirect"
|
||||||
|
type="url"
|
||||||
|
bind:value={redirectInput}
|
||||||
|
placeholder="https://ny-host.example.com/feed.xml"
|
||||||
|
class="w-full rounded border border-red-300 px-3 py-1.5 text-sm focus:border-red-500 focus:outline-none"
|
||||||
|
/>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={!redirectInput.trim()}
|
||||||
|
onclick={() => {
|
||||||
|
redirectFeed = redirectInput.trim();
|
||||||
|
showRedirectConfirm = false;
|
||||||
|
emitConfig();
|
||||||
|
}}
|
||||||
|
class="rounded bg-red-600 px-3 py-1 text-xs font-medium text-white hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
Aktiver redirect
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => { showRedirectConfirm = false; redirectInput = ''; }}
|
||||||
|
class="rounded bg-white border border-gray-300 px-3 py-1 text-xs font-medium text-gray-700 hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
Avbryt
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<!-- Inactive state — one-click to start -->
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => { showRedirectConfirm = true; }}
|
||||||
|
class="rounded border border-gray-300 px-3 py-1.5 text-xs font-medium text-gray-600 hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
Flytt podcast til annen plattform...
|
||||||
|
</button>
|
||||||
|
<p class="mt-0.5 text-[11px] text-gray-400">
|
||||||
|
Aktiverer 301-redirect pa feed-URL-en slik at podcastklienter automatisk oppdaterer.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,19 @@ pub async fn generate_feed(
|
||||||
})?
|
})?
|
||||||
.ok_or(StatusCode::NOT_FOUND)?;
|
.ok_or(StatusCode::NOT_FOUND)?;
|
||||||
|
|
||||||
|
// 301 Moved Permanently når redirect_feed er satt (podcast flyttet til ny host)
|
||||||
|
if let Some(ref redirect_url) = collection.podcast_config.redirect_feed {
|
||||||
|
if !redirect_url.is_empty() {
|
||||||
|
tracing::info!(slug = %slug, redirect = %redirect_url, "Feed-redirect aktiv");
|
||||||
|
return Ok(Response::builder()
|
||||||
|
.status(StatusCode::MOVED_PERMANENTLY)
|
||||||
|
.header(header::LOCATION, redirect_url.as_str())
|
||||||
|
.header(header::CACHE_CONTROL, "public, max-age=3600")
|
||||||
|
.body("Moved Permanently".into())
|
||||||
|
.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let max_items = collection.rss_config.max_items.unwrap_or(50);
|
let max_items = collection.rss_config.max_items.unwrap_or(50);
|
||||||
let items = fetch_feed_items(&state.db, collection.id, max_items, collection.is_podcast)
|
let items = fetch_feed_items(&state.db, collection.id, max_items, collection.is_podcast)
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
3
tasks.md
3
tasks.md
|
|
@ -437,5 +437,4 @@ prøveimport-flyt.
|
||||||
- [x] 30.7 Prøveimport-flyt i frontend: "Importer podcast"-wizard i admin. Steg 1: lim inn RSS-URL, vis forhåndsvisning av episoder. Steg 2: importer (kan ta tid for mange episoder). Steg 3: sjekk resultat, sammenlign feeds. Steg 4: re-importer nye episoder når klar. Steg 5: aktiver 301-redirect på gammel host.
|
- [x] 30.7 Prøveimport-flyt i frontend: "Importer podcast"-wizard i admin. Steg 1: lim inn RSS-URL, vis forhåndsvisning av episoder. Steg 2: importer (kan ta tid for mange episoder). Steg 3: sjekk resultat, sammenlign feeds. Steg 4: re-importer nye episoder når klar. Steg 5: aktiver 301-redirect på gammel host.
|
||||||
|
|
||||||
### Eksport og redirect
|
### Eksport og redirect
|
||||||
- [~] 30.8 Feed-redirect: `redirect_feed`-felt i podcast-trait. Når satt: Caddy returnerer 301 for feed-URL. Brukeren kan alltid flytte bort. Admin-UI med én-klikks aktivering og advarsel.
|
- [x] 30.8 Feed-redirect: `redirect_feed`-felt i podcast-trait. Når satt: Caddy returnerer 301 for feed-URL. Brukeren kan alltid flytte bort. Admin-UI med én-klikks aktivering og advarsel.
|
||||||
> Påbegynt: 2026-03-19T00:25
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue