feat(diigo-dump): session-cookie dump worker, manual test passing

Port of mm-diigo/projects/diigo-bak/diigo-dump.mjs as a Cloudflare Worker:
one page-number field + button, GET interact_api/load_user_items
(sort=updated, count=24) with DIIGO_COOKIE as a Worker secret.
GET / is inert; dumps happen only on POST submit.

Manual integration testing (local wrangler dev, port 8788, 2026-10-06)
shows it works: status 200, total 1674 bookmarks, page-1 items returned.
The browser-session-cookie path authenticates; the static API-key path
(diigo-ping worker, EmDash plugin) is still rejected by Diigo (401).

Also: project checklist (capsite-checklist.md) with step results, and a
.gitignore for local artifacts (.dev.vars, node_modules, wrangler state).

Generated-by: opencode (GLM)
This commit is contained in:
Connolly committed 2026-10-06 21:33:25 -05:00
commit 126aab5cbe
4 files changed
+168

No files matched your search

+90
View File
@@ -0,0 +1,90 @@
// diigo-dump as a worker: one page-number field + button.
// Port of mm-diigo/projects/diigo-bak/diigo-dump.mjs —
// browser-session-cookie access to diigo's interact_api/load_user_items
// (the API-key path is bkmkeep.py / the EmDash plugin; this one is the cookie path).
// Secret: DIIGO_COOKIE (copy the "Cookie" header from diigo.com devtools).
const DIIGO_BASE = "https://www.diigo.com/interact_api/";
const COUNT = 24;
type Env = { DIIGO_COOKIE?: string };
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
if (url.pathname !== "/") return new Response("not found", { status: 404 });
if (request.method !== "POST") return html(null);
const form = await request.formData();
const raw = form.get("page");
const page = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
if (Number.isNaN(page) || page < 1 || page > 10000) {
return html(null, "enter a page number (1 = most recently updated)");
}
const result = await dumpPage(env, page);
return html(result.status, result.detail, String(page));
},
};
async function dumpPage(env: Env, page: number): Promise<{ status: string; detail: string }> {
const cookie = env.DIIGO_COOKIE;
if (!cookie) {
return { status: "no-creds", detail: "Missing DIIGO_COOKIE.\nSet it: npx wrangler secret put DIIGO_COOKIE (or .dev.vars locally)." };
}
const target = new URL(DIIGO_BASE + "load_user_items");
target.searchParams.set("sort", "updated");
target.searchParams.set("count", String(COUNT));
target.searchParams.set("page_num", String(page));
try {
const response = await fetch(target.toString(), {
headers: {
cookie,
// same attenuated-request shape diigo-dump.mjs / diigo-interact.mjs use
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) Gecko/20100101 Firefox/141.0",
"x-requested-with": "XMLHttpRequest",
},
});
const text = await response.text();
let detail = text;
if (response.ok) {
try {
const data = JSON.parse(text) as { total?: number; items?: unknown[] };
detail = `total: ${data.total}\nitems: ${data.items?.length ?? 0}\n\n${JSON.stringify(data.items ?? data, null, 2)}`;
} catch {
// non-JSON; show raw
}
}
return { status: String(response.status), detail };
} catch (err) {
return { status: "error", detail: String(err) };
}
}
function html(status: string | null, detail?: string, page?: string): Response {
const form = `<form method="post" action="/"><label>page <input type="number" name="page" min="1" value="${page ?? 1}"></label> <button type="submit">dump</button></form>`;
const result =
status === null
? ""
: `<p>status: <b>${status}</b></p><pre readonly style="white-space:pre-wrap;border:1px solid #ccc;padding:1rem;max-height:60vh;overflow:auto">${escapeHtml(detail ?? "")}</pre>`;
return new Response(
`<!doctype html>
<html><head><meta charset="utf-8"><title>diigo dump</title></head>
<body>
<h1>diigo dump</h1>
${form}
${result}
</body></html>`,
{ headers: { "content-type": "text/html;charset=utf-8" } },
);
}
function escapeHtml(s: string): string {
return s
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;");
}
+7
View File
@@ -0,0 +1,7 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "diigo-dump",
"main": "src/index.ts",
"compatibility_date": "2026-09-29",
"observability": { "enabled": true }
}