From 85645941a60d75bda02baccd807554e8af18d02d Mon Sep 17 00:00:00 2001 From: richblack Date: Fri, 31 Jul 2026 14:07:40 +0800 Subject: [PATCH] =?UTF-8?q?t159=20=E9=87=8D=E6=89=93=E5=8C=85=EF=BC=88Arcr?= =?UTF-8?q?un@062757f=EF=BC=89=EF=BC=9A=E8=A3=9C=20/portal/daemon/librarie?= =?UTF-8?q?s=20=E7=99=BB=E8=A8=98=E7=AB=AF=E9=BB=9E=EF=BC=88portal=20?= =?UTF-8?q?=E5=BA=AB=E7=9B=AE=E9=8C=84=E7=A9=BA=E7=9A=84=E6=A0=B9=E5=9B=A0?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- manifest.json | 4 +-- tier2/cypher/index.js | 68 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index f20f080..d25bab2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "schema": "arcrun-rag-bundles/v1", "built": "2026-07-31", - "source": "Arcrun@7e631a8", + "source": "Arcrun@062757f", "core": [ { "name": "arcrun-array-ops", @@ -503,7 +503,7 @@ "tier": 2, "main_file": "tier2/cypher/index.js", "main_module": "index.js", - "sha256": "68f723dfe927c8a3dc016189c9f87188f806a443ae96d0632badb2b8541f970d", + "sha256": "73c50e8bc75f13f6f0adcf39d08892d78ceb0ccc34067d3c6533a09c3138b1e0", "compat_date": "2025-02-19", "compat_flags": [ "nodejs_compat", diff --git a/tier2/cypher/index.js b/tier2/cypher/index.js index 5ac6a29..dad4bc1 100644 --- a/tier2/cypher/index.js +++ b/tier2/cypher/index.js @@ -11628,6 +11628,22 @@ webhooksNamedRouter.post("/webhooks/named/:ns/:name/query", async (c) => { webhooksNamedRouter.get("/q/:ns/:name", async (c) => { return queryNamed(c, c.req.param("ns"), c.req.param("name"), queryStringContext(c)); }); +webhooksNamedRouter.get("/webhooks/named/:name/definition", async (c) => { + const apiKey = c.req.header("X-Arcrun-API-Key"); + if (!apiKey) return c.json({ error: "\u7F3A\u5C11 X-Arcrun-API-Key header" }, 401); + const name = c.req.param("name"); + const raw2 = await c.env.WEBHOOKS.get(kvKey(apiKey, name), "text"); + if (!raw2) return c.json({ error: `\u627E\u4E0D\u5230 workflow "${name}"` }, 404); + const rec = JSON.parse(raw2); + return c.json({ + name: rec.name, + description: rec.description ?? "", + graph: rec.graph, + config: rec.config ?? {}, + created_at: rec.created_at ?? "", + ...rec.cron_expr ? { cron_expr: rec.cron_expr } : {} + }); +}); webhooksNamedRouter.get("/webhooks/named", async (c) => { const apiKey = c.req.header("X-Arcrun-API-Key"); if (!apiKey) { @@ -14024,6 +14040,58 @@ portalRouter.post( return c.json({ success: true, library: created.record ? toPublicLibrary(created.record) : { name } }); }) ); +portalRouter.post( + "/portal/daemon/libraries", + (c) => run(c, async () => { + const body = await c.req.json().catch(() => null); + const email = String(body?.email ?? "").trim().toLowerCase(); + const password = String(body?.password ?? ""); + if (!email || !password) return c.json({ error: "email \u8207 password \u5FC5\u586B" }, 400); + const items = Array.isArray(body?.libraries) ? body.libraries : []; + if (items.length === 0) return c.json({ success: true, registered: [], skipped: [] }); + if (await isLocked(c.env, email)) return c.json({ error: "\u767B\u5165\u5931\u6557\u6B21\u6578\u904E\u591A\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66" }, 429); + const recordId = await findUserRecordId(c.env, email); + const rec = recordId ? await getRecordById(c.env, recordId) : null; + if (!rec || (rec.values.status ?? "") !== "active" || !await verifyPassword(password, rec.values.password_hash ?? "")) { + await recordLoginFail(c.env, email); + return c.json({ error: "email \u6216\u5BC6\u78BC\u932F\u8AA4" }, 401); + } + await clearLoginFail(c.env, email); + const seeded = await ensurePortalTemplates(c.env); + if (seeded.errors.length > 0) { + return c.json({ error: `portal templates seed \u5931\u6557\uFF1A${seeded.errors.join("; ")}` }, 502); + } + const existing = await listRecordsByTemplate(c.env, LIBRARY_TEMPLATE); + const have = new Set(existing.map((l) => l.values.name ?? "")); + const ns = portalNamespace(c.env); + const registered = []; + const skipped = []; + for (const it of items) { + const name = String(it?.name ?? "").trim(); + const displayName = String(it?.display_name ?? "").trim() || name; + if (!isValidLibraryName(name) || name === "*") { + skipped.push(name || "(\u7A7A)"); + continue; + } + if (have.has(name)) { + skipped.push(name); + continue; + } + const res = await kbdbFetch(c.env, "/records", { + method: "POST", + body: JSON.stringify({ + template: LIBRARY_TEMPLATE, + owner_id: ns, + values: { name, display_name: displayName, description: "", status: "active" } + }) + }); + if (!res.ok) throw new KbdbError(`POST /records\uFF08portal_library\uFF0Cdaemon \u767B\u8A18\uFF09\u2192 ${res.status}`); + have.add(name); + registered.push(name); + } + return c.json({ success: true, registered, skipped }); + }) +); portalRouter.patch( "/portal/admin/libraries/:id", (c) => run(c, async () => {