Refactor join service and fix imports
Join service was deleted and its functionality was integrated into the team-accounts API. General rearrangement and renaming of server-related files were made, particularly for billing-related services to ensure consistency. This made it necessary to fix import paths across multiple files.
This commit is contained in:
@@ -20,7 +20,7 @@ import { Trans } from '@kit/ui/trans';
|
|||||||
|
|
||||||
import billingConfig from '~/config/billing.config';
|
import billingConfig from '~/config/billing.config';
|
||||||
|
|
||||||
import { createPersonalAccountCheckoutSession } from '../server-actions';
|
import { createPersonalAccountCheckoutSession } from '../_lib/server/server-actions';
|
||||||
|
|
||||||
const EmbeddedCheckout = dynamic(
|
const EmbeddedCheckout = dynamic(
|
||||||
async () => {
|
async () => {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { redirect } from 'next/navigation';
|
|||||||
import { enhanceAction } from '@kit/next/actions';
|
import { enhanceAction } from '@kit/next/actions';
|
||||||
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
|
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
|
||||||
|
|
||||||
import { PersonalAccountCheckoutSchema } from './_lib/schema/personal-account-checkout.schema';
|
import { PersonalAccountCheckoutSchema } from '../schema/personal-account-checkout.schema';
|
||||||
import { UserBillingService } from './_lib/server/user-billing.service';
|
import { UserBillingService } from './user-billing.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name createPersonalAccountCheckoutSession
|
* @name createPersonalAccountCheckoutSession
|
||||||
@@ -16,7 +16,7 @@ import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
|||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
import { UserAccountHeader } from '../_components/user-account-header';
|
import { UserAccountHeader } from '../_components/user-account-header';
|
||||||
import { createPersonalAccountBillingPortalSession } from '../billing/server-actions';
|
import { createPersonalAccountBillingPortalSession } from '../billing/_lib/server/server-actions';
|
||||||
import { PersonalAccountCheckoutForm } from './_components/personal-account-checkout-form';
|
import { PersonalAccountCheckoutForm } from './_components/personal-account-checkout-form';
|
||||||
// user billing imports
|
// user billing imports
|
||||||
import { loadPersonalAccountBillingPageData } from './_lib/server/personal-account-billing-page.loader';
|
import { loadPersonalAccountBillingPageData } from './_lib/server/personal-account-billing-page.loader';
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { Trans } from '@kit/ui/trans';
|
|||||||
|
|
||||||
import billingConfig from '~/config/billing.config';
|
import billingConfig from '~/config/billing.config';
|
||||||
|
|
||||||
import { createTeamAccountCheckoutSession } from '../server-actions';
|
import { createTeamAccountCheckoutSession } from '../_lib/server/server-actions';
|
||||||
|
|
||||||
const EmbeddedCheckout = dynamic(
|
const EmbeddedCheckout = dynamic(
|
||||||
async () => {
|
async () => {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-clie
|
|||||||
import {
|
import {
|
||||||
TeamBillingPortalSchema,
|
TeamBillingPortalSchema,
|
||||||
TeamCheckoutSchema,
|
TeamCheckoutSchema,
|
||||||
} from '../_lib/schema/team-billing.schema';
|
} from '../schema/team-billing.schema';
|
||||||
import { TeamBillingService } from '../_lib/server/team-billing.service';
|
import { TeamBillingService } from './team-billing.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name createTeamAccountCheckoutSession
|
* @name createTeamAccountCheckoutSession
|
||||||
@@ -16,7 +16,7 @@ import billingConfig from '~/config/billing.config';
|
|||||||
import pathsConfig from '~/config/paths.config';
|
import pathsConfig from '~/config/paths.config';
|
||||||
import { Database } from '~/lib/database.types';
|
import { Database } from '~/lib/database.types';
|
||||||
|
|
||||||
import { TeamCheckoutSchema } from '../../_lib/schema/team-billing.schema';
|
import { TeamCheckoutSchema } from '../schema/team-billing.schema';
|
||||||
|
|
||||||
export class TeamBillingService {
|
export class TeamBillingService {
|
||||||
private readonly namespace = 'billing.team-account';
|
private readonly namespace = 'billing.team-account';
|
||||||
@@ -18,8 +18,8 @@ import { withI18n } from '~/lib/i18n/with-i18n';
|
|||||||
import { AccountLayoutHeader } from '../_components/account-layout-header';
|
import { AccountLayoutHeader } from '../_components/account-layout-header';
|
||||||
import { loadTeamAccountBillingPage } from '../_lib/server/team-account-billing-page.loader';
|
import { loadTeamAccountBillingPage } from '../_lib/server/team-account-billing-page.loader';
|
||||||
import { loadTeamWorkspace } from '../_lib/server/team-account-workspace.loader';
|
import { loadTeamWorkspace } from '../_lib/server/team-account-workspace.loader';
|
||||||
import { createBillingPortalSession } from '../billing/server-actions';
|
|
||||||
import { TeamAccountCheckoutForm } from './_components/team-account-checkout-form';
|
import { TeamAccountCheckoutForm } from './_components/team-account-checkout-form';
|
||||||
|
import { createBillingPortalSession } from './_lib/server/server-actions';
|
||||||
|
|
||||||
interface Params {
|
interface Params {
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
|
||||||
|
|
||||||
export class JoinTeamService {
|
|
||||||
async isCurrentUserAlreadyInAccount(accountId: string) {
|
|
||||||
const client = getSupabaseServerComponentClient();
|
|
||||||
|
|
||||||
const { data } = await client
|
|
||||||
.from('accounts')
|
|
||||||
.select('id')
|
|
||||||
.eq('id', accountId)
|
|
||||||
.maybeSingle();
|
|
||||||
|
|
||||||
return !!data?.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getInviteDataFromInviteToken(token: string) {
|
|
||||||
// we use an admin client to be able to read the pending membership
|
|
||||||
// without having to be logged in
|
|
||||||
const adminClient = getSupabaseServerComponentClient({ admin: true });
|
|
||||||
|
|
||||||
const { data: invitation, error } = await adminClient
|
|
||||||
.from('invitations')
|
|
||||||
.select<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
id: string;
|
|
||||||
account: {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
picture_url: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
>(
|
|
||||||
'id, expires_at, account: account_id !inner (id, name, slug, picture_url)',
|
|
||||||
)
|
|
||||||
.eq('invite_token', token)
|
|
||||||
.gte('expires_at', new Date().toISOString())
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!invitation ?? error) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return invitation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import { ArrowLeft } from 'lucide-react';
|
|||||||
|
|
||||||
import { requireUser } from '@kit/supabase/require-user';
|
import { requireUser } from '@kit/supabase/require-user';
|
||||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||||
|
import { createTeamAccountsApi } from '@kit/team-accounts/api';
|
||||||
import { AcceptInvitationContainer } from '@kit/team-accounts/components';
|
import { AcceptInvitationContainer } from '@kit/team-accounts/components';
|
||||||
import { Button } from '@kit/ui/button';
|
import { Button } from '@kit/ui/button';
|
||||||
import { Heading } from '@kit/ui/heading';
|
import { Heading } from '@kit/ui/heading';
|
||||||
@@ -14,8 +15,6 @@ import pathsConfig from '~/config/paths.config';
|
|||||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
import { JoinTeamService } from './_lib/server/join-team.service';
|
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
searchParams: {
|
searchParams: {
|
||||||
invite_token?: string;
|
invite_token?: string;
|
||||||
@@ -50,10 +49,12 @@ async function JoinTeamAccountPage({ searchParams }: Context) {
|
|||||||
permanentRedirect(path);
|
permanentRedirect(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
const service = new JoinTeamService();
|
// get api to interact with team accounts
|
||||||
|
const adminClient = getSupabaseServerComponentClient({ admin: true });
|
||||||
|
const api = createTeamAccountsApi(client);
|
||||||
|
|
||||||
// the user is logged in, we can now check if the token is valid
|
// the user is logged in, we can now check if the token is valid
|
||||||
const invitation = await service.getInviteDataFromInviteToken(token);
|
const invitation = await api.getInvitation(adminClient, token);
|
||||||
|
|
||||||
// the invitation is not found or expired
|
// the invitation is not found or expired
|
||||||
if (!invitation) {
|
if (!invitation) {
|
||||||
@@ -61,10 +62,12 @@ async function JoinTeamAccountPage({ searchParams }: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we need to verify the user isn't already in the account
|
// we need to verify the user isn't already in the account
|
||||||
const isSignedInUserPartOfAccount =
|
// we do so by checking if the user can read the account
|
||||||
await service.isCurrentUserAlreadyInAccount(invitation.account.id);
|
// if the user can read the account, then they are already in the account
|
||||||
|
const account = await api.getTeamAccountById(invitation.account.id);
|
||||||
|
|
||||||
if (isSignedInUserPartOfAccount) {
|
// if the user is already in the account redirect to the home page
|
||||||
|
if (account) {
|
||||||
const { getLogger } = await import('@kit/shared/logger');
|
const { getLogger } = await import('@kit/shared/logger');
|
||||||
const logger = await getLogger();
|
const logger = await getLogger();
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,25 @@ import { Database } from '@kit/supabase/database';
|
|||||||
export class TeamAccountsApi {
|
export class TeamAccountsApi {
|
||||||
constructor(private readonly client: SupabaseClient<Database>) {}
|
constructor(private readonly client: SupabaseClient<Database>) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name getTeamAccountById
|
||||||
|
* @description Check if the user is already in the account.
|
||||||
|
* @param accountId
|
||||||
|
*/
|
||||||
|
async getTeamAccountById(accountId: string) {
|
||||||
|
const { data, error } = await this.client
|
||||||
|
.from('accounts')
|
||||||
|
.select('*')
|
||||||
|
.eq('id', accountId)
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name getAccountWorkspace
|
* @name getAccountWorkspace
|
||||||
* @description Get the account workspace data.
|
* @description Get the account workspace data.
|
||||||
@@ -135,6 +154,40 @@ export class TeamAccountsApi {
|
|||||||
|
|
||||||
return data?.customer_id;
|
return data?.customer_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name getInvitation
|
||||||
|
* @description Get the invitation data from the invite token.
|
||||||
|
* @param adminClient - The admin client instance. Since the user is not yet part of the account, we need to use an admin client to read the pending membership
|
||||||
|
* @param token - The invitation token.
|
||||||
|
*/
|
||||||
|
async getInvitation(adminClient: SupabaseClient<Database>, token: string) {
|
||||||
|
const { data: invitation, error } = await adminClient
|
||||||
|
.from('invitations')
|
||||||
|
.select<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
id: string;
|
||||||
|
account: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
picture_url: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
'id, expires_at, account: account_id !inner (id, name, slug, picture_url)',
|
||||||
|
)
|
||||||
|
.eq('invite_token', token)
|
||||||
|
.gte('expires_at', new Date().toISOString())
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (!invitation ?? error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return invitation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTeamAccountsApi(client: SupabaseClient<Database>) {
|
export function createTeamAccountsApi(client: SupabaseClient<Database>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user