mirror of
https://github.com/papra-hq/papra.git
synced 2025-12-18 12:25:44 -06:00
Compare commits
5 Commits
@papra/doc
...
n8n-nodes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5d951cc82 | ||
|
|
47f9c5b186 | ||
|
|
0b97e58785 | ||
|
|
d51779aeb8 | ||
|
|
8f30ec0281 |
5
.changeset/solid-items-warn.md
Normal file
5
.changeset/solid-items-warn.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"n8n-nodes-papra": major
|
||||
---
|
||||
|
||||
Added n8n nodes package for Papra
|
||||
41
.github/workflows/ci-packages-n8n-nodes.yaml
vendored
Normal file
41
.github/workflows/ci-packages-n8n-nodes.yaml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: CI - N8N Nodes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
ci-packages-n8n-nodes:
|
||||
name: CI - N8N Nodes
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: packages/n8n-nodes
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm i
|
||||
|
||||
- name: Run linters
|
||||
run: pnpm lint
|
||||
|
||||
- name: Type check
|
||||
run: pnpm typecheck
|
||||
|
||||
# - name: Run unit test
|
||||
# run: pnpm test
|
||||
|
||||
- name: Build the app
|
||||
run: pnpm build
|
||||
74
packages/n8n-nodes/README.md
Normal file
74
packages/n8n-nodes/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# n8n Integration
|
||||
|
||||
A community node package that integrates [Papra](https://papra.app) (the document archiving platform) with [n8n](https://n8n.io), enabling you to automate document management workflows.
|
||||
|
||||
## Installation
|
||||
|
||||
1. In your n8n instance, go to **Settings** → **Community Nodes**
|
||||
2. Click **Install** and enter: `@papra/n8n-nodes-papra`
|
||||
3. Install the package and restart your n8n instance
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Create API Credentials
|
||||
Before using this integration, you need to create API credentials in your Papra workspace:
|
||||
|
||||
1. Log in to your Papra instance
|
||||
2. Navigate to **Settings** → **API Keys**
|
||||
3. Click **Create New API Key**
|
||||
4. Copy the generated API key and your Organization ID (from the url)
|
||||
|
||||
For detailed instructions, visit the [Papra API documentation](https://docs.papra.app/resources/api-endpoints/#authentication).
|
||||
|
||||
### 2. Configure n8n Credentials
|
||||
1. In n8n, create a new workflow
|
||||
2. Add a Papra node
|
||||
3. Create new credentials with:
|
||||
- **Papra API URL**: `https://api.papra.app` (or your self-hosted instance URL)
|
||||
- **Organization ID**: Your organization ID from Papra
|
||||
- **API Key**: Your generated API key
|
||||
|
||||
## Available Operations
|
||||
|
||||
| Resource | Operations |
|
||||
|----------|------------|
|
||||
| Document | `create`, `list`, `get`, `update`, `remove`, `get_file`, `get_activity` |
|
||||
| Tag | Standard CRUD operations |
|
||||
| Document Tag | Link/unlink tags to/from documents |
|
||||
| Statistics | Retrieve workspace analytics |
|
||||
| Trash | List deleted documents |
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
- Node.js 20.15 or higher
|
||||
- pnpm package manager
|
||||
- n8n instance for testing
|
||||
- you can use `pnpx n8n` or `pnpm i -g n8n` command to install n8n globally
|
||||
|
||||
### Testing the Integration
|
||||
|
||||
#### Option 1: Local n8n Instance
|
||||
1. Build this package:
|
||||
```bash
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
2. Link the package to your local n8n:
|
||||
```bash
|
||||
# Navigate to your n8n nodes directory
|
||||
cd ~/.n8n/nodes
|
||||
|
||||
# Install the package locally
|
||||
npm install /path/to/papra/packages/n8n-nodes
|
||||
```
|
||||
|
||||
3. Start n8n:
|
||||
```bash
|
||||
npx n8n
|
||||
```
|
||||
|
||||
4. In n8n, create a new workflow and search for "Papra" to find the node
|
||||
|
||||
#### Option 2: Docker
|
||||
Build a custom n8n Docker image with the Papra node included. Follow the [n8n documentation](https://docs.n8n.io/integrations/creating-nodes/deploy/install-private-nodes/#install-your-node-in-a-docker-n8n-instance) for detailed instructions.
|
||||
41
packages/n8n-nodes/credentials/PapraApi.credentials.ts
Normal file
41
packages/n8n-nodes/credentials/PapraApi.credentials.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export class PapraApi implements ICredentialType {
|
||||
name = 'papraApi';
|
||||
displayName = 'Papra API';
|
||||
documentationUrl = 'https://docs.papra.app/resources/api-endpoints/#authentication';
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
name: 'url',
|
||||
displayName: 'Papra API URL',
|
||||
default: 'https://api.papra.app',
|
||||
required: true,
|
||||
type: 'string',
|
||||
validateType: 'url',
|
||||
},
|
||||
{
|
||||
name: 'organization_id',
|
||||
displayName: 'Organization ID',
|
||||
default: '',
|
||||
required: true,
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'apiKey',
|
||||
displayName: 'Papra API Key',
|
||||
default: '',
|
||||
required: true,
|
||||
type: 'string',
|
||||
typeOptions: { password: true },
|
||||
},
|
||||
];
|
||||
|
||||
authenticate: IAuthenticateGeneric = {
|
||||
type: 'generic',
|
||||
properties: {
|
||||
headers: {
|
||||
Authorization: '=Bearer {{$credentials.apiKey}}',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
24
packages/n8n-nodes/eslint.config.js
Normal file
24
packages/n8n-nodes/eslint.config.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import antfu from '@antfu/eslint-config';
|
||||
|
||||
export default antfu({
|
||||
stylistic: {
|
||||
semi: true,
|
||||
},
|
||||
|
||||
// TODO: include the n8n rules package when it's eslint-9 ready
|
||||
// https://github.com/ivov/eslint-plugin-n8n-nodes-base/issues/196
|
||||
|
||||
rules: {
|
||||
// To allow export on top of files
|
||||
'ts/no-use-before-define': ['error', { allowNamedExports: true, functions: false }],
|
||||
'curly': ['error', 'all'],
|
||||
'vitest/consistent-test-it': ['error', { fn: 'test' }],
|
||||
'ts/consistent-type-definitions': ['error', 'type'],
|
||||
'style/brace-style': ['error', '1tbs', { allowSingleLine: false }],
|
||||
'unused-imports/no-unused-vars': ['error', {
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
}],
|
||||
},
|
||||
});
|
||||
16
packages/n8n-nodes/gulpfile.js
Normal file
16
packages/n8n-nodes/gulpfile.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const path = require('node:path');
|
||||
const { task, src, dest } = require('gulp');
|
||||
|
||||
task('build:icons', copyIcons);
|
||||
|
||||
function copyIcons() {
|
||||
const nodeSource = path.resolve('nodes', '**', '*.{png,svg}');
|
||||
const nodeDestination = path.resolve('dist', 'nodes');
|
||||
|
||||
src(nodeSource).pipe(dest(nodeDestination));
|
||||
|
||||
const credSource = path.resolve('credentials', '**', '*.{png,svg}');
|
||||
const credDestination = path.resolve('dist', 'credentials');
|
||||
|
||||
return src(credSource).pipe(dest(credDestination));
|
||||
}
|
||||
0
packages/n8n-nodes/index.js
Normal file
0
packages/n8n-nodes/index.js
Normal file
18
packages/n8n-nodes/nodes/Papra.node.json
Normal file
18
packages/n8n-nodes/nodes/Papra.node.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.papra",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Data & Storage"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.papra.app/resources/api-endpoints/#authentication"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.papra.app/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
24
packages/n8n-nodes/nodes/Papra.node.ts
Normal file
24
packages/n8n-nodes/nodes/Papra.node.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { INodeTypeBaseDescription, IVersionedNodeType } from 'n8n-workflow';
|
||||
import { VersionedNodeType } from 'n8n-workflow';
|
||||
|
||||
import { PapraV1 } from './v1/PapraV1.node';
|
||||
|
||||
export class Papra extends VersionedNodeType {
|
||||
constructor() {
|
||||
const baseDescription: INodeTypeBaseDescription = {
|
||||
displayName: 'Papra',
|
||||
name: 'papra',
|
||||
icon: 'file:papra.svg',
|
||||
group: ['input'],
|
||||
description: 'Read, update, write and delete data from Papra',
|
||||
defaultVersion: 1,
|
||||
usableAsTool: true,
|
||||
};
|
||||
|
||||
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
|
||||
1: new PapraV1(baseDescription),
|
||||
};
|
||||
|
||||
super(nodeVersions, baseDescription);
|
||||
}
|
||||
}
|
||||
31
packages/n8n-nodes/nodes/v1/PapraV1.node.ts
Normal file
31
packages/n8n-nodes/nodes/v1/PapraV1.node.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeBaseDescription,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { router } from './actions/router';
|
||||
import * as version from './actions/version';
|
||||
import { listSearch } from './methods';
|
||||
|
||||
export class PapraV1 implements INodeType {
|
||||
description: INodeTypeDescription;
|
||||
|
||||
constructor(baseDescription: INodeTypeBaseDescription) {
|
||||
this.description = {
|
||||
...baseDescription,
|
||||
...version.description,
|
||||
usableAsTool: true,
|
||||
};
|
||||
}
|
||||
|
||||
methods = {
|
||||
listSearch,
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
return await router.call(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
|
||||
import { Buffer } from 'node:buffer';
|
||||
import FormData from 'form-data';
|
||||
import { apiRequest } from '../../transport/index.js';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Input Binary Field',
|
||||
name: 'binary_property_name',
|
||||
default: 'data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
hint: 'The name of the input field containing the file data to be processed',
|
||||
required: true,
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additional_fields',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Field',
|
||||
options: [
|
||||
{
|
||||
displayName: 'OCR languages',
|
||||
name: 'ocr_languages',
|
||||
default: '',
|
||||
description: 'The languages of the document',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const endpoint = `/documents`;
|
||||
const formData = new FormData();
|
||||
|
||||
const binaryPropertyName = this.getNodeParameter('binary_property_name', itemIndex) as string;
|
||||
const binaryData = this.helpers.assertBinaryData(itemIndex, binaryPropertyName);
|
||||
const data = binaryData.id
|
||||
? await this.helpers.getBinaryStream(binaryData.id)
|
||||
: Buffer.from(binaryData.data, 'base64');
|
||||
|
||||
formData.append('file', data, {
|
||||
filename: binaryData.fileName,
|
||||
contentType: binaryData.mimeType,
|
||||
});
|
||||
|
||||
const additionalFields = this.getNodeParameter('additional_fields', itemIndex) as any;
|
||||
Object.entries({
|
||||
ocrLanguages: additionalFields.ocr_languages,
|
||||
})
|
||||
.filter(([, value]) => value !== undefined && value !== '')
|
||||
.forEach(([key, value]) => {
|
||||
formData.append(key, value);
|
||||
});
|
||||
|
||||
const response = (await apiRequest.call(
|
||||
this,
|
||||
itemIndex,
|
||||
'POST',
|
||||
endpoint,
|
||||
undefined,
|
||||
undefined,
|
||||
{ headers: formData.getHeaders(), formData },
|
||||
)) as any;
|
||||
|
||||
return { json: { results: [response] } };
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as get_activity from './get_activity.operation';
|
||||
import * as get_file from './get_file.operation';
|
||||
import * as list from './list.operation';
|
||||
import * as remove from './remove.operation';
|
||||
import * as update from './update.operation';
|
||||
|
||||
export {
|
||||
create,
|
||||
get,
|
||||
get_activity,
|
||||
get_file,
|
||||
list,
|
||||
remove,
|
||||
update,
|
||||
};
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
default: 'list',
|
||||
displayOptions: {
|
||||
show: { resource: ['document'] },
|
||||
},
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Create a document',
|
||||
value: 'create',
|
||||
action: 'Create a new document',
|
||||
},
|
||||
{
|
||||
name: 'List documents',
|
||||
value: 'list',
|
||||
action: 'List all documents',
|
||||
},
|
||||
{
|
||||
name: 'Update a document',
|
||||
value: 'update',
|
||||
action: 'Update a document',
|
||||
},
|
||||
{
|
||||
name: 'Get a document',
|
||||
value: 'get',
|
||||
action: 'Get a document',
|
||||
},
|
||||
{
|
||||
name: 'Get the document file',
|
||||
value: 'get_file',
|
||||
action: 'Get the file of the document',
|
||||
},
|
||||
{
|
||||
name: 'Delete a document',
|
||||
value: 'remove',
|
||||
action: 'Delete a document',
|
||||
},
|
||||
{
|
||||
name: 'Get the document activity log',
|
||||
value: 'get_activity',
|
||||
action: 'Get the activity log of a document',
|
||||
},
|
||||
|
||||
],
|
||||
type: 'options',
|
||||
},
|
||||
...create.description,
|
||||
...list.description,
|
||||
...update.description,
|
||||
...get.description,
|
||||
...get_file.description,
|
||||
...get_activity.description,
|
||||
...remove.description,
|
||||
];
|
||||
@@ -0,0 +1,83 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Document...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'documentSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Document ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
placeholder: `Enter Document URL...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
errorMessage: 'The URL must be a valid Papra document URL (e.g. https://papra.example.com/organizations/org_xxx/documents/doc_xxx?tab=info)',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the document',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
|
||||
const endpoint = `/documents/${id}`;
|
||||
const response = (await apiRequest.call(this, itemIndex, 'GET', endpoint)) as any;
|
||||
|
||||
return { json: { results: [response] } };
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequestPaginated } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document'],
|
||||
operation: ['get_activity'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Document...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'documentSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Document ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
placeholder: `Enter Document URL...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
errorMessage: 'The URL must be a valid Papra document URL (e.g. https://papra.example.com/organizations/org_xxx/documents/doc_xxx?tab=info)',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the document',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const endpoint = `/documents/${id}/activity`;
|
||||
const responses = (await apiRequestPaginated.call(this, itemIndex, 'GET', endpoint)) as any[];
|
||||
|
||||
const statusCode = responses.reduce((acc, response) => acc + response.statusCode, 0) / responses.length;
|
||||
|
||||
if (statusCode !== 200) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The documents you are requesting could not be found`,
|
||||
{
|
||||
description: JSON.stringify(
|
||||
responses.map(response => response?.body?.details ?? response?.statusMessage),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
return {
|
||||
json: { results: responses.flatMap(response => response.body.activities) },
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { Buffer } from 'node:buffer';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document'],
|
||||
operation: ['get_file'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Document...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'documentSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Document ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
placeholder: `Enter Document URL...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
errorMessage: 'The URL must be a valid Papra document URL (e.g. https://papra.example.com/organizations/org_xxx/documents/doc_xxx?tab=info)',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the document',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const endpoint = `/documents/${id}`;
|
||||
const preview = (await apiRequest.call(
|
||||
this,
|
||||
itemIndex,
|
||||
'GET',
|
||||
`${endpoint}/file`,
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
json: false,
|
||||
encoding: null,
|
||||
resolveWithFullResponse: true,
|
||||
},
|
||||
)) as any;
|
||||
|
||||
// TODO: fix
|
||||
const filename = preview.headers['content-disposition']
|
||||
?.match(/filename="(?:b['"])?([^"]+)['"]?"/)?.[1]
|
||||
?.replace(/^['"]|['"]$/g, '') ?? `${id}.pdf`;
|
||||
|
||||
const mimeType = preview.headers['content-type'];
|
||||
|
||||
return {
|
||||
json: {},
|
||||
binary: {
|
||||
data: await this.helpers.prepareBinaryData(
|
||||
Buffer.from(preview.body),
|
||||
filename,
|
||||
mimeType,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequestPaginated } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const endpoint = '/documents';
|
||||
const responses = (await apiRequestPaginated.call(this, itemIndex, 'GET', endpoint)) as any[];
|
||||
|
||||
const statusCode = responses.reduce((acc, response) => acc + response.statusCode, 0) / responses.length;
|
||||
|
||||
if (statusCode !== 200) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The documents you are requesting could not be found`,
|
||||
{
|
||||
description: JSON.stringify(
|
||||
responses.map(response => response?.body?.error?.message ?? response?.error?.code),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
json: { results: responses.flatMap(response => response.body.documents) },
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document'],
|
||||
operation: ['remove'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Document...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'documentSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Document ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
placeholder: `Enter Document URL...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
errorMessage: 'The URL must be a valid Papra document URL (e.g. https://papra.example.com/organizations/org_xxx/documents/doc_xxx?tab=info)',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the document',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const endpoint = `/documents/${id}`;
|
||||
await apiRequest.call(this, itemIndex, 'DELETE', endpoint);
|
||||
|
||||
return { json: { results: [true] } };
|
||||
}
|
||||
130
packages/n8n-nodes/nodes/v1/actions/document/update.operation.ts
Normal file
130
packages/n8n-nodes/nodes/v1/actions/document/update.operation.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
description: 'ID of the document',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
hint: 'The ID of the document',
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Document...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'documentSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Document ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
placeholder: `Enter Document URL...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
errorMessage: 'The URL must be a valid Papra document URL (e.g. https://papra.example.com/organizations/org_xxx/documents/doc_xxx?tab=info)',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the document',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
{
|
||||
displayName: 'Update fields',
|
||||
name: 'update_fields',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
default: '',
|
||||
description: 'The name of the document',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
displayName: 'Content',
|
||||
name: 'content',
|
||||
default: '',
|
||||
description: 'The content of the document, for search purposes',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
placeholder: 'Add Field',
|
||||
type: 'collection',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const endpoint = `/documents/${id}`;
|
||||
|
||||
const updateFields = this.getNodeParameter('update_fields', itemIndex, {}) as any;
|
||||
|
||||
const body: { [key: string]: any } = {};
|
||||
|
||||
for (const key of Object.keys(updateFields)) {
|
||||
if (updateFields[key] !== null && updateFields[key] !== undefined) {
|
||||
body[key] = updateFields[key];
|
||||
}
|
||||
}
|
||||
|
||||
const response = (await apiRequest.call(
|
||||
this,
|
||||
itemIndex,
|
||||
'PATCH',
|
||||
endpoint,
|
||||
body,
|
||||
)) as any;
|
||||
|
||||
return { json: { results: [response] } };
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import type { IExecuteFunctions, INodeExecutionData, INodeParameterResourceLocator, INodeProperties } from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document_tag'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Document...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'documentSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Document ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
placeholder: `Enter Document URL...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
errorMessage: 'The URL must be a valid Papra document URL (e.g. https://papra.example.com/organizations/org_xxx/documents/doc_xxx?tab=info)',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the document',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
{
|
||||
displayName: 'Tag ID',
|
||||
name: 'tag_id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document_tag'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Tag...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'tagSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Tag ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9]+$',
|
||||
errorMessage: 'The ID must be an alphanumeric string',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the tag',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const document_id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const tag_id = (this.getNodeParameter('tag_id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const endpoint = `/documents/${document_id}/tags`;
|
||||
|
||||
const body = {
|
||||
tagId: tag_id,
|
||||
};
|
||||
|
||||
await apiRequest.call(this, itemIndex, 'POST', endpoint, body);
|
||||
|
||||
return {
|
||||
json: { results: [true] },
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as remove from './remove.operation';
|
||||
|
||||
export { create, remove };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
default: 'list',
|
||||
displayOptions: {
|
||||
show: { resource: ['document_tag'] },
|
||||
},
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Add a tag to a document',
|
||||
value: 'create',
|
||||
action: 'Add a tag to a document',
|
||||
},
|
||||
{
|
||||
name: 'Remove a tag from a document',
|
||||
value: 'remove',
|
||||
action: 'Remove a tag from a document',
|
||||
},
|
||||
],
|
||||
type: 'options',
|
||||
},
|
||||
...create.description,
|
||||
...remove.description,
|
||||
];
|
||||
@@ -0,0 +1,126 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document_tag'],
|
||||
operation: ['remove'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Document...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'documentSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Document ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
placeholder: `Enter Document URL...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
errorMessage: 'The URL must be a valid Papra document URL (e.g. https://papra.example.com/organizations/org_xxx/documents/doc_xxx?tab=info)',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: '^(?:http|https)://(?:.+?)/documents/([a-zA-Z0-9_]+)/?(?:\\?.*)?$',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the document',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
{
|
||||
displayName: 'Tag ID',
|
||||
name: 'tag_id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['document_tag'],
|
||||
operation: ['remove'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Tag...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'tagSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Tag ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9]+$',
|
||||
errorMessage: 'The ID must be an alphanumeric string',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the tag',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const document_id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const tag_id = (this.getNodeParameter('tag_id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const endpoint = `/documents/${document_id}/tags/${tag_id}`;
|
||||
|
||||
await apiRequest.call(this, itemIndex, 'DELETE', endpoint);
|
||||
|
||||
return { json: { results: [true] } };
|
||||
}
|
||||
9
packages/n8n-nodes/nodes/v1/actions/node.type.ts
Normal file
9
packages/n8n-nodes/nodes/v1/actions/node.type.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { AllEntities } from 'n8n-workflow';
|
||||
|
||||
export type PapraType = AllEntities<{
|
||||
statistics: 'get';
|
||||
document: 'create' | 'list' | 'update' | 'get' | 'get_file' | 'get_activity' | 'remove';
|
||||
document_tag: 'create' | 'remove';
|
||||
tag: 'create' | 'list' | 'update' | 'remove';
|
||||
trash: 'list';
|
||||
}>;
|
||||
54
packages/n8n-nodes/nodes/v1/actions/router.ts
Normal file
54
packages/n8n-nodes/nodes/v1/actions/router.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
||||
import type { PapraType } from './node.type.ts';
|
||||
|
||||
import * as document from './document/document.resource';
|
||||
import * as document_tag from './document_tag/document_tag.resource';
|
||||
import * as statistics from './statistics/statistics.resource';
|
||||
import * as tag from './tag/tag.resource';
|
||||
import * as trash from './trash/trash.resource';
|
||||
|
||||
export async function router(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
|
||||
for (let itemIndex = 0; itemIndex < this.getInputData().length; itemIndex++) {
|
||||
const resource = this.getNodeParameter<PapraType>('resource', itemIndex);
|
||||
const operation = this.getNodeParameter('operation', itemIndex);
|
||||
const papraNodeData = { resource, operation } as PapraType;
|
||||
|
||||
try {
|
||||
switch (papraNodeData.resource) {
|
||||
case 'statistics':
|
||||
returnData.push(await statistics[papraNodeData.operation].execute.call(this, itemIndex));
|
||||
break;
|
||||
case 'document':
|
||||
returnData.push(
|
||||
await document[papraNodeData.operation].execute.call(this, itemIndex),
|
||||
);
|
||||
break;
|
||||
case 'document_tag':
|
||||
returnData.push(
|
||||
await document_tag[papraNodeData.operation].execute.call(this, itemIndex),
|
||||
);
|
||||
break;
|
||||
case 'tag':
|
||||
returnData.push(
|
||||
await tag[papraNodeData.operation].execute.call(this, itemIndex),
|
||||
);
|
||||
break;
|
||||
case 'trash':
|
||||
returnData.push(
|
||||
await trash[papraNodeData.operation].execute.call(this, itemIndex),
|
||||
);
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.description?.includes('cannot accept the provided value')) {
|
||||
error.description += '. Consider using \'Typecast\' option';
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return [returnData];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const endpoint = `/documents/statistics`;
|
||||
const response = (await apiRequest.call(this, itemIndex, 'GET', endpoint)) as any;
|
||||
|
||||
return {
|
||||
json: response,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as get from './get.operation';
|
||||
|
||||
export { get };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
default: 'get',
|
||||
displayOptions: {
|
||||
show: { resource: ['statistics'] },
|
||||
},
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Get statistics of the organization',
|
||||
value: 'get',
|
||||
action: 'Get statistics',
|
||||
},
|
||||
],
|
||||
type: 'options',
|
||||
},
|
||||
...get.description,
|
||||
];
|
||||
60
packages/n8n-nodes/nodes/v1/actions/tag/create.operation.ts
Normal file
60
packages/n8n-nodes/nodes/v1/actions/tag/create.operation.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['tag'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
placeholder: 'Name of the tag',
|
||||
required: true,
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Color',
|
||||
name: 'color',
|
||||
default: '#000000',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['tag'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
type: 'color',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['tag'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
placeholder: 'Description of the tag',
|
||||
type: 'string',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const endpoint = `/tags`;
|
||||
const body = {
|
||||
name: this.getNodeParameter('name', itemIndex),
|
||||
color: this.getNodeParameter('color', itemIndex)?.toString().toLowerCase(),
|
||||
description: this.getNodeParameter('description', itemIndex, ''),
|
||||
};
|
||||
|
||||
const response = (await apiRequest.call(this, itemIndex, 'POST', endpoint, body)) as any;
|
||||
|
||||
return { json: { results: [response] } };
|
||||
}
|
||||
20
packages/n8n-nodes/nodes/v1/actions/tag/list.operation.ts
Normal file
20
packages/n8n-nodes/nodes/v1/actions/tag/list.operation.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const endpoint = '/tags';
|
||||
const response = (await apiRequest.call(this, itemIndex, 'GET', endpoint)) as any;
|
||||
|
||||
return {
|
||||
json: { results: response.tags },
|
||||
};
|
||||
}
|
||||
63
packages/n8n-nodes/nodes/v1/actions/tag/remove.operation.ts
Normal file
63
packages/n8n-nodes/nodes/v1/actions/tag/remove.operation.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['tag'],
|
||||
operation: ['remove'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Tag...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'tagSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Tag ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the tag',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const endpoint = `/tags/${id}`;
|
||||
await apiRequest.call(this, itemIndex, 'DELETE', endpoint);
|
||||
|
||||
return { json: { results: [true] } };
|
||||
}
|
||||
47
packages/n8n-nodes/nodes/v1/actions/tag/tag.resource.ts
Normal file
47
packages/n8n-nodes/nodes/v1/actions/tag/tag.resource.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as list from './list.operation';
|
||||
import * as remove from './remove.operation';
|
||||
import * as update from './update.operation';
|
||||
|
||||
export { create, list, remove, update };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
default: 'list',
|
||||
displayOptions: {
|
||||
show: { resource: ['tag'] },
|
||||
},
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Create a tag',
|
||||
value: 'create',
|
||||
action: 'Create a new tag',
|
||||
},
|
||||
{
|
||||
name: 'Delete a tag',
|
||||
value: 'remove',
|
||||
action: 'Delete a tag',
|
||||
},
|
||||
{
|
||||
name: 'List tags',
|
||||
value: 'list',
|
||||
action: 'List all tags',
|
||||
},
|
||||
{
|
||||
name: 'Update a tag',
|
||||
value: 'update',
|
||||
action: 'Update a tag',
|
||||
},
|
||||
],
|
||||
type: 'options',
|
||||
},
|
||||
...create.description,
|
||||
...list.description,
|
||||
...remove.description,
|
||||
...update.description,
|
||||
];
|
||||
111
packages/n8n-nodes/nodes/v1/actions/tag/update.operation.ts
Normal file
111
packages/n8n-nodes/nodes/v1/actions/tag/update.operation.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
default: { mode: 'list', value: '' },
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['tag'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
placeholder: `Select a Tag...`,
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'tagSearch',
|
||||
searchFilterRequired: false,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: `Enter Tag ID...`,
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[a-zA-Z0-9_]+$',
|
||||
errorMessage: 'The ID must be valid',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
placeholder: 'ID of the tag',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
{
|
||||
displayName: 'Update fields',
|
||||
name: 'update_fields',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['tag'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
placeholder: 'Name of the tag',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Color',
|
||||
name: 'color',
|
||||
default: '#000000',
|
||||
type: 'color',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
default: '',
|
||||
placeholder: 'Description of the tag',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
placeholder: 'Add Field',
|
||||
type: 'collection',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const id = (this.getNodeParameter('id', itemIndex) as INodeParameterResourceLocator).value;
|
||||
const endpoint = `/tags/${id}`;
|
||||
|
||||
const updateFields = this.getNodeParameter('update_fields', itemIndex, {}) as {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
const body: { [key: string]: any } = {};
|
||||
|
||||
for (const key of Object.keys(updateFields)) {
|
||||
if (updateFields[key] !== null && updateFields[key] !== undefined) {
|
||||
body[key] = key === 'color' ? updateFields[key].toString().toLowerCase() : updateFields[key];
|
||||
}
|
||||
}
|
||||
|
||||
const response = (await apiRequest.call(this, itemIndex, 'PUT', endpoint, body)) as any;
|
||||
|
||||
return { json: { results: [response] } };
|
||||
}
|
||||
37
packages/n8n-nodes/nodes/v1/actions/trash/list.operation.ts
Normal file
37
packages/n8n-nodes/nodes/v1/actions/trash/list.operation.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequestPaginated } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = [];
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData> {
|
||||
const endpoint = `/documents/deleted`;
|
||||
const responses = (await apiRequestPaginated.call(this, itemIndex, 'GET', endpoint)) as any[];
|
||||
|
||||
const statusCode = responses.reduce((acc, response) => acc + response.statusCode, 0) / responses.length;
|
||||
|
||||
if (statusCode !== 200) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The trash you are requesting could not be found`,
|
||||
{
|
||||
description: JSON.stringify(
|
||||
responses.map(response => response?.body?.details ?? response?.statusMessage),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
json: { results: responses.flatMap(response => response.body.documents) },
|
||||
};
|
||||
}
|
||||
26
packages/n8n-nodes/nodes/v1/actions/trash/trash.resource.ts
Normal file
26
packages/n8n-nodes/nodes/v1/actions/trash/trash.resource.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as list from './list.operation';
|
||||
|
||||
export { list };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
default: 'list',
|
||||
displayOptions: {
|
||||
show: { resource: ['trash'] },
|
||||
},
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'List trash',
|
||||
value: 'list',
|
||||
action: 'List all trash',
|
||||
},
|
||||
],
|
||||
type: 'options',
|
||||
},
|
||||
...list.description,
|
||||
];
|
||||
66
packages/n8n-nodes/nodes/v1/actions/version.ts
Normal file
66
packages/n8n-nodes/nodes/v1/actions/version.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
import * as document from './document/document.resource';
|
||||
import * as document_tag from './document_tag/document_tag.resource';
|
||||
import * as statistics from './statistics/statistics.resource';
|
||||
import * as tag from './tag/tag.resource';
|
||||
import * as trash from './trash/trash.resource';
|
||||
|
||||
export const description: INodeTypeDescription = {
|
||||
displayName: 'Papra',
|
||||
name: 'papra',
|
||||
icon: 'file:papra.svg',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{ $parameter.operation + ": " + $parameter.resource }}',
|
||||
description: 'Consume documents and metadata from Papra API',
|
||||
defaults: { name: 'Papra' },
|
||||
|
||||
credentials: [{ name: 'papraApi', required: true }],
|
||||
|
||||
inputs: [NodeConnectionType.Main],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
default: 'document',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Statistics',
|
||||
value: 'statistics',
|
||||
description: 'Statistics about the documents',
|
||||
},
|
||||
{
|
||||
name: 'Document',
|
||||
value: 'document',
|
||||
description: 'Scanned document or file saved in Papra',
|
||||
},
|
||||
{
|
||||
name: 'Document tag',
|
||||
value: 'document_tag',
|
||||
description: 'Associate a tag to a document',
|
||||
},
|
||||
{
|
||||
name: 'Tag',
|
||||
value: 'tag',
|
||||
description: 'Label for documents',
|
||||
},
|
||||
{
|
||||
name: 'Trash',
|
||||
value: 'trash',
|
||||
description: 'All deleted documents',
|
||||
},
|
||||
],
|
||||
type: 'options',
|
||||
},
|
||||
...document.description,
|
||||
...tag.description,
|
||||
...trash.description,
|
||||
...statistics.description,
|
||||
...document_tag.description,
|
||||
],
|
||||
};
|
||||
1
packages/n8n-nodes/nodes/v1/methods/index.ts
Normal file
1
packages/n8n-nodes/nodes/v1/methods/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * as listSearch from './listSearch';
|
||||
65
packages/n8n-nodes/nodes/v1/methods/listSearch.ts
Normal file
65
packages/n8n-nodes/nodes/v1/methods/listSearch.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import type {
|
||||
ILoadOptionsFunctions,
|
||||
INodeListSearchResult,
|
||||
} from 'n8n-workflow';
|
||||
import { apiRequest, apiRequestPaginated } from '../transport';
|
||||
|
||||
export async function documentSearch(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
if (filter && filter.trim().length >= 3) {
|
||||
const endpoint = `/documents/search`;
|
||||
const query = { searchQuery: filter };
|
||||
|
||||
const responses = (await apiRequestPaginated.call(
|
||||
this,
|
||||
0,
|
||||
'GET',
|
||||
endpoint,
|
||||
undefined,
|
||||
query,
|
||||
)) as {
|
||||
body: {
|
||||
documents: { id: number; name: string }[];
|
||||
};
|
||||
}[];
|
||||
|
||||
const [result] = responses;
|
||||
return {
|
||||
results: result
|
||||
? result.body.documents.map(item => ({
|
||||
name: item.name,
|
||||
value: item.id,
|
||||
}))
|
||||
: [],
|
||||
};
|
||||
}
|
||||
|
||||
const endpoint = `/documents`;
|
||||
const response = (await apiRequest.call(this, 0, 'GET', endpoint, {}, { pageSize: 30, pageIndex: 0 })) as { documents: { id: number; name: string }[] };
|
||||
|
||||
return {
|
||||
results: response.documents.map(item => ({
|
||||
name: item.name,
|
||||
value: item.id,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export async function tagSearch(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const endpoint = `/tags`;
|
||||
const response = (await apiRequest.call(this, 0, 'GET', endpoint)) as { tags: { id: number; name: string }[] };
|
||||
|
||||
return {
|
||||
results: response.tags
|
||||
.filter(item => !filter || item.name.includes(filter))
|
||||
.map(item => ({
|
||||
name: item.name.trim().length > 80 ? `${item.name.trim().slice(0, 80)}...` : item.name.trim(),
|
||||
value: item.id,
|
||||
})),
|
||||
};
|
||||
}
|
||||
1
packages/n8n-nodes/nodes/v1/papra.svg
Normal file
1
packages/n8n-nodes/nodes/v1/papra.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none" stroke="#403a3a" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2zM9 9h1m-1 4h6m-6 4h6"/></g></svg>
|
||||
|
After Width: | Height: | Size: 318 B |
96
packages/n8n-nodes/nodes/v1/transport/index.ts
Normal file
96
packages/n8n-nodes/nodes/v1/transport/index.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IHttpRequestMethods,
|
||||
ILoadOptionsFunctions,
|
||||
IRequestOptions,
|
||||
PaginationOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function apiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
itemIndex: number,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
query?: IDataObject,
|
||||
option: IRequestOptions = {},
|
||||
): Promise<unknown> {
|
||||
const queryParams = query || {};
|
||||
|
||||
const credentials = await this.getCredentials('papraApi');
|
||||
const options: IRequestOptions = {
|
||||
headers: {},
|
||||
method,
|
||||
body,
|
||||
qs: queryParams,
|
||||
uri: `${credentials.url}/api/organizations/${credentials.organization_id}${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (Object.keys(option).length) {
|
||||
Object.assign(options, option);
|
||||
}
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
options.body = undefined;
|
||||
}
|
||||
|
||||
return this.helpers.requestWithAuthentication.call(
|
||||
this,
|
||||
'papraApi',
|
||||
options,
|
||||
undefined,
|
||||
itemIndex,
|
||||
);
|
||||
}
|
||||
|
||||
export async function apiRequestPaginated(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
itemIndex: number,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
query?: IDataObject,
|
||||
option: IRequestOptions = {},
|
||||
): Promise<unknown[]> {
|
||||
query = query || {};
|
||||
|
||||
const credentials = await this.getCredentials('papraApi');
|
||||
const options: IRequestOptions = {
|
||||
headers: {},
|
||||
method,
|
||||
body,
|
||||
qs: query,
|
||||
uri: `${credentials.url}/api/organizations/${credentials.organization_id}${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (Object.keys(option).length) {
|
||||
Object.assign(options, option);
|
||||
}
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
const paginationOptions: PaginationOptions = {
|
||||
// TODO: make continue condition generic
|
||||
continue: '={{ $response.body.documents && $response.body.documents.length > 0 }}',
|
||||
request: {
|
||||
qs: {
|
||||
pageSize: '={{ $request.qs.pageSize || 50 }}',
|
||||
pageIndex: '={{ $pageCount }}',
|
||||
},
|
||||
},
|
||||
requestInterval: 100,
|
||||
};
|
||||
|
||||
return this.helpers.requestWithAuthenticationPaginated.call(
|
||||
this,
|
||||
options,
|
||||
itemIndex,
|
||||
paginationOptions,
|
||||
'papraApi',
|
||||
);
|
||||
}
|
||||
61
packages/n8n-nodes/package.json
Normal file
61
packages/n8n-nodes/package.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "@papra/n8n-nodes-papra",
|
||||
"version": "0.1.0",
|
||||
"description": "n8n nodes for Papra, the document archiving platform.",
|
||||
"author": "Corentin Thomasset <corentinth@proton.me> (https://corentin.tech)",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/papra-hq/papra",
|
||||
"directory": "packages/n8n-nodes"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/papra-hq/papra/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"n8n-community-node-package",
|
||||
"n8n",
|
||||
"papra",
|
||||
"document",
|
||||
"archiving",
|
||||
"self-hosted"
|
||||
],
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.15"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"prepublishOnly": "pnpm build",
|
||||
"build": "pnpm build:clean && tsc && gulp build:icons",
|
||||
"build:clean": "rm -rf dist",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"n8n": {
|
||||
"n8nNodesApiVersion": 1,
|
||||
"credentials": [
|
||||
"dist/credentials/PapraApi.credentials.js"
|
||||
],
|
||||
"nodes": [
|
||||
"dist/nodes/Papra.node.js"
|
||||
]
|
||||
},
|
||||
"peerDependencies": {
|
||||
"n8n-workflow": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-data": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"eslint-plugin-n8n-nodes-base": "^1.16.3",
|
||||
"gulp": "^5.0.0",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:"
|
||||
}
|
||||
}
|
||||
30
packages/n8n-nodes/tsconfig.json
Normal file
30
packages/n8n-nodes/tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"target": "es2019",
|
||||
"lib": ["es2019", "es2020", "es2022.error"],
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"declaration": true,
|
||||
"outDir": "./dist/",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"credentials/**/*",
|
||||
"nodes/**/*",
|
||||
"nodes/**/*.json",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
1455
pnpm-lock.yaml
generated
1455
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user