@ysgroup/core 0.1.2 → 0.1.3
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.
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/rest.ts +82 -2
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/rest.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
/* @ysgroup/core ——
|
|
1
|
+
/* @ysgroup/core —— 管理接口客户端(支持 HTTP 或统一 WS RPC 传输)。 */
|
|
2
|
+
|
|
3
|
+
export type YsAdminRpcCall = (data: {
|
|
4
|
+
method: string;
|
|
5
|
+
path: string;
|
|
6
|
+
access_token: string;
|
|
7
|
+
body?: unknown;
|
|
8
|
+
upload?: { name: string; content_type: string; base64: string };
|
|
9
|
+
user_agent: string;
|
|
10
|
+
}) => Promise<unknown>;
|
|
2
11
|
|
|
3
12
|
export class YsRestError extends Error {
|
|
4
13
|
constructor(
|
|
@@ -33,9 +42,23 @@ export class YsRest {
|
|
|
33
42
|
constructor(
|
|
34
43
|
private baseUrl: string,
|
|
35
44
|
private getToken: () => string,
|
|
45
|
+
private rpcCall?: YsAdminRpcCall,
|
|
36
46
|
) {}
|
|
37
47
|
|
|
38
48
|
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
49
|
+
if (this.rpcCall) {
|
|
50
|
+
try {
|
|
51
|
+
return await this.rpcCall({
|
|
52
|
+
method,
|
|
53
|
+
path,
|
|
54
|
+
access_token: this.getToken(),
|
|
55
|
+
body,
|
|
56
|
+
user_agent: navigator.userAgent,
|
|
57
|
+
}) as T;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw rpcError(error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
39
62
|
const resp = await fetch(`${this.baseUrl}${path}`, {
|
|
40
63
|
method,
|
|
41
64
|
headers: {
|
|
@@ -59,6 +82,23 @@ export class YsRest {
|
|
|
59
82
|
}
|
|
60
83
|
|
|
61
84
|
private async upload<T>(path: string, file: File): Promise<T> {
|
|
85
|
+
if (this.rpcCall) {
|
|
86
|
+
try {
|
|
87
|
+
return await this.rpcCall({
|
|
88
|
+
method: "POST",
|
|
89
|
+
path,
|
|
90
|
+
access_token: this.getToken(),
|
|
91
|
+
upload: {
|
|
92
|
+
name: file.name,
|
|
93
|
+
content_type: file.type || "application/octet-stream",
|
|
94
|
+
base64: await fileToBase64(file),
|
|
95
|
+
},
|
|
96
|
+
user_agent: navigator.userAgent,
|
|
97
|
+
}) as T;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw rpcError(error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
62
102
|
const form = new FormData();
|
|
63
103
|
form.append("upload", file);
|
|
64
104
|
const resp = await fetch(`${this.baseUrl}${path}`, {
|
|
@@ -73,6 +113,10 @@ export class YsRest {
|
|
|
73
113
|
return (await resp.json()) as T;
|
|
74
114
|
}
|
|
75
115
|
|
|
116
|
+
raw<T>(method: string, path: string, body?: unknown) {
|
|
117
|
+
return this.request<T>(method, path, body);
|
|
118
|
+
}
|
|
119
|
+
|
|
76
120
|
list<T = Record<string, unknown>>(table: string) {
|
|
77
121
|
return this.request<T[]>("GET", `/api/tables/${table}`);
|
|
78
122
|
}
|
|
@@ -199,7 +243,14 @@ export class YsRest {
|
|
|
199
243
|
listOwnerFiles(ownerId: string) {
|
|
200
244
|
return this.request<Record<string, unknown>[]>("GET", `/api/owners/${ownerId}/files`);
|
|
201
245
|
}
|
|
202
|
-
downloadFile(fileId: string) {
|
|
246
|
+
async downloadFile(fileId: string) {
|
|
247
|
+
if (this.rpcCall) {
|
|
248
|
+
const result = await this.request<{
|
|
249
|
+
base64: string;
|
|
250
|
+
content_type: string;
|
|
251
|
+
}>("GET", `/api/files/${fileId}/download`);
|
|
252
|
+
return base64ToBlob(result.base64, result.content_type);
|
|
253
|
+
}
|
|
203
254
|
return fetch(`${this.baseUrl}/api/files/${fileId}/download`, {
|
|
204
255
|
headers: { Authorization: `Bearer ${this.getToken()}` },
|
|
205
256
|
}).then(async (resp) => {
|
|
@@ -212,6 +263,35 @@ export class YsRest {
|
|
|
212
263
|
}
|
|
213
264
|
}
|
|
214
265
|
|
|
266
|
+
function rpcError(error: unknown): YsRestError {
|
|
267
|
+
const message = String(error).replace(/^Error:\s*/, "");
|
|
268
|
+
const match = message.match(/^\[(\d+)\]\s*(.*)$/s);
|
|
269
|
+
if (!match) return new YsRestError(500, message);
|
|
270
|
+
let detail: unknown = match[2];
|
|
271
|
+
try {
|
|
272
|
+
detail = JSON.parse(match[2]);
|
|
273
|
+
} catch {
|
|
274
|
+
// 普通文本错误保持原文。
|
|
275
|
+
}
|
|
276
|
+
return new YsRestError(Number(match[1]), detail);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function fileToBase64(file: File): Promise<string> {
|
|
280
|
+
return new Promise((resolve, reject) => {
|
|
281
|
+
const reader = new FileReader();
|
|
282
|
+
reader.onerror = () => reject(reader.error ?? new Error("读取上传文件失败"));
|
|
283
|
+
reader.onload = () => resolve(String(reader.result).split(",", 2)[1] ?? "");
|
|
284
|
+
reader.readAsDataURL(file);
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function base64ToBlob(value: string, contentType: string): Blob {
|
|
289
|
+
const binary = atob(value);
|
|
290
|
+
const bytes = new Uint8Array(binary.length);
|
|
291
|
+
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
|
|
292
|
+
return new Blob([bytes], { type: contentType });
|
|
293
|
+
}
|
|
294
|
+
|
|
215
295
|
export interface TopologyNode {
|
|
216
296
|
name: string;
|
|
217
297
|
title: string;
|