(PS-378) Seclore custom n8n node #1
|
|
@ -1,4 +1,4 @@
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
.n8n/.env
|
.n8n
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
import {
|
|
||||||
ICredentialTestRequest,
|
|
||||||
ICredentialType,
|
|
||||||
INodeProperties,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
|
|
||||||
export class SecloreProtectApi implements ICredentialType {
|
|
||||||
name = 'secloreProtectApi';
|
|
||||||
displayName = 'Seclore Protect API';
|
|
||||||
documentationUrl = 'https://docs.seclore.com/';
|
|
||||||
properties: INodeProperties[] = [
|
|
||||||
{
|
|
||||||
displayName: 'Base URL',
|
|
||||||
name: 'baseUrl',
|
|
||||||
type: 'string',
|
|
||||||
default: 'https://api.seclore.com',
|
|
||||||
placeholder: 'https://api.seclore.com',
|
|
||||||
description: 'The base URL of your Seclore API instance',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Tenant ID',
|
|
||||||
name: 'tenantId',
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
placeholder: 'your-tenant-id',
|
|
||||||
description: 'Your Seclore tenant ID',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Tenant Secret',
|
|
||||||
name: 'tenantSecret',
|
|
||||||
type: 'string',
|
|
||||||
typeOptions: {
|
|
||||||
password: true,
|
|
||||||
},
|
|
||||||
default: '',
|
|
||||||
description: 'Your Seclore tenant secret',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Optional: Add credential test
|
|
||||||
test: ICredentialTestRequest = {
|
|
||||||
request: {
|
|
||||||
baseURL: '={{$credentials.baseUrl}}',
|
|
||||||
url: '/seclore/drm/1.0/auth/login',
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
tenantId: '={{$credentials.tenantId}}',
|
|
||||||
tenantSecret: '={{$credentials.tenantSecret}}',
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { ICredentialTestRequest, ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class SecloreProtectApi implements ICredentialType {
|
||||||
|
name = 'secloreProtectApi';
|
||||||
|
displayName = 'Seclore Protect API';
|
||||||
|
documentationUrl = 'https://docs.seclore.com/';
|
||||||
|
icon: Icon = {
|
||||||
|
light: 'file:../icons/SecloreProtect.light.svg',
|
||||||
|
dark: 'file:../icons/SecloreProtect.dark.svg',
|
||||||
|
};
|
||||||
|
properties: INodeProperties[] = [
|
||||||
|
{
|
||||||
|
displayName: 'Base URL',
|
||||||
|
name: 'baseUrl',
|
||||||
|
type: 'string',
|
||||||
|
default: 'https://api.seclore.com',
|
||||||
|
placeholder: 'https://api.seclore.com',
|
||||||
|
description: 'The base URL of your Seclore API instance',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Tenant ID',
|
||||||
|
name: 'tenantId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'your-tenant-id',
|
||||||
|
description: 'Your Seclore tenant ID',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Tenant Secret',
|
||||||
|
name: 'tenantSecret',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
password: true,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Your Seclore tenant secret',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Optional: Add credential test
|
||||||
|
test: ICredentialTestRequest = {
|
||||||
|
request: {
|
||||||
|
baseURL: '={{$credentials.baseUrl}}',
|
||||||
|
url: '/seclore/drm/1.0/auth/login',
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
tenantId: '={{$credentials.tenantId}}',
|
||||||
|
tenantSecret: '={{$credentials.tenantSecret}}',
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 506 B |
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #e6244e;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-1" d="M1410.69,1080H336.23v-225.18h983.63v-196.85H538.82c-135.08-7.21-202.59-53.13-202.59-137.81V128.33C336.23,49.07,403.68,6.3,538.62,0H1583.77V230.56H596.13v199.54h813.03c108.4,0,166.61,37.75,174.6,113.27v404.49c-8,88.11-65.68,132.14-173.08,132.14Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 506 B |
|
|
@ -1,3 +0,0 @@
|
||||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0165 0C8.94791 0 0 9.01388 0 20.1653C0 29.0792 5.73324 36.6246 13.6868 39.2952C14.6812 39.496 15.0454 38.8613 15.0454 38.3274C15.0454 37.8599 15.0126 36.2575 15.0126 34.5879C9.4445 35.79 8.28498 32.1841 8.28498 32.1841C7.39015 29.847 6.06429 29.2463 6.06429 29.2463C4.24185 28.011 6.19704 28.011 6.19704 28.011C8.21861 28.1446 9.27938 30.081 9.27938 30.081C11.0686 33.1522 13.9518 32.2844 15.1118 31.7502C15.2773 30.4481 15.8079 29.5467 16.3713 29.046C11.9303 28.5785 7.25781 26.8425 7.25781 19.0967C7.25781 16.8932 8.05267 15.0905 9.31216 13.6884C9.11344 13.1877 8.41732 11.1174 9.51128 8.34644C9.51128 8.34644 11.2014 7.81217 15.0122 10.4164C16.6438 9.97495 18.3263 9.7504 20.0165 9.74851C21.7067 9.74851 23.4295 9.98246 25.0205 10.4164C28.8317 7.81217 30.5218 8.34644 30.5218 8.34644C31.6158 11.1174 30.9192 13.1877 30.7205 13.6884C32.0132 15.0905 32.7753 16.8932 32.7753 19.0967C32.7753 26.8425 28.1028 28.5449 23.6287 29.046C24.358 29.6802 24.9873 30.882 24.9873 32.7851C24.9873 35.4893 24.9545 37.6596 24.9545 38.327C24.9545 38.8613 25.3192 39.496 26.3132 39.2956C34.2667 36.6242 39.9999 29.0792 39.9999 20.1653C40.0327 9.01388 31.052 0 20.0165 0Z" fill="white"/>
|
|
||||||
</svg>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0165 0C8.94791 0 0 9.01388 0 20.1653C0 29.0792 5.73324 36.6246 13.6868 39.2952C14.6812 39.496 15.0454 38.8613 15.0454 38.3274C15.0454 37.8599 15.0126 36.2575 15.0126 34.5879C9.4445 35.79 8.28498 32.1841 8.28498 32.1841C7.39015 29.847 6.06429 29.2463 6.06429 29.2463C4.24185 28.011 6.19704 28.011 6.19704 28.011C8.21861 28.1446 9.27938 30.081 9.27938 30.081C11.0686 33.1522 13.9518 32.2844 15.1118 31.7502C15.2773 30.4481 15.8079 29.5467 16.3713 29.046C11.9303 28.5785 7.25781 26.8425 7.25781 19.0967C7.25781 16.8932 8.05267 15.0905 9.31216 13.6884C9.11344 13.1877 8.41732 11.1174 9.51128 8.34644C9.51128 8.34644 11.2014 7.81217 15.0122 10.4164C16.6438 9.97495 18.3263 9.7504 20.0165 9.74851C21.7067 9.74851 23.4295 9.98246 25.0205 10.4164C28.8317 7.81217 30.5218 8.34644 30.5218 8.34644C31.6158 11.1174 30.9192 13.1877 30.7205 13.6884C32.0132 15.0905 32.7753 16.8932 32.7753 19.0967C32.7753 26.8425 28.1028 28.5449 23.6287 29.046C24.358 29.6802 24.9873 30.882 24.9873 32.7851C24.9873 35.4893 24.9545 37.6596 24.9545 38.327C24.9545 38.8613 25.3192 39.496 26.3132 39.2956C34.2667 36.6242 39.9999 29.0792 39.9999 20.1653C40.0327 9.01388 31.052 0 20.0165 0Z" fill="#24292F"/>
|
|
||||||
</svg>
|
|
||||||
|
|
@ -12,7 +12,8 @@ export class SecloreProtect implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Seclore Protect',
|
displayName: 'Seclore Protect',
|
||||||
name: 'secloreProtect',
|
name: 'secloreProtect',
|
||||||
icon: 'file:SecloreProtect.svg',
|
icon: 'file:../../icons/SecloreProtect.light.svg',
|
||||||
|
usableAsTool: true, // TODO: make it false/ don't allow it to be used as a tool
|
||||||
group: ['transform'],
|
group: ['transform'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '={{$parameter["operation"]}}',
|
subtitle: '={{$parameter["operation"]}}',
|
||||||
|
|
@ -39,7 +40,7 @@ export class SecloreProtect implements INodeType {
|
||||||
name: 'Protect File with HotFolder',
|
name: 'Protect File with HotFolder',
|
||||||
value: 'protectWithHotFolder',
|
value: 'protectWithHotFolder',
|
||||||
description: 'Protect a file using HotFolder ID configuration',
|
description: 'Protect a file using HotFolder ID configuration',
|
||||||
action: 'Protect file with HotFolder',
|
action: 'Protect file with hotfolder',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default: 'protectWithHotFolder',
|
default: 'protectWithHotFolder',
|
||||||
|
|
@ -129,6 +130,8 @@ export class SecloreProtect implements INodeType {
|
||||||
const operation = this.getNodeParameter('operation', 0) as string;
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
console.log('Data');
|
||||||
|
console.log(items[i]);
|
||||||
try {
|
try {
|
||||||
if (operation === 'protectWithHotFolder') {
|
if (operation === 'protectWithHotFolder') {
|
||||||
// Get parameters for this item
|
// Get parameters for this item
|
||||||
|
|
@ -148,10 +151,16 @@ export class SecloreProtect implements INodeType {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('assertBinaryData');
|
||||||
// Get input binary data
|
// Get input binary data
|
||||||
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
|
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
|
||||||
|
|
||||||
|
console.log('getBinaryDataBuffer');
|
||||||
const fileBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
const fileBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
||||||
|
|
||||||
|
console.log('fileBuffer', fileBuffer);
|
||||||
|
console.log('binaryData.fileName', binaryData.fileName);
|
||||||
|
|
||||||
// Upload the file first
|
// Upload the file first
|
||||||
const uploadResult = await fileService.uploadFile(
|
const uploadResult = await fileService.uploadFile(
|
||||||
new Uint8Array(fileBuffer),
|
new Uint8Array(fileBuffer),
|
||||||
|
|
@ -160,6 +169,8 @@ export class SecloreProtect implements INodeType {
|
||||||
retryCount,
|
retryCount,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log('File upload response', uploadResult);
|
||||||
|
|
||||||
// Protect the uploaded file with HotFolder
|
// Protect the uploaded file with HotFolder
|
||||||
const protectResult = await fileService.protectWithHotFolder(
|
const protectResult = await fileService.protectWithHotFolder(
|
||||||
{
|
{
|
||||||
|
|
@ -170,6 +181,8 @@ export class SecloreProtect implements INodeType {
|
||||||
retryCount,
|
retryCount,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log('File protect response', protectResult);
|
||||||
|
|
||||||
// Download the protected file
|
// Download the protected file
|
||||||
const protectedFileData = await fileService.downloadFile(
|
const protectedFileData = await fileService.downloadFile(
|
||||||
protectResult.fileStorageId,
|
protectResult.fileStorageId,
|
||||||
|
|
@ -177,6 +190,8 @@ export class SecloreProtect implements INodeType {
|
||||||
retryCount,
|
retryCount,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log('Protected file data', protectedFileData);
|
||||||
|
|
||||||
// Create output binary data
|
// Create output binary data
|
||||||
const outputBinaryData = await this.helpers.prepareBinaryData(
|
const outputBinaryData = await this.helpers.prepareBinaryData(
|
||||||
Buffer.from(protectedFileData),
|
Buffer.from(protectedFileData),
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,24 @@
|
||||||
import { IExecuteFunctions, IHttpRequestOptions } from 'n8n-workflow';
|
import { IExecuteFunctions, IHttpRequestOptions, NodeApiError } from 'n8n-workflow';
|
||||||
import { ILoginRequest, ILoginResponse, IRefreshTokenRequest } from './Interfaces/LoginInterfaces';
|
|
||||||
import { IErrorResponse } from './Interfaces/ErrorInterfaces';
|
import { IErrorResponse } from './Interfaces/ErrorInterfaces';
|
||||||
import { IProtectWithExternalRefIdRequest, IProtectWithExternalRefIdResponse, IProtectWithFileIdRequest, IProtectWithFileIdResponse, IProtectWithHotFolderRequest, IProtectWithHotFolderResponse } from './Interfaces/ProtectInterfaces';
|
|
||||||
import FormData from 'form-data';
|
|
||||||
import { IUnprotectRequest, IUnprotectResponse } from './Interfaces/UnprotectInterfaces';
|
|
||||||
import { IFileUploadResponse } from './Interfaces/FileStorageInterfaces';
|
import { IFileUploadResponse } from './Interfaces/FileStorageInterfaces';
|
||||||
|
import { ILoginRequest, ILoginResponse, IRefreshTokenRequest } from './Interfaces/LoginInterfaces';
|
||||||
|
import {
|
||||||
|
IProtectWithExternalRefIdRequest,
|
||||||
|
IProtectWithExternalRefIdResponse,
|
||||||
|
IProtectWithFileIdRequest,
|
||||||
|
IProtectWithFileIdResponse,
|
||||||
|
IProtectWithHotFolderRequest,
|
||||||
|
IProtectWithHotFolderResponse,
|
||||||
|
} from './Interfaces/ProtectInterfaces';
|
||||||
|
import { IUnprotectRequest, IUnprotectResponse } from './Interfaces/UnprotectInterfaces';
|
||||||
|
|
||||||
export class SecloreDRMApiService {
|
export class SecloreDRMApiService {
|
||||||
private baseUrl: string;
|
private baseUrl: string;
|
||||||
|
|
||||||
constructor(private context: IExecuteFunctions, baseUrl: string) {
|
constructor(
|
||||||
|
private context: IExecuteFunctions,
|
||||||
|
baseUrl: string,
|
||||||
|
) {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,12 +27,17 @@ export class SecloreDRMApiService {
|
||||||
* @param error - The error object from httpRequest
|
* @param error - The error object from httpRequest
|
||||||
* @param customMessages - Optional custom error messages for specific status codes
|
* @param customMessages - Optional custom error messages for specific status codes
|
||||||
*/
|
*/
|
||||||
private handleHttpError(error: any, customMessages?: { [statusCode: number]: string }): never {
|
private handleHttpError(
|
||||||
const statusCode = error.statusCode;
|
error: NodeApiError,
|
||||||
const errorResponse = error.response?.body as IErrorResponse;
|
customMessages?: { [statusCode: number]: string },
|
||||||
|
): never {
|
||||||
|
const statusCode: number = parseInt(error.httpCode ?? '0');
|
||||||
|
const errorResponse = error.errorResponse as unknown as IErrorResponse;
|
||||||
|
|
||||||
if (customMessages && customMessages[statusCode]) {
|
if (customMessages && customMessages[statusCode]) {
|
||||||
throw new Error(`${customMessages[statusCode]}: ${errorResponse?.errorMessage || 'Unknown error'}`);
|
throw new Error(
|
||||||
|
`${customMessages[statusCode]}: ${errorResponse?.errorMessage || 'Unknown error'}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default error handling
|
// Default error handling
|
||||||
|
|
@ -33,7 +47,9 @@ export class SecloreDRMApiService {
|
||||||
case 401:
|
case 401:
|
||||||
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid credentials'}`);
|
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid credentials'}`);
|
||||||
case 413:
|
case 413:
|
||||||
throw new Error(`Payload Too Large: ${errorResponse?.errorMessage || 'File size exceeds limit'}`);
|
throw new Error(
|
||||||
|
`Payload Too Large: ${errorResponse?.errorMessage || 'File size exceeds limit'}`,
|
||||||
|
);
|
||||||
case 500:
|
case 500:
|
||||||
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
|
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
|
||||||
default:
|
default:
|
||||||
|
|
@ -51,7 +67,11 @@ export class SecloreDRMApiService {
|
||||||
* @returns Promise<ILoginResponse> - Access token and refresh token
|
* @returns Promise<ILoginResponse> - Access token and refresh token
|
||||||
* @throws Error on authentication failure or server error
|
* @throws Error on authentication failure or server error
|
||||||
*/
|
*/
|
||||||
async login(tenantId: string, tenantSecret: string, correlationId?: string): Promise<ILoginResponse> {
|
async login(
|
||||||
|
tenantId: string,
|
||||||
|
tenantSecret: string,
|
||||||
|
correlationId?: string,
|
||||||
|
): Promise<ILoginResponse> {
|
||||||
const requestBody: ILoginRequest = {
|
const requestBody: ILoginRequest = {
|
||||||
tenantId,
|
tenantId,
|
||||||
tenantSecret,
|
tenantSecret,
|
||||||
|
|
@ -77,8 +97,8 @@ export class SecloreDRMApiService {
|
||||||
try {
|
try {
|
||||||
const response = await this.context.helpers.httpRequest(options);
|
const response = await this.context.helpers.httpRequest(options);
|
||||||
return response as ILoginResponse;
|
return response as ILoginResponse;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.handleHttpError(error);
|
this.handleHttpError(error as NodeApiError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,8 +136,8 @@ export class SecloreDRMApiService {
|
||||||
try {
|
try {
|
||||||
const response = await this.context.helpers.httpRequest(options);
|
const response = await this.context.helpers.httpRequest(options);
|
||||||
return response as ILoginResponse;
|
return response as ILoginResponse;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.handleHttpError(error, { 401: 'Unauthorized' });
|
this.handleHttpError(error as NodeApiError, { 401: 'Unauthorized' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,11 +153,11 @@ export class SecloreDRMApiService {
|
||||||
async protectWithExternalRefId(
|
async protectWithExternalRefId(
|
||||||
protectRequest: IProtectWithExternalRefIdRequest,
|
protectRequest: IProtectWithExternalRefIdRequest,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
correlationId?: string
|
correlationId?: string,
|
||||||
): Promise<IProtectWithExternalRefIdResponse> {
|
): Promise<IProtectWithExternalRefIdResponse> {
|
||||||
const headers: { [key: string]: string } = {
|
const headers: { [key: string]: string } = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add correlation ID if provided
|
// Add correlation ID if provided
|
||||||
|
|
@ -156,8 +176,8 @@ export class SecloreDRMApiService {
|
||||||
try {
|
try {
|
||||||
const response = await this.context.helpers.httpRequest(options);
|
const response = await this.context.helpers.httpRequest(options);
|
||||||
return response as IProtectWithExternalRefIdResponse;
|
return response as IProtectWithExternalRefIdResponse;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.handleHttpError(error);
|
this.handleHttpError(error as NodeApiError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,11 +193,11 @@ export class SecloreDRMApiService {
|
||||||
async protectWithFileId(
|
async protectWithFileId(
|
||||||
protectRequest: IProtectWithFileIdRequest,
|
protectRequest: IProtectWithFileIdRequest,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
correlationId?: string
|
correlationId?: string,
|
||||||
): Promise<IProtectWithFileIdResponse> {
|
): Promise<IProtectWithFileIdResponse> {
|
||||||
const headers: { [key: string]: string } = {
|
const headers: { [key: string]: string } = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add correlation ID if provided
|
// Add correlation ID if provided
|
||||||
|
|
@ -196,8 +216,8 @@ export class SecloreDRMApiService {
|
||||||
try {
|
try {
|
||||||
const response = await this.context.helpers.httpRequest(options);
|
const response = await this.context.helpers.httpRequest(options);
|
||||||
return response as IProtectWithFileIdResponse;
|
return response as IProtectWithFileIdResponse;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.handleHttpError(error);
|
this.handleHttpError(error as NodeApiError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,11 +233,11 @@ export class SecloreDRMApiService {
|
||||||
async protectWithHotFolder(
|
async protectWithHotFolder(
|
||||||
protectRequest: IProtectWithHotFolderRequest,
|
protectRequest: IProtectWithHotFolderRequest,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
correlationId?: string
|
correlationId?: string,
|
||||||
): Promise<IProtectWithHotFolderResponse> {
|
): Promise<IProtectWithHotFolderResponse> {
|
||||||
const headers: { [key: string]: string } = {
|
const headers: { [key: string]: string } = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add correlation ID if provided
|
// Add correlation ID if provided
|
||||||
|
|
@ -236,8 +256,8 @@ export class SecloreDRMApiService {
|
||||||
try {
|
try {
|
||||||
const response = await this.context.helpers.httpRequest(options);
|
const response = await this.context.helpers.httpRequest(options);
|
||||||
return response as IProtectWithHotFolderResponse;
|
return response as IProtectWithHotFolderResponse;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.handleHttpError(error);
|
this.handleHttpError(error as NodeApiError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,11 +273,11 @@ export class SecloreDRMApiService {
|
||||||
async unprotect(
|
async unprotect(
|
||||||
unprotectRequest: IUnprotectRequest,
|
unprotectRequest: IUnprotectRequest,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
correlationId?: string
|
correlationId?: string,
|
||||||
): Promise<IUnprotectResponse> {
|
): Promise<IUnprotectResponse> {
|
||||||
const headers: { [key: string]: string } = {
|
const headers: { [key: string]: string } = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add correlation ID if provided
|
// Add correlation ID if provided
|
||||||
|
|
@ -276,8 +296,8 @@ export class SecloreDRMApiService {
|
||||||
try {
|
try {
|
||||||
const response = await this.context.helpers.httpRequest(options);
|
const response = await this.context.helpers.httpRequest(options);
|
||||||
return response as IUnprotectResponse;
|
return response as IUnprotectResponse;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.handleHttpError(error);
|
this.handleHttpError(error as NodeApiError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,10 +315,10 @@ export class SecloreDRMApiService {
|
||||||
fileBuffer: Uint8Array,
|
fileBuffer: Uint8Array,
|
||||||
fileName: string,
|
fileName: string,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
correlationId?: string
|
correlationId?: string,
|
||||||
): Promise<IFileUploadResponse> {
|
): Promise<IFileUploadResponse> {
|
||||||
const headers: { [key: string]: string } = {
|
const headers: { [key: string]: string } = {
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add correlation ID if provided
|
// Add correlation ID if provided
|
||||||
|
|
@ -308,7 +328,8 @@ export class SecloreDRMApiService {
|
||||||
|
|
||||||
// Create FormData for multipart/form-data upload
|
// Create FormData for multipart/form-data upload
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', fileBuffer, fileName);
|
const file = new Blob([fileBuffer], { type: 'application/octet-stream' });
|
||||||
|
formData.append('file', file, fileName);
|
||||||
|
|
||||||
const options: IHttpRequestOptions = {
|
const options: IHttpRequestOptions = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -320,8 +341,8 @@ export class SecloreDRMApiService {
|
||||||
try {
|
try {
|
||||||
const response = await this.context.helpers.httpRequest(options);
|
const response = await this.context.helpers.httpRequest(options);
|
||||||
return response as IFileUploadResponse;
|
return response as IFileUploadResponse;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.handleHttpError(error);
|
this.handleHttpError(error as NodeApiError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,10 +359,10 @@ export class SecloreDRMApiService {
|
||||||
async downloadFile(
|
async downloadFile(
|
||||||
fileStorageId: string,
|
fileStorageId: string,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
correlationId?: string
|
correlationId?: string,
|
||||||
): Promise<Uint8Array> {
|
): Promise<Uint8Array> {
|
||||||
const headers: { [key: string]: string } = {
|
const headers: { [key: string]: string } = {
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add correlation ID if provided
|
// Add correlation ID if provided
|
||||||
|
|
@ -359,9 +380,8 @@ export class SecloreDRMApiService {
|
||||||
try {
|
try {
|
||||||
const response = await this.context.helpers.httpRequest(options);
|
const response = await this.context.helpers.httpRequest(options);
|
||||||
return new Uint8Array(response as ArrayBuffer);
|
return new Uint8Array(response as ArrayBuffer);
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.handleHttpError(error);
|
this.handleHttpError(error as NodeApiError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,15 @@
|
||||||
import { IExecuteFunctions } from 'n8n-workflow';
|
import { IExecuteFunctions } from 'n8n-workflow';
|
||||||
import { SecloreDRMApiService } from './SecloreDRMApiService';
|
|
||||||
import { IProtectWithExternalRefIdRequest, IProtectWithExternalRefIdResponse, IProtectWithFileIdRequest, IProtectWithFileIdResponse, IProtectWithHotFolderRequest, IProtectWithHotFolderResponse } from './Interfaces/ProtectInterfaces';
|
|
||||||
import { IUnprotectRequest, IUnprotectResponse } from './Interfaces/UnprotectInterfaces';
|
|
||||||
import { IFileUploadResponse } from './Interfaces/FileStorageInterfaces';
|
import { IFileUploadResponse } from './Interfaces/FileStorageInterfaces';
|
||||||
|
import {
|
||||||
|
IProtectWithExternalRefIdRequest,
|
||||||
|
IProtectWithExternalRefIdResponse,
|
||||||
|
IProtectWithFileIdRequest,
|
||||||
|
IProtectWithFileIdResponse,
|
||||||
|
IProtectWithHotFolderRequest,
|
||||||
|
IProtectWithHotFolderResponse,
|
||||||
|
} from './Interfaces/ProtectInterfaces';
|
||||||
|
import { IUnprotectRequest, IUnprotectResponse } from './Interfaces/UnprotectInterfaces';
|
||||||
|
import { SecloreDRMApiService } from './SecloreDRMApiService';
|
||||||
|
|
||||||
export class SecloreDRMFileService {
|
export class SecloreDRMFileService {
|
||||||
private apiService: SecloreDRMApiService;
|
private apiService: SecloreDRMApiService;
|
||||||
|
|
@ -19,7 +26,7 @@ export class SecloreDRMFileService {
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
tenantId: string,
|
tenantId: string,
|
||||||
tenantSecret: string,
|
tenantSecret: string,
|
||||||
private defaultRetryCount: number = 3
|
private defaultRetryCount: number = 3,
|
||||||
) {
|
) {
|
||||||
this.apiService = new SecloreDRMApiService(context, baseUrl);
|
this.apiService = new SecloreDRMApiService(context, baseUrl);
|
||||||
this.tenantId = tenantId;
|
this.tenantId = tenantId;
|
||||||
|
|
@ -58,7 +65,7 @@ export class SecloreDRMFileService {
|
||||||
const loginResponse = await this.apiService.login(
|
const loginResponse = await this.apiService.login(
|
||||||
this.tenantId,
|
this.tenantId,
|
||||||
this.tenantSecret,
|
this.tenantSecret,
|
||||||
correlationId
|
correlationId,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.accessToken = loginResponse.accessToken;
|
this.accessToken = loginResponse.accessToken;
|
||||||
|
|
@ -102,10 +109,7 @@ export class SecloreDRMFileService {
|
||||||
*/
|
*/
|
||||||
private async performTokenRefresh(correlationId?: string): Promise<void> {
|
private async performTokenRefresh(correlationId?: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const refreshResponse = await this.apiService.refreshToken(
|
const refreshResponse = await this.apiService.refreshToken(this.refreshToken!, correlationId);
|
||||||
this.refreshToken!,
|
|
||||||
correlationId
|
|
||||||
);
|
|
||||||
|
|
||||||
this.accessToken = refreshResponse.accessToken;
|
this.accessToken = refreshResponse.accessToken;
|
||||||
this.refreshToken = refreshResponse.refreshToken;
|
this.refreshToken = refreshResponse.refreshToken;
|
||||||
|
|
@ -138,7 +142,7 @@ export class SecloreDRMFileService {
|
||||||
private async executeWithRetry<T>(
|
private async executeWithRetry<T>(
|
||||||
apiCall: (accessToken: string) => Promise<T>,
|
apiCall: (accessToken: string) => Promise<T>,
|
||||||
retryCount: number = this.defaultRetryCount,
|
retryCount: number = this.defaultRetryCount,
|
||||||
correlationId?: string
|
correlationId?: string,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
let lastError: Error;
|
let lastError: Error;
|
||||||
|
|
||||||
|
|
@ -153,16 +157,15 @@ export class SecloreDRMFileService {
|
||||||
|
|
||||||
// Execute the API call
|
// Execute the API call
|
||||||
return await apiCall(this.accessToken);
|
return await apiCall(this.accessToken);
|
||||||
|
} catch (error: unknown) {
|
||||||
} catch (error: any) {
|
lastError = error as Error;
|
||||||
lastError = error;
|
|
||||||
|
|
||||||
// If it's an authentication error and we have retries left, try to refresh token
|
// If it's an authentication error and we have retries left, try to refresh token
|
||||||
if (error.message.includes('Unauthorized') && attempt < retryCount) {
|
if ((error as Error).message.includes('Unauthorized') && attempt < retryCount) {
|
||||||
try {
|
try {
|
||||||
await this.refreshAccessToken(correlationId);
|
await this.refreshAccessToken(correlationId);
|
||||||
continue; // Retry with new token
|
continue; // Retry with new token
|
||||||
} catch (refreshError) {
|
} catch (error: unknown) {
|
||||||
|
console.error(error);
|
||||||
// If refresh fails, clear tokens and try full login on next attempt
|
// If refresh fails, clear tokens and try full login on next attempt
|
||||||
this.clearTokens();
|
this.clearTokens();
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +177,7 @@ export class SecloreDRMFileService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait before retry (exponential backoff)
|
// Wait before retry (exponential backoff)
|
||||||
await new Promise(resolve => {
|
await new Promise((resolve) => {
|
||||||
const delay = Math.pow(2, attempt) * 1000;
|
const delay = Math.pow(2, attempt) * 1000;
|
||||||
// Use a simple delay implementation
|
// Use a simple delay implementation
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
@ -195,12 +198,13 @@ export class SecloreDRMFileService {
|
||||||
async protectWithExternalRefId(
|
async protectWithExternalRefId(
|
||||||
protectRequest: IProtectWithExternalRefIdRequest,
|
protectRequest: IProtectWithExternalRefIdRequest,
|
||||||
correlationId?: string,
|
correlationId?: string,
|
||||||
retryCount?: number
|
retryCount?: number,
|
||||||
): Promise<IProtectWithExternalRefIdResponse> {
|
): Promise<IProtectWithExternalRefIdResponse> {
|
||||||
return this.executeWithRetry(
|
return this.executeWithRetry(
|
||||||
(accessToken) => this.apiService.protectWithExternalRefId(protectRequest, accessToken, correlationId),
|
(accessToken) =>
|
||||||
|
this.apiService.protectWithExternalRefId(protectRequest, accessToken, correlationId),
|
||||||
retryCount,
|
retryCount,
|
||||||
correlationId
|
correlationId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,12 +214,13 @@ export class SecloreDRMFileService {
|
||||||
async protectWithFileId(
|
async protectWithFileId(
|
||||||
protectRequest: IProtectWithFileIdRequest,
|
protectRequest: IProtectWithFileIdRequest,
|
||||||
correlationId?: string,
|
correlationId?: string,
|
||||||
retryCount?: number
|
retryCount?: number,
|
||||||
): Promise<IProtectWithFileIdResponse> {
|
): Promise<IProtectWithFileIdResponse> {
|
||||||
return this.executeWithRetry(
|
return this.executeWithRetry(
|
||||||
(accessToken) => this.apiService.protectWithFileId(protectRequest, accessToken, correlationId),
|
(accessToken) =>
|
||||||
|
this.apiService.protectWithFileId(protectRequest, accessToken, correlationId),
|
||||||
retryCount,
|
retryCount,
|
||||||
correlationId
|
correlationId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,12 +230,13 @@ export class SecloreDRMFileService {
|
||||||
async protectWithHotFolder(
|
async protectWithHotFolder(
|
||||||
protectRequest: IProtectWithHotFolderRequest,
|
protectRequest: IProtectWithHotFolderRequest,
|
||||||
correlationId?: string,
|
correlationId?: string,
|
||||||
retryCount?: number
|
retryCount?: number,
|
||||||
): Promise<IProtectWithHotFolderResponse> {
|
): Promise<IProtectWithHotFolderResponse> {
|
||||||
return this.executeWithRetry(
|
return this.executeWithRetry(
|
||||||
(accessToken) => this.apiService.protectWithHotFolder(protectRequest, accessToken, correlationId),
|
(accessToken) =>
|
||||||
|
this.apiService.protectWithHotFolder(protectRequest, accessToken, correlationId),
|
||||||
retryCount,
|
retryCount,
|
||||||
correlationId
|
correlationId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,12 +246,12 @@ export class SecloreDRMFileService {
|
||||||
async unprotect(
|
async unprotect(
|
||||||
unprotectRequest: IUnprotectRequest,
|
unprotectRequest: IUnprotectRequest,
|
||||||
correlationId?: string,
|
correlationId?: string,
|
||||||
retryCount?: number
|
retryCount?: number,
|
||||||
): Promise<IUnprotectResponse> {
|
): Promise<IUnprotectResponse> {
|
||||||
return this.executeWithRetry(
|
return this.executeWithRetry(
|
||||||
(accessToken) => this.apiService.unprotect(unprotectRequest, accessToken, correlationId),
|
(accessToken) => this.apiService.unprotect(unprotectRequest, accessToken, correlationId),
|
||||||
retryCount,
|
retryCount,
|
||||||
correlationId
|
correlationId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,12 +262,12 @@ export class SecloreDRMFileService {
|
||||||
fileBuffer: Uint8Array,
|
fileBuffer: Uint8Array,
|
||||||
fileName: string,
|
fileName: string,
|
||||||
correlationId?: string,
|
correlationId?: string,
|
||||||
retryCount?: number
|
retryCount?: number,
|
||||||
): Promise<IFileUploadResponse> {
|
): Promise<IFileUploadResponse> {
|
||||||
return this.executeWithRetry(
|
return this.executeWithRetry(
|
||||||
(accessToken) => this.apiService.uploadFile(fileBuffer, fileName, accessToken, correlationId),
|
(accessToken) => this.apiService.uploadFile(fileBuffer, fileName, accessToken, correlationId),
|
||||||
retryCount,
|
retryCount,
|
||||||
correlationId
|
correlationId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,12 +278,12 @@ export class SecloreDRMFileService {
|
||||||
async downloadFile(
|
async downloadFile(
|
||||||
fileStorageId: string,
|
fileStorageId: string,
|
||||||
correlationId?: string,
|
correlationId?: string,
|
||||||
retryCount?: number
|
retryCount?: number,
|
||||||
): Promise<Uint8Array> {
|
): Promise<Uint8Array> {
|
||||||
return this.executeWithRetry(
|
return this.executeWithRetry(
|
||||||
(accessToken) => this.apiService.downloadFile(fileStorageId, accessToken, correlationId),
|
(accessToken) => this.apiService.downloadFile(fileStorageId, accessToken, correlationId),
|
||||||
retryCount,
|
retryCount,
|
||||||
correlationId
|
correlationId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,5 +300,4 @@ export class SecloreDRMFileService {
|
||||||
isAuthenticated(): boolean {
|
isAuthenticated(): boolean {
|
||||||
return !!this.accessToken && (!this.tokenExpiry || new Date() < this.tokenExpiry);
|
return !!this.accessToken && (!this.tokenExpiry || new Date() < this.tokenExpiry);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue