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));
|
||||
}
|
||||
|
||||
// Reads existing config JSON from the edit page's textarea (raw value,
|
||||
// not affected by the markdown plugin).
|
||||
async function readExistingConfig(editUrl) {
|
||||
try {
|
||||
const doc = await fetchDoc(editUrl);
|
||||
const ta = doc.querySelector('textarea[name="lf_description"]');
|
||||
if (!ta) return null;
|
||||
const raw = (ta.textContent || '').trim();
|
||||
if (!raw) return null;
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getTokenFrom(url) {
|
||||
const doc = await fetchDoc(url);
|
||||
// Reads the shared edit page (GET /admin/shaare?post=<url>):
|
||||
// - Shaarli returns the editlink form pre-filled with the existing
|
||||
// bookmark (lf_id populated) when the URL already exists, or a blank
|
||||
// edit form (no lf_id) when creating new.
|
||||
// Returns { token, lfId, existing } where existing is the parsed JSON
|
||||
// description (or null).
|
||||
async function readEditForm(postUrl) {
|
||||
const basePath = getBasePath();
|
||||
const src = basePath + '/admin/shaare?post=' + encodeURIComponent(postUrl);
|
||||
const doc = await fetchDoc(src);
|
||||
const tokenEl = doc.querySelector('input[name="token"]');
|
||||
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 {
|
||||
token: tokenEl ? tokenEl.value : '',
|
||||
lfId: idEl ? idEl.value : '',
|
||||
existing,
|
||||
};
|
||||
}
|
||||
|
||||
@ -77,18 +79,27 @@
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Serialize so rapid toggles do not race and create duplicates.
|
||||
const run = async () => {
|
||||
const basePath = getBasePath();
|
||||
const candidates = await findCandidates();
|
||||
const primary = candidates[0] || null;
|
||||
const duplicates = candidates.slice(1);
|
||||
|
||||
// Build merged config
|
||||
let existing = null;
|
||||
if (primary) existing = await readExistingConfig(primary.editUrl);
|
||||
// Collect duplicates via tag search (best-effort)
|
||||
const candidates = await findCandidates();
|
||||
|
||||
// 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 config = Object.assign(
|
||||
{ version: 2, themes: [], default: 'DEFAULT', mode: 'dark' },
|
||||
@ -96,11 +107,6 @@
|
||||
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();
|
||||
fd.append('lf_title', CONFIG_TITLE);
|
||||
fd.append('lf_url', CONFIG_URL);
|
||||
@ -117,13 +123,15 @@
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
|
||||
// Clean up any leftover duplicates (reuse the same token, Shaarli
|
||||
// accepts the session token for both edit and delete).
|
||||
for (const dup of duplicates) {
|
||||
await deleteBookmark(dup.id, token);
|
||||
// Delete any duplicates left over from previous buggy saves,
|
||||
// keeping the one we just saved (lfId) or the first candidate.
|
||||
const keepId = lfId || (candidates[0] && candidates[0].id) || null;
|
||||
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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user