2.18.0: New Invitation flow, refactored Database Webhooks, new ShadCN UI Components (#384)
* Streamlined invitations flow * Removed web hooks in favor of handling logic directly in server actions * Added new Shadcn UI Components
This commit is contained in:
committed by
GitHub
parent
195cf41680
commit
2e20d3e76f
@@ -85,8 +85,10 @@ export const deletePersonalAccountAction = enhanceAction(
|
||||
// delete the user's account and cancel all subscriptions
|
||||
await service.deletePersonalAccount({
|
||||
adminClient: getSupabaseServerAdminClient(),
|
||||
userId: user.id,
|
||||
userEmail: user.email ?? null,
|
||||
account: {
|
||||
id: user.id,
|
||||
email: user.email ?? null,
|
||||
},
|
||||
});
|
||||
|
||||
// sign out the user after deleting their account
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'server-only';
|
||||
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { getLogger } from '@kit/shared/logger';
|
||||
import { Database } from '@kit/supabase/database';
|
||||
|
||||
@@ -30,13 +32,14 @@ class DeletePersonalAccountService {
|
||||
*/
|
||||
async deletePersonalAccount(params: {
|
||||
adminClient: SupabaseClient<Database>;
|
||||
|
||||
userId: string;
|
||||
userEmail: string | null;
|
||||
account: {
|
||||
id: string;
|
||||
email: string | null;
|
||||
};
|
||||
}) {
|
||||
const logger = await getLogger();
|
||||
|
||||
const userId = params.userId;
|
||||
const userId = params.account.id;
|
||||
const ctx = { userId, name: this.namespace };
|
||||
|
||||
logger.info(
|
||||
@@ -54,6 +57,14 @@ class DeletePersonalAccountService {
|
||||
|
||||
logger.info(ctx, 'User successfully deleted!');
|
||||
|
||||
if (params.account.email) {
|
||||
// dispatch the delete account email. Errors are handled in the method.
|
||||
await this.dispatchDeleteAccountEmail({
|
||||
email: params.account.email,
|
||||
id: params.account.id,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
@@ -69,4 +80,71 @@ class DeletePersonalAccountService {
|
||||
throw new Error('Error deleting user');
|
||||
}
|
||||
}
|
||||
|
||||
private async dispatchDeleteAccountEmail(account: {
|
||||
email: string;
|
||||
id: string;
|
||||
}) {
|
||||
const logger = await getLogger();
|
||||
const ctx = { name: this.namespace, userId: account.id };
|
||||
|
||||
try {
|
||||
logger.info(ctx, 'Sending delete account email...');
|
||||
|
||||
await this.sendDeleteAccountEmail(account);
|
||||
|
||||
logger.info(ctx, 'Delete account email sent successfully');
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
{
|
||||
...ctx,
|
||||
error,
|
||||
},
|
||||
'Failed to send delete account email',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendDeleteAccountEmail(account: { email: string }) {
|
||||
const emailSettings = this.getEmailSettings();
|
||||
|
||||
const { renderAccountDeleteEmail } = await import('@kit/email-templates');
|
||||
const { getMailer } = await import('@kit/mailers');
|
||||
|
||||
const mailer = await getMailer();
|
||||
|
||||
const { html, subject } = await renderAccountDeleteEmail({
|
||||
productName: emailSettings.productName,
|
||||
});
|
||||
|
||||
await mailer.sendEmail({
|
||||
from: emailSettings.fromEmail,
|
||||
html,
|
||||
subject,
|
||||
to: account.email,
|
||||
});
|
||||
}
|
||||
|
||||
private getEmailSettings() {
|
||||
const productName = process.env.NEXT_PUBLIC_PRODUCT_NAME;
|
||||
const fromEmail = process.env.EMAIL_SENDER;
|
||||
|
||||
return z
|
||||
.object({
|
||||
productName: z
|
||||
.string({
|
||||
required_error: 'NEXT_PUBLIC_PRODUCT_NAME is required',
|
||||
})
|
||||
.min(1),
|
||||
fromEmail: z
|
||||
.string({
|
||||
required_error: 'EMAIL_SENDER is required',
|
||||
})
|
||||
.min(1),
|
||||
})
|
||||
.parse({
|
||||
productName,
|
||||
fromEmail,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user