Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion apps/web/src/app/api/frame/route.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { normalizeUrl, probePage, readableArticle, reframePage } from '@rssamplifier/feed';
import { removals } from '@rssamplifier/db';
import { withPageSlot } from '../../../lib/pageGate.js';

import { siteUrl } from '../../../lib/db.js';
import { db, siteUrl } from '../../../lib/db.js';
import { removedPublisherResponse } from '../../../lib/frameRemoval.js';

export const dynamic = 'force-dynamic';

Expand Down Expand Up @@ -79,6 +81,11 @@ export async function GET(req) {
* @returns {Promise<Response>}
*/
async function openPage({ target, site, self, through }) {
const removed = await removedPublisherResponse(target, (url) =>
removals.isRemovedUrl(db(), url),
);
if (removed) return removed;

const probe = await probePage(target, { origin: site, wantHtml: 'always' });
const landed = probe.url ?? target;

Expand Down
20 changes: 20 additions & 0 deletions apps/web/src/lib/frameRemoval.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Refuse a publisher that has been removed from the directory before the
* reader's frame makes any request to that host.
*
* @param {string} url
* @param {(url: string) => Promise<boolean>} isRemovedUrl
* @returns {Promise<Response|null>}
*/
export async function removedPublisherResponse(url, isRemovedUrl) {
if (!(await isRemovedUrl(url))) return null;

return new Response('This publisher has been removed from RSS Amplifier.', {
status: 410,
headers: {
'cache-control': 'no-store',
'content-type': 'text/plain; charset=utf-8',
'x-robots-tag': 'noindex',
},
});
}
26 changes: 26 additions & 0 deletions apps/web/test/frame-removal.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';

import { removedPublisherResponse } from '../src/lib/frameRemoval.js';

test('a removed publisher gets a no-store 410 response before it can be framed', async () => {
let checkedUrl = null;
const response = await removedPublisherResponse(
'https://blog.marcolancini.it/posts/one',
async (url) => {
checkedUrl = url;
return true;
},
);

assert.equal(checkedUrl, 'https://blog.marcolancini.it/posts/one');
assert.equal(response.status, 410);
assert.equal(response.headers.get('cache-control'), 'no-store');
assert.equal(response.headers.get('x-robots-tag'), 'noindex');
assert.match(await response.text(), /removed from RSS Amplifier/);
});

test('a publisher without a removal record continues through the normal frame path', async () => {
const response = await removedPublisherResponse('https://example.com/post', async () => false);
assert.equal(response, null);
});
Loading