services added

This commit is contained in:
atharva.dev 2025-10-22 11:39:55 +05:30
parent d214bcae3c
commit aabc1e9af8
25 changed files with 8019 additions and 0 deletions

28
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: CI
on:
pull_request:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: 'npm ci'
- name: Run lint
run: 'npm run lint'
- name: Run build
run: 'npm run build'

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
dist
node_modules

51
.prettierrc.js Normal file
View File

@ -0,0 +1,51 @@
module.exports = {
/**
* https://prettier.io/docs/en/options.html#semicolons
*/
semi: true,
/**
* https://prettier.io/docs/en/options.html#trailing-commas
*/
trailingComma: 'all',
/**
* https://prettier.io/docs/en/options.html#bracket-spacing
*/
bracketSpacing: true,
/**
* https://prettier.io/docs/en/options.html#tabs
*/
useTabs: true,
/**
* https://prettier.io/docs/en/options.html#tab-width
*/
tabWidth: 2,
/**
* https://prettier.io/docs/en/options.html#arrow-function-parentheses
*/
arrowParens: 'always',
/**
* https://prettier.io/docs/en/options.html#quotes
*/
singleQuote: true,
/**
* https://prettier.io/docs/en/options.html#quote-props
*/
quoteProps: 'as-needed',
/**
* https://prettier.io/docs/en/options.html#end-of-line
*/
endOfLine: 'lf',
/**
* https://prettier.io/docs/en/options.html#print-width
*/
printWidth: 100,
};

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"esbenp.prettier-vscode",
]
}

12
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to running n8n",
"processId": "${command:PickProcess}",
"request": "attach",
"skipFiles": ["<node_internals>/**"],
"type": "node"
}
]
}

0
CHANGELOG.md Normal file
View File

76
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at jan@n8n.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

19
LICENSE.md Normal file
View File

@ -0,0 +1,19 @@
Copyright 2022 n8n
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

48
README_TEMPLATE.md Normal file
View File

@ -0,0 +1,48 @@
# n8n-nodes-_node-name_
This is an n8n community node. It lets you use _app/service name_ in your n8n workflows.
_App/service name_ is _one or two sentences describing the service this node integrates with_.
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
[Installation](#installation)
[Operations](#operations)
[Credentials](#credentials) <!-- delete if no auth needed -->
[Compatibility](#compatibility)
[Usage](#usage) <!-- delete if not using this section -->
[Resources](#resources)
[Version history](#version-history) <!-- delete if not using this section -->
## Installation
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
## Operations
_List the operations supported by your node._
## Credentials
_If users need to authenticate with the app/service, provide details here. You should include prerequisites (such as signing up with the service), available authentication methods, and how to set them up._
## Compatibility
_State the minimum n8n version, as well as which versions you test against. You can also include any known version incompatibility issues._
## Usage
_This is an optional section. Use it to help users with any difficult or confusing aspects of the node._
_By the time users are looking for community nodes, they probably already know n8n basics. But if you expect new users, you can link to the [Try it out](https://docs.n8n.io/try-it-out/) documentation to help them get started._
## Resources
* [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
* _Link to app/service documentation._
## Version history
_This is another optional section. If your node has multiple versions, include a short description of available versions and what changed, as well as any compatibility impact._

3
eslint.config.mjs Normal file
View File

@ -0,0 +1,3 @@
import { config } from '@n8n/node-cli/eslint';
export default config;

3
icons/github.dark.svg Normal file
View File

@ -0,0 +1,3 @@
<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>

After

Width:  |  Height:  |  Size: 1.3 KiB

3
icons/github.svg Normal file
View File

@ -0,0 +1,3 @@
<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>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
import { IDataObject, INodeExecutionData, IExecuteFunctions } from 'n8n-workflow';

View File

@ -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

View File

@ -0,0 +1,4 @@
export interface IErrorResponse {
errorCode: string;
errorMessage: string;
}

View File

@ -0,0 +1,9 @@
export interface IFileUploadResponse {
fileStorageId: string;
fileName: string;
downloadUrl: string;
fileType: string;
fileSize: number;
secloreFileId: string;
protected: boolean;
}

View File

@ -0,0 +1,13 @@
export interface ILoginRequest {
tenantId: string;
tenantSecret: string;
}
export interface ILoginResponse {
accessToken: string;
refreshToken: string;
}
export interface IRefreshTokenRequest {
refreshToken: string;
}

View File

@ -0,0 +1,37 @@
export interface IExtRefProtectionDetail {
externalReferenceId: string;
externalReferenceName?: string;
externalReferenceData?: string;
externalAppId?: string;
}
export interface IProtectWithExternalRefIdRequest {
hotfolderExternalReference: IExtRefProtectionDetail;
fileExternalReference?: IExtRefProtectionDetail;
fileStorageId: string;
}
export interface IProtectWithExternalRefIdResponse {
fileStorageId: string;
secloreFileId: string;
}
export interface IProtectWithFileIdRequest {
existingProtectedFileId: string;
fileStorageId: string;
}
export interface IProtectWithFileIdResponse {
fileStorageId: string;
secloreFileId: string;
}
export interface IProtectWithHotFolderRequest {
hotfolderId: string;
fileStorageId: string;
}
export interface IProtectWithHotFolderResponse {
fileStorageId: string;
secloreFileId: string;
}

View File

@ -0,0 +1,7 @@
export interface IUnprotectRequest {
fileStorageId: string;
}
export interface IUnprotectResponse {
fileStorageId: string;
}

View File

@ -0,0 +1,384 @@
import { IExecuteFunctions, IHttpRequestOptions } from 'n8n-workflow';
import { ILoginRequest, ILoginResponse, IRefreshTokenRequest } from './Interfaces/LoginInterfaces';
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';
export class SecloreDRMApiService {
private baseUrl: string;
constructor(private context: IExecuteFunctions, baseUrl: string) {
this.baseUrl = baseUrl;
}
/**
* Login Endpoint to generate Access Token and Refresh Token for JWT Authorization.
* Upon successful login, all the existing previous tokens for that tenant will be invalidated.
*
* @param tenantId - The tenant ID
* @param tenantSecret - The tenant secret
* @param correlationId - Optional request ID for logging purpose
* @returns Promise<ILoginResponse> - Access token and refresh token
* @throws Error on authentication failure or server error
*/
async login(tenantId: string, tenantSecret: string, correlationId?: string): Promise<ILoginResponse> {
const requestBody: ILoginRequest = {
tenantId,
tenantSecret,
};
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
};
// Add correlation ID if provided
if (correlationId) {
headers['X-SECLORE-CORRELATION-ID'] = correlationId;
}
const options: IHttpRequestOptions = {
method: 'POST',
url: `${this.baseUrl}/seclore/drm/1.0/auth/login`,
headers,
body: requestBody,
json: true,
};
try {
const response = await this.context.helpers.httpRequest(options);
return response as ILoginResponse;
} catch (error: any) {
// Handle specific HTTP error responses
if (error.statusCode === 401) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid credentials'}`);
} else if (error.statusCode === 500) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
}
// Re-throw other errors
throw error;
}
}
/**
* Endpoint for generating new Access Token and Refresh Token using an existing valid Refresh Token.
* Upon successful response, all the previous existing Access Tokens and Refresh Tokens of that tenant will be invalidated.
*
* @param refreshToken - The existing valid refresh token
* @param correlationId - Optional request ID for logging purpose
* @returns Promise<ILoginResponse> - New access token and refresh token
* @throws Error on authentication failure or server error
*/
async refreshToken(refreshToken: string, correlationId?: string): Promise<ILoginResponse> {
const requestBody: IRefreshTokenRequest = {
refreshToken,
};
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
};
// Add correlation ID if provided
if (correlationId) {
headers['X-SECLORE-CORRELATION-ID'] = correlationId;
}
const options: IHttpRequestOptions = {
method: 'POST',
url: `${this.baseUrl}/seclore/drm/1.0/auth/refresh`,
headers,
body: requestBody,
json: true,
};
try {
const response = await this.context.helpers.httpRequest(options);
return response as ILoginResponse;
} catch (error: any) {
// Handle specific HTTP error responses
if (error.statusCode === 401) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid refresh token'}`);
} else if (error.statusCode === 500) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
}
// Re-throw other errors
throw error;
}
}
/**
* Protect file using external identifier of protected File and HotFolder with PS configured against the logged in Tenant in application.
*
* @param protectRequest - The protection request details
* @param accessToken - JWT access token for authorization
* @param correlationId - Optional request ID for logging purpose
* @returns Promise<IProtectWithExternalRefIdResponse> - File storage ID and Seclore file ID
* @throws Error on bad request, authentication failure or server error
*/
async protectWithExternalRefId(
protectRequest: IProtectWithExternalRefIdRequest,
accessToken: string,
correlationId?: string
): Promise<IProtectWithExternalRefIdResponse> {
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
};
// Add correlation ID if provided
if (correlationId) {
headers['X-SECLORE-CORRELATION-ID'] = correlationId;
}
const options: IHttpRequestOptions = {
method: 'POST',
url: `${this.baseUrl}/seclore/drm/1.0/protect/externalref`,
headers,
body: protectRequest,
json: true,
};
try {
const response = await this.context.helpers.httpRequest(options);
return response as IProtectWithExternalRefIdResponse;
} catch (error: any) {
// Handle specific HTTP error responses
if (error.statusCode === 400) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Bad Request: ${errorResponse?.errorMessage || 'Invalid request data'}`);
} else if (error.statusCode === 401) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid access token'}`);
} else if (error.statusCode === 500) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
}
// Re-throw other errors
throw error;
}
}
/**
* Protects file using File ID of already protected file with PS configured against the logged in Tenant in application.
*
* @param protectRequest - The protection request details with existing protected file ID
* @param accessToken - JWT access token for authorization
* @param correlationId - Optional request ID for logging purpose
* @returns Promise<IProtectWithFileIdResponse> - File storage ID and Seclore file ID
* @throws Error on bad request, authentication failure or server error
*/
async protectWithFileId(
protectRequest: IProtectWithFileIdRequest,
accessToken: string,
correlationId?: string
): Promise<IProtectWithFileIdResponse> {
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
};
// Add correlation ID if provided
if (correlationId) {
headers['X-SECLORE-CORRELATION-ID'] = correlationId;
}
const options: IHttpRequestOptions = {
method: 'POST',
url: `${this.baseUrl}/seclore/drm/1.0/protect/fileid`,
headers,
body: protectRequest,
json: true,
};
try {
const response = await this.context.helpers.httpRequest(options);
return response as IProtectWithFileIdResponse;
} catch (error: any) {
// Handle specific HTTP error responses
if (error.statusCode === 400) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Bad Request: ${errorResponse?.errorMessage || 'Invalid request data'}`);
} else if (error.statusCode === 401) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid access token'}`);
} else if (error.statusCode === 500) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
}
// Re-throw other errors
throw error;
}
}
/**
* Protects file using HotFolder ID with PS configured against the logged in Tenant in application.
*
* @param protectRequest - The protection request details with hotfolder ID
* @param accessToken - JWT access token for authorization
* @param correlationId - Optional request ID for logging purpose
* @returns Promise<IProtectWithHotFolderResponse> - File storage ID and Seclore file ID
* @throws Error on bad request, authentication failure or server error
*/
async protectWithHotFolder(
protectRequest: IProtectWithHotFolderRequest,
accessToken: string,
correlationId?: string
): Promise<IProtectWithHotFolderResponse> {
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
};
// Add correlation ID if provided
if (correlationId) {
headers['X-SECLORE-CORRELATION-ID'] = correlationId;
}
const options: IHttpRequestOptions = {
method: 'POST',
url: `${this.baseUrl}/seclore/drm/1.0/protect/hf`,
headers,
body: protectRequest,
json: true,
};
try {
const response = await this.context.helpers.httpRequest(options);
return response as IProtectWithHotFolderResponse;
} catch (error: any) {
// Handle specific HTTP error responses
if (error.statusCode === 400) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Bad Request: ${errorResponse?.errorMessage || 'Invalid request data'}`);
} else if (error.statusCode === 401) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid access token'}`);
} else if (error.statusCode === 500) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
}
// Re-throw other errors
throw error;
}
}
/**
* Unprotects file with PS configured against the logged in Tenant in application.
*
* @param unprotectRequest - The unprotect request details with file storage ID
* @param accessToken - JWT access token for authorization
* @param correlationId - Optional request ID for logging purpose
* @returns Promise<IUnprotectResponse> - File storage ID of unprotected file
* @throws Error on bad request, authentication failure or server error
*/
async unprotect(
unprotectRequest: IUnprotectRequest,
accessToken: string,
correlationId?: string
): Promise<IUnprotectResponse> {
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
};
// Add correlation ID if provided
if (correlationId) {
headers['X-SECLORE-CORRELATION-ID'] = correlationId;
}
const options: IHttpRequestOptions = {
method: 'POST',
url: `${this.baseUrl}/seclore/drm/1.0/unprotect`,
headers,
body: unprotectRequest,
json: true,
};
try {
const response = await this.context.helpers.httpRequest(options);
return response as IUnprotectResponse;
} catch (error: any) {
// Handle specific HTTP error responses
if (error.statusCode === 400) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Bad Request: ${errorResponse?.errorMessage || 'Invalid request data'}`);
} else if (error.statusCode === 401) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid access token'}`);
} else if (error.statusCode === 500) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
}
// Re-throw other errors
throw error;
}
}
/**
* Adds a new file to the file storage for currently logged in Tenant.
*
* @param fileBuffer - The file buffer data
* @param fileName - The name of the file
* @param accessToken - JWT access token for authorization
* @param correlationId - Optional request ID for logging purpose
* @returns Promise<IFileUploadResponse> - File storage details including file ID and metadata
* @throws Error on authentication failure, payload too large, or server error
*/
async uploadFile(
fileBuffer: Uint8Array,
fileName: string,
accessToken: string,
correlationId?: string
): Promise<IFileUploadResponse> {
const headers: { [key: string]: string } = {
'Authorization': `Bearer ${accessToken}`,
};
// Add correlation ID if provided
if (correlationId) {
headers['X-SECLORE-CORRELATION-ID'] = correlationId;
}
// Create FormData for multipart/form-data upload
const formData = new FormData();
formData.append('file', fileBuffer, fileName);
const options: IHttpRequestOptions = {
method: 'POST',
url: `${this.baseUrl}/seclore/drm/filestorage/1.0/upload`,
headers,
body: formData,
};
try {
const response = await this.context.helpers.httpRequest(options);
return response as IFileUploadResponse;
} catch (error: any) {
// Handle specific HTTP error responses
if (error.statusCode === 401) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid access token'}`);
} else if (error.statusCode === 413) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Payload Too Large: ${errorResponse?.errorMessage || 'File size exceeds limit'}`);
} else if (error.statusCode === 500) {
const errorResponse = error.response?.body as IErrorResponse;
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
}
// Re-throw other errors
throw error;
}
}
}

7226
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

50
package.json Normal file
View File

@ -0,0 +1,50 @@
{
"name": "n8n-nodes-secloreprotect",
"version": "0.1.0",
"description": "",
"license": "MIT",
"homepage": "",
"keywords": [
"n8n-community-node-package"
],
"author": {
"name": "",
"email": ""
},
"repository": {
"type": "git",
"url": "https://github.com/seclore/n8n-nodes-secloreprotect.git"
},
"scripts": {
"build": "n8n-node build",
"build:watch": "tsc --watch",
"dev": "n8n-node dev",
"lint": "n8n-node lint",
"lint:fix": "n8n-node lint --fix",
"release": "n8n-node release",
"prepublishOnly": "n8n-node prerelease"
},
"files": [
"dist"
],
"n8n": {
"n8nNodesApiVersion": 1,
"strict": true,
"credentials": [
"dist/credentials/SecloreProtectApi.credentials.js"
],
"nodes": [
"dist/nodes/SecloreProtect/SecloreProtect.node.js"
]
},
"devDependencies": {
"@n8n/node-cli": "*",
"eslint": "9.32.0",
"prettier": "3.6.2",
"release-it": "^19.0.4",
"typescript": "5.9.2"
},
"peerDependencies": {
"n8n-workflow": "*"
}
}

25
tsconfig.json Normal file
View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"strict": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2019",
"lib": ["es2019", "es2020", "es2022.error"],
"removeComments": true,
"useUnknownInCatchVariables": false,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"preserveConstEnums": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"declaration": true,
"sourceMap": true,
"skipLibCheck": true,
"outDir": "./dist/"
},
"include": ["credentials/**/*", "nodes/**/*", "nodes/**/*.json", "package.json"]
}