← back to Rentv Adintel
src/adapters/manual-review.js
71 lines
'use strict';
/**
* Manual-review-only adapter (spec §6, §9, §10).
*
* The safe fallback the spec MANDATES for sites that do not permit automation
* (§9: "Do not assume any listed site permits automation. Validate first, and
* fall back to manual review or user upload.").
*
* This adapter NEVER fetches anything: discover() yields nothing and fetch()
* refuses. Instead it exposes queueForReview(), which records a
* manual_review_items row (review_type 'SOURCE') so a human can inspect the
* public page in a normal browser and decide what, if anything, to store.
*/
const { AdvertiserSourceAdapter } = require('./base');
class ManualReviewAdapter extends AdvertiserSourceAdapter {
constructor(policy) {
// Force the access method to manual_review_only so it can never be run
// through the automated fetch path even if a caller mis-configures it.
const safePolicy = Object.assign({}, policy, {
accessMethod: 'manual_review_only',
allowsAutomatedAccess: false,
});
super(safePolicy);
}
// eslint-disable-next-line require-yield
async *discover(_cursor) {
// Intentionally yields nothing — no automated discovery.
return;
}
async fetch(_item) {
throw new Error(
'ManualReviewAdapter.fetch: this source is manual_review_only and MUST NOT be fetched automatically (§6/§9)'
);
}
/**
* queueForReview(pool, { sourceKey, url, title, discoveryQuery })
* Inserts a manual_review_items row of review_type 'SOURCE'. `pool` is a
* pg Pool or any object with a .query() method.
* Returns the new row id.
*/
static async queueForReview(pool, { sourceKey, url, title, discoveryQuery } = {}) {
if (!pool || typeof pool.query !== 'function') {
throw new Error('queueForReview: a pg pool/client with .query() is required');
}
if (!url) throw new Error('queueForReview: url is required');
const payload = {
sourceKey: sourceKey || null,
url,
title: title || null,
note: 'Queued for human review — source not permitted for automation (§9).',
};
const res = await pool.query(
`INSERT INTO manual_review_items
(review_type, payload, status, discovery_query, discovered_at)
VALUES ('SOURCE', $1::jsonb, 'PENDING', $2, now())
RETURNING id`,
[JSON.stringify(payload), discoveryQuery || null]
);
return res.rows[0].id;
}
}
module.exports = { ManualReviewAdapter };