|
|
@@ -29,6 +29,23 @@ import type {
|
|
29
|
29
|
|
|
30
|
30
|
const BLOCKS_BASE_URL = '/api/v1/documents';
|
|
31
|
31
|
|
|
|
32
|
+function encodeResourceId(value: string, name: string): string {
|
|
|
33
|
+ const hasUnsafeCharacter = Array.from(value).some((character) => {
|
|
|
34
|
+ const codePoint = character.codePointAt(0) ?? 0;
|
|
|
35
|
+ return codePoint < 32 || codePoint === 127 || character === '/' || character === '\\';
|
|
|
36
|
+ });
|
|
|
37
|
+
|
|
|
38
|
+ if (!value || value.length > 128 || hasUnsafeCharacter) {
|
|
|
39
|
+ throw new Error(`无效的${name}`);
|
|
|
40
|
+ }
|
|
|
41
|
+
|
|
|
42
|
+ return encodeURIComponent(value);
|
|
|
43
|
+}
|
|
|
44
|
+
|
|
|
45
|
+function documentBlocksPath(documentId: string): string {
|
|
|
46
|
+ return `${BLOCKS_BASE_URL}/${encodeResourceId(documentId, '文档 ID')}/blocks`;
|
|
|
47
|
+}
|
|
|
48
|
+
|
|
32
|
49
|
function createServiceError(message: string, error: unknown): Error {
|
|
33
|
50
|
const detail = getErrorMessage(error);
|
|
34
|
51
|
return new Error(detail === '发生未知错误' ? message : `${message}: ${detail}`, {
|
|
|
@@ -61,7 +78,7 @@ export const blockService = {
|
|
61
|
78
|
options?: { signal?: AbortSignal }
|
|
62
|
79
|
): Promise<GetBlocksResponse> {
|
|
63
|
80
|
try {
|
|
64
|
|
- const response = await apiClient.get(`${BLOCKS_BASE_URL}/${documentId}/blocks`, {
|
|
|
81
|
+ const response = await apiClient.get(documentBlocksPath(documentId), {
|
|
65
|
82
|
signal: options?.signal,
|
|
66
|
83
|
});
|
|
67
|
84
|
|
|
|
@@ -94,7 +111,9 @@ export const blockService = {
|
|
94
|
111
|
*/
|
|
95
|
112
|
async getBlock(documentId: string, blockId: string): Promise<GetBlockResponse> {
|
|
96
|
113
|
try {
|
|
97
|
|
- const response = await apiClient.get(`${BLOCKS_BASE_URL}/${documentId}/blocks/${blockId}`);
|
|
|
114
|
+ const response = await apiClient.get(
|
|
|
115
|
+ `${documentBlocksPath(documentId)}/${encodeResourceId(blockId, '块 ID')}`
|
|
|
116
|
+ );
|
|
98
|
117
|
|
|
99
|
118
|
const data = response.data?.data || response.data;
|
|
100
|
119
|
return {
|
|
|
@@ -135,7 +154,7 @@ export const blockService = {
|
|
135
|
154
|
): Promise<UpdateBlockResponse> {
|
|
136
|
155
|
try {
|
|
137
|
156
|
const response = await apiClient.put(
|
|
138
|
|
- `${BLOCKS_BASE_URL}/${documentId}/blocks/${blockId}`,
|
|
|
157
|
+ `${documentBlocksPath(documentId)}/${encodeResourceId(blockId, '块 ID')}`,
|
|
139
|
158
|
updates,
|
|
140
|
159
|
{ signal: options?.signal }
|
|
141
|
160
|
);
|
|
|
@@ -170,7 +189,7 @@ export const blockService = {
|
|
170
|
189
|
*/
|
|
171
|
190
|
async createBlock(documentId: string, block: CreateBlockRequest): Promise<CreateBlockResponse> {
|
|
172
|
191
|
try {
|
|
173
|
|
- const response = await apiClient.post(`${BLOCKS_BASE_URL}/${documentId}/blocks`, block);
|
|
|
192
|
+ const response = await apiClient.post(documentBlocksPath(documentId), block);
|
|
174
|
193
|
|
|
175
|
194
|
return response.data?.data || response.data;
|
|
176
|
195
|
} catch (error: unknown) {
|
|
|
@@ -191,7 +210,9 @@ export const blockService = {
|
|
191
|
210
|
*/
|
|
192
|
211
|
async deleteBlock(documentId: string, blockId: string): Promise<void> {
|
|
193
|
212
|
try {
|
|
194
|
|
- await apiClient.delete(`${BLOCKS_BASE_URL}/${documentId}/blocks/${blockId}`);
|
|
|
213
|
+ await apiClient.delete(
|
|
|
214
|
+ `${documentBlocksPath(documentId)}/${encodeResourceId(blockId, '块 ID')}`
|
|
|
215
|
+ );
|
|
195
|
216
|
} catch (error: unknown) {
|
|
196
|
217
|
throw createServiceError('删除块失败', error);
|
|
197
|
218
|
}
|
|
|
@@ -222,7 +243,7 @@ export const blockService = {
|
|
222
|
243
|
params.type = type;
|
|
223
|
244
|
}
|
|
224
|
245
|
|
|
225
|
|
- const response = await apiClient.get(`${BLOCKS_BASE_URL}/${documentId}/blocks/search`, {
|
|
|
246
|
+ const response = await apiClient.get(`${documentBlocksPath(documentId)}/search`, {
|
|
226
|
247
|
params,
|
|
227
|
248
|
});
|
|
228
|
249
|
|
|
|
@@ -251,7 +272,7 @@ export const blockService = {
|
|
251
|
272
|
*/
|
|
252
|
273
|
async getTOC(documentId: string): Promise<GetTOCResponse> {
|
|
253
|
274
|
try {
|
|
254
|
|
- const response = await apiClient.get(`${BLOCKS_BASE_URL}/${documentId}/blocks/toc`);
|
|
|
275
|
+ const response = await apiClient.get(`${documentBlocksPath(documentId)}/toc`);
|
|
255
|
276
|
|
|
256
|
277
|
const data = response.data?.data || response.data;
|
|
257
|
278
|
return {
|
|
|
@@ -276,7 +297,7 @@ export const blockService = {
|
|
276
|
297
|
*/
|
|
277
|
298
|
async getStats(documentId: string): Promise<BlockStatsResponse> {
|
|
278
|
299
|
try {
|
|
279
|
|
- const response = await apiClient.get(`${BLOCKS_BASE_URL}/${documentId}/blocks/stats`);
|
|
|
300
|
+ const response = await apiClient.get(`${documentBlocksPath(documentId)}/stats`);
|
|
280
|
301
|
|
|
281
|
302
|
const data = response.data?.data || response.data;
|
|
282
|
303
|
return {
|