208 lines
5.6 KiB
TypeScript
208 lines
5.6 KiB
TypeScript
'use server';
|
|
|
|
import { revalidatePath } from 'next/cache';
|
|
import { redirect } from 'next/navigation';
|
|
|
|
import { z } from 'zod';
|
|
|
|
import { enhanceAction } from '@kit/next/actions';
|
|
import { createNotificationsApi } from '@kit/notifications/api';
|
|
import { getLogger } from '@kit/shared/logger';
|
|
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
|
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
|
|
|
import { AcceptInvitationSchema } from '../../schema/accept-invitation.schema';
|
|
import { DeleteInvitationSchema } from '../../schema/delete-invitation.schema';
|
|
import { InviteMembersSchema } from '../../schema/invite-members.schema';
|
|
import { RenewInvitationSchema } from '../../schema/renew-invitation.schema';
|
|
import { UpdateInvitationSchema } from '../../schema/update-invitation.schema';
|
|
import { createAccountInvitationsService } from '../services/account-invitations.service';
|
|
import { createAccountPerSeatBillingService } from '../services/account-per-seat-billing.service';
|
|
|
|
/**
|
|
* @name createInvitationsAction
|
|
* @description Creates invitations for inviting members.
|
|
*/
|
|
export const createInvitationsAction = enhanceAction(
|
|
async (params) => {
|
|
const logger = await getLogger();
|
|
const client = getSupabaseServerClient();
|
|
const serviceClient = getSupabaseServerAdminClient();
|
|
|
|
const service = createAccountInvitationsService(client);
|
|
const api = createNotificationsApi(serviceClient);
|
|
|
|
//TODO: This does not send email. It only creates invitations in the database.
|
|
await service.sendInvitations(params);
|
|
|
|
const { invitations: invitationParams, accountSlug } = params;
|
|
const personalCodes = invitationParams.map(
|
|
({ personal_code }) => personal_code,
|
|
);
|
|
|
|
const { data: company, error: companyError } = await client
|
|
.from('accounts')
|
|
.select('id')
|
|
.eq('slug', accountSlug);
|
|
|
|
logger.debug({ company, companyError, personalCodes });
|
|
|
|
if (companyError || !company?.length || !company[0]) {
|
|
throw new Error(
|
|
`Failed to fetch company id: ${companyError?.message || 'not found'}`,
|
|
);
|
|
}
|
|
|
|
const { data: invitations, error: invitationError } =
|
|
await serviceClient.rpc('get_invitations_with_account_ids', {
|
|
company_id: company[0].id,
|
|
personal_codes: personalCodes,
|
|
});
|
|
|
|
logger.debug({ invitations, invitationError });
|
|
|
|
if (invitationError) {
|
|
throw new Error(
|
|
`Failed to fetch invitations with accounts: ${invitationError.message}`,
|
|
);
|
|
}
|
|
|
|
const notificationPromises = invitations.map(
|
|
({ invite_token, account_id }) =>
|
|
api.createNotification({
|
|
account_id: account_id!,
|
|
body: `You are invited to join the company: ${accountSlug}`,
|
|
link: `/join?invite_token=${invite_token}`,
|
|
}),
|
|
);
|
|
|
|
await Promise.all(notificationPromises);
|
|
|
|
logger.info('All invitation notifications are sent');
|
|
revalidateMemberPage();
|
|
|
|
return {
|
|
success: true,
|
|
};
|
|
},
|
|
{
|
|
schema: InviteMembersSchema.and(
|
|
z.object({
|
|
accountSlug: z.string().min(1),
|
|
}),
|
|
),
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @name deleteInvitationAction
|
|
* @description Deletes an invitation specified by the invitation ID.
|
|
*/
|
|
export const deleteInvitationAction = enhanceAction(
|
|
async (data) => {
|
|
const client = getSupabaseServerClient();
|
|
const service = createAccountInvitationsService(client);
|
|
|
|
// Delete the invitation
|
|
await service.deleteInvitation(data);
|
|
|
|
revalidateMemberPage();
|
|
|
|
return {
|
|
success: true,
|
|
};
|
|
},
|
|
{
|
|
schema: DeleteInvitationSchema,
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @name updateInvitationAction
|
|
* @description Updates an invitation.
|
|
*/
|
|
export const updateInvitationAction = enhanceAction(
|
|
async (invitation) => {
|
|
const client = getSupabaseServerClient();
|
|
const service = createAccountInvitationsService(client);
|
|
|
|
await service.updateInvitation(invitation);
|
|
|
|
revalidateMemberPage();
|
|
|
|
return {
|
|
success: true,
|
|
};
|
|
},
|
|
{
|
|
schema: UpdateInvitationSchema,
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @name acceptInvitationAction
|
|
* @description Accepts an invitation to join a team.
|
|
*/
|
|
export const acceptInvitationAction = enhanceAction(
|
|
async (data: FormData, user) => {
|
|
const client = getSupabaseServerClient();
|
|
|
|
const { inviteToken, nextPath } = AcceptInvitationSchema.parse(
|
|
Object.fromEntries(data),
|
|
);
|
|
|
|
// create the services
|
|
const perSeatBillingService = createAccountPerSeatBillingService(client);
|
|
const service = createAccountInvitationsService(client);
|
|
|
|
// use admin client to accept invitation
|
|
const adminClient = getSupabaseServerAdminClient();
|
|
|
|
// Accept the invitation
|
|
const accountId = await service.acceptInvitationToTeam(adminClient, {
|
|
inviteToken,
|
|
userId: user.id,
|
|
});
|
|
|
|
// If the account ID is not present, throw an error
|
|
if (!accountId) {
|
|
throw new Error('Failed to accept invitation');
|
|
}
|
|
|
|
// Increase the seats for the account
|
|
await perSeatBillingService.increaseSeats(accountId);
|
|
|
|
return redirect(nextPath);
|
|
},
|
|
{},
|
|
);
|
|
|
|
/**
|
|
* @name renewInvitationAction
|
|
* @description Renews an invitation.
|
|
*/
|
|
export const renewInvitationAction = enhanceAction(
|
|
async (params) => {
|
|
const client = getSupabaseServerClient();
|
|
const { invitationId } = RenewInvitationSchema.parse(params);
|
|
|
|
const service = createAccountInvitationsService(client);
|
|
|
|
// Renew the invitation
|
|
await service.renewInvitation(invitationId);
|
|
|
|
revalidateMemberPage();
|
|
|
|
return {
|
|
success: true,
|
|
};
|
|
},
|
|
{
|
|
schema: RenewInvitationSchema,
|
|
},
|
|
);
|
|
|
|
function revalidateMemberPage() {
|
|
revalidatePath('/home/[account]/members', 'page');
|
|
}
|