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; } /** * 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: any, customMessages?: { [statusCode: number]: string }): never { const statusCode = error.statusCode; const errorResponse = error.response?.body 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 - Access token and refresh token * @throws Error on authentication failure or server error */ async login(tenantId: string, tenantSecret: string, correlationId?: string): Promise { 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) { this.handleHttpError(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 - New access token and refresh token * @throws Error on authentication failure or server error */ async refreshToken(refreshToken: string, correlationId?: string): Promise { 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) { this.handleHttpError(error, { 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 - 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 { 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) { this.handleHttpError(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 - 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 { 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) { this.handleHttpError(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 - 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 { 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) { this.handleHttpError(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 - 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 { 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) { this.handleHttpError(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 - 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 { 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) { this.handleHttpError(error); } } }