@baruchiro/paperless-mcp 0.0.0-test1
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/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.cursor/rules/http-transport.mdc +13 -0
- package/.cursor/rules/types-usage.mdc +18 -0
- package/.cursor/rules/typescript-mcp-migration.mdc +12 -0
- package/.dockerignore +2 -0
- package/.github/FUNDING.yml +15 -0
- package/.github/workflows/docker-publish.yml +57 -0
- package/.github/workflows/release.yml +40 -0
- package/CODE_OF_CONDUCT.md +5 -0
- package/CONTRIBUTING.md +17 -0
- package/Dockerfile +19 -0
- package/LICENSE +7 -0
- package/Paperless-ngx-REST-API.yaml +9975 -0
- package/README.md +363 -0
- package/package.json +48 -0
- package/src/api/PaperlessAPI.ts +257 -0
- package/src/api/types.ts +97 -0
- package/src/api/utils.ts +11 -0
- package/src/index.ts +165 -0
- package/src/tools/correspondents.ts +137 -0
- package/src/tools/documentTypes.ts +137 -0
- package/src/tools/documents.ts +316 -0
- package/src/tools/tags.ts +143 -0
- package/src/tools/utils/middlewares.ts +23 -0
- package/src/tools/utils/queryString.ts +9 -0
- package/tsconfig.json +108 -0
package/README.md
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# Paperless-NGX MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://smithery.ai/server/@baruchiro/paperless-mcp)
|
|
4
|
+
|
|
5
|
+
An MCP (Model Context Protocol) server for interacting with a Paperless-NGX API server. This server provides tools for managing documents, tags, correspondents, and document types in your Paperless-NGX instance.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
### Installing via Smithery
|
|
10
|
+
|
|
11
|
+
To install Paperless NGX MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@baruchiro/paperless-mcp):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx -y @smithery/cli install @baruchiro/paperless-mcp --client claude
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Manual Installation
|
|
18
|
+
1. Install the MCP server:
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g paperless-mcp
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. Add it to your Claude's MCP configuration:
|
|
24
|
+
|
|
25
|
+
For VSCode extension, edit `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`:
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"paperless": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["paperless-mcp", "http://your-paperless-instance:8000", "your-api-token"]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For Claude desktop app, edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"paperless": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["paperless-mcp", "http://your-paperless-instance:8000", "your-api-token"]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
3. Get your API token:
|
|
50
|
+
1. Log into your Paperless-NGX instance
|
|
51
|
+
2. Click your username in the top right
|
|
52
|
+
3. Select "My Profile"
|
|
53
|
+
4. Click the circular arrow button to generate a new token
|
|
54
|
+
|
|
55
|
+
4. Replace the placeholders in your MCP config:
|
|
56
|
+
- `http://your-paperless-instance:8000` with your Paperless-NGX URL
|
|
57
|
+
- `your-api-token` with the token you just generated
|
|
58
|
+
|
|
59
|
+
That's it! Now you can ask Claude to help you manage your Paperless-NGX documents.
|
|
60
|
+
|
|
61
|
+
## Example Usage
|
|
62
|
+
|
|
63
|
+
Here are some things you can ask Claude to do:
|
|
64
|
+
|
|
65
|
+
- "Show me all documents tagged as 'Invoice'"
|
|
66
|
+
- "Search for documents containing 'tax return'"
|
|
67
|
+
- "Create a new tag called 'Receipts' with color #FF0000"
|
|
68
|
+
- "Download document #123"
|
|
69
|
+
- "List all correspondents"
|
|
70
|
+
- "Create a new document type called 'Bank Statement'"
|
|
71
|
+
|
|
72
|
+
## Available Tools
|
|
73
|
+
|
|
74
|
+
### Document Operations
|
|
75
|
+
|
|
76
|
+
#### list_documents
|
|
77
|
+
Get a paginated list of all documents.
|
|
78
|
+
|
|
79
|
+
Parameters:
|
|
80
|
+
- page (optional): Page number
|
|
81
|
+
- page_size (optional): Number of documents per page
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
list_documents({
|
|
85
|
+
page: 1,
|
|
86
|
+
page_size: 25
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### get_document
|
|
91
|
+
Get a specific document by ID.
|
|
92
|
+
|
|
93
|
+
Parameters:
|
|
94
|
+
- id: Document ID
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
get_document({
|
|
98
|
+
id: 123
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### search_documents
|
|
103
|
+
Full-text search across documents.
|
|
104
|
+
|
|
105
|
+
Parameters:
|
|
106
|
+
- query: Search query string
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
search_documents({
|
|
110
|
+
query: "invoice 2024"
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### download_document
|
|
115
|
+
Download a document file by ID.
|
|
116
|
+
|
|
117
|
+
Parameters:
|
|
118
|
+
- id: Document ID
|
|
119
|
+
- original (optional): If true, downloads original file instead of archived version
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
download_document({
|
|
123
|
+
id: 123,
|
|
124
|
+
original: false
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### bulk_edit_documents
|
|
129
|
+
Perform bulk operations on multiple documents.
|
|
130
|
+
|
|
131
|
+
Parameters:
|
|
132
|
+
- documents: Array of document IDs
|
|
133
|
+
- method: One of:
|
|
134
|
+
- set_correspondent: Set correspondent for documents
|
|
135
|
+
- set_document_type: Set document type for documents
|
|
136
|
+
- set_storage_path: Set storage path for documents
|
|
137
|
+
- add_tag: Add a tag to documents
|
|
138
|
+
- remove_tag: Remove a tag from documents
|
|
139
|
+
- modify_tags: Add and/or remove multiple tags
|
|
140
|
+
- delete: Delete documents
|
|
141
|
+
- reprocess: Reprocess documents
|
|
142
|
+
- set_permissions: Set document permissions
|
|
143
|
+
- merge: Merge multiple documents
|
|
144
|
+
- split: Split a document into multiple documents
|
|
145
|
+
- rotate: Rotate document pages
|
|
146
|
+
- delete_pages: Delete specific pages from a document
|
|
147
|
+
- Additional parameters based on method:
|
|
148
|
+
- correspondent: ID for set_correspondent
|
|
149
|
+
- document_type: ID for set_document_type
|
|
150
|
+
- storage_path: ID for set_storage_path
|
|
151
|
+
- tag: ID for add_tag/remove_tag
|
|
152
|
+
- add_tags: Array of tag IDs for modify_tags
|
|
153
|
+
- remove_tags: Array of tag IDs for modify_tags
|
|
154
|
+
- permissions: Object for set_permissions with owner, permissions, merge flag
|
|
155
|
+
- metadata_document_id: ID for merge to specify metadata source
|
|
156
|
+
- delete_originals: Boolean for merge/split
|
|
157
|
+
- pages: String for split "[1,2-3,4,5-7]" or delete_pages "[2,3,4]"
|
|
158
|
+
- degrees: Number for rotate (90, 180, or 270)
|
|
159
|
+
|
|
160
|
+
Examples:
|
|
161
|
+
```typescript
|
|
162
|
+
// Add a tag to multiple documents
|
|
163
|
+
bulk_edit_documents({
|
|
164
|
+
documents: [1, 2, 3],
|
|
165
|
+
method: "add_tag",
|
|
166
|
+
tag: 5
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// Set correspondent and document type
|
|
170
|
+
bulk_edit_documents({
|
|
171
|
+
documents: [4, 5],
|
|
172
|
+
method: "set_correspondent",
|
|
173
|
+
correspondent: 2
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// Merge documents
|
|
177
|
+
bulk_edit_documents({
|
|
178
|
+
documents: [6, 7, 8],
|
|
179
|
+
method: "merge",
|
|
180
|
+
metadata_document_id: 6,
|
|
181
|
+
delete_originals: true
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// Split document into parts
|
|
185
|
+
bulk_edit_documents({
|
|
186
|
+
documents: [9],
|
|
187
|
+
method: "split",
|
|
188
|
+
pages: "[1-2,3-4,5]"
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
// Modify multiple tags at once
|
|
192
|
+
bulk_edit_documents({
|
|
193
|
+
documents: [10, 11],
|
|
194
|
+
method: "modify_tags",
|
|
195
|
+
add_tags: [1, 2],
|
|
196
|
+
remove_tags: [3, 4]
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### post_document
|
|
201
|
+
Upload a new document to Paperless-NGX.
|
|
202
|
+
|
|
203
|
+
Parameters:
|
|
204
|
+
- file: Base64 encoded file content
|
|
205
|
+
- filename: Name of the file
|
|
206
|
+
- title (optional): Title for the document
|
|
207
|
+
- created (optional): DateTime when the document was created (e.g. "2024-01-19" or "2024-01-19 06:15:00+02:00")
|
|
208
|
+
- correspondent (optional): ID of a correspondent
|
|
209
|
+
- document_type (optional): ID of a document type
|
|
210
|
+
- storage_path (optional): ID of a storage path
|
|
211
|
+
- tags (optional): Array of tag IDs
|
|
212
|
+
- archive_serial_number (optional): Archive serial number
|
|
213
|
+
- custom_fields (optional): Array of custom field IDs
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
post_document({
|
|
217
|
+
file: "base64_encoded_content",
|
|
218
|
+
filename: "invoice.pdf",
|
|
219
|
+
title: "January Invoice",
|
|
220
|
+
created: "2024-01-19",
|
|
221
|
+
correspondent: 1,
|
|
222
|
+
document_type: 2,
|
|
223
|
+
tags: [1, 3],
|
|
224
|
+
archive_serial_number: "2024-001"
|
|
225
|
+
})
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Tag Operations
|
|
229
|
+
|
|
230
|
+
#### list_tags
|
|
231
|
+
Get all tags.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
list_tags()
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### create_tag
|
|
238
|
+
Create a new tag.
|
|
239
|
+
|
|
240
|
+
Parameters:
|
|
241
|
+
- name: Tag name
|
|
242
|
+
- color (optional): Hex color code (e.g. "#ff0000")
|
|
243
|
+
- match (optional): Text pattern to match
|
|
244
|
+
- matching_algorithm (optional): One of "any", "all", "exact", "regular expression", "fuzzy"
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
create_tag({
|
|
248
|
+
name: "Invoice",
|
|
249
|
+
color: "#ff0000",
|
|
250
|
+
match: "invoice",
|
|
251
|
+
matching_algorithm: "fuzzy"
|
|
252
|
+
})
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Correspondent Operations
|
|
256
|
+
|
|
257
|
+
#### list_correspondents
|
|
258
|
+
Get all correspondents.
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
list_correspondents()
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### create_correspondent
|
|
265
|
+
Create a new correspondent.
|
|
266
|
+
|
|
267
|
+
Parameters:
|
|
268
|
+
- name: Correspondent name
|
|
269
|
+
- match (optional): Text pattern to match
|
|
270
|
+
- matching_algorithm (optional): One of "any", "all", "exact", "regular expression", "fuzzy"
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
create_correspondent({
|
|
274
|
+
name: "ACME Corp",
|
|
275
|
+
match: "ACME",
|
|
276
|
+
matching_algorithm: "fuzzy"
|
|
277
|
+
})
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Document Type Operations
|
|
281
|
+
|
|
282
|
+
#### list_document_types
|
|
283
|
+
Get all document types.
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
list_document_types()
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
#### create_document_type
|
|
290
|
+
Create a new document type.
|
|
291
|
+
|
|
292
|
+
Parameters:
|
|
293
|
+
- name: Document type name
|
|
294
|
+
- match (optional): Text pattern to match
|
|
295
|
+
- matching_algorithm (optional): One of "any", "all", "exact", "regular expression", "fuzzy"
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
create_document_type({
|
|
299
|
+
name: "Invoice",
|
|
300
|
+
match: "invoice total amount due",
|
|
301
|
+
matching_algorithm: "any"
|
|
302
|
+
})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Error Handling
|
|
306
|
+
|
|
307
|
+
The server will show clear error messages if:
|
|
308
|
+
- The Paperless-NGX URL or API token is incorrect
|
|
309
|
+
- The Paperless-NGX server is unreachable
|
|
310
|
+
- The requested operation fails
|
|
311
|
+
- The provided parameters are invalid
|
|
312
|
+
|
|
313
|
+
## Development
|
|
314
|
+
|
|
315
|
+
Want to contribute or modify the server? Here's what you need to know:
|
|
316
|
+
|
|
317
|
+
1. Clone the repository
|
|
318
|
+
2. Install dependencies:
|
|
319
|
+
```bash
|
|
320
|
+
npm install
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
3. Make your changes to server.js
|
|
324
|
+
4. Test locally:
|
|
325
|
+
```bash
|
|
326
|
+
node server.js http://localhost:8000 your-test-token
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
The server is built with:
|
|
330
|
+
- [litemcp](https://github.com/wong2/litemcp): A TypeScript framework for building MCP servers
|
|
331
|
+
- [zod](https://github.com/colinhacks/zod): TypeScript-first schema validation
|
|
332
|
+
|
|
333
|
+
## API Documentation
|
|
334
|
+
|
|
335
|
+
This MCP server implements endpoints from the Paperless-NGX REST API. For more details about the underlying API, see the [official documentation](https://docs.paperless-ngx.com/api/).
|
|
336
|
+
|
|
337
|
+
## Running the MCP Server
|
|
338
|
+
|
|
339
|
+
The MCP server can be run in two modes:
|
|
340
|
+
|
|
341
|
+
### 1. stdio (default)
|
|
342
|
+
|
|
343
|
+
This is the default mode. The server communicates over stdio, suitable for CLI and direct integrations.
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
npm run start -- <baseUrl> <token>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### 2. HTTP (Streamable HTTP Transport)
|
|
350
|
+
|
|
351
|
+
To run the server as an HTTP service, use the `--http` flag. You can also specify the port with `--port` (default: 3000). This mode requires [Express](https://expressjs.com/) to be installed (it is included as a dependency).
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
npm run start -- <baseUrl> <token> --http --port 3000
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
- The MCP API will be available at `POST /mcp` on the specified port.
|
|
358
|
+
- Each request is handled statelessly, following the [StreamableHTTPServerTransport](https://github.com/modelcontextprotocol/typescript-sdk) pattern.
|
|
359
|
+
- GET and DELETE requests to `/mcp` will return 405 Method Not Allowed.
|
|
360
|
+
|
|
361
|
+
# Credits
|
|
362
|
+
|
|
363
|
+
This project is a fork of [nloui/paperless-mcp](https://github.com/nloui/paperless-mcp). Many thanks to the original author for their work. Contributions and improvements may be returned upstream.
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@baruchiro/paperless-mcp",
|
|
3
|
+
"version": "0.0.0-test1",
|
|
4
|
+
"description": "Model Context Protocol (MCP) server for interacting with Paperless-NGX document management system. Enables AI assistants to manage documents, tags, correspondents, and document types through the Paperless-NGX API.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"paperless-mcp": "src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"start": "ts-node src/index.ts",
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"inspect": "npm run build && npx -y @modelcontextprotocol/inspector node build/index.js",
|
|
14
|
+
"prepare": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"paperless-ngx",
|
|
19
|
+
"document-management",
|
|
20
|
+
"ai",
|
|
21
|
+
"claude",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"paperless"
|
|
24
|
+
],
|
|
25
|
+
"author": "Baruch Odem",
|
|
26
|
+
"license": "ISC",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/baruchiro/paperless-mcp"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/baruchiro/paperless-mcp",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/baruchiro/paperless-mcp/issues"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.11.1",
|
|
37
|
+
"axios": "^1.9.0",
|
|
38
|
+
"express": "^5.1.0",
|
|
39
|
+
"form-data": "^4.0.2",
|
|
40
|
+
"typescript": "^5.8.3",
|
|
41
|
+
"zod": "^3.24.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@changesets/cli": "^2.29.4",
|
|
45
|
+
"@types/node": "^22.15.17",
|
|
46
|
+
"ts-node": "^10.9.2"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import FormData from "form-data";
|
|
3
|
+
import {
|
|
4
|
+
BulkEditDocumentsResult,
|
|
5
|
+
Correspondent,
|
|
6
|
+
Document,
|
|
7
|
+
DocumentsResponse,
|
|
8
|
+
DocumentType,
|
|
9
|
+
GetCorrespondentsResponse,
|
|
10
|
+
GetDocumentTypesResponse,
|
|
11
|
+
GetTagsResponse,
|
|
12
|
+
Tag,
|
|
13
|
+
} from "./types";
|
|
14
|
+
import { headersToObject } from "./utils";
|
|
15
|
+
|
|
16
|
+
export class PaperlessAPI {
|
|
17
|
+
constructor(
|
|
18
|
+
private readonly baseUrl: string,
|
|
19
|
+
private readonly token: string
|
|
20
|
+
) {
|
|
21
|
+
this.baseUrl = baseUrl;
|
|
22
|
+
this.token = token;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async request<T = any>(path: string, options: RequestInit = {}) {
|
|
26
|
+
const url = `${this.baseUrl}/api${path}`;
|
|
27
|
+
const isJson = !options.body || typeof options.body === "string";
|
|
28
|
+
|
|
29
|
+
const mergedHeaders = {
|
|
30
|
+
Authorization: `Token ${this.token}`,
|
|
31
|
+
Accept: "application/json; version=5",
|
|
32
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
33
|
+
...(isJson ? { "Content-Type": "application/json" } : {}),
|
|
34
|
+
...headersToObject(options.headers),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const response = await axios<T>({
|
|
39
|
+
url,
|
|
40
|
+
method: options.method || "GET",
|
|
41
|
+
headers: mergedHeaders,
|
|
42
|
+
data: options.body,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const body = response.data;
|
|
46
|
+
if (response.status < 200 || response.status >= 300) {
|
|
47
|
+
console.error({
|
|
48
|
+
error: "Error executing request",
|
|
49
|
+
url,
|
|
50
|
+
options,
|
|
51
|
+
status: response.status,
|
|
52
|
+
response: body,
|
|
53
|
+
});
|
|
54
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return body;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error({
|
|
60
|
+
error: "Error executing request",
|
|
61
|
+
message: error instanceof Error ? error.message : String(error),
|
|
62
|
+
url,
|
|
63
|
+
options,
|
|
64
|
+
});
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Document operations
|
|
70
|
+
async bulkEditDocuments(
|
|
71
|
+
documents: number[],
|
|
72
|
+
method: string,
|
|
73
|
+
parameters = {}
|
|
74
|
+
): Promise<BulkEditDocumentsResult> {
|
|
75
|
+
return this.request<BulkEditDocumentsResult>("/documents/bulk_edit/", {
|
|
76
|
+
method: "POST",
|
|
77
|
+
body: JSON.stringify({
|
|
78
|
+
documents,
|
|
79
|
+
method,
|
|
80
|
+
parameters,
|
|
81
|
+
}),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async postDocument(
|
|
86
|
+
file: File,
|
|
87
|
+
metadata: Record<string, string | string[] | number | number[]> = {}
|
|
88
|
+
): Promise<string> {
|
|
89
|
+
const formData = new FormData();
|
|
90
|
+
formData.append("document", file);
|
|
91
|
+
|
|
92
|
+
// Add optional metadata fields
|
|
93
|
+
if (metadata.title) formData.append("title", metadata.title);
|
|
94
|
+
if (metadata.created) formData.append("created", metadata.created);
|
|
95
|
+
if (metadata.correspondent)
|
|
96
|
+
formData.append("correspondent", metadata.correspondent);
|
|
97
|
+
if (metadata.document_type)
|
|
98
|
+
formData.append("document_type", metadata.document_type);
|
|
99
|
+
if (metadata.storage_path)
|
|
100
|
+
formData.append("storage_path", metadata.storage_path);
|
|
101
|
+
if (metadata.tags) {
|
|
102
|
+
(metadata.tags as string[]).forEach((tag) =>
|
|
103
|
+
formData.append("tags", tag)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (metadata.archive_serial_number) {
|
|
107
|
+
formData.append("archive_serial_number", metadata.archive_serial_number);
|
|
108
|
+
}
|
|
109
|
+
if (metadata.custom_fields) {
|
|
110
|
+
(metadata.custom_fields as string[]).forEach((field) =>
|
|
111
|
+
formData.append("custom_fields", field)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const response = await axios.post<string>(
|
|
116
|
+
`${this.baseUrl}/api/documents/post_document/`,
|
|
117
|
+
formData,
|
|
118
|
+
{
|
|
119
|
+
headers: {
|
|
120
|
+
Authorization: `Token ${this.token}`,
|
|
121
|
+
...formData.getHeaders(),
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (response.status < 200 || response.status >= 300) {
|
|
127
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return response.data;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async getDocuments(query = ""): Promise<DocumentsResponse> {
|
|
134
|
+
return this.request<DocumentsResponse>(`/documents/${query}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async getDocument(id: number): Promise<Document> {
|
|
138
|
+
return this.request<Document>(`/documents/${id}/`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async searchDocuments(query: string): Promise<DocumentsResponse> {
|
|
142
|
+
const response = await this.request<DocumentsResponse>(
|
|
143
|
+
`/documents/?query=${encodeURIComponent(query)}`
|
|
144
|
+
);
|
|
145
|
+
return response;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async downloadDocument(id: number, asOriginal = false) {
|
|
149
|
+
const query = asOriginal ? "?original=true" : "";
|
|
150
|
+
const response = await axios.get(
|
|
151
|
+
`${this.baseUrl}/api/documents/${id}/download/${query}`,
|
|
152
|
+
{
|
|
153
|
+
headers: {
|
|
154
|
+
Authorization: `Token ${this.token}`,
|
|
155
|
+
},
|
|
156
|
+
responseType: "arraybuffer",
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
return response;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Tag operations
|
|
163
|
+
async getTags(): Promise<GetTagsResponse> {
|
|
164
|
+
return this.request<GetTagsResponse>("/tags/");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async createTag(data: Partial<Tag>): Promise<Tag> {
|
|
168
|
+
return this.request<Tag>("/tags/", {
|
|
169
|
+
method: "POST",
|
|
170
|
+
body: JSON.stringify(data),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async updateTag(id: number, data: Partial<Tag>): Promise<Tag> {
|
|
175
|
+
return this.request<Tag>(`/tags/${id}/`, {
|
|
176
|
+
method: "PUT",
|
|
177
|
+
body: JSON.stringify(data),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async deleteTag(id: number): Promise<void> {
|
|
182
|
+
return this.request<void>(`/tags/${id}/`, {
|
|
183
|
+
method: "DELETE",
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Correspondent operations
|
|
188
|
+
async getCorrespondents(): Promise<GetCorrespondentsResponse> {
|
|
189
|
+
return this.request<GetCorrespondentsResponse>("/correspondents/");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async createCorrespondent(
|
|
193
|
+
data: Partial<Correspondent>
|
|
194
|
+
): Promise<Correspondent> {
|
|
195
|
+
return this.request<Correspondent>("/correspondents/", {
|
|
196
|
+
method: "POST",
|
|
197
|
+
body: JSON.stringify(data),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async updateCorrespondent(
|
|
202
|
+
id: number,
|
|
203
|
+
data: Partial<Correspondent>
|
|
204
|
+
): Promise<Correspondent> {
|
|
205
|
+
return this.request<Correspondent>(`/correspondents/${id}/`, {
|
|
206
|
+
method: "PUT",
|
|
207
|
+
body: JSON.stringify(data),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async deleteCorrespondent(id: number): Promise<void> {
|
|
212
|
+
return this.request<void>(`/correspondents/${id}/`, {
|
|
213
|
+
method: "DELETE",
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Document type operations
|
|
218
|
+
async getDocumentTypes(): Promise<GetDocumentTypesResponse> {
|
|
219
|
+
return this.request<GetDocumentTypesResponse>("/document_types/");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async createDocumentType(data: Partial<DocumentType>): Promise<DocumentType> {
|
|
223
|
+
return this.request<DocumentType>("/document_types/", {
|
|
224
|
+
method: "POST",
|
|
225
|
+
body: JSON.stringify(data),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async updateDocumentType(
|
|
230
|
+
id: number,
|
|
231
|
+
data: Partial<DocumentType>
|
|
232
|
+
): Promise<DocumentType> {
|
|
233
|
+
return this.request<DocumentType>(`/document_types/${id}/`, {
|
|
234
|
+
method: "PUT",
|
|
235
|
+
body: JSON.stringify(data),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async deleteDocumentType(id: number): Promise<void> {
|
|
240
|
+
return this.request<void>(`/document_types/${id}/`, {
|
|
241
|
+
method: "DELETE",
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Bulk object operations
|
|
246
|
+
async bulkEditObjects(objects, objectType, operation, parameters = {}) {
|
|
247
|
+
return this.request("/bulk_edit_objects/", {
|
|
248
|
+
method: "POST",
|
|
249
|
+
body: JSON.stringify({
|
|
250
|
+
objects,
|
|
251
|
+
object_type: objectType,
|
|
252
|
+
operation,
|
|
253
|
+
...parameters,
|
|
254
|
+
}),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|