Skip to content

Commit

Permalink
Merge pull request #882 from ajeeshRS/feature/add-payout-method-section
Browse files Browse the repository at this point in the history
feat: added payout methods section
  • Loading branch information
siinghd authored Aug 14, 2024
2 parents 2e590da + 06d1b01 commit 2a5a2aa
Show file tree
Hide file tree
Showing 8 changed files with 607 additions and 0 deletions.
29 changes: 29 additions & 0 deletions prisma/migrations/20240811175241_add_payout_methods/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- CreateTable
CREATE TABLE "UpiId" (
"id" SERIAL NOT NULL,
"value" VARCHAR(256) NOT NULL,
"userId" TEXT NOT NULL,

CONSTRAINT "UpiId_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "SolanaAddress" (
"id" SERIAL NOT NULL,
"value" CHAR(44) NOT NULL,
"userId" TEXT NOT NULL,

CONSTRAINT "SolanaAddress_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "UpiId_userId_value_key" ON "UpiId"("userId", "value");

-- CreateIndex
CREATE UNIQUE INDEX "SolanaAddress_userId_value_key" ON "SolanaAddress"("userId", "value");

-- AddForeignKey
ALTER TABLE "UpiId" ADD CONSTRAINT "UpiId_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "SolanaAddress" ADD CONSTRAINT "SolanaAddress_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
20 changes: 20 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,26 @@ model User {
questions Question[]
answers Answer[]
certificate Certificate[]
upiIds UpiId[] @relation("UserUpiIds")
solanaAddresses SolanaAddress[] @relation("UserSolanaAddresses")
}

model UpiId {
id Int @id @default(autoincrement())
value String @db.VarChar(256)
userId String
user User @relation("UserUpiIds", fields: [userId], references: [id])
@@unique([userId, value])
}

model SolanaAddress {
id Int @id @default(autoincrement())
value String @db.Char(44)
userId String
user User @relation("UserSolanaAddresses", fields: [userId], references: [id])
@@unique([userId, value])
}

model DiscordConnect {
Expand Down
237 changes: 237 additions & 0 deletions src/actions/payoutMethods/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
'use server';

import db from '@/db';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import {
payoutMethodDeleteSchema,
solanaAddressInsertSchema,
upiIdInsertSchema,
} from './schema';
import {
DeleteTypePayoutMethod,
InputTypeCreateSolana,
InputTypeCreateUpi,
ReturnTypeCreateSolana,
ReturnTypeCreateUpi,
ReturnTypePayoutMethodDelete,
} from './types';
import { createSafeAction } from '@/lib/create-safe-action';

const addUpiHandler = async (
data: InputTypeCreateUpi,
): Promise<ReturnTypeCreateUpi> => {
console.log(data);
const session = await getServerSession(authOptions);

if (!session || !session.user.id) {
throw new Error('Unauthorized');
}

const parsed = upiIdInsertSchema.safeParse(data);

if (!parsed.success) {
throw new Error('Invalid UPI ID');
}
try {
const existingIds = await db.upiId.findMany({
where: {
userId: session.user.id,
},
});

if (existingIds.length === 2) {
return { error: 'You can only add 2 UPI ids' };
}

const isIdExist = await db.upiId.findUnique({
where: {
userId_value: {
userId: session.user.id,
value: parsed.data.upiId,
},
},
});

if (isIdExist) {
return { error: 'UPI id already exists' };
}

const upiId = await db.upiId.create({
data: {
value: parsed.data.upiId,
userId: session.user.id,
},
});

return { data: upiId };
} catch (error) {
console.error(error);
return { error: 'Failed to add UPI id' };
}
};

const addSolanaHandler = async (
data: InputTypeCreateSolana,
): Promise<ReturnTypeCreateSolana> => {
const session = await getServerSession(authOptions);

if (!session || !session.user.id) {
throw new Error('Unauthorized');
}

const parsed = solanaAddressInsertSchema.safeParse(data);

if (!parsed.success) {
throw new Error('Invalid Solana Address');
}

try {
const existingAddresses = await db.solanaAddress.findMany({
where: {
userId: session.user.id,
},
});

if (existingAddresses.length === 2) {
return { error: 'You can only add 2 Solana addresses' };
}

const isIdExist = await db.solanaAddress.findUnique({
where: {
userId_value: {
userId: session.user.id,
value: parsed.data.solanaAddress,
},
},
});

if (isIdExist) {
return { error: 'Solana address already exists' };
}

const solanaAddress = await db.solanaAddress.create({
data: {
value: parsed.data.solanaAddress,
userId: session.user.id,
},
});

return { data: solanaAddress };
} catch (error) {
console.error(error);
return { error: 'Failed to add Solana Address' };
}
};

export const getPayoutMethods = async () => {
const session = await getServerSession(authOptions);

if (!session || !session.user.id) {
throw new Error('Unauthorized');
}
try {
const upiIds = await db.upiId.findMany({
where: {
userId: session.user.id,
},
});
const solanaAddresses = await db.solanaAddress.findMany({
where: {
userId: session.user.id,
},
});

if (!upiIds || !solanaAddresses) {
return { error: 'Failed to fetch payout methods' };
}

return { upiIds, solanaAddresses };
} catch (error) {
console.error(error);
return { error: 'Failed to fetch payout methods' };
}
};
const deleteUpiHandler = async (
data: DeleteTypePayoutMethod,
): Promise<ReturnTypePayoutMethodDelete> => {
const session = await getServerSession(authOptions);

if (!session || !session.user.id) {
throw new Error('Unauthorized');
}

const parsed = payoutMethodDeleteSchema.safeParse(data);

if (!parsed.success) {
throw new Error('Invalid Id');
}

const result = await db.upiId.deleteMany({
where: {
id: data.id,
userId: session.user.id,
},
});

if (result.count === 0) {
return { error: 'Failed to delete UPI id' };
}

return {
data: {
message: 'UPI address successfully deleted',
},
};
};

const deleteSolanaHandler = async (
data: DeleteTypePayoutMethod,
): Promise<ReturnTypePayoutMethodDelete> => {
const session = await getServerSession(authOptions);

if (!session || !session.user.id) {
throw new Error('Unauthorized');
}

const parsed = payoutMethodDeleteSchema.safeParse(data);

if (!parsed.success) {
throw new Error('Invalid Address');
}

const result = await db.solanaAddress.deleteMany({
where: {
id: data.id,
userId: session.user.id,
},
});

if (result.count === 0) {
return { error: 'Failed to delete Solana Address' };
}

return {
data: {
message: 'Solana address successfully deleted',
},
};
};



export const addUpi = createSafeAction(upiIdInsertSchema, addUpiHandler);

export const addSolanaAddress = createSafeAction(
solanaAddressInsertSchema,
addSolanaHandler,
);

export const deleteUpiId = createSafeAction(
payoutMethodDeleteSchema,
deleteUpiHandler,
);
export const deleteSolanaAddress = createSafeAction(
payoutMethodDeleteSchema,
deleteSolanaHandler,
);
34 changes: 34 additions & 0 deletions src/actions/payoutMethods/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { z } from 'zod';

export const payoutMethodSchema = z.object({
upiId: z
.string()
.refine((value) => /^[0-9A-Za-z._-]{2,256}@[A-Za-z]{2,64}$/.test(value), {
message: 'Enter a valid UPI address',
})
.optional(),
solanaAddress: z
.string()
.refine((value) => /^[A-Za-z0-9]{44}$/.test(value), {
message: 'Enter a valid Solana address',
})
.optional(),
});

export const upiIdInsertSchema = z.object({
upiId: z
.string()
.refine((value) => /^[0-9A_Za-z._-]{2,256}@[A_Za-z]{2,64}$/.test(value), {
message: 'Invalid UPI address',
}),
});

export const solanaAddressInsertSchema = z.object({
solanaAddress: z.string().refine((value) => /^[A-Za-z0-9]{44}$/.test(value), {
message: 'Invalid Solana address',
}),
});

export const payoutMethodDeleteSchema = z.object({
id: z.number(),
});
14 changes: 14 additions & 0 deletions src/actions/payoutMethods/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from 'zod';
import { payoutMethodDeleteSchema, solanaAddressInsertSchema, upiIdInsertSchema } from './schema';
import { ActionState } from '@/lib/create-safe-action';
import { SolanaAddress, UpiId } from '@prisma/client';
import { Delete } from '@/lib/utils';

export type InputTypeCreateUpi = z.infer<typeof upiIdInsertSchema>;
export type ReturnTypeCreateUpi = ActionState<InputTypeCreateUpi, UpiId>;

export type InputTypeCreateSolana = z.infer<typeof solanaAddressInsertSchema>;
export type ReturnTypeCreateSolana = ActionState<InputTypeCreateSolana, SolanaAddress>;

export type DeleteTypePayoutMethod = z.infer<typeof payoutMethodDeleteSchema>;
export type ReturnTypePayoutMethodDelete = ActionState<DeleteTypePayoutMethod, Delete>;
Loading

0 comments on commit 2a5a2aa

Please sign in to comment.