n8n-nodes-seclore/nodes/SecloreProtect/Services/SecloreDRMApiService.ts

388 lines
12 KiB
TypeScript

import { IExecuteFunctions, IHttpRequestOptions, NodeApiError } from 'n8n-workflow';
import { IErrorResponse } from './Interfaces/ErrorInterfaces';
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 {
private baseUrl: string;
constructor(
private context: IExecuteFunctions,
baseUrl: string,
) {
this.baseUrl = baseUrl;
}
/**
* Common error handler for HTTP responses
* @param error - The error object from httpRequest
* @param customMessages - Optional custom error messages for specific status codes
*/
private handleHttpError(
error: NodeApiError,
customMessages?: { [statusCode: number]: string },
): never {
const statusCode: number = parseInt(error.httpCode ?? '0');
const errorResponse = error.errorResponse as unknown as IErrorResponse;
if (customMessages && customMessages[statusCode]) {
throw new Error(
`${customMessages[statusCode]}: ${errorResponse?.errorMessage || 'Unknown error'}`,
);
}
// Default error handling
switch (statusCode) {
case 400:
throw new Error(`Bad Request: ${errorResponse?.errorMessage || 'Invalid request data'}`);
case 401:
throw new Error(`Unauthorized: ${errorResponse?.errorMessage || 'Invalid credentials'}`);
case 413:
throw new Error(
`Payload Too Large: ${errorResponse?.errorMessage || 'File size exceeds limit'}`,
);
case 500:
throw new Error(`Server Error: ${errorResponse?.errorMessage || 'Internal server error'}`);
default:
throw error;
}
}
/**
* 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: unknown) {
this.handleHttpError(error as NodeApiError);
}
}
/**
* 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: unknown) {
this.handleHttpError(error as NodeApiError, { 401: 'Unauthorized' });
}
}
/**
* 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: unknown) {
this.handleHttpError(error as NodeApiError);
}
}
/**
* 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: unknown) {
this.handleHttpError(error as NodeApiError);
}
}
/**
* 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: unknown) {
this.handleHttpError(error as NodeApiError);
}
}
/**
* 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: unknown) {
this.handleHttpError(error as NodeApiError);
}
}
/**
* 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();
const file = new Blob([fileBuffer], { type: 'application/octet-stream' });
formData.append('file', file, 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: unknown) {
this.handleHttpError(error as NodeApiError);
}
}
/**
* Downloads file with fileStorageId from file storage of currently logged in Tenant.
* NOTE: Files whose fileStorageId has 'DL_' prefix will be deleted from the file storage after download.
*
* @param fileStorageId - Storage ID of the file to be retrieved
* @param accessToken - JWT access token for authorization
* @param correlationId - Optional request ID for logging purpose
* @returns Promise<Uint8Array> - The downloaded file data
* @throws Error on authentication failure or server error
*/
async downloadFile(
fileStorageId: string,
accessToken: string,
correlationId?: string,
): Promise<Uint8Array> {
const headers: { [key: string]: string } = {
Authorization: `Bearer ${accessToken}`,
};
// Add correlation ID if provided
if (correlationId) {
headers['X-SECLORE-CORRELATION-ID'] = correlationId;
}
const options: IHttpRequestOptions = {
method: 'GET',
url: `${this.baseUrl}/seclore/drm/filestorage/1.0/download/${fileStorageId}`,
headers,
encoding: 'arraybuffer',
};
try {
const response = await this.context.helpers.httpRequest(options);
return new Uint8Array(response as ArrayBuffer);
} catch (error: unknown) {
this.handleHttpError(error as NodeApiError);
}
}
}