@actiondock/core 2.0.9 → 2.0.11-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,133 @@
1
+ import { readJsonBody } from "../body";
2
+ import { type RouteContext, jsonResponse, resolveStorageForPackage } from "./common";
3
+
4
+ /**
5
+ * 处理状态键名列表、读取、写入、删除及清空接口。
6
+ */
7
+ export async function handleStateRoutes(ctx: RouteContext): Promise<Response | null> {
8
+ const { req, url, pathname, corsHeaders, projectRoot, customHome, runtimeRegistry, options } = ctx;
9
+
10
+ // 1. State List: GET /api/v1/state
11
+ if (pathname === "/api/v1/state" && req.method === "GET") {
12
+ try {
13
+ const pkgParam = url.searchParams.get("package") || undefined;
14
+ const nsParam = url.searchParams.get("namespace") ?? undefined;
15
+ const prefix = url.searchParams.get("prefix") || "";
16
+
17
+ const { packageId, storage } = resolveStorageForPackage(
18
+ pkgParam,
19
+ runtimeRegistry,
20
+ projectRoot,
21
+ customHome
22
+ );
23
+ const keys = await storage.listStateKeys(nsParam !== undefined ? nsParam : null, prefix);
24
+ return jsonResponse({ ok: true, packageId, keys }, 200, corsHeaders);
25
+ } catch (err: any) {
26
+ return jsonResponse(
27
+ { ok: false, error: { code: "STATE_LIST_ERROR", message: err.message } },
28
+ 500,
29
+ corsHeaders
30
+ );
31
+ }
32
+ }
33
+
34
+ // 2. State Clear: POST /api/v1/state/clear
35
+ if (pathname === "/api/v1/state/clear" && req.method === "POST") {
36
+ try {
37
+ const body = await readJsonBody(req, { maxBytes: options.maxBodyBytes }).catch(() => ({}));
38
+ const pkgParam = url.searchParams.get("package") || body.package || undefined;
39
+ const { packageId, storage } = resolveStorageForPackage(
40
+ pkgParam,
41
+ runtimeRegistry,
42
+ projectRoot,
43
+ customHome
44
+ );
45
+ const clearedCount = await storage.clearState({
46
+ namespace: body.namespace ?? (url.searchParams.get("namespace") || undefined),
47
+ all: Boolean(body.all ?? url.searchParams.get("all") === "true"),
48
+ prefix: body.prefix ?? (url.searchParams.get("prefix") || undefined),
49
+ });
50
+ return jsonResponse({ ok: true, packageId, clearedCount }, 200, corsHeaders);
51
+ } catch (err: any) {
52
+ return jsonResponse(
53
+ { ok: false, error: { code: "STATE_CLEAR_ERROR", message: err.message } },
54
+ 500,
55
+ corsHeaders
56
+ );
57
+ }
58
+ }
59
+
60
+ // 3. State Key CRUD: GET / PUT / POST / DELETE /api/v1/state/:key
61
+ const stateKeyMatch = pathname.match(/^\/api\/v1\/state\/([^/]+)$/);
62
+ if (stateKeyMatch) {
63
+ const key = decodeURIComponent(stateKeyMatch[1]);
64
+ const pkgParam = url.searchParams.get("package") || undefined;
65
+ const nsParam = url.searchParams.get("namespace") || undefined;
66
+
67
+ const { packageId, storage } = resolveStorageForPackage(
68
+ pkgParam,
69
+ runtimeRegistry,
70
+ projectRoot,
71
+ customHome
72
+ );
73
+
74
+ if (req.method === "GET") {
75
+ const entry = await storage.findState(key, nsParam);
76
+ if (!entry || entry.value === undefined) {
77
+ return jsonResponse(
78
+ { ok: false, error: { code: "STATE_KEY_NOT_FOUND", message: `State key '${key}' not found` } },
79
+ 404,
80
+ corsHeaders
81
+ );
82
+ }
83
+ return jsonResponse(
84
+ {
85
+ ok: true,
86
+ packageId,
87
+ key: entry.key,
88
+ namespace: entry.namespace,
89
+ value: entry.value,
90
+ expiresAt: entry.expiresAt,
91
+ },
92
+ 200,
93
+ corsHeaders
94
+ );
95
+ }
96
+
97
+ if (req.method === "PUT" || req.method === "POST") {
98
+ const body = await readJsonBody(req, { maxBytes: options.maxBodyBytes });
99
+ const val = body.value !== undefined ? body.value : body;
100
+ const ttl = typeof body.ttl === "number" ? body.ttl : undefined;
101
+ const namespace = body.namespace || nsParam || "";
102
+
103
+ let actualKey = key;
104
+ let ns = namespace;
105
+ if (!ns && key.includes(":")) {
106
+ const idx = key.indexOf(":");
107
+ ns = key.slice(0, idx);
108
+ actualKey = key.slice(idx + 1);
109
+ }
110
+
111
+ await storage.setState(ns, actualKey, val, ttl);
112
+ return jsonResponse(
113
+ { ok: true, packageId, key: actualKey, namespace: ns, message: "updated" },
114
+ 200,
115
+ corsHeaders
116
+ );
117
+ }
118
+
119
+ if (req.method === "DELETE") {
120
+ const deleted = await storage.deleteStateSmart(key, nsParam);
121
+ if (!deleted) {
122
+ return jsonResponse(
123
+ { ok: false, error: { code: "STATE_KEY_NOT_FOUND", message: `State key '${key}' not found` } },
124
+ 404,
125
+ corsHeaders
126
+ );
127
+ }
128
+ return jsonResponse({ ok: true, packageId, key, deleted: true }, 200, corsHeaders);
129
+ }
130
+ }
131
+
132
+ return null;
133
+ }