feat: refactorer ShaaritThemeConfig.save() pour utiliser readEditForm() unifié (GET /admin/shaare?post=<url>) qui retourne token/lfId/existing en une seule requête, éliminer readExistingConfig() et getTokenFrom() redondants, auto-détection create vs update via présence lf_id dans form Shaarli, cleanup duplicates en excluant bookmark sauvegardé (lfId ou premier candidat), et retourner flag created dans résultat save
This commit is contained in:
parent
f90f8146ce
commit
aaf8e902a1
@ -42,28 +42,30 @@
|
|||||||
return [...byId.values()].sort((a, b) => Number(a.id) - Number(b.id));
|
return [...byId.values()].sort((a, b) => Number(a.id) - Number(b.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads existing config JSON from the edit page's textarea (raw value,
|
// Reads the shared edit page (GET /admin/shaare?post=<url>):
|
||||||
// not affected by the markdown plugin).
|
// - Shaarli returns the editlink form pre-filled with the existing
|
||||||
async function readExistingConfig(editUrl) {
|
// bookmark (lf_id populated) when the URL already exists, or a blank
|
||||||
try {
|
// edit form (no lf_id) when creating new.
|
||||||
const doc = await fetchDoc(editUrl);
|
// Returns { token, lfId, existing } where existing is the parsed JSON
|
||||||
const ta = doc.querySelector('textarea[name="lf_description"]');
|
// description (or null).
|
||||||
if (!ta) return null;
|
async function readEditForm(postUrl) {
|
||||||
const raw = (ta.textContent || '').trim();
|
const basePath = getBasePath();
|
||||||
if (!raw) return null;
|
const src = basePath + '/admin/shaare?post=' + encodeURIComponent(postUrl);
|
||||||
return JSON.parse(raw);
|
const doc = await fetchDoc(src);
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTokenFrom(url) {
|
|
||||||
const doc = await fetchDoc(url);
|
|
||||||
const tokenEl = doc.querySelector('input[name="token"]');
|
const tokenEl = doc.querySelector('input[name="token"]');
|
||||||
const idEl = doc.querySelector('input[name="lf_id"]');
|
const idEl = doc.querySelector('input[name="lf_id"]');
|
||||||
|
const ta = doc.querySelector('textarea[name="lf_description"]');
|
||||||
|
let existing = null;
|
||||||
|
if (ta) {
|
||||||
|
const raw = (ta.textContent || '').trim();
|
||||||
|
if (raw) {
|
||||||
|
try { existing = JSON.parse(raw); } catch (e) { /* non-JSON */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
token: tokenEl ? tokenEl.value : '',
|
token: tokenEl ? tokenEl.value : '',
|
||||||
lfId: idEl ? idEl.value : '',
|
lfId: idEl ? idEl.value : '',
|
||||||
|
existing,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,18 +79,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Merge partial updates into the persisted config and save to the single
|
// Merge partial updates into the persisted config and save to the single
|
||||||
// bookmark. Deletes any duplicates left over from previous buggy saves.
|
// bookmark. Creates one when none exists, updates the existing one, and
|
||||||
|
// deletes any duplicates left over from previous buggy saves.
|
||||||
async function save(partial) {
|
async function save(partial) {
|
||||||
// Serialize so rapid toggles do not race and create duplicates.
|
// Serialize so rapid toggles do not race and create duplicates.
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
const basePath = getBasePath();
|
const basePath = getBasePath();
|
||||||
const candidates = await findCandidates();
|
|
||||||
const primary = candidates[0] || null;
|
|
||||||
const duplicates = candidates.slice(1);
|
|
||||||
|
|
||||||
// Build merged config
|
// Collect duplicates via tag search (best-effort)
|
||||||
let existing = null;
|
const candidates = await findCandidates();
|
||||||
if (primary) existing = await readExistingConfig(primary.editUrl);
|
|
||||||
|
// Always read the edit form for CONFIG_URL: Shaarli returns the
|
||||||
|
// existing bookmark pre-filled (with lf_id) when the URL exists,
|
||||||
|
// or a blank edit form (no lf_id) otherwise. This path works for
|
||||||
|
// both create and update and self-heals if search missed it.
|
||||||
|
const { token, lfId, existing } = await readEditForm(CONFIG_URL);
|
||||||
|
if (!token) {
|
||||||
|
console.warn('[shaarit] theme-config: no CSRF token from edit form');
|
||||||
|
return { ok: false, reason: 'no-token' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge config (preserve any existing fields not in partial)
|
||||||
const base = existing && typeof existing === 'object' ? existing : {};
|
const base = existing && typeof existing === 'object' ? existing : {};
|
||||||
const config = Object.assign(
|
const config = Object.assign(
|
||||||
{ version: 2, themes: [], default: 'DEFAULT', mode: 'dark' },
|
{ version: 2, themes: [], default: 'DEFAULT', mode: 'dark' },
|
||||||
@ -96,11 +107,6 @@
|
|||||||
partial || {}
|
partial || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get token + lf_id from the primary's edit page (or add page)
|
|
||||||
const src = primary ? primary.editUrl : basePath + '/admin/add-shaare';
|
|
||||||
const { token, lfId } = await getTokenFrom(src);
|
|
||||||
if (!token) return { ok: false, reason: 'no-token' };
|
|
||||||
|
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('lf_title', CONFIG_TITLE);
|
fd.append('lf_title', CONFIG_TITLE);
|
||||||
fd.append('lf_url', CONFIG_URL);
|
fd.append('lf_url', CONFIG_URL);
|
||||||
@ -117,13 +123,15 @@
|
|||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clean up any leftover duplicates (reuse the same token, Shaarli
|
// Delete any duplicates left over from previous buggy saves,
|
||||||
// accepts the session token for both edit and delete).
|
// keeping the one we just saved (lfId) or the first candidate.
|
||||||
for (const dup of duplicates) {
|
const keepId = lfId || (candidates[0] && candidates[0].id) || null;
|
||||||
await deleteBookmark(dup.id, token);
|
for (const c of candidates) {
|
||||||
|
if (keepId && String(c.id) === String(keepId)) continue;
|
||||||
|
await deleteBookmark(c.id, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ok: true, id: lfId || (primary && primary.id) || null, config };
|
return { ok: true, id: keepId, config, created: !lfId };
|
||||||
};
|
};
|
||||||
|
|
||||||
const p = (inflight ? inflight.catch(() => {}) : Promise.resolve()).then(run);
|
const p = (inflight ? inflight.catch(() => {}) : Promise.resolve()).then(run);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user