Make scanning an admin setting, and document SSO
The per-feed schedule picker is gone and global Settings is admin-only, enforced in the handlers with 403s rather than just hidden: polling costs bandwidth and affects everyone reading the feed, so it belongs to the operator. Folders, keywords and per-feed limits stay open to anyone. docs/sso.md covers Cloudflare Zero Trust and Authentik end to end, including why trusted_proxies names the proxy and not a subnet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh
This commit is contained in:
@@ -500,7 +500,7 @@ function nav(on){ $('#sidebar').classList.toggle('open',on); $('#scrim').hidden=
|
||||
/* ---------------- state ---------------- */
|
||||
const S = {
|
||||
feeds:[], feed:null, entries:[], total:0, offset:0, limit:50,
|
||||
filter:'all', q:'', sel:null, busy:new Set(),
|
||||
filter:'all', q:'', sel:null, busy:new Set(), me:null,
|
||||
};
|
||||
const LIMIT = 50;
|
||||
|
||||
@@ -1223,7 +1223,6 @@ async function prefsModal(){
|
||||
}
|
||||
|
||||
function settingsModal(f){
|
||||
const fs = splitEvery(f.schedule_mins || globalEvery);
|
||||
const isGroup = S.feeds.some(c=>c.group===f.id);
|
||||
openModal(`<h3>${esc(f.title||f.id)}</h3>
|
||||
${isGroup?`<p class="hint" style="margin:-6px 0 12px">This is an OPML subscription. These
|
||||
@@ -1236,13 +1235,6 @@ function settingsModal(f){
|
||||
<div class="field"><label>Keywords</label>
|
||||
<input type="text" id="skw" value="${esc(f.keywords.join(', '))}">
|
||||
<span class="hint">Comma separated. Empty takes everything.</span></div>
|
||||
<div class="field"><label>Check schedule</label>
|
||||
<div class="inline">
|
||||
<input type="number" id="snum" min="1" max="999" value="${fs.n}" ${f.schedule_mins?'':'disabled'}>
|
||||
<select id="sunit">${unitOptions(f.schedule_mins?fs.u:'', `Use the default — every ${everyText(globalEvery)}`)}</select>
|
||||
</div>
|
||||
<span class="hint">Overrides the global schedule, and the feed's own suggested
|
||||
interval, for this feed only.</span></div>
|
||||
<div class="field"><label>Max new downloads per scan</label>
|
||||
<input type="number" id="smax" min="0" value="${f.max_new_per_check??''}">
|
||||
<span class="hint">Blank follows the global default (${globalMax}). The rest wait for
|
||||
@@ -1259,16 +1251,12 @@ function settingsModal(f){
|
||||
<div class="cardacts"><button class="btn" onclick="closeModal()">Cancel</button>
|
||||
<button class="btn primary" id="ssave">Save</button></div>`);
|
||||
$('#scopy').onclick=()=>copyText($('#surl').value,$('#scopy'));
|
||||
// An empty unit means "follow the global default", so the number has nothing to say.
|
||||
$('#sunit').onchange=()=>{ $('#snum').disabled = !$('#sunit').value; };
|
||||
$('#ssave').onclick=async()=>{
|
||||
const max=$('#smax').value;
|
||||
try{
|
||||
await api(`/api/feeds/${encodeURIComponent(f.id)}`,{method:'PATCH',body:JSON.stringify({
|
||||
url:$('#surl').value.trim(),
|
||||
folder:$('#sfolder').value.trim()||null,
|
||||
schedule:$('#sunit').value
|
||||
? `every ${Math.max(1,Number($('#snum').value)||1)}${$('#sunit').value}` : null,
|
||||
keywords:$('#skw').value.split(',').map(s=>s.trim()).filter(Boolean),
|
||||
max_new_per_check:max===''?null:Number(max),
|
||||
auto_download:$('#sauto').checked, allow_explicit:$('#sexp').checked})});
|
||||
@@ -1335,7 +1323,12 @@ function on(sel,ev,fn){
|
||||
$('#scanAll').onclick=async()=>{ toast('Scanning all feeds…'); await api('/api/fetch',{method:'POST',body:JSON.stringify({force:true})}); };
|
||||
on('#prefs','onclick',prefsModal);
|
||||
on('#signout','onclick',async()=>{ await api('/api/logout',{method:'POST'}); location.href='/login'; });
|
||||
api('/api/me').then(u=>{ $('#who').textContent=u.name+(u.admin?' · admin':''); }).catch(()=>{});
|
||||
api('/api/me').then(u=>{
|
||||
S.me=u;
|
||||
$('#who').textContent=u.name+(u.admin?' · admin':'');
|
||||
// Scanning, quotas and the download folder are the operator's business.
|
||||
if(!u.admin) $('#prefs').hidden=true;
|
||||
}).catch(()=>{});
|
||||
on('#logs','onclick',logsModal);
|
||||
$('#feedFilter').oninput=renderFeeds;
|
||||
$('#burger').onclick=()=>nav(!$('#sidebar').classList.contains('open'));
|
||||
|
||||
Reference in New Issue
Block a user