Refactor schemas, enhance data validation

Implemented improvements in the data validation methods used in Stripe and Team Account features by refining the schemas. Adapted string types to UUID for accountId attributes in various services and ensured that Stripe keys follow appropriate prefixes. Autocomplete was turned off for destructive actions for enhanced security.
This commit is contained in:
giancarlo
2024-03-29 18:21:12 +08:00
parent 59c08c59d7
commit 65e970424f
13 changed files with 44 additions and 25 deletions

View File

@@ -25,7 +25,7 @@ import {
import { If } from '@kit/ui/if'; import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans'; import { Trans } from '@kit/ui/trans';
import { UpdateMemberRoleSchema } from '../../schema/update-member-role-schema'; import { UpdateMemberRoleSchema } from '../../schema/update-member-role.schema';
import { updateInvitationAction } from '../../server/actions/team-invitations-server-actions'; import { updateInvitationAction } from '../../server/actions/team-invitations-server-actions';
import { MembershipRoleSelector } from '../members/membership-role-selector'; import { MembershipRoleSelector } from '../members/membership-role-selector';
import { RolesDataProvider } from '../members/roles-data-provider'; import { RolesDataProvider } from '../members/roles-data-provider';

View File

@@ -25,7 +25,7 @@ import {
import { If } from '@kit/ui/if'; import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans'; import { Trans } from '@kit/ui/trans';
import { RoleSchema } from '../../schema/update-member-role-schema'; import { RoleSchema } from '../../schema/update-member-role.schema';
import { updateMemberRoleAction } from '../../server/actions/team-members-server-actions'; import { updateMemberRoleAction } from '../../server/actions/team-members-server-actions';
import { MembershipRoleSelector } from './membership-role-selector'; import { MembershipRoleSelector } from './membership-role-selector';
import { RolesDataProvider } from './roles-data-provider'; import { RolesDataProvider } from './roles-data-provider';

View File

@@ -4,7 +4,6 @@ import { useTransition } from 'react';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { Button } from '@kit/ui/button'; import { Button } from '@kit/ui/button';
import { import {
@@ -17,12 +16,9 @@ import {
import { Input } from '@kit/ui/input'; import { Input } from '@kit/ui/input';
import { Trans } from '@kit/ui/trans'; import { Trans } from '@kit/ui/trans';
import { TeamNameFormSchema } from '../../schema/update-team-name.schema';
import { updateTeamAccountName } from '../../server/actions/team-details-server-actions'; import { updateTeamAccountName } from '../../server/actions/team-details-server-actions';
const Schema = z.object({
name: z.string().min(1).max(255),
});
export const UpdateTeamAccountNameForm = (props: { export const UpdateTeamAccountNameForm = (props: {
account: { account: {
name: string; name: string;
@@ -34,7 +30,7 @@ export const UpdateTeamAccountNameForm = (props: {
const [pending, startTransition] = useTransition(); const [pending, startTransition] = useTransition();
const form = useForm({ const form = useForm({
resolver: zodResolver(Schema), resolver: zodResolver(TeamNameFormSchema),
defaultValues: { defaultValues: {
name: props.account.name, name: props.account.name,
}, },

View File

@@ -0,0 +1,5 @@
import { z } from 'zod';
export const RenewInvitationSchema = z.object({
invitationId: z.number().positive(),
});

View File

@@ -0,0 +1,12 @@
import { z } from 'zod';
export const TeamNameFormSchema = z.object({
name: z.string().min(1).max(255),
});
export const UpdateTeamNameSchema = TeamNameFormSchema.merge(
z.object({
slug: z.string().min(1).max(255),
path: z.string().min(1).max(255),
}),
);

View File

@@ -12,8 +12,8 @@ import { LeaveTeamAccountService } from '../services/leave-team-account.service'
export async function leaveTeamAccountAction(formData: FormData) { export async function leaveTeamAccountAction(formData: FormData) {
const body = Object.fromEntries(formData.entries()); const body = Object.fromEntries(formData.entries());
const params = LeaveTeamAccountSchema.parse(body); const params = LeaveTeamAccountSchema.parse(body);
const client = getSupabaseServerActionClient();
const client = getSupabaseServerActionClient();
const auth = await requireUser(client); const auth = await requireUser(client);
if (auth.error) { if (auth.error) {

View File

@@ -2,23 +2,26 @@
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { z } from 'zod';
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
export async function updateTeamAccountName(params: { import { UpdateTeamNameSchema } from '../../schema/update-team-name.schema';
name: string;
slug: string; export async function updateTeamAccountName(
path: string; params: z.infer<typeof UpdateTeamNameSchema>,
}) { ) {
const client = getSupabaseServerComponentClient(); const client = getSupabaseServerComponentClient();
const { name, slug, path } = UpdateTeamNameSchema.parse(params);
const { error, data } = await client const { error, data } = await client
.from('accounts') .from('accounts')
.update({ .update({
name: params.name, name,
slug: params.slug, slug,
}) })
.match({ .match({
slug: params.slug, slug,
}) })
.select('slug') .select('slug')
.single(); .single();
@@ -30,9 +33,9 @@ export async function updateTeamAccountName(params: {
const newSlug = data.slug; const newSlug = data.slug;
if (newSlug) { if (newSlug) {
const path = params.path.replace('[account]', newSlug); const nextPath = path.replace('[account]', newSlug);
redirect(path); redirect(nextPath);
} }
return { success: true }; return { success: true };

View File

@@ -14,7 +14,8 @@ import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-clie
import { AcceptInvitationSchema } from '../../schema/accept-invitation.schema'; import { AcceptInvitationSchema } from '../../schema/accept-invitation.schema';
import { DeleteInvitationSchema } from '../../schema/delete-invitation.schema'; import { DeleteInvitationSchema } from '../../schema/delete-invitation.schema';
import { InviteMembersSchema } from '../../schema/invite-members.schema'; import { InviteMembersSchema } from '../../schema/invite-members.schema';
import { UpdateInvitationSchema } from '../../schema/update-invitation-schema'; import { RenewInvitationSchema } from '../../schema/renew-invitation.schema';
import { UpdateInvitationSchema } from '../../schema/update-invitation.schema';
import { AccountInvitationsService } from '../services/account-invitations.service'; import { AccountInvitationsService } from '../services/account-invitations.service';
/** /**
@@ -107,9 +108,11 @@ export async function acceptInvitationAction(data: FormData) {
return redirect(nextPath); return redirect(nextPath);
} }
export async function renewInvitationAction(params: { invitationId: number }) { export async function renewInvitationAction(
params: z.infer<typeof RenewInvitationSchema>,
) {
const client = getSupabaseServerActionClient(); const client = getSupabaseServerActionClient();
const { invitationId } = params; const { invitationId } = RenewInvitationSchema.parse(params);
await assertSession(client); await assertSession(client);

View File

@@ -11,7 +11,7 @@ import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-clie
import { RemoveMemberSchema } from '../../schema/remove-member.schema'; import { RemoveMemberSchema } from '../../schema/remove-member.schema';
import { TransferOwnershipConfirmationSchema } from '../../schema/transfer-ownership-confirmation.schema'; import { TransferOwnershipConfirmationSchema } from '../../schema/transfer-ownership-confirmation.schema';
import { UpdateMemberRoleSchema } from '../../schema/update-member-role-schema'; import { UpdateMemberRoleSchema } from '../../schema/update-member-role.schema';
import { AccountMembersService } from '../services/account-members.service'; import { AccountMembersService } from '../services/account-members.service';
export async function removeMemberFromAccountAction( export async function removeMemberFromAccountAction(

View File

@@ -11,7 +11,7 @@ import { requireUser } from '@kit/supabase/require-user';
import { DeleteInvitationSchema } from '../../schema/delete-invitation.schema'; import { DeleteInvitationSchema } from '../../schema/delete-invitation.schema';
import { InviteMembersSchema } from '../../schema/invite-members.schema'; import { InviteMembersSchema } from '../../schema/invite-members.schema';
import { UpdateInvitationSchema } from '../../schema/update-invitation-schema'; import { UpdateInvitationSchema } from '../../schema/update-invitation.schema';
const invitePath = process.env.INVITATION_PAGE_PATH; const invitePath = process.env.INVITATION_PAGE_PATH;
const siteURL = process.env.NEXT_PUBLIC_SITE_URL; const siteURL = process.env.NEXT_PUBLIC_SITE_URL;

View File

@@ -8,7 +8,7 @@ import { Database } from '@kit/supabase/database';
import { RemoveMemberSchema } from '../../schema/remove-member.schema'; import { RemoveMemberSchema } from '../../schema/remove-member.schema';
import { TransferOwnershipConfirmationSchema } from '../../schema/transfer-ownership-confirmation.schema'; import { TransferOwnershipConfirmationSchema } from '../../schema/transfer-ownership-confirmation.schema';
import { UpdateMemberRoleSchema } from '../../schema/update-member-role-schema'; import { UpdateMemberRoleSchema } from '../../schema/update-member-role.schema';
export class AccountMembersService { export class AccountMembersService {
private readonly namespace = 'account-members'; private readonly namespace = 'account-members';