@adminforth/open-signup 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,278 @@
1
+ <template>
2
+ <div class="relative flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800 relative w-screen h-screen"
3
+ :style="coreStore.config?.loginBackgroundImage && backgroundPosition === 'over' ? {
4
+ 'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
5
+ 'background-size': 'cover',
6
+ 'background-position': 'center',
7
+ 'background-blend-mode': 'darken'
8
+ }: {}"
9
+ >
10
+
11
+ <img v-if="coreStore.config?.loginBackgroundImage && backgroundPosition !== 'over'"
12
+ :src="loadFile(coreStore.config?.loginBackgroundImage)"
13
+ class="position-absolute top-0 left-0 h-screen object-cover w-0"
14
+ :class="{
15
+ '1/2': 'md:w-1/2',
16
+ '1/3': 'md:w-1/3',
17
+ '2/3': 'md:w-2/3',
18
+ '3/4': 'md:w-3/4',
19
+ '2/5': 'md:w-2/5',
20
+ '3/5': 'md:w-3/5',
21
+ }[backgroundPosition]"
22
+ />
23
+
24
+ <!-- Main modal -->
25
+ <div id="authentication-modal" tabindex="-1"
26
+ class="overflow-y-auto flex flex-grow
27
+ overflow-x-hidden z-50 min-w-[350px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
28
+ <div class="relative p-4 w-full max-h-full max-w-[400px]">
29
+ <!-- Modal content -->
30
+ <div class="relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black" >
31
+ <!-- Modal header -->
32
+ <div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
33
+ <h3 class="text-xl font-semibold text-gray-900 dark:text-white">
34
+ Sign up into {{ coreStore.config?.brandName }}
35
+ </h3>
36
+ </div>
37
+ <!-- Modal body -->
38
+ <div class="p-4 md:p-5">
39
+ <form v-if="!requestSent" class="space-y-4" role="alert" @submit.prevent>
40
+ <div class="relative">
41
+ <label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your email</label>
42
+ <input type="email" name="email" id="email"
43
+ tabindex="1"
44
+ @keydown.enter="passwordInput.focus()"
45
+ ref="emailInput"
46
+ class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
47
+ placeholder="name@company.com"
48
+ required
49
+ />
50
+ </div>
51
+ <div class="relative">
52
+ <label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">New password</label>
53
+ <input
54
+ tabindex="2"
55
+ ref="passwordInput"
56
+ :type="unmasked ? 'text' : 'password'"
57
+ name="password" id="password"
58
+ v-model="password"
59
+ @keydown.enter="passwordConfirmationInput.focus()"
60
+ class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
61
+ placeholder="New password" required
62
+ />
63
+
64
+ <button
65
+ type="button"
66
+ @click="unmasked = !unmasked"
67
+ class="h-6 absolute inset-y-2 top-9 right-1 flex items-center pr-2 z-index-100 focus:outline-none"
68
+ >
69
+ <IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked" />
70
+ <IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
71
+ </button>
72
+ </div>
73
+
74
+ <div class="relative">
75
+ <label for="password_confirmation" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Confirm new password</label>
76
+ <input
77
+ ref="passwordConfirmationInput"
78
+ :type="unmasked ? 'text' : 'password'"
79
+ name="password_confirmation" id="password_confirmation"
80
+ tabindex="3"
81
+ v-model="passwordConfirmation"
82
+ class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
83
+ placeholder="Confirm new password" required
84
+ />
85
+
86
+ <button
87
+ type="button"
88
+ @click="unmasked = !unmasked"
89
+ class="h-6 absolute inset-y-2 top-9 right-1 flex items-center pr-2 z-index-100 focus:outline-none"
90
+ >
91
+ <IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked" />
92
+ <IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
93
+ </button>
94
+ </div>
95
+
96
+ <div v-if="validationError || error" class="flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
97
+ <svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
98
+ <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
99
+ </svg>
100
+ <span class="sr-only">Info</span>
101
+ <div>
102
+ {{ validationError || error }}
103
+ </div>
104
+ </div>
105
+
106
+ <Button
107
+ :disabled="inProgress || (validationRunning && validationError)"
108
+ @click="doSignup"
109
+ type="submit"
110
+ class="w-full"
111
+ :loader="inProgress"
112
+ >
113
+ Sign up
114
+ </Button>
115
+ </form>
116
+ <!-- END of set new paasord -->
117
+ <div v-else class="flex items center justify-center p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-green-800 dark:text-green-400" role="alert">
118
+ If user with specified email exists in our db, then request was sent. Please check your email at {{ sentToEmail }} to reset your password.
119
+ </div>
120
+
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </div>
125
+
126
+ </div>
127
+ </template>
128
+
129
+
130
+ <script setup lang="ts">
131
+
132
+ import { onMounted, ref, computed, Ref } from 'vue';
133
+ import { useCoreStore } from '@/stores/core';
134
+ import { useUserStore } from '@/stores/user';
135
+ import { callAdminForthApi, loadFile, applyRegexValidation } from '@/utils';
136
+ import { useRoute, useRouter } from 'vue-router';
137
+ import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
138
+ import Button from '@/afcl/Button.vue';
139
+
140
+ const inProgress = ref(false);
141
+
142
+ const coreStore = useCoreStore();
143
+ const requestSent = ref(false);
144
+
145
+ const emailInput = ref(null);
146
+ const passwordInput = ref(null);
147
+ const passwordConfirmationInput = ref(null);
148
+
149
+ const password = ref("");
150
+ const passwordConfirmation = ref("");
151
+ const unmasked = ref(false);
152
+ const sentToEmail: Ref<string> = ref('');
153
+
154
+ const user = useUserStore();
155
+
156
+ const route = useRoute();
157
+ const router = useRouter();
158
+
159
+ function checkPassowrd() {
160
+
161
+ if (!password.value || !passwordConfirmation.value) {
162
+ return 'Please enter both password and password confirmation';
163
+ }
164
+ if (password.value !== passwordConfirmation.value) {
165
+ return 'Passwords do not match';
166
+ }
167
+
168
+ if (password.value.length < passwordField.value.minLength) {
169
+ return `Password must be at least ${passwordField.value.minLength} characters long`;
170
+ }
171
+
172
+ if (password.value.length > passwordField.value.maxLength) {
173
+ return `Password must be at most ${passwordField.value.maxLength} characters long`;
174
+ }
175
+
176
+ if (passwordField.value.validation) {
177
+ const valError = applyRegexValidation(password.value, passwordField.value.validation);
178
+ if (valError) {
179
+ return valError;
180
+ }
181
+ }
182
+
183
+ return null;
184
+ }
185
+
186
+ const validationRunning = ref(false);
187
+
188
+ const validationError = computed(() => {
189
+ if (validationRunning.value) {
190
+ return checkPassowrd();
191
+ }
192
+ return null;
193
+ });
194
+
195
+ const error: Ref<str | null> = ref(null);
196
+
197
+ onMounted(async () => {
198
+ if (route.query.token) {
199
+ // call method completeVerifiedSignup to confirm { token }
200
+ // if success, then redirect to login page
201
+
202
+ const resp = await callAdminForthApi({
203
+ path: `/plugin/${route.meta.pluginInstanceId}/complete-verified-signup`,
204
+ method: 'POST',
205
+ body: {
206
+ token: route.query.token,
207
+ }
208
+ });
209
+ if (resp.error) {
210
+ window.adminforth.alert({
211
+ message: `Error fetching data: ${error.message}`,
212
+ variant: 'danger',
213
+ });
214
+ } else if (resp.redirectTo) {
215
+ router.push(resp.redirectTo);
216
+ } else {
217
+ await user.finishLogin();
218
+ }
219
+ return;
220
+
221
+ }
222
+ emailInput.value.focus();
223
+ });
224
+
225
+
226
+ const backgroundPosition = computed(() => {
227
+ return coreStore.config?.loginBackgroundPosition || '1/2';
228
+ });
229
+
230
+ const passwordField = computed(
231
+ () => route.meta.passwordField
232
+ )
233
+
234
+ onMounted(async () => {
235
+ await coreStore.getPublicConfig();
236
+ });
237
+
238
+ async function doSignup() {
239
+ error.value = null;
240
+ const email = emailInput.value!.value;
241
+ if (!email) {
242
+ error.value = 'Please enter your email';
243
+ return;
244
+ }
245
+ if (checkPassowrd()) {
246
+ validationRunning.value = true;
247
+ return;
248
+ }
249
+
250
+ inProgress.value = true;
251
+ const resp = await callAdminForthApi({
252
+ path: `/plugin/${route.meta.pluginInstanceId}/signup`,
253
+ method: 'POST',
254
+ body: {
255
+ email,
256
+ url: window.location.origin + window.location.pathname,
257
+ password: password.value,
258
+ }
259
+ });
260
+ inProgress.value = false;
261
+ if (resp.error) {
262
+ error.value = resp.error;
263
+ } else {
264
+ error.value = null;
265
+ requestSent.value = true;
266
+ sentToEmail.value = email;
267
+ if (resp.redirectTo) {
268
+ router.push(resp.redirectTo);
269
+ } else {
270
+ error.value = null;
271
+ await user.finishLogin();
272
+ }
273
+ }
274
+ }
275
+
276
+
277
+
278
+ </script>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <p class="text-gray-500 dark:text-gray-400 font-sm text-left mt-3">
3
+ Have no account?
4
+ <Link to="/signup">
5
+ sign up now
6
+ </Link>
7
+ </p>
8
+ </template>
9
+
10
+ <script setup>
11
+ import Link from '@/afcl/Link.vue';
12
+
13
+ </script>
package/dist/index.js ADDED
@@ -0,0 +1,269 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import AdminForth, { AdminForthPlugin, Filters, suggestIfTypo, AdminForthDataTypes } from "adminforth";
11
+ import { SendEmailCommand, SESClient } from '@aws-sdk/client-ses';
12
+ export default class OpenSignupPlugin extends AdminForthPlugin {
13
+ constructor(options) {
14
+ super(options, import.meta.url);
15
+ this.options = options;
16
+ }
17
+ modifyResourceConfig(adminforth, resourceConfig) {
18
+ const _super = Object.create(null, {
19
+ modifyResourceConfig: { get: () => super.modifyResourceConfig }
20
+ });
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ _super.modifyResourceConfig.call(this, adminforth, resourceConfig);
23
+ if (!this.options.emailField) {
24
+ throw new Error(`emailField is required and should be a name of field in auth resource`);
25
+ }
26
+ if (this.options.confirmEmails) {
27
+ if (!this.options.confirmEmails.emailProvider) {
28
+ throw new Error(`confirmEmails.emailProvider is required and should be a name of field in auth resource`);
29
+ }
30
+ if (!this.options.confirmEmails.emailConfirmedField) {
31
+ throw new Error(`confirmEmails.emailConfirmedField is required and should be a name of field in auth resource`);
32
+ }
33
+ const emailConfirmedField = this.authResource.columns.find(f => f.name === this.options.confirmEmails.emailConfirmedField);
34
+ if (!emailConfirmedField) {
35
+ const similar = suggestIfTypo(this.authResource.columns.map(f => f.name), this.options.confirmEmails.emailConfirmedField);
36
+ throw new Error(`Field with name ${this.options.confirmEmails.emailConfirmedField} not found in resource ${this.authResource.resourceId}.
37
+ ${similar ? `Did you mean ${similar}?` : ''}
38
+ `);
39
+ }
40
+ this.emailConfirmedField = emailConfirmedField;
41
+ if (this.emailConfirmedField.type !== AdminForthDataTypes.BOOLEAN) {
42
+ throw new Error(`Field ${this.emailConfirmedField.name} must be of type boolean`);
43
+ }
44
+ }
45
+ // find field with name resourceConfig.emailField in adminforth.auth.usersResourceId and show error if it doesn't exist
46
+ const authResource = adminforth.config.resources.find(r => r.resourceId === adminforth.config.auth.usersResourceId);
47
+ if (!authResource) {
48
+ throw new Error(`Resource with id config.auth.usersResourceId=${adminforth.config.auth.usersResourceId} not found`);
49
+ }
50
+ this.authResource = authResource;
51
+ const emailField = authResource.columns.find(f => f.name === this.options.emailField);
52
+ if (!emailField) {
53
+ const similar = suggestIfTypo(authResource.columns.map(f => f.name), this.options.emailField);
54
+ throw new Error(`Field with name ${this.options.emailField} not found in resource ${authResource.resourceId}.
55
+ ${similar ? `Did you mean ${similar}?` : ''}
56
+ `);
57
+ }
58
+ this.emailField = emailField;
59
+ if (!this.options.passwordField) {
60
+ throw new Error(`passwordField is required to get password constraints and should be a name of virtual field in auth resource`);
61
+ }
62
+ const passwordField = authResource.columns.find(f => f.name === this.options.passwordField);
63
+ if (!passwordField) {
64
+ const similar = suggestIfTypo(authResource.columns.map(f => f.name), this.options.passwordField);
65
+ throw new Error(`Field with name ${this.options.passwordField} not found in resource ${authResource.resourceId}.
66
+ ${similar ? `Did you mean ${similar}?` : ''}
67
+ `);
68
+ }
69
+ this.passwordField = passwordField;
70
+ adminforth.config.customization.loginPageInjections.underInputs.push({
71
+ file: this.componentPath('SignupUnderLogin.vue'),
72
+ });
73
+ adminforth.config.customization.customPages.push({
74
+ path: '/signup',
75
+ component: {
76
+ file: this.componentPath('SignupPage.vue'),
77
+ meta: {
78
+ customLayout: true,
79
+ pluginInstanceId: this.pluginInstanceId,
80
+ passwordField: {
81
+ minLength: passwordField.minLength,
82
+ maxLength: passwordField.maxLength,
83
+ validation: passwordField.validation
84
+ }
85
+ }
86
+ }
87
+ });
88
+ // for confirmation disable login if email is not confirmed
89
+ if (this.options.confirmEmails) {
90
+ if (!adminforth.config.auth.beforeLoginConfirmation) {
91
+ adminforth.config.auth.beforeLoginConfirmation = [];
92
+ }
93
+ // unshift because if e.g. 2fa set it's hook, this one should be first
94
+ adminforth.config.auth.beforeLoginConfirmation.unshift((_a) => __awaiter(this, [_a], void 0, function* ({ adminUser }) {
95
+ if (!adminUser.dbUser[this.emailConfirmedField.name]) {
96
+ return { body: { allowedLogin: false }, error: 'You need to confirm your email to be able to login' };
97
+ }
98
+ }));
99
+ }
100
+ });
101
+ }
102
+ validateConfigAfterDiscover(adminforth, resourceConfig) {
103
+ // optional method where you can safely check field types after database discovery was performed
104
+ }
105
+ instanceUniqueRepresentation(pluginOptions) {
106
+ // optional method to return unique string representation of plugin instance.
107
+ // Needed if plugin can have multiple instances on one resource
108
+ return `single`;
109
+ }
110
+ doLogin(email, response) {
111
+ return __awaiter(this, void 0, void 0, function* () {
112
+ const username = email;
113
+ const userRecord = yield this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, email));
114
+ const adminUser = {
115
+ dbUser: userRecord,
116
+ pk: userRecord[this.authResource.columns.find((col) => col.primaryKey).name],
117
+ username,
118
+ };
119
+ const toReturn = { allowedLogin: true, error: '' };
120
+ yield this.adminforth.restApi.processLoginCallbacks(adminUser, toReturn, response);
121
+ if (toReturn.allowedLogin) {
122
+ this.adminforth.auth.setAuthCookie({
123
+ response,
124
+ username,
125
+ pk: userRecord[this.authResource.columns.find((col) => col.primaryKey).name]
126
+ });
127
+ }
128
+ return toReturn;
129
+ });
130
+ }
131
+ setupEndpoints(server) {
132
+ server.endpoint({
133
+ method: 'POST',
134
+ path: `/plugin/${this.pluginInstanceId}/completeVerifiedSignup`,
135
+ noAuth: true,
136
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response }) {
137
+ const { token } = body;
138
+ const { email } = yield this.adminforth.auth.verify(token, 'tempVerifyEmailToken');
139
+ if (!email) {
140
+ return { error: 'Invalid token', ok: false };
141
+ }
142
+ const userRecord = yield this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, email));
143
+ if (!userRecord) {
144
+ return { error: 'User not found', ok: false };
145
+ }
146
+ yield this.adminforth.resource(this.authResource.resourceId).update(userRecord[this.authResource.columns.find((col) => col.primaryKey).name], {
147
+ [this.options.confirmEmails.emailConfirmedField]: true
148
+ });
149
+ return yield this.doLogin(email, response);
150
+ })
151
+ });
152
+ server.endpoint({
153
+ method: 'POST',
154
+ path: `/plugin/${this.pluginInstanceId}/signup`,
155
+ noAuth: true,
156
+ handler: (_b) => __awaiter(this, [_b], void 0, function* ({ body, response }) {
157
+ const { email, url, password } = body;
158
+ // validate email
159
+ if (this.emailField.validation) {
160
+ for (const { regExp, message } of this.emailField.validation) {
161
+ if (!new RegExp(regExp).test(password)) {
162
+ return { error: message, ok: false };
163
+ }
164
+ }
165
+ }
166
+ // validate password
167
+ if (password.length < this.passwordField.minLength) {
168
+ return { error: `Password must be at least ${this.passwordField.minLength} characters long`, ok: false };
169
+ }
170
+ if (password.length > this.passwordField.maxLength) {
171
+ return { error: `Password must be at most ${this.passwordField.maxLength} characters long`, ok: false };
172
+ }
173
+ if (this.passwordField.validation) {
174
+ for (const { regExp, message } of this.passwordField.validation) {
175
+ if (!new RegExp(regExp).test(password)) {
176
+ return { error: message, ok: false };
177
+ }
178
+ }
179
+ }
180
+ // first check again if email already exists
181
+ const existing = yield this.adminforth.resource(this.authResource.resourceId).count(Filters.EQ(this.emailField.name, email));
182
+ if (existing > 0) {
183
+ return { error: 'Email already exists', ok: false };
184
+ }
185
+ // create user
186
+ const created = yield this.adminforth.resource(this.authResource.resourceId).create(Object.assign(Object.assign(Object.assign({}, (this.options.defaultFieldValues || {})), (this.options.confirmEmails ? { [this.options.confirmEmails.emailConfirmedField]: false } : {})), { [this.emailField.name]: email, [this.options.passwordHashField]: yield AdminForth.Utils.generatePasswordHash(password) }));
187
+ if (!this.options.confirmEmails) {
188
+ const resp = yield this.doLogin(email, response);
189
+ return resp;
190
+ }
191
+ // send confirmation email
192
+ const brandName = this.adminforth.config.customization.brandName;
193
+ const verifyToken = this.adminforth.auth.issueJWT({ email, issuer: brandName }, 'tempVerifyEmailToken', '2h');
194
+ // send email with AWS SES this.options.providerOptions.AWS_SES
195
+ const ses = new SESClient({
196
+ region: this.options.confirmEmails.providerOptions.AWS_SES.region,
197
+ credentials: {
198
+ accessKeyId: this.options.confirmEmails.providerOptions.AWS_SES.accessKeyId,
199
+ secretAccessKey: this.options.confirmEmails.providerOptions.AWS_SES.secretAccessKey
200
+ }
201
+ });
202
+ process.env.HEAVY_DEBUG && console.log('🐛Sending reset tok to', verifyToken);
203
+ const emailCommand = new SendEmailCommand({
204
+ Destination: {
205
+ ToAddresses: [email]
206
+ },
207
+ Message: {
208
+ Body: {
209
+ Text: {
210
+ Charset: "UTF-8",
211
+ Data: `
212
+ Dear user,
213
+ Welcome to ${brandName}!
214
+
215
+ To confirm your email, click the link below:\n\n
216
+
217
+ ${url}?verifyToken=${verifyToken}\n\n
218
+
219
+ If you didn't request this, please ignore this email.\n\n
220
+ Link is valid for 2 hours.\n\n
221
+
222
+ Thanks,
223
+ The ${brandName} Team
224
+
225
+ `
226
+ },
227
+ Html: {
228
+ Charset: "UTF-8",
229
+ Data: `
230
+ <html>
231
+ <head></head>
232
+ <body>
233
+ <p>Dear user,</p>
234
+ <p>Welcome to ${brandName}!</p>
235
+ <p>To confirm your email, click the link below:</p>
236
+ <a href="${url}?token=${verifyToken}">Confirm email</a>
237
+ <p>If you didn't request this, please ignore this email.</p>
238
+ <p>Link is valid for 2 hours.</p>
239
+ <p>Thanks,</p>
240
+ <p>The ${brandName} Team</p>
241
+ </body>
242
+ </html>
243
+
244
+
245
+ `
246
+ }
247
+ },
248
+ Subject: {
249
+ Charset: "UTF-8",
250
+ Data: `Signup request at ${brandName}`
251
+ }
252
+ },
253
+ Source: `${this.options.confirmEmails.sendFrom}`
254
+ });
255
+ try {
256
+ const sRes = yield ses.send(emailCommand);
257
+ }
258
+ catch (e) {
259
+ console.error('Error sending email', e);
260
+ if (process.env.NODE_ENV === 'development') {
261
+ return { error: 'Some thing went wrong, please check the console', ok: false };
262
+ }
263
+ return { error: 'Something went wrong, please contact support', ok: false };
264
+ }
265
+ return { ok: true };
266
+ })
267
+ });
268
+ }
269
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};