mirror of
https://github.com/thegeneralist01/archivr
synced 2026-07-22 03:05:32 +02:00
feat: cookie consent extension support (ARCHIVR_COOKIE_EXT)
Mirrors the uBlock Origin Lite integration exactly: Backend: - singlefile.rs: resolve_cookie_ext_config() reads ARCHIVR_COOKIE_CONSENT (default true) + ARCHIVR_COOKIE_EXT path; extension paths comma-joined into --load-extension / --disable-extensions-except so uBlock and cookie ext can coexist; SaveResult.cookie_ext_skipped tracks miss - database.rs: cookie_ext_enabled column on instance_settings (DEFAULT 1); idempotent ALTER TABLE migration; get/update wired through - capture.rs: CaptureConfig.cookie_ext_enabled: Option<bool>; threaded to singlefile::save(); cookie_ext_skipped surfaced in CaptureResult - routes.rs: CaptureBody + UpdateInstanceSettingsBody get cookie_ext_enabled; capture handler resolves effective value (body overrides global); notes_json only includes skipped fields that are true; GET instance-settings includes cookie_ext_available from env path check Frontend: - api.js: submitCapture forwards cookie_ext_enabled - SettingsView.jsx: 'I Still Don't Care About Cookies' card in Extensions tab; always-active toggle (user can disable even when ext not installed); amber 'Not configured' hint + ARCHIVR_COOKIE_EXT guidance when unavailable - CaptureDialog.jsx: 'Block cookie banners' toggle in Advanced options; always shown with amber hint when ext not configured; defaults from global setting Operator setup: download + unzip the extension from GitHub releases, set ARCHIVR_COOKIE_EXT=/path/to/unpacked/ext. No Node daemon needed.
This commit is contained in:
parent
a6ca24b846
commit
52effb60f6
10 changed files with 260 additions and 70 deletions
|
|
@ -102,6 +102,7 @@ export async function submitCapture(archiveId, locator, quality = null, extensio
|
|||
if (extensions) {
|
||||
if (typeof extensions.ublock_enabled === 'boolean') payload.ublock_enabled = extensions.ublock_enabled
|
||||
if (typeof extensions.reader_mode === 'boolean') payload.reader_mode = extensions.reader_mode
|
||||
if (typeof extensions.cookie_ext_enabled === 'boolean') payload.cookie_ext_enabled = extensions.cookie_ext_enabled
|
||||
}
|
||||
const res = await fetch(`/api/archives/${archiveId}/captures`, {
|
||||
method: "POST",
|
||||
|
|
|
|||
|
|
@ -116,18 +116,24 @@ export default function CaptureDialog({ open, archiveId, onClose, onCaptured, on
|
|||
const [advancedOpen, setAdvancedOpen] = useState(false)
|
||||
// null = use server default; true/false = per-session override
|
||||
const [ublockOverride, setUblockOverride] = useState(null)
|
||||
// Server-side global default (loaded on mount, null until loaded)
|
||||
const [globalUblock, setGlobalUblock] = useState(null)
|
||||
// Server-side global settings (loaded on mount, null until loaded)
|
||||
const [globalSettings, setGlobalSettings] = useState(null)
|
||||
// Cookie consent: session-level only, initialized from server default
|
||||
const [cookieExtEnabled, setCookieExtEnabled] = useState(true)
|
||||
|
||||
// Load global uBlock setting from server once on mount
|
||||
// Load global settings from server once on mount
|
||||
useEffect(() => {
|
||||
getInstanceSettings()
|
||||
.then(s => setGlobalUblock(s.ublock_enabled ?? true))
|
||||
.catch(() => setGlobalUblock(true)) // safe default
|
||||
.then(s => {
|
||||
setGlobalSettings(s)
|
||||
setCookieExtEnabled(s.cookie_ext_enabled ?? true)
|
||||
})
|
||||
.catch(() => setGlobalSettings({}))
|
||||
}, [])
|
||||
|
||||
// Effective uBlock for this session
|
||||
const ublockEnabled = ublockOverride !== null ? ublockOverride : (globalUblock ?? true)
|
||||
const ublockEnabled = ublockOverride !== null ? ublockOverride : (globalSettings?.ublock_enabled ?? true)
|
||||
|
||||
// Reader mode: off by default, per-session only
|
||||
const [readerMode, setReaderMode] = useState(false)
|
||||
|
||||
|
|
@ -242,7 +248,7 @@ export default function CaptureDialog({ open, archiveId, onClose, onCaptured, on
|
|||
const qual = item.quality || 'best'
|
||||
setItems(prev => prev.map(it => it.id === item.id ? { ...it, status: 'submitting', error: null } : it))
|
||||
try {
|
||||
const extensions = { ublock_enabled: ublockEnabled, reader_mode: readerMode }
|
||||
const extensions = { ublock_enabled: ublockEnabled, reader_mode: readerMode, cookie_ext_enabled: cookieExtEnabled }
|
||||
const job = await submitCapture(aid, loc, qual, extensions)
|
||||
setItems(prev => prev.map(it =>
|
||||
it.id === item.id ? { ...it, status: 'running', jobUid: job.job_uid, archiveId: aid } : it
|
||||
|
|
@ -397,6 +403,25 @@ export default function CaptureDialog({ open, archiveId, onClose, onCaptured, on
|
|||
<span className="ext-toggle-knob" />
|
||||
</button>
|
||||
</label>
|
||||
<label className="capture-ext-row" style={{ marginTop: 8 }}>
|
||||
<span className="capture-ext-label">
|
||||
<span className="capture-ext-name">Block cookie banners</span>
|
||||
<span className="capture-ext-desc">Dismiss cookie consent banners during this capture</span>
|
||||
{!globalSettings?.cookie_ext_available && (
|
||||
<span className="capture-ext-hint">Not configured — set <code>ARCHIVR_COOKIE_EXT</code></span>
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={cookieExtEnabled}
|
||||
className={`ext-toggle ext-toggle--sm${cookieExtEnabled ? ' ext-toggle--on' : ''}`}
|
||||
onClick={() => setCookieExtEnabled(v => !v)}
|
||||
aria-label="Toggle cookie banner blocking for this capture"
|
||||
>
|
||||
<span className="ext-toggle-knob" />
|
||||
</button>
|
||||
</label>
|
||||
<label className="capture-ext-row" style={{ marginTop: 8 }}>
|
||||
<span className="capture-ext-label">
|
||||
<span className="capture-ext-name">Reader mode</span>
|
||||
|
|
|
|||
|
|
@ -669,10 +669,26 @@ function ExtensionsTab() {
|
|||
}
|
||||
}
|
||||
|
||||
async function toggleCookieExt(val) {
|
||||
setSaving(true)
|
||||
setMsg(null)
|
||||
try {
|
||||
await updateInstanceSettings({ cookie_ext_enabled: val })
|
||||
setSettings(s => ({ ...s, cookie_ext_enabled: val }))
|
||||
setMsg({ ok: true, text: 'Saved.' })
|
||||
} catch (e) {
|
||||
setMsg({ ok: false, text: e.message })
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) return <div className="muted">Loading\u2026</div>
|
||||
|
||||
const extAvailable = settings?.ublock_ext_available ?? false
|
||||
const extEnabled = settings?.ublock_enabled ?? true
|
||||
const cookieExtAvailable = settings?.cookie_ext_available ?? false
|
||||
const cookieExtEnabled = settings?.cookie_ext_enabled ?? true
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: 560 }}>
|
||||
|
|
@ -712,6 +728,34 @@ function ExtensionsTab() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ext-card">
|
||||
<div className="ext-card-header">
|
||||
<div className="ext-card-info">
|
||||
<span className="ext-card-name">I Still Don’t Care About Cookies</span>
|
||||
<span className="ext-card-desc">
|
||||
Dismiss cookie consent banners during archiving.
|
||||
</span>
|
||||
{!cookieExtAvailable && (
|
||||
<span className="ext-card-hint">
|
||||
Not configured — set <code>ARCHIVR_COOKIE_EXT</code> to the
|
||||
unpacked extension directory to enable.
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={cookieExtEnabled}
|
||||
className={`ext-toggle${cookieExtEnabled ? ' ext-toggle--on' : ''}`}
|
||||
onClick={() => toggleCookieExt(!cookieExtEnabled)}
|
||||
disabled={saving}
|
||||
aria-label="Toggle I Still Don't Care About Cookies"
|
||||
>
|
||||
<span className="ext-toggle-knob" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{msg && <div className={`form-msg form-msg--${msg.ok ? 'ok' : 'err'}`}>{msg.text}</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue