@alphayard/unibox-sdk 0.1.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.
- package/README.md +73 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.js +306 -0
- package/package.json +41 -0
- package/src/index.ts +447 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# `@alphayard/unibox-sdk`
|
|
2
|
+
|
|
3
|
+
UniBox client SDK for file storage in Boundary and other UniApps clients.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
- Upload files
|
|
8
|
+
- List files
|
|
9
|
+
- List folders
|
|
10
|
+
- Create folders
|
|
11
|
+
- Load circle folders
|
|
12
|
+
- Read folder breadcrumb paths
|
|
13
|
+
- Fetch file details
|
|
14
|
+
- Update file metadata
|
|
15
|
+
- Delete files
|
|
16
|
+
- Search files
|
|
17
|
+
- Read quota and analytics
|
|
18
|
+
- Build public or proxied file URLs
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @alphayard/unibox-sdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
For local development in this monorepo, `boundary-app` uses:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
"@alphayard/unibox-sdk": "file:../packages/unibox-sdk"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { createUniBoxClient } from '@alphayard/unibox-sdk';
|
|
36
|
+
|
|
37
|
+
const unibox = createUniBoxClient({
|
|
38
|
+
baseURL: 'http://localhost:4000/api/v1',
|
|
39
|
+
appId: 'your-app-id',
|
|
40
|
+
getAccessToken: async () => 'jwt-token',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const result = await unibox.uploadFile({
|
|
44
|
+
file,
|
|
45
|
+
description: 'Profile image',
|
|
46
|
+
isPublic: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const folders = await unibox.getCircleFolders('circle-id');
|
|
50
|
+
|
|
51
|
+
const createdFolder = await unibox.createCircleFolder('circle-id', {
|
|
52
|
+
name: 'Invoices',
|
|
53
|
+
parentId: 'optional-parent-folder-id',
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Publish
|
|
58
|
+
|
|
59
|
+
For local Boundary app development, Metro resolves the package from `src/index.ts`.
|
|
60
|
+
|
|
61
|
+
Before publishing to npm, build the package so `dist/` is up to date:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd packages/unibox-sdk
|
|
65
|
+
npm run build
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Publishing still requires an authenticated npm session:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
cd packages/unibox-sdk
|
|
72
|
+
npm publish
|
|
73
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export type UniBoxFileType = 'image' | 'video' | 'audio' | 'document' | 'other';
|
|
2
|
+
export interface UniBoxFile {
|
|
3
|
+
id: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
applicationId?: string;
|
|
6
|
+
circleId: string;
|
|
7
|
+
fileName: string;
|
|
8
|
+
originalName: string;
|
|
9
|
+
fileSize: number;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
fileType: UniBoxFileType;
|
|
12
|
+
filePath: string;
|
|
13
|
+
url: string;
|
|
14
|
+
thumbnailPath?: string;
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
isPublic: boolean;
|
|
17
|
+
tags: string[];
|
|
18
|
+
description?: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
}
|
|
22
|
+
export interface UniBoxFolder {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
parentId?: string;
|
|
27
|
+
ownerId: string;
|
|
28
|
+
circleId?: string;
|
|
29
|
+
color?: string;
|
|
30
|
+
icon?: string;
|
|
31
|
+
isFavorite: boolean;
|
|
32
|
+
isPinned: boolean;
|
|
33
|
+
sortOrder: number;
|
|
34
|
+
itemCount: number;
|
|
35
|
+
totalSize: number;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
updatedAt: string;
|
|
38
|
+
}
|
|
39
|
+
export interface UniBoxUploadRequest {
|
|
40
|
+
file: unknown;
|
|
41
|
+
description?: string;
|
|
42
|
+
folderId?: string;
|
|
43
|
+
isPublic?: boolean;
|
|
44
|
+
tags?: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface UniBoxClientOptions {
|
|
47
|
+
baseURL: string;
|
|
48
|
+
appId?: string;
|
|
49
|
+
appSlug?: string;
|
|
50
|
+
getAccessToken?: () => Promise<string | null> | string | null;
|
|
51
|
+
}
|
|
52
|
+
export interface UniBoxListResult {
|
|
53
|
+
success: boolean;
|
|
54
|
+
files: UniBoxFile[];
|
|
55
|
+
total: number;
|
|
56
|
+
pagination?: unknown;
|
|
57
|
+
}
|
|
58
|
+
export interface UniBoxFolderListResult {
|
|
59
|
+
success: boolean;
|
|
60
|
+
folders: UniBoxFolder[];
|
|
61
|
+
total: number;
|
|
62
|
+
pagination?: unknown;
|
|
63
|
+
}
|
|
64
|
+
export interface UniBoxClient {
|
|
65
|
+
uploadFile(data: UniBoxUploadRequest): Promise<{
|
|
66
|
+
success: boolean;
|
|
67
|
+
file: UniBoxFile;
|
|
68
|
+
}>;
|
|
69
|
+
getFiles(params?: Record<string, string | number | boolean>): Promise<UniBoxListResult>;
|
|
70
|
+
getCircleFiles(circleId: string, params?: Record<string, string | number | boolean>): Promise<UniBoxListResult>;
|
|
71
|
+
getFile(fileId: string): Promise<{
|
|
72
|
+
success: boolean;
|
|
73
|
+
file: UniBoxFile;
|
|
74
|
+
}>;
|
|
75
|
+
updateFile(fileId: string, data: Record<string, unknown>): Promise<{
|
|
76
|
+
success: boolean;
|
|
77
|
+
file: UniBoxFile;
|
|
78
|
+
}>;
|
|
79
|
+
deleteFile(fileId: string): Promise<{
|
|
80
|
+
success: boolean;
|
|
81
|
+
message: string;
|
|
82
|
+
}>;
|
|
83
|
+
getFolders(params?: Record<string, string | number | boolean>): Promise<UniBoxFolderListResult>;
|
|
84
|
+
getCircleFolders(circleId: string, params?: Record<string, string | number | boolean>): Promise<UniBoxFolderListResult>;
|
|
85
|
+
createFolder(data: Record<string, unknown>): Promise<{
|
|
86
|
+
success: boolean;
|
|
87
|
+
folder: UniBoxFolder;
|
|
88
|
+
}>;
|
|
89
|
+
createCircleFolder(circleId: string, data: Record<string, unknown>): Promise<{
|
|
90
|
+
success: boolean;
|
|
91
|
+
folder: UniBoxFolder;
|
|
92
|
+
}>;
|
|
93
|
+
getFolderPath(folderId: string): Promise<{
|
|
94
|
+
success: boolean;
|
|
95
|
+
path: UniBoxFolder[];
|
|
96
|
+
}>;
|
|
97
|
+
getQuota(): Promise<unknown>;
|
|
98
|
+
getCircleQuota(circleId: string): Promise<unknown>;
|
|
99
|
+
getAnalytics(): Promise<unknown>;
|
|
100
|
+
searchFiles(query: string, params?: Record<string, string | number | boolean>): Promise<UniBoxListResult>;
|
|
101
|
+
getFileUrl(path: string): string;
|
|
102
|
+
}
|
|
103
|
+
export declare function normalizeFileType(fileType?: string): UniBoxFileType;
|
|
104
|
+
export declare function mapUniBoxFile(baseURL: string, file: any): UniBoxFile;
|
|
105
|
+
export declare function mapUniBoxFolder(folder: any): UniBoxFolder;
|
|
106
|
+
export declare function createUniBoxClient(options: UniBoxClientOptions): UniBoxClient;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeFileType = normalizeFileType;
|
|
4
|
+
exports.mapUniBoxFile = mapUniBoxFile;
|
|
5
|
+
exports.mapUniBoxFolder = mapUniBoxFolder;
|
|
6
|
+
exports.createUniBoxClient = createUniBoxClient;
|
|
7
|
+
function normalizeBaseURL(baseURL) {
|
|
8
|
+
const trimmed = String(baseURL || '').replace(/\/$/, '');
|
|
9
|
+
return trimmed.endsWith('/api/v1') ? trimmed : `${trimmed}/api/v1`;
|
|
10
|
+
}
|
|
11
|
+
function normalizeFileType(fileType) {
|
|
12
|
+
if (fileType === 'image' || fileType === 'video' || fileType === 'audio') {
|
|
13
|
+
return fileType;
|
|
14
|
+
}
|
|
15
|
+
if (fileType === 'pdf' || fileType === 'document') {
|
|
16
|
+
return 'document';
|
|
17
|
+
}
|
|
18
|
+
return 'other';
|
|
19
|
+
}
|
|
20
|
+
function normalizeFileURL(baseURL, path) {
|
|
21
|
+
if (!path) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
if (/^https?:\/\//i.test(path) || path.startsWith('data:')) {
|
|
25
|
+
return path;
|
|
26
|
+
}
|
|
27
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(path)) {
|
|
28
|
+
return `${baseURL.replace(/\/api\/v1$/, '')}/api/v1/storage/proxy/${path}`;
|
|
29
|
+
}
|
|
30
|
+
const origin = baseURL.replace(/\/api\/v1$/, '');
|
|
31
|
+
const normalizedPath = path.startsWith('/') ? path : `/api/uploads/${path}`;
|
|
32
|
+
return `${origin}${normalizedPath}`;
|
|
33
|
+
}
|
|
34
|
+
function mapUniBoxFile(baseURL, file) {
|
|
35
|
+
var _a, _b, _c;
|
|
36
|
+
return {
|
|
37
|
+
id: (file === null || file === void 0 ? void 0 : file.id) || '',
|
|
38
|
+
userId: (file === null || file === void 0 ? void 0 : file.uploadedBy) || (file === null || file === void 0 ? void 0 : file.userId) || '',
|
|
39
|
+
applicationId: file === null || file === void 0 ? void 0 : file.applicationId,
|
|
40
|
+
circleId: (file === null || file === void 0 ? void 0 : file.circleId) || '',
|
|
41
|
+
fileName: (file === null || file === void 0 ? void 0 : file.fileName) || (file === null || file === void 0 ? void 0 : file.filename) || (file === null || file === void 0 ? void 0 : file.originalName) || '',
|
|
42
|
+
originalName: (file === null || file === void 0 ? void 0 : file.originalName) || (file === null || file === void 0 ? void 0 : file.filename) || (file === null || file === void 0 ? void 0 : file.fileName) || '',
|
|
43
|
+
fileSize: Number((_b = (_a = file === null || file === void 0 ? void 0 : file.size) !== null && _a !== void 0 ? _a : file === null || file === void 0 ? void 0 : file.fileSize) !== null && _b !== void 0 ? _b : 0),
|
|
44
|
+
mimeType: (file === null || file === void 0 ? void 0 : file.mimeType) || 'application/octet-stream',
|
|
45
|
+
fileType: normalizeFileType(file === null || file === void 0 ? void 0 : file.fileType),
|
|
46
|
+
filePath: (file === null || file === void 0 ? void 0 : file.filePath) || (file === null || file === void 0 ? void 0 : file.storagePath) || '',
|
|
47
|
+
url: normalizeFileURL(baseURL, (file === null || file === void 0 ? void 0 : file.url) || (file === null || file === void 0 ? void 0 : file.filePath) || (file === null || file === void 0 ? void 0 : file.storagePath) || (file === null || file === void 0 ? void 0 : file.id) || ''),
|
|
48
|
+
thumbnailPath: (file === null || file === void 0 ? void 0 : file.thumbnailUrl) || (file === null || file === void 0 ? void 0 : file.thumbnailPath),
|
|
49
|
+
metadata: file === null || file === void 0 ? void 0 : file.metadata,
|
|
50
|
+
isPublic: (_c = file === null || file === void 0 ? void 0 : file.isPublic) !== null && _c !== void 0 ? _c : true,
|
|
51
|
+
tags: Array.isArray(file === null || file === void 0 ? void 0 : file.tags)
|
|
52
|
+
? file.tags.map((tag) => (typeof tag === 'string' ? tag : tag === null || tag === void 0 ? void 0 : tag.name)).filter(Boolean)
|
|
53
|
+
: [],
|
|
54
|
+
description: file === null || file === void 0 ? void 0 : file.description,
|
|
55
|
+
createdAt: (file === null || file === void 0 ? void 0 : file.createdAt) || new Date().toISOString(),
|
|
56
|
+
updatedAt: (file === null || file === void 0 ? void 0 : file.updatedAt) || (file === null || file === void 0 ? void 0 : file.createdAt) || new Date().toISOString(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function mapUniBoxFolder(folder) {
|
|
60
|
+
return {
|
|
61
|
+
id: (folder === null || folder === void 0 ? void 0 : folder.id) || '',
|
|
62
|
+
name: (folder === null || folder === void 0 ? void 0 : folder.name) || '',
|
|
63
|
+
description: folder === null || folder === void 0 ? void 0 : folder.description,
|
|
64
|
+
parentId: folder === null || folder === void 0 ? void 0 : folder.parentId,
|
|
65
|
+
ownerId: (folder === null || folder === void 0 ? void 0 : folder.ownerId) || (folder === null || folder === void 0 ? void 0 : folder.userId) || '',
|
|
66
|
+
circleId: folder === null || folder === void 0 ? void 0 : folder.circleId,
|
|
67
|
+
color: folder === null || folder === void 0 ? void 0 : folder.color,
|
|
68
|
+
icon: folder === null || folder === void 0 ? void 0 : folder.icon,
|
|
69
|
+
isFavorite: Boolean(folder === null || folder === void 0 ? void 0 : folder.isFavorite),
|
|
70
|
+
isPinned: Boolean(folder === null || folder === void 0 ? void 0 : folder.isPinned),
|
|
71
|
+
sortOrder: Number((folder === null || folder === void 0 ? void 0 : folder.sortOrder) || 0),
|
|
72
|
+
itemCount: Number((folder === null || folder === void 0 ? void 0 : folder.itemCount) || 0),
|
|
73
|
+
totalSize: Number((folder === null || folder === void 0 ? void 0 : folder.totalSize) || 0),
|
|
74
|
+
createdAt: (folder === null || folder === void 0 ? void 0 : folder.createdAt) || new Date().toISOString(),
|
|
75
|
+
updatedAt: (folder === null || folder === void 0 ? void 0 : folder.updatedAt) || (folder === null || folder === void 0 ? void 0 : folder.createdAt) || new Date().toISOString(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function createUniBoxClient(options) {
|
|
79
|
+
const baseURL = normalizeBaseURL(options.baseURL);
|
|
80
|
+
async function buildHeaders(extraHeaders) {
|
|
81
|
+
const accessToken = options.getAccessToken ? await options.getAccessToken() : null;
|
|
82
|
+
const headers = { ...(extraHeaders || {}) };
|
|
83
|
+
if (options.appId) {
|
|
84
|
+
headers['X-App-ID'] = options.appId;
|
|
85
|
+
}
|
|
86
|
+
else if (options.appSlug) {
|
|
87
|
+
headers['X-App-Slug'] = options.appSlug;
|
|
88
|
+
}
|
|
89
|
+
if (accessToken) {
|
|
90
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
91
|
+
}
|
|
92
|
+
return headers;
|
|
93
|
+
}
|
|
94
|
+
async function request(path, init) {
|
|
95
|
+
const response = await fetch(`${baseURL}${path}`, init);
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
let message = `Request failed: HTTP ${response.status}`;
|
|
98
|
+
try {
|
|
99
|
+
const payload = await response.json();
|
|
100
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.message) || (payload === null || payload === void 0 ? void 0 : payload.error)) {
|
|
101
|
+
message = payload.message || payload.error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
try {
|
|
106
|
+
const text = await response.text();
|
|
107
|
+
if (text) {
|
|
108
|
+
message = text;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Ignore text parsing error.
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
throw new Error(message);
|
|
116
|
+
}
|
|
117
|
+
return response.json();
|
|
118
|
+
}
|
|
119
|
+
function buildQuery(params) {
|
|
120
|
+
const searchParams = new URLSearchParams();
|
|
121
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
122
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
123
|
+
searchParams.set(key, String(value));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
const query = searchParams.toString();
|
|
127
|
+
return query ? `?${query}` : '';
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
uploadFile: async (data) => {
|
|
131
|
+
var _a;
|
|
132
|
+
const formData = new FormData();
|
|
133
|
+
formData.append('file', data.file);
|
|
134
|
+
if (data.description) {
|
|
135
|
+
formData.append('description', data.description);
|
|
136
|
+
}
|
|
137
|
+
if (data.folderId) {
|
|
138
|
+
formData.append('folderId', data.folderId);
|
|
139
|
+
}
|
|
140
|
+
if (data.isPublic !== undefined) {
|
|
141
|
+
formData.append('isPublic', String(data.isPublic));
|
|
142
|
+
}
|
|
143
|
+
if (Array.isArray(data.tags) && data.tags.length > 0) {
|
|
144
|
+
formData.append('tags', JSON.stringify(data.tags));
|
|
145
|
+
}
|
|
146
|
+
const response = await request('/files/files/upload', {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: await buildHeaders(),
|
|
149
|
+
body: formData,
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
153
|
+
file: mapUniBoxFile(baseURL, (response === null || response === void 0 ? void 0 : response.file) || ((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.file) || (response === null || response === void 0 ? void 0 : response.data)),
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
getFiles: async (params) => {
|
|
157
|
+
const response = await request(`/files/files${buildQuery(params)}`, {
|
|
158
|
+
method: 'GET',
|
|
159
|
+
headers: await buildHeaders(),
|
|
160
|
+
});
|
|
161
|
+
const files = Array.isArray(response === null || response === void 0 ? void 0 : response.files) ? response.files : [];
|
|
162
|
+
return {
|
|
163
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
164
|
+
files: files.map((file) => mapUniBoxFile(baseURL, file)),
|
|
165
|
+
total: Number((response === null || response === void 0 ? void 0 : response.total) || files.length || 0),
|
|
166
|
+
pagination: response === null || response === void 0 ? void 0 : response.pagination,
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
getCircleFiles: async (circleId, params) => {
|
|
170
|
+
const response = await request(`/files/circles/${encodeURIComponent(circleId)}/files${buildQuery(params)}`, {
|
|
171
|
+
method: 'GET',
|
|
172
|
+
headers: await buildHeaders(),
|
|
173
|
+
});
|
|
174
|
+
const files = Array.isArray(response === null || response === void 0 ? void 0 : response.files) ? response.files : [];
|
|
175
|
+
return {
|
|
176
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
177
|
+
files: files.map((file) => mapUniBoxFile(baseURL, file)),
|
|
178
|
+
total: Number((response === null || response === void 0 ? void 0 : response.total) || files.length || 0),
|
|
179
|
+
pagination: response === null || response === void 0 ? void 0 : response.pagination,
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
getFile: async (fileId) => {
|
|
183
|
+
var _a;
|
|
184
|
+
const response = await request(`/files/files/${encodeURIComponent(fileId)}`, {
|
|
185
|
+
method: 'GET',
|
|
186
|
+
headers: await buildHeaders(),
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
190
|
+
file: mapUniBoxFile(baseURL, (response === null || response === void 0 ? void 0 : response.file) || ((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.file) || (response === null || response === void 0 ? void 0 : response.data)),
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
updateFile: async (fileId, data) => {
|
|
194
|
+
var _a;
|
|
195
|
+
const response = await request(`/files/files/${encodeURIComponent(fileId)}`, {
|
|
196
|
+
method: 'PUT',
|
|
197
|
+
headers: await buildHeaders({ 'Content-Type': 'application/json' }),
|
|
198
|
+
body: JSON.stringify(data || {}),
|
|
199
|
+
});
|
|
200
|
+
return {
|
|
201
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
202
|
+
file: mapUniBoxFile(baseURL, (response === null || response === void 0 ? void 0 : response.file) || ((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.file) || (response === null || response === void 0 ? void 0 : response.data)),
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
deleteFile: async (fileId) => {
|
|
206
|
+
const response = await request(`/files/files/${encodeURIComponent(fileId)}`, {
|
|
207
|
+
method: 'DELETE',
|
|
208
|
+
headers: await buildHeaders(),
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
success: Boolean((response === null || response === void 0 ? void 0 : response.success) !== false),
|
|
212
|
+
message: (response === null || response === void 0 ? void 0 : response.message) || (response === null || response === void 0 ? void 0 : response.error) || 'File deleted',
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
getFolders: async (params) => {
|
|
216
|
+
const response = await request(`/files/folders${buildQuery(params)}`, {
|
|
217
|
+
method: 'GET',
|
|
218
|
+
headers: await buildHeaders(),
|
|
219
|
+
});
|
|
220
|
+
const folders = Array.isArray(response === null || response === void 0 ? void 0 : response.folders) ? response.folders : [];
|
|
221
|
+
return {
|
|
222
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
223
|
+
folders: folders.map((folder) => mapUniBoxFolder(folder)),
|
|
224
|
+
total: Number((response === null || response === void 0 ? void 0 : response.total) || folders.length || 0),
|
|
225
|
+
pagination: response === null || response === void 0 ? void 0 : response.pagination,
|
|
226
|
+
};
|
|
227
|
+
},
|
|
228
|
+
getCircleFolders: async (circleId, params) => {
|
|
229
|
+
const response = await request(`/files/circles/${encodeURIComponent(circleId)}/folders${buildQuery(params)}`, {
|
|
230
|
+
method: 'GET',
|
|
231
|
+
headers: await buildHeaders(),
|
|
232
|
+
});
|
|
233
|
+
const folders = Array.isArray(response === null || response === void 0 ? void 0 : response.folders) ? response.folders : [];
|
|
234
|
+
return {
|
|
235
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
236
|
+
folders: folders.map((folder) => mapUniBoxFolder(folder)),
|
|
237
|
+
total: Number((response === null || response === void 0 ? void 0 : response.total) || folders.length || 0),
|
|
238
|
+
pagination: response === null || response === void 0 ? void 0 : response.pagination,
|
|
239
|
+
};
|
|
240
|
+
},
|
|
241
|
+
createFolder: async (data) => {
|
|
242
|
+
var _a;
|
|
243
|
+
const response = await request('/files/folders', {
|
|
244
|
+
method: 'POST',
|
|
245
|
+
headers: await buildHeaders({ 'Content-Type': 'application/json' }),
|
|
246
|
+
body: JSON.stringify(data || {}),
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
250
|
+
folder: mapUniBoxFolder((response === null || response === void 0 ? void 0 : response.folder) || ((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.folder) || (response === null || response === void 0 ? void 0 : response.data)),
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
createCircleFolder: async (circleId, data) => {
|
|
254
|
+
var _a;
|
|
255
|
+
const response = await request(`/files/circles/${encodeURIComponent(circleId)}/folders`, {
|
|
256
|
+
method: 'POST',
|
|
257
|
+
headers: await buildHeaders({ 'Content-Type': 'application/json' }),
|
|
258
|
+
body: JSON.stringify(data || {}),
|
|
259
|
+
});
|
|
260
|
+
return {
|
|
261
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
262
|
+
folder: mapUniBoxFolder((response === null || response === void 0 ? void 0 : response.folder) || ((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.folder) || (response === null || response === void 0 ? void 0 : response.data)),
|
|
263
|
+
};
|
|
264
|
+
},
|
|
265
|
+
getFolderPath: async (folderId) => {
|
|
266
|
+
const response = await request(`/files/folders/${encodeURIComponent(folderId)}/path`, {
|
|
267
|
+
method: 'GET',
|
|
268
|
+
headers: await buildHeaders(),
|
|
269
|
+
});
|
|
270
|
+
const path = Array.isArray(response === null || response === void 0 ? void 0 : response.path) ? response.path : [];
|
|
271
|
+
return {
|
|
272
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
273
|
+
path: path.map((folder) => mapUniBoxFolder(folder)),
|
|
274
|
+
};
|
|
275
|
+
},
|
|
276
|
+
getQuota: async () => request('/files/quota', {
|
|
277
|
+
method: 'GET',
|
|
278
|
+
headers: await buildHeaders(),
|
|
279
|
+
}),
|
|
280
|
+
getCircleQuota: async (circleId) => request(`/files/circles/${encodeURIComponent(circleId)}/quota`, {
|
|
281
|
+
method: 'GET',
|
|
282
|
+
headers: await buildHeaders(),
|
|
283
|
+
}),
|
|
284
|
+
getAnalytics: async () => request('/files/analytics', {
|
|
285
|
+
method: 'GET',
|
|
286
|
+
headers: await buildHeaders(),
|
|
287
|
+
}),
|
|
288
|
+
searchFiles: async (query, params) => {
|
|
289
|
+
const response = await request(`/files/search${buildQuery({
|
|
290
|
+
query,
|
|
291
|
+
...(params || {}),
|
|
292
|
+
})}`, {
|
|
293
|
+
method: 'GET',
|
|
294
|
+
headers: await buildHeaders(),
|
|
295
|
+
});
|
|
296
|
+
const files = Array.isArray(response === null || response === void 0 ? void 0 : response.files) ? response.files : [];
|
|
297
|
+
return {
|
|
298
|
+
success: Boolean(response === null || response === void 0 ? void 0 : response.success),
|
|
299
|
+
files: files.map((file) => mapUniBoxFile(baseURL, file)),
|
|
300
|
+
total: Number((response === null || response === void 0 ? void 0 : response.total) || files.length || 0),
|
|
301
|
+
pagination: response === null || response === void 0 ? void 0 : response.pagination,
|
|
302
|
+
};
|
|
303
|
+
},
|
|
304
|
+
getFileUrl: (path) => normalizeFileURL(baseURL, path),
|
|
305
|
+
};
|
|
306
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alphayard/unibox-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "UniBox storage SDK for Boundary and other UniApps clients",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"react-native": "src/index.ts",
|
|
8
|
+
"source": "src/index.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./src/index.ts",
|
|
12
|
+
"react-native": "./src/index.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"type-check": "tsc --noEmit -p tsconfig.json",
|
|
19
|
+
"prepublishOnly": "npm run type-check && npm run build"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"src",
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"unibox",
|
|
32
|
+
"storage",
|
|
33
|
+
"sdk",
|
|
34
|
+
"boundary",
|
|
35
|
+
"uniapps"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"license": "UNLICENSED"
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
export type UniBoxFileType = 'image' | 'video' | 'audio' | 'document' | 'other';
|
|
2
|
+
|
|
3
|
+
export interface UniBoxFile {
|
|
4
|
+
id: string;
|
|
5
|
+
userId: string;
|
|
6
|
+
applicationId?: string;
|
|
7
|
+
circleId: string;
|
|
8
|
+
fileName: string;
|
|
9
|
+
originalName: string;
|
|
10
|
+
fileSize: number;
|
|
11
|
+
mimeType: string;
|
|
12
|
+
fileType: UniBoxFileType;
|
|
13
|
+
filePath: string;
|
|
14
|
+
url: string;
|
|
15
|
+
thumbnailPath?: string;
|
|
16
|
+
metadata?: Record<string, unknown>;
|
|
17
|
+
isPublic: boolean;
|
|
18
|
+
tags: string[];
|
|
19
|
+
description?: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
updatedAt: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface UniBoxFolder {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
parentId?: string;
|
|
29
|
+
ownerId: string;
|
|
30
|
+
circleId?: string;
|
|
31
|
+
color?: string;
|
|
32
|
+
icon?: string;
|
|
33
|
+
isFavorite: boolean;
|
|
34
|
+
isPinned: boolean;
|
|
35
|
+
sortOrder: number;
|
|
36
|
+
itemCount: number;
|
|
37
|
+
totalSize: number;
|
|
38
|
+
createdAt: string;
|
|
39
|
+
updatedAt: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface UniBoxUploadRequest {
|
|
43
|
+
file: unknown;
|
|
44
|
+
description?: string;
|
|
45
|
+
folderId?: string;
|
|
46
|
+
isPublic?: boolean;
|
|
47
|
+
tags?: string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface UniBoxClientOptions {
|
|
51
|
+
baseURL: string;
|
|
52
|
+
appId?: string;
|
|
53
|
+
appSlug?: string;
|
|
54
|
+
getAccessToken?: () => Promise<string | null> | string | null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface UniBoxListResult {
|
|
58
|
+
success: boolean;
|
|
59
|
+
files: UniBoxFile[];
|
|
60
|
+
total: number;
|
|
61
|
+
pagination?: unknown;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface UniBoxFolderListResult {
|
|
65
|
+
success: boolean;
|
|
66
|
+
folders: UniBoxFolder[];
|
|
67
|
+
total: number;
|
|
68
|
+
pagination?: unknown;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface UniBoxClient {
|
|
72
|
+
uploadFile(data: UniBoxUploadRequest): Promise<{ success: boolean; file: UniBoxFile }>;
|
|
73
|
+
getFiles(params?: Record<string, string | number | boolean>): Promise<UniBoxListResult>;
|
|
74
|
+
getCircleFiles(circleId: string, params?: Record<string, string | number | boolean>): Promise<UniBoxListResult>;
|
|
75
|
+
getFile(fileId: string): Promise<{ success: boolean; file: UniBoxFile }>;
|
|
76
|
+
updateFile(fileId: string, data: Record<string, unknown>): Promise<{ success: boolean; file: UniBoxFile }>;
|
|
77
|
+
deleteFile(fileId: string): Promise<{ success: boolean; message: string }>;
|
|
78
|
+
getFolders(params?: Record<string, string | number | boolean>): Promise<UniBoxFolderListResult>;
|
|
79
|
+
getCircleFolders(circleId: string, params?: Record<string, string | number | boolean>): Promise<UniBoxFolderListResult>;
|
|
80
|
+
createFolder(data: Record<string, unknown>): Promise<{ success: boolean; folder: UniBoxFolder }>;
|
|
81
|
+
createCircleFolder(circleId: string, data: Record<string, unknown>): Promise<{ success: boolean; folder: UniBoxFolder }>;
|
|
82
|
+
getFolderPath(folderId: string): Promise<{ success: boolean; path: UniBoxFolder[] }>;
|
|
83
|
+
getQuota(): Promise<unknown>;
|
|
84
|
+
getCircleQuota(circleId: string): Promise<unknown>;
|
|
85
|
+
getAnalytics(): Promise<unknown>;
|
|
86
|
+
searchFiles(query: string, params?: Record<string, string | number | boolean>): Promise<UniBoxListResult>;
|
|
87
|
+
getFileUrl(path: string): string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function normalizeBaseURL(baseURL: string): string {
|
|
91
|
+
const trimmed = String(baseURL || '').replace(/\/$/, '');
|
|
92
|
+
return trimmed.endsWith('/api/v1') ? trimmed : `${trimmed}/api/v1`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function normalizeFileType(fileType?: string): UniBoxFileType {
|
|
96
|
+
if (fileType === 'image' || fileType === 'video' || fileType === 'audio') {
|
|
97
|
+
return fileType;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (fileType === 'pdf' || fileType === 'document') {
|
|
101
|
+
return 'document';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return 'other';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function normalizeFileURL(baseURL: string, path: string): string {
|
|
108
|
+
if (!path) {
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (/^https?:\/\//i.test(path) || path.startsWith('data:')) {
|
|
113
|
+
return path;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(path)) {
|
|
117
|
+
return `${baseURL.replace(/\/api\/v1$/, '')}/api/v1/storage/proxy/${path}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const origin = baseURL.replace(/\/api\/v1$/, '');
|
|
121
|
+
const normalizedPath = path.startsWith('/') ? path : `/api/uploads/${path}`;
|
|
122
|
+
return `${origin}${normalizedPath}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function mapUniBoxFile(baseURL: string, file: any): UniBoxFile {
|
|
126
|
+
return {
|
|
127
|
+
id: file?.id || '',
|
|
128
|
+
userId: file?.uploadedBy || file?.userId || '',
|
|
129
|
+
applicationId: file?.applicationId,
|
|
130
|
+
circleId: file?.circleId || '',
|
|
131
|
+
fileName: file?.fileName || file?.filename || file?.originalName || '',
|
|
132
|
+
originalName: file?.originalName || file?.filename || file?.fileName || '',
|
|
133
|
+
fileSize: Number(file?.size ?? file?.fileSize ?? 0),
|
|
134
|
+
mimeType: file?.mimeType || 'application/octet-stream',
|
|
135
|
+
fileType: normalizeFileType(file?.fileType),
|
|
136
|
+
filePath: file?.filePath || file?.storagePath || '',
|
|
137
|
+
url: normalizeFileURL(baseURL, file?.url || file?.filePath || file?.storagePath || file?.id || ''),
|
|
138
|
+
thumbnailPath: file?.thumbnailUrl || file?.thumbnailPath,
|
|
139
|
+
metadata: file?.metadata,
|
|
140
|
+
isPublic: file?.isPublic ?? true,
|
|
141
|
+
tags: Array.isArray(file?.tags)
|
|
142
|
+
? file.tags.map((tag: any) => (typeof tag === 'string' ? tag : tag?.name)).filter(Boolean)
|
|
143
|
+
: [],
|
|
144
|
+
description: file?.description,
|
|
145
|
+
createdAt: file?.createdAt || new Date().toISOString(),
|
|
146
|
+
updatedAt: file?.updatedAt || file?.createdAt || new Date().toISOString(),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function mapUniBoxFolder(folder: any): UniBoxFolder {
|
|
151
|
+
return {
|
|
152
|
+
id: folder?.id || '',
|
|
153
|
+
name: folder?.name || '',
|
|
154
|
+
description: folder?.description,
|
|
155
|
+
parentId: folder?.parentId,
|
|
156
|
+
ownerId: folder?.ownerId || folder?.userId || '',
|
|
157
|
+
circleId: folder?.circleId,
|
|
158
|
+
color: folder?.color,
|
|
159
|
+
icon: folder?.icon,
|
|
160
|
+
isFavorite: Boolean(folder?.isFavorite),
|
|
161
|
+
isPinned: Boolean(folder?.isPinned),
|
|
162
|
+
sortOrder: Number(folder?.sortOrder || 0),
|
|
163
|
+
itemCount: Number(folder?.itemCount || 0),
|
|
164
|
+
totalSize: Number(folder?.totalSize || 0),
|
|
165
|
+
createdAt: folder?.createdAt || new Date().toISOString(),
|
|
166
|
+
updatedAt: folder?.updatedAt || folder?.createdAt || new Date().toISOString(),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function createUniBoxClient(options: UniBoxClientOptions): UniBoxClient {
|
|
171
|
+
const baseURL = normalizeBaseURL(options.baseURL);
|
|
172
|
+
|
|
173
|
+
async function buildHeaders(extraHeaders?: Record<string, string>): Promise<Record<string, string>> {
|
|
174
|
+
const accessToken = options.getAccessToken ? await options.getAccessToken() : null;
|
|
175
|
+
const headers: Record<string, string> = { ...(extraHeaders || {}) };
|
|
176
|
+
|
|
177
|
+
if (options.appId) {
|
|
178
|
+
headers['X-App-ID'] = options.appId;
|
|
179
|
+
} else if (options.appSlug) {
|
|
180
|
+
headers['X-App-Slug'] = options.appSlug;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (accessToken) {
|
|
184
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return headers;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function request(path: string, init: RequestInit): Promise<any> {
|
|
191
|
+
const response = await fetch(`${baseURL}${path}`, init);
|
|
192
|
+
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
let message = `Request failed: HTTP ${response.status}`;
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const payload = await response.json();
|
|
198
|
+
if (payload?.message || payload?.error) {
|
|
199
|
+
message = payload.message || payload.error;
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
try {
|
|
203
|
+
const text = await response.text();
|
|
204
|
+
if (text) {
|
|
205
|
+
message = text;
|
|
206
|
+
}
|
|
207
|
+
} catch {
|
|
208
|
+
// Ignore text parsing error.
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
throw new Error(message);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return response.json();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function buildQuery(params?: Record<string, string | number | boolean>): string {
|
|
219
|
+
const searchParams = new URLSearchParams();
|
|
220
|
+
|
|
221
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
222
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
223
|
+
searchParams.set(key, String(value));
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const query = searchParams.toString();
|
|
228
|
+
return query ? `?${query}` : '';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
uploadFile: async (data) => {
|
|
233
|
+
const formData = new FormData();
|
|
234
|
+
formData.append('file', data.file as any);
|
|
235
|
+
|
|
236
|
+
if (data.description) {
|
|
237
|
+
formData.append('description', data.description);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (data.folderId) {
|
|
241
|
+
formData.append('folderId', data.folderId);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (data.isPublic !== undefined) {
|
|
245
|
+
formData.append('isPublic', String(data.isPublic));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (Array.isArray(data.tags) && data.tags.length > 0) {
|
|
249
|
+
formData.append('tags', JSON.stringify(data.tags));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const response = await request('/files/files/upload', {
|
|
253
|
+
method: 'POST',
|
|
254
|
+
headers: await buildHeaders(),
|
|
255
|
+
body: formData,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
success: Boolean(response?.success),
|
|
260
|
+
file: mapUniBoxFile(baseURL, response?.file || response?.data?.file || response?.data),
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
getFiles: async (params) => {
|
|
265
|
+
const response = await request(`/files/files${buildQuery(params)}`, {
|
|
266
|
+
method: 'GET',
|
|
267
|
+
headers: await buildHeaders(),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const files = Array.isArray(response?.files) ? response.files : [];
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
success: Boolean(response?.success),
|
|
274
|
+
files: files.map((file: any) => mapUniBoxFile(baseURL, file)),
|
|
275
|
+
total: Number(response?.total || files.length || 0),
|
|
276
|
+
pagination: response?.pagination,
|
|
277
|
+
};
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
getCircleFiles: async (circleId, params) => {
|
|
281
|
+
const response = await request(`/files/circles/${encodeURIComponent(circleId)}/files${buildQuery(params)}`, {
|
|
282
|
+
method: 'GET',
|
|
283
|
+
headers: await buildHeaders(),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const files = Array.isArray(response?.files) ? response.files : [];
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
success: Boolean(response?.success),
|
|
290
|
+
files: files.map((file: any) => mapUniBoxFile(baseURL, file)),
|
|
291
|
+
total: Number(response?.total || files.length || 0),
|
|
292
|
+
pagination: response?.pagination,
|
|
293
|
+
};
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
getFile: async (fileId) => {
|
|
297
|
+
const response = await request(`/files/files/${encodeURIComponent(fileId)}`, {
|
|
298
|
+
method: 'GET',
|
|
299
|
+
headers: await buildHeaders(),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
success: Boolean(response?.success),
|
|
304
|
+
file: mapUniBoxFile(baseURL, response?.file || response?.data?.file || response?.data),
|
|
305
|
+
};
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
updateFile: async (fileId, data) => {
|
|
309
|
+
const response = await request(`/files/files/${encodeURIComponent(fileId)}`, {
|
|
310
|
+
method: 'PUT',
|
|
311
|
+
headers: await buildHeaders({ 'Content-Type': 'application/json' }),
|
|
312
|
+
body: JSON.stringify(data || {}),
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
success: Boolean(response?.success),
|
|
317
|
+
file: mapUniBoxFile(baseURL, response?.file || response?.data?.file || response?.data),
|
|
318
|
+
};
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
deleteFile: async (fileId) => {
|
|
322
|
+
const response = await request(`/files/files/${encodeURIComponent(fileId)}`, {
|
|
323
|
+
method: 'DELETE',
|
|
324
|
+
headers: await buildHeaders(),
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
success: Boolean(response?.success !== false),
|
|
329
|
+
message: response?.message || response?.error || 'File deleted',
|
|
330
|
+
};
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
getFolders: async (params) => {
|
|
334
|
+
const response = await request(`/files/folders${buildQuery(params)}`, {
|
|
335
|
+
method: 'GET',
|
|
336
|
+
headers: await buildHeaders(),
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const folders = Array.isArray(response?.folders) ? response.folders : [];
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
success: Boolean(response?.success),
|
|
343
|
+
folders: folders.map((folder: any) => mapUniBoxFolder(folder)),
|
|
344
|
+
total: Number(response?.total || folders.length || 0),
|
|
345
|
+
pagination: response?.pagination,
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
getCircleFolders: async (circleId, params) => {
|
|
350
|
+
const response = await request(`/files/circles/${encodeURIComponent(circleId)}/folders${buildQuery(params)}`, {
|
|
351
|
+
method: 'GET',
|
|
352
|
+
headers: await buildHeaders(),
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const folders = Array.isArray(response?.folders) ? response.folders : [];
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
success: Boolean(response?.success),
|
|
359
|
+
folders: folders.map((folder: any) => mapUniBoxFolder(folder)),
|
|
360
|
+
total: Number(response?.total || folders.length || 0),
|
|
361
|
+
pagination: response?.pagination,
|
|
362
|
+
};
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
createFolder: async (data) => {
|
|
366
|
+
const response = await request('/files/folders', {
|
|
367
|
+
method: 'POST',
|
|
368
|
+
headers: await buildHeaders({ 'Content-Type': 'application/json' }),
|
|
369
|
+
body: JSON.stringify(data || {}),
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
success: Boolean(response?.success),
|
|
374
|
+
folder: mapUniBoxFolder(response?.folder || response?.data?.folder || response?.data),
|
|
375
|
+
};
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
createCircleFolder: async (circleId, data) => {
|
|
379
|
+
const response = await request(`/files/circles/${encodeURIComponent(circleId)}/folders`, {
|
|
380
|
+
method: 'POST',
|
|
381
|
+
headers: await buildHeaders({ 'Content-Type': 'application/json' }),
|
|
382
|
+
body: JSON.stringify(data || {}),
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
success: Boolean(response?.success),
|
|
387
|
+
folder: mapUniBoxFolder(response?.folder || response?.data?.folder || response?.data),
|
|
388
|
+
};
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
getFolderPath: async (folderId) => {
|
|
392
|
+
const response = await request(`/files/folders/${encodeURIComponent(folderId)}/path`, {
|
|
393
|
+
method: 'GET',
|
|
394
|
+
headers: await buildHeaders(),
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const path = Array.isArray(response?.path) ? response.path : [];
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
success: Boolean(response?.success),
|
|
401
|
+
path: path.map((folder: any) => mapUniBoxFolder(folder)),
|
|
402
|
+
};
|
|
403
|
+
},
|
|
404
|
+
|
|
405
|
+
getQuota: async () =>
|
|
406
|
+
request('/files/quota', {
|
|
407
|
+
method: 'GET',
|
|
408
|
+
headers: await buildHeaders(),
|
|
409
|
+
}),
|
|
410
|
+
|
|
411
|
+
getCircleQuota: async (circleId) =>
|
|
412
|
+
request(`/files/circles/${encodeURIComponent(circleId)}/quota`, {
|
|
413
|
+
method: 'GET',
|
|
414
|
+
headers: await buildHeaders(),
|
|
415
|
+
}),
|
|
416
|
+
|
|
417
|
+
getAnalytics: async () =>
|
|
418
|
+
request('/files/analytics', {
|
|
419
|
+
method: 'GET',
|
|
420
|
+
headers: await buildHeaders(),
|
|
421
|
+
}),
|
|
422
|
+
|
|
423
|
+
searchFiles: async (query, params) => {
|
|
424
|
+
const response = await request(
|
|
425
|
+
`/files/search${buildQuery({
|
|
426
|
+
query,
|
|
427
|
+
...(params || {}),
|
|
428
|
+
})}`,
|
|
429
|
+
{
|
|
430
|
+
method: 'GET',
|
|
431
|
+
headers: await buildHeaders(),
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
const files = Array.isArray(response?.files) ? response.files : [];
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
success: Boolean(response?.success),
|
|
439
|
+
files: files.map((file: any) => mapUniBoxFile(baseURL, file)),
|
|
440
|
+
total: Number(response?.total || files.length || 0),
|
|
441
|
+
pagination: response?.pagination,
|
|
442
|
+
};
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
getFileUrl: (path) => normalizeFileURL(baseURL, path),
|
|
446
|
+
};
|
|
447
|
+
}
|