@ysgroup/core 0.1.2 → 0.1.4
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 -8
- package/src/rest.ts +82 -2
- package/src/store.ts +24 -3
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,11 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
export { YsWsClient, type Envelope } from "./ws";
|
|
4
4
|
export { useYsStore } from "./store";
|
|
5
|
-
export {
|
|
6
|
-
YsRest,
|
|
7
|
-
YsRestError,
|
|
8
|
-
type DeleteBlocker,
|
|
9
|
-
type DeleteImpact,
|
|
10
|
-
type RevealedCredential,
|
|
11
|
-
type TopologyNode,
|
|
12
|
-
} from "./rest";
|
|
5
|
+
export { YsRest, YsRestError, type YsAdminRpcCall, type DeleteBlocker, type DeleteImpact, type RevealedCredential, type TopologyNode } from "./rest";
|
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;
|
package/src/store.ts
CHANGED
|
@@ -139,7 +139,9 @@ export const useYsStore = defineStore("ys", () => {
|
|
|
139
139
|
if (!ws || !accessToken.value) throw new Error("未登录");
|
|
140
140
|
if (
|
|
141
141
|
String(user.value?.role ?? "") === "Root" ||
|
|
142
|
-
String(user.value?.username ?? "")
|
|
142
|
+
String(user.value?.username ?? "")
|
|
143
|
+
.trim()
|
|
144
|
+
.toLowerCase() === "root"
|
|
143
145
|
) {
|
|
144
146
|
throw new Error("Root 不允许修改登录密码");
|
|
145
147
|
}
|
|
@@ -196,6 +198,11 @@ export const useYsStore = defineStore("ys", () => {
|
|
|
196
198
|
clearSession();
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
function forceLogout(message = "") {
|
|
202
|
+
clearSession();
|
|
203
|
+
sessionMessage.value = message;
|
|
204
|
+
}
|
|
205
|
+
|
|
199
206
|
function clearSession() {
|
|
200
207
|
user.value = null;
|
|
201
208
|
accessToken.value = "";
|
|
@@ -207,8 +214,22 @@ export const useYsStore = defineStore("ys", () => {
|
|
|
207
214
|
}
|
|
208
215
|
|
|
209
216
|
return {
|
|
210
|
-
user,
|
|
211
|
-
|
|
217
|
+
user,
|
|
218
|
+
accessToken,
|
|
219
|
+
refreshToken,
|
|
220
|
+
connected,
|
|
221
|
+
isLoggedIn,
|
|
222
|
+
sessionMessage,
|
|
223
|
+
tables,
|
|
224
|
+
tableList,
|
|
225
|
+
connect,
|
|
226
|
+
login,
|
|
227
|
+
loginByCode,
|
|
228
|
+
waitUntilConnected,
|
|
229
|
+
changeMyPassword,
|
|
230
|
+
renew,
|
|
231
|
+
logout,
|
|
232
|
+
forceLogout,
|
|
212
233
|
client: () => ws,
|
|
213
234
|
};
|
|
214
235
|
});
|