@yeying-community/web3-bs 1.0.7 → 1.0.8
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/README.md +25 -0
- package/dist/storage/webdav.d.ts +39 -0
- package/dist/web3-bs.esm.js +69 -0
- package/dist/web3-bs.esm.js.map +1 -1
- package/dist/web3-bs.umd.js +69 -0
- package/dist/web3-bs.umd.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -59,6 +59,7 @@ npm install @yeying-community/web3-bs
|
|
|
59
59
|
|
|
60
60
|
- `createWebDavClient`
|
|
61
61
|
- `initWebDavStorage`(自动生成/复用 WebDAV Invocation UCAN,可自动创建应用目录)
|
|
62
|
+
- `createShareLink` / `listShareLinks` / `revokeShareLink`(公开分享链接管理)
|
|
62
63
|
- `initDappSession`(SIWE 登录 + WebDAV UCAN 初始化)
|
|
63
64
|
- `deriveAppIdFromLocation` / `deriveAppIdFromHost`
|
|
64
65
|
|
|
@@ -97,6 +98,30 @@ const webdav = await initWebDavStorage({
|
|
|
97
98
|
await webdav.client.upload(`${webdav.appDir}/hello.txt`, 'Hello WebDAV');
|
|
98
99
|
```
|
|
99
100
|
|
|
101
|
+
### WebDAV 分享链接(warehouse)
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { createWebDavClient } from '@yeying-community/web3-bs';
|
|
105
|
+
|
|
106
|
+
const client = createWebDavClient({
|
|
107
|
+
baseUrl: 'http://localhost:6065',
|
|
108
|
+
token: '<JWT_OR_UCAN>',
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// expiresValue = 0 表示长期(不过期)分享链接
|
|
112
|
+
const share = await client.createShareLink({
|
|
113
|
+
path: '/apps/demo/hello.txt',
|
|
114
|
+
expiresValue: 0,
|
|
115
|
+
expiresUnit: 'day',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log(share.url);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
分享链接权限边界(warehouse 当前实现):
|
|
122
|
+
- 使用 UCAN 且 capability 带 `app` scope(如 `app:all:<appId>`)时,`createShareLink` / `listShareLinks` 仅允许授权目录内的文件路径。
|
|
123
|
+
- 使用 JWT(或未携带 UCAN `app` scope)时,不会自动套用上述目录过滤,最终范围由后端鉴权策略决定。
|
|
124
|
+
|
|
100
125
|
## Demo
|
|
101
126
|
|
|
102
127
|
- 前端 Demo(双 Tab:单后端 SIWE / 多后端 UCAN+WebDAV):
|
package/dist/storage/webdav.d.ts
CHANGED
|
@@ -23,6 +23,40 @@ export type WebDavRequestOptions = {
|
|
|
23
23
|
contentType?: string;
|
|
24
24
|
signal?: AbortSignal;
|
|
25
25
|
};
|
|
26
|
+
export type WebDavShareExpiresUnit = 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
|
|
27
|
+
export type WebDavShareItem = {
|
|
28
|
+
token: string;
|
|
29
|
+
name: string;
|
|
30
|
+
path: string;
|
|
31
|
+
url: string;
|
|
32
|
+
viewCount: number;
|
|
33
|
+
downloadCount: number;
|
|
34
|
+
expiresAt?: string;
|
|
35
|
+
createdAt?: string;
|
|
36
|
+
};
|
|
37
|
+
export type CreateWebDavShareLinkOptions = {
|
|
38
|
+
path: string;
|
|
39
|
+
expiresIn?: number;
|
|
40
|
+
expiresValue?: number;
|
|
41
|
+
expiresUnit?: WebDavShareExpiresUnit;
|
|
42
|
+
auth?: WebDavAuth;
|
|
43
|
+
token?: string;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
};
|
|
46
|
+
export type WebDavShareListOptions = {
|
|
47
|
+
auth?: WebDavAuth;
|
|
48
|
+
token?: string;
|
|
49
|
+
signal?: AbortSignal;
|
|
50
|
+
};
|
|
51
|
+
export type WebDavShareRevokeOptions = {
|
|
52
|
+
auth?: WebDavAuth;
|
|
53
|
+
token?: string;
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
};
|
|
56
|
+
export type WebDavShareRevokeResult = {
|
|
57
|
+
message?: string;
|
|
58
|
+
revoked?: boolean;
|
|
59
|
+
};
|
|
26
60
|
export declare class WebDavClient {
|
|
27
61
|
private baseUrl;
|
|
28
62
|
private prefix;
|
|
@@ -51,5 +85,10 @@ export declare class WebDavClient {
|
|
|
51
85
|
recoverRecycle(hash: string): Promise<unknown>;
|
|
52
86
|
deleteRecycle(hash: string): Promise<unknown>;
|
|
53
87
|
clearRecycle(): Promise<unknown>;
|
|
88
|
+
private requestApiJson;
|
|
89
|
+
getShareAccessUrl(token: string, fileName?: string): string;
|
|
90
|
+
createShareLink(options: CreateWebDavShareLinkOptions): Promise<WebDavShareItem>;
|
|
91
|
+
listShareLinks(options?: WebDavShareListOptions): Promise<WebDavShareItem[]>;
|
|
92
|
+
revokeShareLink(token: string, options?: WebDavShareRevokeOptions): Promise<WebDavShareRevokeResult>;
|
|
54
93
|
}
|
|
55
94
|
export declare function createWebDavClient(options: WebDavClientOptions): WebDavClient;
|
package/dist/web3-bs.esm.js
CHANGED
|
@@ -1435,6 +1435,9 @@ function joinUrl(baseUrl, path) {
|
|
|
1435
1435
|
const suffix = path.startsWith('/') ? path : `/${path}`;
|
|
1436
1436
|
return `${base}${suffix}`;
|
|
1437
1437
|
}
|
|
1438
|
+
function isRecord(value) {
|
|
1439
|
+
return typeof value === 'object' && value !== null;
|
|
1440
|
+
}
|
|
1438
1441
|
function resolveAuthHeader(auth, token) {
|
|
1439
1442
|
if (auth?.type === 'bearer') {
|
|
1440
1443
|
return `Bearer ${auth.token}`;
|
|
@@ -1623,6 +1626,72 @@ class WebDavClient {
|
|
|
1623
1626
|
}
|
|
1624
1627
|
return await res.json();
|
|
1625
1628
|
}
|
|
1629
|
+
async requestApiJson(method, apiPath, body, options) {
|
|
1630
|
+
const headers = this.buildHeaders({
|
|
1631
|
+
auth: options?.auth,
|
|
1632
|
+
token: options?.token,
|
|
1633
|
+
contentType: body === undefined ? undefined : 'application/json',
|
|
1634
|
+
});
|
|
1635
|
+
const response = await this.fetcher(joinUrl(this.baseUrl, apiPath), {
|
|
1636
|
+
method,
|
|
1637
|
+
headers,
|
|
1638
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
1639
|
+
credentials: this.credentials,
|
|
1640
|
+
signal: options?.signal,
|
|
1641
|
+
});
|
|
1642
|
+
if (!response.ok) {
|
|
1643
|
+
throw new Error(`WebDAV ${method} ${apiPath} failed: ${response.status} ${response.statusText}`);
|
|
1644
|
+
}
|
|
1645
|
+
return await response.json();
|
|
1646
|
+
}
|
|
1647
|
+
getShareAccessUrl(token, fileName) {
|
|
1648
|
+
const normalizedToken = encodeURIComponent(String(token || '').trim());
|
|
1649
|
+
if (!normalizedToken) {
|
|
1650
|
+
throw new Error('Share token is required');
|
|
1651
|
+
}
|
|
1652
|
+
const encodedFileName = String(fileName || '').trim()
|
|
1653
|
+
? `/${encodeURIComponent(String(fileName || '').trim())}`
|
|
1654
|
+
: '';
|
|
1655
|
+
return joinUrl(this.baseUrl, `/api/v1/public/share/${normalizedToken}${encodedFileName}`);
|
|
1656
|
+
}
|
|
1657
|
+
async createShareLink(options) {
|
|
1658
|
+
const normalizedPath = String(options.path || '').trim();
|
|
1659
|
+
if (!normalizedPath) {
|
|
1660
|
+
throw new Error('Share path is required');
|
|
1661
|
+
}
|
|
1662
|
+
const payload = await this.requestApiJson('POST', '/api/v1/public/share/create', {
|
|
1663
|
+
path: normalizedPath,
|
|
1664
|
+
expiresIn: options.expiresIn,
|
|
1665
|
+
expiresValue: options.expiresValue,
|
|
1666
|
+
expiresUnit: options.expiresUnit,
|
|
1667
|
+
}, options);
|
|
1668
|
+
if (!isRecord(payload)) {
|
|
1669
|
+
throw new Error('WebDAV share create response is invalid');
|
|
1670
|
+
}
|
|
1671
|
+
return payload;
|
|
1672
|
+
}
|
|
1673
|
+
async listShareLinks(options = {}) {
|
|
1674
|
+
const payload = await this.requestApiJson('GET', '/api/v1/public/share/list', undefined, options);
|
|
1675
|
+
if (!isRecord(payload)) {
|
|
1676
|
+
return [];
|
|
1677
|
+
}
|
|
1678
|
+
const items = payload.items;
|
|
1679
|
+
if (!Array.isArray(items)) {
|
|
1680
|
+
return [];
|
|
1681
|
+
}
|
|
1682
|
+
return items.filter(isRecord);
|
|
1683
|
+
}
|
|
1684
|
+
async revokeShareLink(token, options = {}) {
|
|
1685
|
+
const normalizedToken = String(token || '').trim();
|
|
1686
|
+
if (!normalizedToken) {
|
|
1687
|
+
throw new Error('Share token is required');
|
|
1688
|
+
}
|
|
1689
|
+
const payload = await this.requestApiJson('POST', '/api/v1/public/share/revoke', { token: normalizedToken }, options);
|
|
1690
|
+
if (!isRecord(payload)) {
|
|
1691
|
+
return {};
|
|
1692
|
+
}
|
|
1693
|
+
return payload;
|
|
1694
|
+
}
|
|
1626
1695
|
}
|
|
1627
1696
|
function createWebDavClient(options) {
|
|
1628
1697
|
return new WebDavClient(options);
|