From e06089fb530abce3624a0eeec81fef94f49aea07 Mon Sep 17 00:00:00 2001 From: Csaky Date: Fri, 17 May 2024 13:37:33 -0700 Subject: [PATCH] Send Invite notification send invite if scoping to a user email template calls backend and CHES --- .../src/components/common/InviteButton.vue | 108 ++++++++------ frontend/src/services/interceptors.ts | 33 +++++ frontend/src/services/inviteService.ts | 135 +++++++++++++++--- 3 files changed, 217 insertions(+), 59 deletions(-) diff --git a/frontend/src/components/common/InviteButton.vue b/frontend/src/components/common/InviteButton.vue index 9710091f..6b0fb44a 100644 --- a/frontend/src/components/common/InviteButton.vue +++ b/frontend/src/components/common/InviteButton.vue @@ -1,6 +1,6 @@

- {{ obj?.name || bucket?.bucketName }} + {{ resourceType === 'object' ? resource?.name : resource?.bucketName }}

{ {

{{ (props.labelText) }} Invite

Make invite available for

@@ -246,12 +266,11 @@ onMounted(() => {

Restrict to user's email

-
+ +
- { placeholder="Enter email" required type="email" - class="ml-5 mr-8" + class="mt-2 max-w-30rem" /> -
-
+ The Invite will be emailed to this person +
+
-
+ +

{ + const authService = new AuthService(); + const user = await authService.getUser(); + if (!!user && !user.expired) { + cfg.headers.Authorization = `Bearer ${user.access_token}`; + } + return Promise.resolve(cfg); + }, + (error: Error) => { + return Promise.reject(error); + } + ); + + return instance; +} + /** * @function comsAxios * Returns an Axios instance for the COMS API diff --git a/frontend/src/services/inviteService.ts b/frontend/src/services/inviteService.ts index 676de49c..68d4c549 100644 --- a/frontend/src/services/inviteService.ts +++ b/frontend/src/services/inviteService.ts @@ -1,26 +1,127 @@ -import { comsAxios } from './interceptors'; +import { appAxios, comsAxios } from './interceptors'; + +import type { COMSObject, Bucket, User } from '@/types'; const PATH = 'permission/invite'; export default { + + /** + * @function createInvites + * Create an invite url with the COMS api + * if incomming emails then send email notifications with unique invite links + * @param {string} resourceType either 'object' or 'bucket' + * @param {COMSObject | Bucket } resource the COMS object or bucket record + * @param {User | null} currentUser current user sending the invite + * @param {Array} emails array of email adddresses for invitees + * @param {string} expiresAt timestamp for invite token expiry + * @param {Array} permCodes array of permCodes for the invite + * + * @typedef {object} Invite + * @property {string} email - invitee's email + * @property {string} token - invite token + * + * @returns {Array} array of invite email / token pairs + */ + async createInvites( + resourceType: string, + resource: any, + currentUser: any, + emails: Array, + expiresAt?: number, + permCodes?: Array + ) { + + const inviteData ={ + bucketId: resourceType === 'bucket' ? resource?.bucketId : undefined, + objectId: resourceType === 'object' ? resource.id : undefined, + expiresAt: expiresAt, + permCodes: permCodes + }; + + // if emails param exists + if(emails && emails.length > 0){ + // create COMS invites + const invites = await Promise.all( + emails.map(async e => { + const token = (await comsAxios().post(`${PATH}`, { + ...inviteData, + email: e, + })).data; + return { email: e, token: token}; + }) + ); + // send invite email notifications + await this.emailInvites(resourceType, resource, currentUser, invites); + return invites; + } + // else no emails provided so make an 'open' invite + else { + const token = (await comsAxios().post(`${PATH}`, inviteData)).data; + return [{ token: token }]; + } + }, + /** - * @function createInvite - * Post an Invite, exclusive or on bucketId and objectId - * @param {string} bucketId - * @param {string} objectId - * @param {string} email to be used for access - * @param {string} expiration timestamp for token - * @returns {string} uuid token of the invite - * @returns {array} permCodes token of the invite + * @function emailInvites + * Semd email to each invitee containing a link to the resource + * each email needs a unique invite link url so use CHES merge feature + * ref: https://ches.api.gov.bc.ca/api/v1/docs#tag/EmailMerge/operation/postMerge * + * @param {string} resourceType eg bucket or object + * @param {COMSObject | Bucket } resource COMS object or bucket + * @param {User | null} currentUser current user creating the invite + * @param {Array} invites array of email adddresses + * @returns {Promise} CHES TransactionId */ - createInvite(bucketId?: string, email?: string, expiresAt?: number, objectId?: string, permCodes?: Array) { - return comsAxios().post(`${PATH}`, { - bucketId: bucketId || undefined, - email: email || undefined, - expiresAt: expiresAt || undefined, - objectId: objectId || undefined, - permCodes: permCodes || undefined - }); + emailInvites(resourceType: string, resource: any, currentUser: any, invites: any){ + try { + // build template + let resourceName, subject, body; + const currentUserEmail = `${currentUser.email}`; + // alternate templates depending if resource is a file or a folder + if (resourceType === 'object') { + resourceName = resource.name; + subject = `You have been invited to access ${resourceName} on BCBox`; + body = `

+

${currentUserEmail} invited you to access a file on BCBox

+

Here's a link to access the file that ${currentUserEmail} shared with you:

`; + } + else if (resourceType === 'bucket') { + resourceName = resource.bucketName; + subject = `You have been invited to access ${resourceName} on BCBox`; + body = `

+

${currentUserEmail} invited you to access a folder on BCBox

\n +

Here's a link to access the folder that ${currentUserEmail} shared with you:

`; + } + body = body + ` + + ${resourceName } + +

+ + This invite will only work for you and people with existing access. If you do not recognize the sender, do not click on the link above. Only open links that you are expecting from a known sender. +

+ Learn more about BCBox + `; + + // define email data matching the structure required by CHES api + const emailData:any = { + contexts: invites.map((invite: any) => { + return { + to: [ invite.email ], + context: { + token: invite.token + } + }; + }), + subject: subject, + body: body + }; + return appAxios().post('email', emailData); + } catch(err) { + console.error(`Failed to send Invite notification: ${err}`); + + } }, /**