@iexec/web3mail
Version:
This product enables users to confidentially store data–such as mail address, documents, personal information ...
171 lines (151 loc) • 4.83 kB
text/typescript
import { Buffer } from 'buffer';
import {
DEFAULT_CONTENT_TYPE,
MAX_DESIRED_APP_ORDER_PRICE,
MAX_DESIRED_WORKERPOOL_ORDER_PRICE,
} from '../config/config.js';
import { handleIfProtocolError, WorkflowError } from '../utils/errors.js';
import * as ipfs from '../utils/ipfs-service.js';
import {
addressSchema,
contentTypeSchema,
emailContentSchema,
emailSubjectSchema,
labelSchema,
positiveNumberSchema,
senderNameSchema,
throwIfMissing,
} from '../utils/validators.js';
import {
PrepareEmailCampaignParams,
PrepareEmailCampaignResponse,
} from './types.js';
import {
DappAddressConsumer,
DataProtectorConsumer,
IExecConsumer,
IpfsGatewayConfigConsumer,
IpfsNodeConfigConsumer,
} from './internalTypes.js';
export type PrepareEmailCampaign = typeof prepareEmailCampaign;
export const prepareEmailCampaign = async ({
iexec = throwIfMissing(),
dataProtector = throwIfMissing(),
workerpoolAddress,
dappAddress,
ipfsNode,
ipfsGateway,
senderName,
emailSubject,
emailContent,
contentType = DEFAULT_CONTENT_TYPE,
label,
appMaxPrice = MAX_DESIRED_APP_ORDER_PRICE,
workerpoolMaxPrice = MAX_DESIRED_WORKERPOOL_ORDER_PRICE,
grantedAccesses,
maxProtectedDataPerTask,
}: IExecConsumer &
DappAddressConsumer &
IpfsNodeConfigConsumer &
IpfsGatewayConfigConsumer &
DataProtectorConsumer &
PrepareEmailCampaignParams): Promise<PrepareEmailCampaignResponse> => {
try {
const vWorkerpoolAddress = addressSchema()
.label('workerpoolAddress')
.validateSync(workerpoolAddress);
const vSenderName = senderNameSchema()
.label('senderName')
.validateSync(senderName);
const vEmailSubject = emailSubjectSchema()
.required()
.label('emailSubject')
.validateSync(emailSubject);
const vEmailContent = emailContentSchema()
.required()
.label('emailContent')
.validateSync(emailContent);
const vContentType = contentTypeSchema()
.label('contentType')
.validateSync(contentType);
const vLabel = labelSchema().label('label').validateSync(label);
const vDappAddress = addressSchema()
.required()
.label('dappAddress')
.validateSync(dappAddress);
const vAppMaxPrice = positiveNumberSchema()
.label('appMaxPrice')
.validateSync(appMaxPrice);
const vWorkerpoolMaxPrice = positiveNumberSchema()
.label('workerpoolMaxPrice')
.validateSync(workerpoolMaxPrice);
const vMaxProtectedDataPerTask = positiveNumberSchema()
.label('maxProtectedDataPerTask')
.validateSync(maxProtectedDataPerTask);
// TODO: factor this
// Encrypt email content
const emailContentEncryptionKey = iexec.dataset.generateEncryptionKey();
const encryptedFile = await iexec.dataset
.encrypt(Buffer.from(vEmailContent, 'utf8'), emailContentEncryptionKey)
.catch((e) => {
throw new WorkflowError({
message: 'Failed to encrypt email content',
errorCause: e,
});
});
// Push email content to IPFS
const cid = await ipfs
.add(encryptedFile, {
ipfsNode,
ipfsGateway,
})
.catch((e) => {
throw new WorkflowError({
message: 'Failed to upload encrypted email content',
errorCause: e,
});
});
const multiaddr = `/ipfs/${cid}`;
// Prepare secrets for the requester
// Use a positive integer as secret ID (required by iexec)
// Using "1" as a fixed ID for the requester secret
const requesterSecretId = 1;
const secrets = {
[requesterSecretId]: JSON.stringify({
emailSubject: vEmailSubject,
emailContentMultiAddr: multiaddr,
contentType: vContentType,
senderName: vSenderName,
emailContentEncryptionKey,
useCallback: true,
}),
};
// TODO: end factor this
const { bulkRequest: campaignRequest } =
await dataProtector.prepareBulkRequest({
app: vDappAddress,
appMaxPrice: vAppMaxPrice,
workerpoolMaxPrice: vWorkerpoolMaxPrice,
workerpool: vWorkerpoolAddress,
args: vLabel,
inputFiles: [],
secrets,
bulkAccesses: grantedAccesses,
maxProtectedDataPerTask: vMaxProtectedDataPerTask,
});
return { campaignRequest };
} catch (error) {
// Protocol error detected, re-throwing as-is
if ((error as any)?.isProtocolError === true) {
throw error;
}
// Handle protocol errors - this will throw if it's an ApiCallError
// handleIfProtocolError transforms ApiCallError into a WorkflowError with isProtocolError=true
handleIfProtocolError(error);
// For all other errors
throw new WorkflowError({
message: 'Failed to prepareEmailCampaign',
errorCause: error,
});
}
};